ListView in DialogFragment is disabled in Android 4.0

871 views Asked by At

I have made an custom DialogFragment for selecting an user from the backend. The users are loaded asynchronously so I can't use AlertDialog.Builder.setItems() because onCreateDialog is already called.

So I use a custom layout with a ListView and a ProgressBar. The ListView gets visible, The adapter is set and the onItemClickListener is set after the data is loaded (and the progressbar is hidden).

On another device with Android 5 there is no problem, but on my Transformer TF101 testdevice with Android 4.0.3 I can't press an item.

The strange thing is when I switch apps and return to my app the ListView is enabled and now I can select something.

XML of the layout:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true">

<ListView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/listview_gebruikers"
    android:layout_gravity="center_horizontal"
    />

<ProgressBar
    style="?android:attr/progressBarStyle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/progress_gebruikers"
    android:layout_gravity="center" />
</FrameLayout>

The DialogFragment:

public static final String TAG = OverdragenDialogFragment.class.getSimpleName();

private static final String ARG_OVEREENKOMST = "arg_overeenkomst";
private static final String ARG_ACCOUNT_NAME = "arg_account_name";

private static final String STATE_ACCOUNT_NAME = "state_account_name";
private static final String STATE_OVEREENKOMST = "state_overeenkomst";

private OverdragenCallbacks mListener;

private ListView mListView;
private ProgressBar mProgressBar;


private Overeenkomst mOvereenkomst;
private String mAccountName;

/**
 * A handler object, used for deferring UI operations.
 */
protected Handler mHandler = new Handler();


/**
 * Create a new ChangeStatusDialogFragment
 * @return
 */
public static OverdragenDialogFragment newInstance(String accountName, Overeenkomst overeenkomst) {
    Bundle arguments = new Bundle();
    arguments.putString(ARG_ACCOUNT_NAME, accountName);
    arguments.putSerializable(ARG_OVEREENKOMST, overeenkomst);


    OverdragenDialogFragment fragment = new OverdragenDialogFragment();
    fragment.setArguments(arguments);
    return fragment;
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); //getActivity().getLayoutInflater();
    View parent = inflater.inflate(R.layout.dialog_gebruikers, null);

    if(savedInstanceState != null){
        mOvereenkomst = (Overeenkomst) savedInstanceState.getSerializable(STATE_OVEREENKOMST);
        mAccountName = savedInstanceState.getString(STATE_ACCOUNT_NAME);
    }else{
        mOvereenkomst = (Overeenkomst) getArguments().getSerializable(ARG_OVEREENKOMST);
        mAccountName = getArguments().getString(ARG_ACCOUNT_NAME);
    }

    initViews(parent);

    final Loader<List<GebruikerWrapper>> loader = getLoaderManager().getLoader(LoaderIds.API_GEBRUIKERS);
    if(loader != null){
        if(!loader.isStarted()) {
            loader.reset();
            loader.startLoading();
        }
    }else {
        getLoaderManager().initLoader(LoaderIds.API_GEBRUIKERS, null, new LoaderManager.LoaderCallbacks<List<GebruikerWrapper>>() {
            @Override
            public Loader<List<GebruikerWrapper>> onCreateLoader(int id, Bundle args) {
                mProgressBar.setVisibility(View.VISIBLE);
                return new GetGebruikersLoader(getActivity(), mAccountName);
            }


            @Override
            public void onLoadFinished(Loader<List<GebruikerWrapper>> loader, List<GebruikerWrapper> data) {
                mProgressBar.setVisibility(View.GONE);
                if (data == null) {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getActivity(), "Kan de verkopers niet ophalen...", Toast.LENGTH_SHORT).show(); //TODO fatsoenlijke foutmelding
                            dismiss();
                        }
                    });
                }
                mListView.setAdapter(new GebruikerArrayAdapter(getActivity(), data));
                mListView.setOnItemClickListener(OverdragenDialogFragment.this);
                mListView.requestFocus();
            }

            @Override
            public void onLoaderReset(Loader<List<GebruikerWrapper>> loader) {

            }
        });
    }

    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle(R.string.title_overdragen)
            .setView(parent);
    return builder.create();
}

private void initViews(View parent) {
    mListView = findView(parent, R.id.listview_gebruikers);
    mProgressBar = findView(parent, R.id.progress_gebruikers);
}

@SuppressWarnings("unchecked")
public <T extends View> T findView(View parent, int id) {
    return (T) parent.findViewById(id);
}


@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString(STATE_ACCOUNT_NAME, mAccountName);
    outState.putSerializable(STATE_OVEREENKOMST, mOvereenkomst);
}

public OverdragenDialogFragment setListener(OverdragenCallbacks mListener) {
    this.mListener = mListener;
    return this;
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    //[..] not relevant [..]        
}
2

There are 2 answers

3
mpkuth On BEST ANSWER

As mentioned in the Android Developer Guide for Dialogs, you can use AlertDialog.Builder.setAdapter instead of AlertDialog.Builder.setItems to back the list with dynamic data (it specifically mentions data from a Loader).

The following example uses a Handler to populate the list 5 seconds after start instead of a Loader for simplicity. Works great on my device running Android 4.4.4.

public class ListDialogFragment extends DialogFragment implements DialogInterface.OnClickListener {

    ArrayAdapter<String> arrayAdapter;
    List<String> places = Arrays.asList("Buenos Aires", "Córdoba", "La Plata");

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                arrayAdapter.addAll(places);
            }
        }, 5000);

        arrayAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1);
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Test List").setAdapter(arrayAdapter, this);
        return builder.create();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        Log.d(getClass().getSimpleName(), String.format("User clicked item at index %d", which));
    }

}
0
Simas On

Sounds like a focus problem. Some view is stealing the focus from your ListView and it becomes un-touchable. You can try debugging in your activity with similar code:

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {
        Log.e(TAG, "Activity gained focus: " + getCurrentFocus());
    } else {
        Log.e(TAG, "Activity lost focus");
        try {
            Log.e(TAG, "Dialog window focused on: " +
                    dialogFragment.getDialog().getWindow().getCurrentFocus());
        } catch (NullPointerException e) {
            Log.i(TAG, "NPE", e);
        }
    }
}

Also this shouldn't be needed: mListView.requestFocus();