Был ли PreferenceFragment преднамеренно исключен из пакета совместимости?

153

Я ищу, чтобы написать настройки, которые могут быть применены к устройствам 3.0 и pre-3.0. Обнаружив, что PreferenceActivityсодержит устаревшие методы (хотя они используются в прилагаемом примере кода), я посмотрел PreferenceFragementи пакет совместимости, чтобы решить мои проблемы.

Похоже, что этого PreferenceFragmentнет в пакете совместимости. Кто-нибудь может сказать мне, было ли это преднамеренным? Если да, могу ли я легко настроить таргетинг на ряд устройств (то есть <3.0 и> = 3.0) или мне придется прыгать через обручи? Если это не было преднамеренно исключено, можем ли мы ожидать новый выпуск пакета совместимости? Или есть другой обходной путь, который безопасно использовать?

ура

Джеймс

Джеймс
источник
1
Это мой подход к решению проблемы: stackoverflow.com/questions/14076073/…
ecv 28.12.12
Кто-то сделал третье лицо, PreferenceFragmentкоторое вы забудете даже там. Смотри мой ответ .
theblang
Крис Бейнс обращается к этому в комментарии в своем блоге . Он сказал, что причина в том, что"Because most of Preferences' implementation is hidden, therefore impossible to backport without lots of hackery."
theblang
Смотрите мой обновленный ответ . PreferenceFragmentCompatнедавно был добавлен в библиотеку поддержки.
theblang

Ответы:

90

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

Устаревшие методы устарели с Android 3.0. Они прекрасно работают на всех версиях Android, но нацелены на использование PreferenceFragmentAndroid 3.0 и выше.

Кто-нибудь может сказать мне, было ли это преднамеренным?

Я думаю, это вопрос инженерного времени, но это всего лишь предположение.

Если да, могу ли я легко настроить таргетинг на ряд устройств (то есть <3.0 и> = 3.0) или мне придется прыгать через обручи?

Я считаю, что это сделать "легко". Имеют две отдельные PreferenceActivityреализации, одна с использованием заголовков предпочтений, а PreferenceFragmentsдругая с использованием оригинального подхода. Выберите правильный в нужной точке (например, когда пользователь нажимает на пункт меню параметров). Вот пример проекта, демонстрирующего это. Или есть один, PreferenceActivityкоторый обрабатывает оба случая, как в этом примере проекта .

Если это не было преднамеренно исключено, можем ли мы ожидать новый выпуск пакета совместимости?

Вы узнаете, когда остальные из нас узнают, то есть, если и когда это отправит.

Или есть другой обходной путь, который безопасно использовать?

Смотри выше.

CommonsWare
источник
Ура Марк. Я видел, что вы делали комментарии по этому поводу в нескольких местах (группа android google и ваш блог), но хотели получить окончательный ответ (насколько это возможно, учитывая обстоятельства).
Джеймс
@James: Да, проблема будет в определении XML предпочтения, получая что-то, что будет хорошо работать как фрагменты, а также объединяться вместе, так как я не уверен, что <include>работает с предпочтением XML. Кстати, если вы подписчик, об обновлении книги, касающемся этого проекта, было объявлено несколько минут назад.
CommonsWare
7
Извините, но я не совсем уверен, что вы пытаетесь сделать здесь. Вы вообще ничего не отвечаете, просто комментируете / угадываете / ссылаетесь на нерелевантные внешние ссылки, которые не имеют никакого отношения к проблеме. Вопрос заключается в том, было ли это упущение преднамеренным, без версии совместимости PreferenceFragment нет средств для расширения PreferenceActivity описанным выше способом, потому что если PreferenceFragment не существует, то и getSupportFragmentManager (), или любой другой метод не существует. требуется использовать фрагменты в первую очередь.
Джастин Бузер
8
@JustinBuser: «Вопрос в том, было ли упущение преднамеренным», - единственные люди, которые могут ответить на этот вопрос, работают на Google. Вы можете получить работу в Google, чтобы попытаться выяснить это. «нет способа расширить PreferenceActivity так, как вы описали» - вы можете скачать код, на который я ссылался.
CommonsWare
9
@JustinBuser Для справки, Марк ответил на мой вопрос. Это очевидно из моего принятия его ответа.
Джеймс
21

Тонкий смысл ответа от @CommonsWare заключается в том, что - ваше приложение должно выбирать между API совместимости или встроенным API фрагментов (начиная с SDK 11 или около того). Фактически это то, что сделала «легкая» рекомендация. Другими словами, если вы хотите использовать PreferenceFragment, ваше приложение должно использовать встроенный API-фрагмент и работать с устаревшими методами в PreferenceActivity. И наоборот, если важно, чтобы ваше приложение использовало компат. API вы столкнетесь с отсутствием класса PreferenceFragment вообще. Таким образом, нацеливание на устройства не является проблемой, но скачкообразное изменение происходит, когда вам нужно выбрать один или другой API и, таким образом, представить свой дизайн непредвиденным обходным путям. Мне нужен компат. API, поэтому я собираюсь создать свой собственный класс PreferenceFragment и посмотреть, как это работает. В худшем случае я

РЕДАКТИРОВАТЬ: после попытки и просмотра кода на http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceFragment.java? av = h - создание собственного PreferenceFragment не произойдет. Похоже, что в PreferenceManager либеральное использование package-private вместо «protected» является основным блокирующим фактором. Это действительно не похоже на то, что есть какая-то безопасность или действительно хорошая мотивация, чтобы сделать это, и это не очень хорошо для юнит-тестирования, ну да ладно ... меньше печатать, я думаю ...

РЕДАКТИРОВАТЬ v2: На самом деле это произошло, и это сработало. Определенно было головной болью заставить код работать с JAR API совместимости. Мне пришлось скопировать около 70% пакета com.android.preference из SDK в мое приложение, а затем бороться с типично посредственным Java-кодом в Android. Я использовал v14 SDK. Для инженера Goog было бы намного проще сделать то, что я сделал, вопреки тому, что я слышал от ведущих разработчиков Android по этой теме.

Кстати, я говорил "нацеливание на устройства - это не проблема"? Это полностью ... если вы используете com.android.preference, вы не сможете обменяться с API совместимости без серьезного рефакторинга. Веселый журнал!

цепкий
источник
Позвольте мне быть более прямым. Если все, что вас волнует, это нацеленность на соты и выше (какая доля рынка?), То проголосуйте за ответ от @Commonsware! Если вы заботитесь о большинстве устройств Android на рынке сегодня, вы должны прочитать мой ответ.
Стойкий
4
Хотели бы вы поделиться, как вы это сделали? Я сталкиваюсь с точно такой же проблемой, только моя PreferenceActivity должна использовать Loaders, и поэтому я должен использовать библиотеку совместимости.
Каракури
3
@ Мне нравится твое расследование - молодец. Тем не менее, я считаю, что кто-то должен поставить запись прямо в ваш первый комментарий - код Commonsware будет работать на устройствах до и после HC - попробуйте сначала, прежде чем комментировать подобные. Вам нужно понять, что позднее связывание используется во время выполнения для поддержки предыдущих устройств. Проверка версии во время выполнения заботится о поддержке обоих семейств ОС - это общий шаблон Android (не тот, который мне нравится, а тот, который важно для разработчиков Android изучить и ознакомиться с ним) ... Так что для будущих читателей - не Не отвергайте любой подход.
Ричард Ле Мезье
@RichardLeMesurier, но метод Commonsware не подходит, если вам нужны настройки внутри DrawerLayout
neworld
16

Основываясь на ответе CommonsWare, а также на наблюдениях Tenacious, я придумал единственное решение класса-потомка, способное нацеливаться на все текущие версии Android API с минимальными усилиями и без дублирования кода или ресурсов. Пожалуйста, смотрите мой ответ на связанный вопрос здесь: PreferenceActivity для Android 4.0 и более ранних версий

или в моем блоге: http://www.blackmoonit.com/2012/07/all_api_prefsactivity/

Протестировано на двух планшетах под управлением 4.0.3 и 4.0.4, а также на телефоне под управлением 4.0.4 и 2.3.3, а также на эмуляторе под управлением 1.6.

Дядя Код Обезьяна
источник
10

В августе 2015 года Google выпустила новую библиотеку поддержки предпочтений v7 .

Теперь вы можете использовать PreferenceFragmentCompat с любым ActivityилиAppCompatActivity

public static class PrefsFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
}

Вы должны установить preferenceThemeв своей теме:

<style name="AppTheme" parent="@style/Theme.AppCompat.Light">
  ...
  <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>

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

Габриэле Мариотти
источник
1
Я думаю, что вы пропустите функцию: onCreatePreferences
разработчик Android
Это спасло мой день! Интересно, почему это не видно в структуре проекта Android Studio ... ?? Кстати, у вас есть опечатка в вашем коде. Должно быть "расширяет PreferenceFragmentCompat"
Гжегож Д.
7

Ответ Tenacious правильный, но вот еще несколько деталей.

Причина, по которой вы не можете «создать нормальный макет и связать компоненты вида с sharedprefs вручную», состоит в том, что в API android.preferences есть некоторые неожиданные упущения. PreferenceActivity и PreferenceFragment оба имеют доступ к критическим непубличным методам PreferenceManager, без которых вы не сможете реализовать собственный интерфейс предпочтений.

В частности, для построения иерархии предпочтений из XML-файла вам необходимо использовать PreferenceManager, но все конструкторы PreferenceManager являются либо закрытыми, либо скрытыми. Метод присоединения слушателей Preference onClick к вашей активности также является закрытым для пакета.

И вы не можете обойти эту проблему, украдкой поместив свою реализацию в пакет android.preferences, потому что непубличные методы в API-интерфейсах Android фактически исключены из SDK. Приложив немного креативности, включая рефлексию и динамические прокси, вы все равно можете их получить. Единственная альтернатива, как утверждает Tenacious, - это разветвить весь пакет android.preference, включая не менее 15 классов, 5 макетов и аналогичное количество элементов style.xml и attrs.xml.

Таким образом, чтобы ответить на исходный вопрос, причина, по которой Google не включил PreferenceFragment в пакет совместимости, заключается в том, что у них была бы точно такая же сложность, что и у Tenacious и у меня. Даже Google не может вернуться назад во времени и опубликовать эти методы на старых платформах (хотя я надеюсь, что они сделают это в будущих выпусках).

mhsmith
источник
2

Моя цель приложения - API +14, но из-за использования библиотеки поддержки для некоторой необычной навигации я не мог использовать android.app.Fragmentи должен был использовать android.support.v4.app.Fragment, но мне также нужно было иметь PreferenceFragmentместо без больших изменений в коде позади.

Итак, мое простое исправление - иметь как библиотеки поддержки мира, так и PreferenceFragment:

private android.support.v4.app.Fragment fragment;
private android.app.Fragment nativeFragment = null;

private void selectItem(int position) {
    fragment = null;
    boolean useNativeFragment = false;
    switch (position) {
    case 0:
        fragment = new SampleSupprtFragment1();
        break;
    case 1:
        fragment = new SampleSupprtFragment2();
        break;
    case 2:
        nativeFragment = new SettingsFragment();
        useNativeFragment = true;
        break;
    }
    if (useNativeFragment) {
        android.app.FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, nativeFragment).commit();
    } else {
        if (nativeFragment != null) {
            getFragmentManager().beginTransaction().remove(nativeFragment)
                .commit();
            nativeFragment = null;
        }
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, fragment).commit();
    }
}
Мохсен Афшин
источник
2

Мне нужно было интегрировать настройки в дизайн приложения и поддерживать 2.3 Android. Поэтому мне все еще нужен был PreferencesFragment.

После некоторого поиска я нашел android-support-v4-preferencefragment lib. Эта библиотека экономит много времени на копирование и рефакторинг оригинального PreferencesFragment, как сказал Tenacious. Работает нормально и пользователи наслаждаются настройками.

neworld
источник