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 [..]
}
As mentioned in the Android Developer Guide for Dialogs, you can use
AlertDialog.Builder.setAdapter
instead ofAlertDialog.Builder.setItems
to back the list with dynamic data (it specifically mentions data from aLoader
).The following example uses a
Handler
to populate the list 5 seconds after start instead of aLoader
for simplicity. Works great on my device running Android 4.4.4.