【问题标题】:Android: prevent preference dialog for preference which must be loaded over the networkAndroid:阻止必须通过网络加载的首选项的首选项对话框
【发布时间】:2011-01-12 00:25:10
【问题描述】:

我的应用有一个 ListPreference,其条目来自网络 API。在我的 PreferenceActivity 的 onCreate() 中,我生成了一个后台线程,它进行 API 调用,然后在一两秒后填充 ListPreference 的条目。

如果用户在下载选项之前单击首选项屏幕上的 ListPreference 按钮,我想阻止显示首选项对话框,而是通知用户选项列表仍在加载中。

我怀疑正确的方法是覆盖 OnPreferenceClickListener,如下所示:

ListPreference dpref = (ListPreference) findPreference("debug");
String[] s = {"one", "two", "three"};
dpref.setEntries(s);
dpref.setEntryValues(s);
dpref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
    @Override
    public boolean onPreferenceClick(Preference preference) {
        Toast.makeText(this, "hi there", Toast.LENGTH_SHORT).show();
        return true;
    }
});

toast 会显示,但 ListPreference 选择器对话框也会显示。 OnPreferenceClickListener documentation 表示 onPreferenceClick 应该返回 true 如果点击被处理,但返回 false 具有相同的结果。

如何防止显示首选项对话框?

有没有更好的方法来处理必须先下载选项才能查看的首选项?

【问题讨论】:

    标签: android android-preferences


    【解决方案1】:

    我在同样的问题上苦苦挣扎,但环境更简单。我的 PreferenceScreen 在 xml 文件中定义。我想自己处理一个偏好。所以我只是将“Preference”对象放入 xml 文件中,而不是“ListPreference”或“EditTextPreference”

        <Preference android:title="@string/preloadmaps1" android:summary="@string/preloadmaps2"
            android:key="preloadMaps" />
    

    现在没有更多的编辑器与首选项连接,我可以在“OnPreferenceClickListener”中自己处理编辑

    【讨论】:

      【解决方案2】:

      参考关于showDialog的android dveloper文档:

      显示与此首选项关联的对话框。 这通常在单击首选项时自动启动。如果您需要在其他事件上显示对话框,请调用此方法。

      所以,当点击首选项会自动调用 showDialog() 时,如果你想控制以防止在点击首选项上显示对话框,你需要实现一个自定义首选项,像这样,

      public class MyPreference extends ListPreference {
          private Context context;
      
          // a flag to control show dialog
          private boolean showDialog = false;
      
          public MyPreference(Context context) {
              super(context);
              this.context = context;
          }
      
          public MyPreference(Context context, AttributeSet attrs) {
              super(context, attrs);
              this.context = context;       
          }
      
          @Override
          protected void showDialog(Bundle state) {        
              if (showDialog) {
                  // show dialog 
                  super.showDialog(state);
              } else {
                  // if you don't want to show a dialog when click preference
                  return;
              } /* end of if */
          }
      }
      

      【讨论】:

      • 这对我来说不太适用,至少在 Android 4.1 上使用 ListPreference。我能够通过以下方式抑制列表对话框:if (showDialog) { super.showDialog(state); } else { getDialog().dismiss(); }
      • androidx 不可能了
      【解决方案3】:

      我不确定为什么你的方法不起作用,但这里有一个快速的解决方法:

      为每个应下载的首选项添加存根Preference。您可以以任何您想要的方式自定义您的点击操作。不会显示任何对话框。

      当您的偏好选项准备就绪时,remove old Preference(按其名称)并使用您的选项创建新的 ListPreference(与刚刚删除的名称相同)。

      这将使您可以灵活地为您需要的任何事件添加任何类型的自定义首选项。虽然如您所见,需要一些额外的编码。

      【讨论】:

      • 这就是我最终要做的。谢谢!
      • 我也必须这样做。您是否找到一种方法将新的 Preference 放在已删除的 PreferenceScreen 的位置(在我的情况下)而不是将其添加到列表的末尾?
      • 我自己找到了答案:我将容器的 OrderingAsAdded 设置为 false,并且在替换首选项期间,我根据列表中的当前位置设置了每个首选项的顺序,因此我只需将相同的顺序分配给新的存根首选项。
      【解决方案4】:

      我知道这是一个老问题,但仍然没有答案如何取消这些对话框。 下面是取消首选项对话框的方法:

      dpref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
          @Override
          public boolean onPreferenceClick(Preference preference) {
              ((ListPreference)preference).getDialog().dismiss();   <<< This will do it
              Toast.makeText(this, "hi there", Toast.LENGTH_SHORT).show();
              return true;
          }
      });
      

      但是,与其关闭对话框,不如隐藏它以便稍后在从 web 加载选项列表时显示它。这样做:

      dpref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
          @Override
          public boolean onPreferenceClick(Preference preference) {
              ((ListPreference)preference).getDialog().hide();   <<< This will just hide the dialog and you still can show it later
              Toast.makeText(this, "hi there", Toast.LENGTH_SHORT).show();
              return true;
          }
      });
      

      稍后,当加载选项列表时:

      ...
      dpref.setEntries(<array_of_options_here>);
      dpref.getdialog().show();  <<< If you dismiss the dialog earlier and not hide it, then getDialog() returns null here 
      ...
      

      【讨论】:

      • 效果很好,谢谢!为了安全起见,我添加了以下防护: Dialog dialog = listPreference.getDialog(); if (dialog != null) { dialog.dismiss(); }
      • androidx 不可能了
      【解决方案5】:

      我会使用代理Activity(样式为对话框)下载首选项并在完成后启动实际的PreferenceActivity

      【讨论】:

        【解决方案6】:

        您可以在您的偏好活动中的onResume() 中执行以下操作:

        • 启动async call 以...
          • 将首选项设置为禁用,并提示正在获取值
          • 获取值的网络(当它没有值时或总是) - 异步,因此它不会停止恢复
          • 使用获取的值更新首选项条目
          • 启用首选项并显示默认提示

        如果这样做的缺点是,您的首选项会过于频繁地更新(例如,总是在您的首选项屏幕开始时),但是这样您就可以处理网络调用的结果产生一个列表,其中之前选择的情况value 不再存在(例如,向用户显示一个对话框,说明他的选择不再可用)。

        此外,这是一项相当复杂的任务(因为您必须定义一个Handler,因为异步执行存在于另一个线程中......)

        看起来像这样(假设您在 PreferenceActivity 中执行此操作)

        import android.os.Handler;
        import android.os.AsyncTask;
        import android.os.Message;
        
        public class xyz extends PreferenceActivity {
            ...
            // define a handler to update the preference state
            final Handler handler = new Handler() {
                public void handleMessage( Message msg ) {
                    ListPreference dpref = (ListPreference) getPreferenceManager().findPreference("debug");
                    dpref.setEnabled( msg.getData().getBoolean("enabled") );
                    dpref.setSummary( msg.getData().getString("summary") );
                }
            };
        
        
        
            private class updatePref extends AsyncTask<Void, Void, Void> {
        
                @Override
                protected Void doInBackground(Void... arg) {
        
                    Message msg = handler.obtainMessage();
                    Bundle data = new Bundle();
                    data.putBoolean("enabled", false ); // disable pref
                    data.putString("summary", "Getting vals from network..." ); // set summary
                    msg.setData( data );
                    handler.sendMessage(msg); // send it do the main thread
        
                    ListPreference dpref = (ListPreference) getPreferenceManager().findPreference("debug");
        
        
                    String values[];
                    // call network function and let it fill values[]
        
                    // do things according to the returned values, 
                    // eg check if there are any, check if the user has 
                    // already selected one, display a message if the 
                    // user has selected one before but the values do not
                    // contain them anymore, ...
        
                    // set the new values     
                    dpref.setEntries(values);
                    dpref.setEntryValues(values);
        
                    data.putBoolean("enabled", true ); // enable pref
                    data.putString("summary", "Please choose" ); // set summary
                    msg.setData( data );
                    handler.sendMessage(msg); // send it do the main thread
        
                    return null;
                }
            }
        
            public void onResume() {
                ....
                new updatePref().execute();
        
            }
        
        }
        

        当然,您可以在任何地方调用new updatePref().execute(),因此您也可以将其绑定到ButtononCreate() 或任何地方(无需在onResume() 中执行此操作)。

        【讨论】:

          【解决方案7】:

          收藏的另一种方式。我相信它只适用于android.support.v7.preference libraryPreferenceFragmentCompat(在我的情况下为27.1.1)。这是一个例子:

          public class PrefsFragment extends PreferenceFragmentCompat implements PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
          
              private static final int REQUEST_VIEW_PREFERENCE = 1;
          
              @Override
              public void onActivityResult(int requestCode, int resultCode, Intent data) {
                  if (requestCode == REQUEST_VIEW_PREFERENCE && resultCode == Activity.RESULT_OK) {
                      Preference preference = findPreference("preference_key");
                      getPreferenceManager().showDialog(preference)
                  }
              }
          
              @Override
              public Fragment getCallbackFragment() {
                  this;
              }
          
              @Override
              public boolean onPreferenceDisplayDialog(PreferenceFragmentCompat caller, Preference pref) {
                  if (pref.getKey().equals("preference_key")) {
                      if (canBeOpened(pref)) {
                          return false;
                      } else {
                          // show a fragment loading data with requestCode == REQUEST_VIEW_PREFERENCE
                          ...
                          return true;
                      }
                  }
              }
          
              private boolean canBeOpened(Preference pref) {
                  // check whether a preference can be clicked
                  ...
              }
          }
          

          如果我正确理解了Preference.java 的源代码,则返回trueOnPreferenceClickListener 仅适用于基于意图的首选项,但如果要显示对话框则将被忽略。

          在上面的示例中,OnPreferenceDisplayDialogCallback 用于防止显示首选项对话框。从getCallbackFragment()返回实现此接口的片段很重要。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-04-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多