Ответ 1
Попробуйте это... так мы проверяем правильность фрагмента.
protected boolean isValidFragment(String fragmentName) {
return StockPreferenceFragment.class.getName().equals(fragmentName);
}
Когда я пытаюсь выполнить свое приложение с Android KitKat, у меня есть ошибка в PreferenceActivity.
Подклассы PreferenceActivity должны переопределить isValidFragment (String), чтобы убедиться, что класс Fragment действителен! com.crbin1.labeltodo.ActivityPreference не проверял, действительно ли фрагмент com.crbin1.labeltodo.StockPreferenceFragment действителен
В документации я найду следующее объяснение
protected boolean isValidFragment (String имя_файла)
Добавлен в уровень API 19
Подклассы должны переопределить этот метод и убедиться, что данный фрагмент является допустимым типом, который должен быть присоединен к этому действию. Реализация по умолчанию возвращает true для приложений, созданных для android: targetSdkVersion старше KITKAT. Для более поздних версий он генерирует исключение.
Я не нашел примера для решения проблемы.
Попробуйте это... так мы проверяем правильность фрагмента.
protected boolean isValidFragment(String fragmentName) {
return StockPreferenceFragment.class.getName().equals(fragmentName);
}
Из чистого любопытства вы также можете сделать это:
@Override
protected boolean isValidFragment(String fragmentName) {
return MyPreferenceFragmentA.class.getName().equals(fragmentName)
|| MyPreferenceFragmentB.class.getName().equals(fragmentName)
|| // ... Finish with your last fragment.
;}
Я обнаружил, что могу захватить копию моих имен фрагментов из моего ресурса заголовка, когда он был загружен:
public class MyActivity extends PreferenceActivity
{
private static List<String> fragments = new ArrayList<String>();
@Override
public void onBuildHeaders(List<Header> target)
{
loadHeadersFromResource(R.xml.headers,target);
fragments.clear();
for (Header header : target) {
fragments.add(header.fragment);
}
}
...
@Override
protected boolean isValidFragment(String fragmentName)
{
return fragments.contains(fragmentName);
}
}
Таким образом, мне не нужно забывать обновлять список фрагментов, закодированных в коде, если я хочу их обновить.
Я надеялся использовать getHeaders()
и существующий список заголовков напрямую, но кажется, что действие уничтожается после onBuildHeaders()
и воссоздается до вызова isValidFragment()
.
Возможно, это связано с тем, что Nexus 7, на котором я тестирую, фактически не выполняет действия с двумя панелями. Отсюда и потребность в статическом элементе списка.
Этот API был добавлен из-за недавно обнаруженной уязвимости. См. http://ibm.co/1bAA8kF или http://ibm.co/IDm2Es
10 декабря 2013 г. "Недавно мы раскрыли новую уязвимость для группы безопасности Android. [...] Чтобы быть более точным, любое приложение, которое расширило класс PreferenceActivity с помощью экспортированной активности, было автоматически уязвимо. Патч был предоставлен в Android KitKat. почему ваш код сейчас сломан, это связано с патчем Android KitKat, который требует, чтобы приложения переопределяли новый метод PreferenceActivity.isValidFragment, который был добавлен в Android Framework". - Из первой ссылки выше
Проверено с помощью устройства с 4.4:
(1), если ваш файл proguard.cfg имеет эту строку (которую многие определяют в любом случае):
-keep public class com.fullpackage.MyPreferenceFragment
(2), чем наиболее эффективная реализация:
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class EditPreferencesHC extends PreferenceActivity {
...
protected boolean isValidFragment (String fragmentName) {
return "com.fullpackage.MyPreferenceFragment".equals(fragmentName);
}
}
Я не уверен, что в случае реализации в полосе не будет обсуждаемых уязвимостей здесь, но если это так, то я думаю, что лучшим решением было бы избежать использования этого статического списка и просто выполните следующее:
@Override
protected boolean isValidFragment(String fragmentName)
{
ArrayList<Header> target = new ArrayList<>();
loadHeadersFromResource(R.xml.pref_headers, target);
for (Header h : target) {
if (fragmentName.equals(h.fragment)) return true;
}
return false;
}
это мое решение:
Если вы используете дополнительные функции для запуска активности предпочтений - метод onBuildHeaders() завершится неудачно! (с нижеприведенными дополнениями для запуска - почему??? - просто потому, что onBuildHeaders() никогда не вызывается):
Intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMEN, Fragment.class.getName()); Intent.putExtra(PreferenceActivity.EXTRA_NO_HEADERS, true);
Это пример класса:
/**
* Preference Header for showing settings and add view as two panels for tablets
* for ActionBar we need override onCreate and setContentView
*/
public class SettingsPreferenceActivity extends PreferenceActivity {
/** valid fragment list declaration */
private List<String> validFragmentList;
/** some example irrelevant class for holding user session */
SessionManager _sessionManager;
@Override
public void onBuildHeaders(List<Header> target) {
/** load header from res */
loadHeadersFromResource(getValidResId(), target);
}
/**
* this API method was added due to a newly discovered vulnerability.
*/
@Override
protected boolean isValidFragment(String fragmentName) {
List<Header> headers = new ArrayList<>();
/** fill fragments list */
tryObtainValidFragmentList(getValidResId(), headers);
/** check id valid */
return validFragmentList.contains(fragmentName);
}
/** try fill list of valid fragments */
private void tryObtainValidFragmentList(int resourceId, List<Header> target) {
/** check for null */
if(validFragmentList==null) {
/** init */
validFragmentList = new ArrayList();
} else {
/** clear */
validFragmentList.clear();
}
/** load headers to list */
loadHeadersFromResource(resourceId, target);
/** set headers class names to list */
for (Header header : target) {
/** fill */
validFragmentList.add(header.fragment);
}
}
/** obtain valid res id to build headers */
private int getValidResId() {
/** get session manager */
_sessionManager = SessionManager.getInstance();
/** check if user is authorized */
if (_sessionManager.getCurrentUser().getWebPart().isAuthorized()) {
/** if is return full preferences header */
return R.xml.settings_preferences_header_logged_in;
} else {
/** else return short header */
return R.xml.settings_preferences_header_logged_out;
}
}
}
Вот мой файл headers_preferences.xml:
<?xml version="1.0" encoding="utf-8"?>
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs1Fragment"
android:title="Change Your Name" />
<header
android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs2Fragment"
android:title="Change Your Group' Name" />
<header
android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs3Fragment"
android:title="Change Map View" />
</preference-headers>
В моей PreferencesActivity, где происходит код isValidFragment, я просто повернул его на голову:
@Override
protected boolean isValidFragment(String fragmentName)
{
// return AppPreferencesFragment.class.getName().contains(fragmentName);
return fragmentName.contains (AppPreferencesFragment.class.getName());
}
Пока я использую строку AppPreferencesFragment в начале всех имен моих фрагментов, все они проверяются просто отлично.