Android: запретить диалоговое окно настроек для настроек, которые должны быть загружены по сети

В моем приложении есть ListPreference, записи которого поступают из сетевого API. В onCreate() моей PreferenceActivity я создаю фоновый поток, который выполняет вызов 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;
    }
});

Отображается всплывающее уведомление, но также отображается диалоговое окно выбора ListPreference. В документации OnPreferenceClickListener говорится, что onPreferenceClick должен возвращать true, если щелчок был обрабатывается, но возврат false имеет тот же результат.

Как предотвратить отображение диалогового окна настроек?

Есть ли лучший способ обработки настроек, параметры которых необходимо загрузить перед просмотром?


person Noah    schedule 12.01.2011    source источник


Ответы (7)


Я не уверен, почему ваш способ не работает, но вот быстрое обходное решение:

Добавьте заглушку Preference для каждого параметра, который необходимо загрузить. Вы можете настроить свое действие по клику любым удобным для вас способом. Диалоговые окна отображаться не будут.

Когда параметры предпочтений будут готовы, удалить старый Preference (по его имени) и создайте новый ListPreference (с тем же именем, что и у только что удаленного) с вашими параметрами.

Это даст вам возможность добавлять любые пользовательские настройки для любого события, которое вам нужно. Хотя, как вы можете видеть, требуется дополнительное кодирование.

person inazaruk    schedule 12.01.2011
comment
Это то, что я в итоге сделал. Спасибо! - person Noah; 29.01.2011
comment
Я должен был сделать то же самое. Вы нашли способ поместить новое предпочтение в позицию удаленного PreferenceScreen (в моем случае) вместо того, чтобы добавлять его в конец списка? - person lapis; 06.09.2011
comment
Я сам нашел ответ: я установил для контейнера OrderingAsAdded значение false, и во время замены настроек я установил порядок каждого предпочтения в соответствии с его текущей позицией в списке, поэтому мне просто нужно назначить тот же порядок для нового предпочтения-заглушки. - person lapis; 06.09.2011

Я боролся с той же проблемой, но в более простой среде. Мой PreferenceScreen определен в файле xml. Есть одно предпочтение, которое я хочу решить сам. Поэтому я просто помещаю объект «Preference» в файл xml вместо «ListPreference» или «EditTextPreference».

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

Теперь больше нет редактора, связанного с Preference, и я могу самостоятельно обрабатывать редактирование в «OnPreferenceClickListener».

person Gerhard    schedule 23.05.2011

Ссылка на документ Android dveloper, в котором содержится информация о showDialog:

Показывает диалоговое окно, связанное с этим параметром. Обычно это запускается автоматически при нажатии на настройку. Вызовите этот метод, если вам нужно отобразить диалоговое окно при каком-либо другом событии.

Таким образом, когда предпочтение щелчка будет вызывать 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 */
    }
}
person Jay Parker    schedule 27.06.2012
comment
У меня это не совсем сработало, по крайней мере, с ListPreference на Android 4.1. Мне удалось подавить диалог списка с помощью: if (showDialog) { super.showDialog(state); } else { getDialog().dismiss(); } - person ehartwell; 16.11.2012

Я знаю, что это старый вопрос, но до сих пор нет ответа, как отменить этот диалог. Итак, вот как вы можете отменить диалог настроек:

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

Но вместо того, чтобы закрывать диалоговое окно, лучше скрыть его, чтобы иметь возможность показать его позже, когда список параметров загружается из Интернета. Ну действуй:

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 
...
person mykolaj    schedule 02.12.2014
comment
Отлично работает, спасибо! На всякий случай я добавил следующую защиту: Dialog dialog = listPreference.getDialog(); если (диалог!= ноль) { диалог.отпустить(); } - person Meanman; 23.12.2014

Я бы использовал прокси Activity (стилизованный под диалоговое окно), который загружает настройки и запускает фактический PreferenceActivity после выполнения.

person yanchenko    schedule 12.01.2011

вы можете сделать следующее в onResume() в своем предпочтении:

  • start an async call to...
    • set the preference to disabled with a hint that values are being fetched
    • сеть для получения значений (либо когда у нее нет значений, либо всегда) - асинхронно, поэтому она не останавливает возобновление
    • обновите запись предпочтения с вашими извлеченными значениями
    • включить предпочтение и показать подсказку по умолчанию

Недостатком этого является то, что ваши предпочтения будут обновляться слишком часто (например, всегда при запуске экрана ваших предпочтений), но с этим вы можете справиться со случаем, когда результат сетевого вызова дает список, в котором ранее выбранное значение не в больше (например, показать пользователю диалоговое окно, в котором говорится, что его выбор больше не доступен).

Кроме того, это довольно сложная задача (поскольку вам нужно определить 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() где угодно, так что вы также можете привязать его к Button, onCreate() или куда угодно (нет необходимости делать это в onResume()).

person Markus    schedule 29.01.2011

Еще один способ для коллекции. Я считаю, что это работает только с android.support.v7.preference библиотека и PreferenceFragmentCompat (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, OnPreferenceClickListener, возвращающий true, работает только для предпочтений, основанных на намерениях, но игнорируется, если будет отображаться диалоговое окно.

В приведенном выше примере OnPreferenceDisplayDialogCallback используется для предотвращения отображения диалоговых окон настроек. Важно вернуть фрагмент, реализующий этот интерфейс, из getCallbackFragment().

person st_    schedule 01.06.2018