开发者

Android: prevent preference dialog for preference which must be loaded over the network

My app has a ListPreference, whose entries come from a network API. In my PreferenceActivity's onCreate(), I spawn a background thread which makes the API call and then populates the entries of the开发者_运维问答 ListPreference after one or two seconds.

If the user clicks the ListPreference button on the preference screen before the options have been downloaded, I want to prevent the preference dialog from showing and instead notify the user that the list of options is still being loaded.

I suspect that correct approach is to override the OnPreferenceClickListener, like this:

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;
    }
});

The toast gets shown, but the ListPreference chooser dialog is shown as well. The OnPreferenceClickListener documentation says that onPreferenceClick should return true if the click was handled, but returning false has the same result.

How do I prevent the preference dialog from showing?

Is there a better way to handle preferences whose options must be downloaded before viewing?


I was struggeling with the same problem but in a more simple environment. My PreferenceScreen is defined in an xml file. There is one Preference I want to handle myself. So I simply put a "Preference" object into the xml file instead of a "ListPreference" or "EditTextPreference"

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

Now there is no more Editor connetcted with the Preference and I can savely handle the editing myself in "OnPreferenceClickListener"


Reference the android dveloper document which about showDialog :

Shows the dialog associated with this Preference. This is normally initiated automatically on clicking on the preference. Call this method if you need to show the dialog on some other event.

So, when click preference will call showDialog() automatically, if you want to control to prevent to show a dialog on click preference, you need to implement a custom preference, like this,

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 */
    }
}


I'm not sure why your way is not working, but here is a quick workaround solution:

Add stub Preference for each preference that should be downloaded. You can customize you action on click in any way you want. No dialogs will be shown.

When your preference options are ready, remove old Preference (by its name) and create new ListPreference (with the same name as just removed one) with your options.

This will give you a flexibility to add any kind of custom preferences on any event you need. Though as you can see, some extra coding is required.


I know this is an old question, but there's still no answer how to cancel those dialog. So here's how you can cancel preference dialog:

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;
    }
});

But instead of dismissing the dialog, its better to hide it to be able to show it later, when a list of options is loaded from web. So do:

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;
    }
});

Later on, when list of options is loaded do:

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


I'd use a proxy Activity (styled as a dialog) that downloads preferences and launches the actual PreferenceActivity once done.


you can do the following in the onResume() in your preference activity:

  • start an async call to...
    • set the preference to disabled with a hint that values are being fetched
    • the network to fetch the values (either when it has no values or always) - async, so it does not stop the resuming
    • update the preference entry with your fetched values
    • enable the preference and show the default hint

The drawback if this is, that your preference will either get updated too often (eg always when your preference screen starts), but with this you can handle the case when the result of the network call yields a list where the before selected value is not in anymore (eg show the user a dialog where it says his selection is not available any more).

Also, this is rather a complex task (since you have to define a Handler, because the async execution lives in another thread...)

It would look something like this (assuming you do this in your 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();

    }

}

Of course you can call new updatePref().execute() anywhere, so you could also bind it to a Button, onCreate() or whereever (no need to do this in onResume()).


One more way for a collection. I believe it only works with the android.support.v7.preference library and PreferenceFragmentCompat (27.1.1 in my case). Here is an example:

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
        ...
    }
}

If I understood source code of Preference.java correctly, OnPreferenceClickListener returning true works only for intent based preferences, but is ignored if dialog is going to be shown.

In the example above OnPreferenceDisplayDialogCallback is used to prevent preference dialogs from showing. It is important to return a fragment implementing this interface from getCallbackFragment().

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜