DialogFragment 中的 ListView 在 Android 4.0 中被禁用
ListView in DialogFragment is disabled in Android 4.0
我为 select 后端用户创建了一个自定义 DialogFragment。用户是异步加载的,所以我不能使用 AlertDialog.Builder.setItems()
因为 onCreateDialog
已经被调用了。
所以我使用带有 ListView 和 ProgressBar 的自定义布局。
ListView 可见,设置适配器并在加载数据(隐藏进度条)后设置 onItemClickListener。
在另一台 Android 5 的设备上没有问题,但在我的 Android 4.0.3 的 Transformer TF101 测试设备上我无法按任何项目。
奇怪的是,当我切换应用程序并 return 到我的应用程序时,ListView 已启用,现在我可以 select 一些东西。
XML 布局:
<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>
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 [..]
}
听起来像是焦点问题。某些视图正在从您的 ListView 中窃取焦点,并且它变得不可触摸。您可以尝试在 activity 中使用类似的代码进行调试:
@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);
}
}
}
也不需要这个:mListView.requestFocus();
如 Android Developer Guide for Dialogs 中所述,您可以使用 AlertDialog.Builder.setAdapter
而不是 AlertDialog.Builder.setItems
来支持带有动态数据的列表(它特别提到了来自 Loader
的数据) .
以下示例使用 Handler
在开始后 5 秒填充列表,而不是为简单起见使用 Loader
。在我的设备上运行良好 运行 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));
}
}
我为 select 后端用户创建了一个自定义 DialogFragment。用户是异步加载的,所以我不能使用 AlertDialog.Builder.setItems()
因为 onCreateDialog
已经被调用了。
所以我使用带有 ListView 和 ProgressBar 的自定义布局。 ListView 可见,设置适配器并在加载数据(隐藏进度条)后设置 onItemClickListener。
在另一台 Android 5 的设备上没有问题,但在我的 Android 4.0.3 的 Transformer TF101 测试设备上我无法按任何项目。
奇怪的是,当我切换应用程序并 return 到我的应用程序时,ListView 已启用,现在我可以 select 一些东西。
XML 布局:
<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>
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 [..]
}
听起来像是焦点问题。某些视图正在从您的 ListView 中窃取焦点,并且它变得不可触摸。您可以尝试在 activity 中使用类似的代码进行调试:
@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);
}
}
}
也不需要这个:mListView.requestFocus();
如 Android Developer Guide for Dialogs 中所述,您可以使用 AlertDialog.Builder.setAdapter
而不是 AlertDialog.Builder.setItems
来支持带有动态数据的列表(它特别提到了来自 Loader
的数据) .
以下示例使用 Handler
在开始后 5 秒填充列表,而不是为简单起见使用 Loader
。在我的设备上运行良好 运行 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));
}
}