应用程序

时间:2017-12-20 16:14:28

标签: java android

我在Android工作室中使用偏好设置编写了设置代码,它可以正常使用所有Android版本而不是Android 8.0 Oreo。错误是: E / UncaughtException:java.lang.IllegalArgumentException:找不到片段GeneralPreferenceFragment的id 0x102036d(android:id / prefs)的视图。有没有人知道为什么这个版本的首选项有问题? 我的设置活动是:

private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {

    @Override
    public boolean onPreferenceChange(Preference preference, Object value) {
        String stringValue = value.toString();

        if (preference instanceof ListPreference) {
            // For list preferences, look up the correct display value in
            // the preference's 'entries' list.
            ListPreference listPreference = (ListPreference) preference;
            int index = listPreference.findIndexOfValue(stringValue);

            // Set the summary to reflect the new value.
            preference.setSummary(
                    index >= 0
                            ? listPreference.getEntries()[index]
                            : null);

        } else if (preference instanceof RingtonePreference) {
            // For ringtone preferences, look up the correct display value
            // using RingtoneManager.
            if (TextUtils.isEmpty(stringValue)) {
                // Empty values correspond to 'silent' (no ringtone).
                preference.setSummary(R.string.notification_is_silent);

            } else {
                Ringtone ringtone = RingtoneManager.getRingtone(
                        preference.getContext(), Uri.parse(stringValue));

                if (ringtone == null) {
                    // Clear the summary if there was a lookup error.
                    preference.setSummary(null);
                } else {
                    // Set the summary to reflect the new ringtone display
                    // name.
                    String name = ringtone.getTitle(preference.getContext());
                    preference.setSummary(name);
                }
            }

        } else {
            // For all other preferences, set the summary to the value's
            // simple string representation.
            preference.setSummary(stringValue);
        }

        return true;
    }
};

/**
 * Binds a preference's summary to its value. More specifically, when the
 * preference's value is changed, its summary (line of text below the
 * preference title) is updated to reflect the value. The summary is also
 * immediately updated upon calling this method. The exact display format is
 * dependent on the type of preference.
 *
 * @see #sBindPreferenceSummaryToValueListener
 */
private static void bindPreferenceSummaryToValue(Preference preference) {
    // Set the listener to watch for value changes.
    preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);

    // Trigger the listener immediately with the preference's
    // current value.
    sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
            PreferenceManager
                    .getDefaultSharedPreferences(preference.getContext())
                    .getString(preference.getKey(), ""));
}

boolean mAttachedFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    mAttachedFragment = false;
    super.onCreate(savedInstanceState);
}

/**
 * Set up the {@link android.app.ActionBar}, if the API is available.
 */
private void setupActionBar() {
    ActionBar actionBar = getSupportActionBar();
    if (actionBar != null) {
        // Show the Up button in the action bar.
        actionBar.setDisplayHomeAsUpEnabled(true);
    }
}

/**
 * {@inheritDoc}
 */
@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void onBuildHeaders(List<Header> target) {
    loadHeadersFromResource(R.xml.pref_headers, target);
    setTitle(getString(R.string.activity_settings));
}

@Override
public void onAttachFragment(Fragment fragment) {
    mAttachedFragment = true;
    super.onAttachFragment(fragment);
}

@Override
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);

    //if we didn't attach a fragment, go ahead and apply the layout
    if (!mAttachedFragment) {
        setContentView(R.layout.activity_setting);
        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
    }
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // handle arrow click here
    if (item.getItemId() == android.R.id.home) {
        finish();
    }
    return super.onOptionsItemSelected(item);
}

@Override
public void onResume() {
    super.onResume();

    int recreateCounter = App.getRecreateUICounter();
    if (recreateCounter == 2){
        Intent intent = getIntent();
        startActivity(intent);
        finish();
        overridePendingTransition(0, 0);
    }

    if (recreateCounter > 1) {
        App.decreaseRecreateUICounter();
        Log.v("SETTINGS", "Decreasing! " + App.getRecreateUICounter());
    }
}

/**
 * This method stops fragment injection in malicious applications.
 * Make sure to deny any unknown fragments here.
 */
protected boolean isValidFragment(String fragmentName) {
    return PreferenceFragment.class.getName().equals(fragmentName)
            || GeneralPreferenceFragment.class.getName().equals(fragmentName)
            || NotificationPreferenceFragment.class.getName().equals(fragmentName)
            || PrivacyAndSecurityFragment.class.getName().equals(fragmentName)
            || ChatBackgroundsFragment.class.getName().equals(fragmentName);
}

@Override
public void onConnectionEstablished() {

}

@Override
public void onConnectFailed() {

}

@Override
public void onAuthFailed() {

}

/**
 * This fragment shows general preferences only. It is used when the
 * activity is showing a two-pane settings UI.
 */
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class GeneralPreferenceFragment extends AppCompatPreferenceFragment {

    private Context context;
    private PreferenceScreen languageListener;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.pref_general);
        setHasOptionsMenu(true);
        context = getActivity();

        //Enter button is send
        SwitchPreference enterIsSend = (SwitchPreference)findPreference("enter_is_send_key");
        enterIsSend.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                putPref("enter_is_send_key" , newValue, context);
                return true;
            }
        });

        // Delete all chats listening
        Preference deleteAllChats = findPreference("delete_all_chats_key");
        deleteAllChats.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

                builder.setMessage(R.string.delete_all_chats_warning);

                builder.setPositiveButton(R.string.contact_confirm, new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface dialog, int id) {
                        DatabaseController db = DatabaseController.getInstance(context);
                        db.openDatabase();
                        // Delete all messages in the message table
                        ChatMessage.clearAllMessagesForAllUsers(db);

                        // Delete all chat backgrounds
                        ChatBackground.clearAllBackgroundsForAllUsers(db);

                        // Set all active contacts as inactive
                        ContactData.clearAllActiveContacts(db);

                        db.close();
                    }

                });


                builder.setNegativeButton(R.string.contact_cancel, null);
                builder.show();
                return true;
            }
        });

        // Clear all chats listening
        Preference clearAllChats = findPreference("clear_all_chats_key");
        clearAllChats.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {

                AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                builder.setMessage(R.string.clear_all_chats_warning);

                builder.setPositiveButton(R.string.contact_confirm, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        DatabaseController db = DatabaseController.getInstance(context);
                        db.openDatabase();
                        // Delete all messages in the message table
                        ChatMessage.clearAllMessagesForAllUsers(db);

                        // Delete all chat backgrounds
                        ChatBackground.clearAllBackgroundsForAllUsers(db);
                        db.close();
                    }
                });

                builder.setNegativeButton(R.string.contact_cancel, null);
                builder.show();
                return true;
            }
        });

        //Number picker listening
        final NumberPickerFragment numberPicker =
                (NumberPickerFragment) findPreference("message_text_size_key");

        numberPicker.setSummary(Integer.toString(numberPicker.getValue()));

        numberPicker.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                numberPicker.setSummary(newValue.toString());
                return true;
            }
        });

        //Language setting
        languageListener = (PreferenceScreen) findPreference("UI_LOCALE");
        languageListener.setSummary(LocaleHelper.getLanguage(getPrefString("UI_LOCALE", context)));
        languageListener.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                Intent intent = new Intent(getActivity(), UILanguageActivity.class);
                startActivityForResult(intent, RESULT_EDIT_LANGUAGE);
                return true;
            }
        });
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (requestCode == RESULT_EDIT_LANGUAGE && resultCode == RESULT_OK) {
            if (data.getBooleanExtra("new_UI_language", false)) {
                Intent intent = getActivity().getIntent();
                getActivity().startActivity(intent);
                getActivity().finish();
                getActivity().overridePendingTransition(0, 0);
            }
        }
    }
}

/**
 * This fragment shows notification preferences only. It is used when the
 * activity is showing a two-pane settings UI.
 */
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class NotificationPreferenceFragment extends AppCompatPreferenceFragment {
    private Context context;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.pref_notification);
        setHasOptionsMenu(true);
        context = getActivity();

        //Notification tone is listening
        RingtonePreference ringtonePreference = (RingtonePreference)findPreference("notifications_new_message_key");
        ringtonePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                putPref("notifications_new_message_key", newValue, context);
                return true;
            }
        });

        //Vibrate swtich is listening
        SwitchPreference vibrateSwitch = (SwitchPreference)findPreference("new_message_vibrate_key");
        vibrateSwitch.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                putPref("new_message_vibrate_key", newValue, context);
                return true;
            }
        });

        //LED color is listening
        ListPreference ledColor = (ListPreference)findPreference("led_color_key");
        ledColor.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                putPref("led_color_key", newValue, context);
                return true;
            }
        });

        ListPreference priority = (ListPreference)findPreference("popup_priority_key");
        priority.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                putPref("popup_priority_key", newValue , context);
                return true;
            }
        });

        // Bind the summaries of EditText/List/Dialog/Ringtone preferences
        // to their values. When their values change, their summaries are
        // updated to reflect the new value, per the Android Design
        // guidelines.
        bindPreferenceSummaryToValue(findPreference("notifications_new_message_key"));
        bindPreferenceSummaryToValue(findPreference("led_color_key"));
        bindPreferenceSummaryToValue(findPreference("popup_priority_key"));

    }

}


@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class PrivacyAndSecurityFragment extends AppCompatPreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.xml.pref_privacy);
        setHasOptionsMenu(true);

        SwitchPreference readStatues = (SwitchPreference)findPreference("show_read_statues_key");
        readStatues.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                putPref("show_read_statues_key", newValue , getActivity().getApplicationContext());
                return true;
            }
        });

        ListPreference privacy = (ListPreference)findPreference("button_show_profile_pic_key");
        int index = privacy.findIndexOfValue(getPrefString("button_show_profile_pic_key", getActivity()));
        privacy.setSummary(index >= 0
                        ? privacy.getEntries()[index]
                        : null);

        privacy.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                ListPreference listPreference = (ListPreference) preference;
                int index = listPreference.findIndexOfValue(newValue.toString());

                preference.setSummary(
                        index >= 0
                                ? listPreference.getEntries()[index]
                                : null);

                if (!getPrefString("button_show_profile_pic_key", getActivity()).equals(newValue.toString())) {
                    ProfileHelper.sendProfileToServer(false, getActivity().getApplicationContext());
                }
                return true;
            }
        });


        // Delete all chats listening
        Preference deleteAccount = findPreference("delete_account_key");
        deleteAccount.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

                builder.setMessage(R.string.delete_account_warning);

                builder.setPositiveButton(R.string.contact_confirm, new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface dialog, int id) {
                        // Set init state
                        /*SharedPrefManager.setAccountState(States.ACC_INIT);

                        // Send delete account iq stanza to message server
                        SocketConnection socketConnection = ((App)getActivity().getApplicationContext()).getSocketConnection();

                        if (socketConnection != null) {
                            socketConnection.sendDeleteAccount();
                        }*/
                    }

                });


                builder.setNegativeButton(R.string.contact_cancel, null);
                builder.show();
                return true;
            }
        });
    }
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class ChatBackgroundsFragment extends AppCompatPreferenceFragment {
    private Context context;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context = getActivity();
        addPreferencesFromResource(R.xml.pref_chat_backgrounds);
        setHasOptionsMenu(true);

        //Switches values changes and listening
        final SwitchPreference retainChatSwitch = (SwitchPreference)findPreference("retain_chat_key");
        final ListPreference backgroundQaulWifi = (ListPreference)findPreference("backgrounds_wifi_quality_key");
        final ListPreference backgroundQaulMobileData = (ListPreference)findPreference("backgrounds_mobile_data_quality_key");
        final ListPreference backgroundQaulRoaming = (ListPreference)findPreference("backgrounds_roaming_quality_key");
        final ListPreference backgroundChangingModelRetain = (ListPreference)findPreference("background_changing_retain_chat_key");

        retainChatSwitch.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {

                    putPref("retain_chat_key", newValue, context);
                    return true;
            }
        });


        backgroundChangingModelRetain.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener(){
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                return true;
            }
        });


        backgroundQaulWifi.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                return true;
            }
        });

        backgroundQaulMobileData.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                return true;
            }
        });

        backgroundQaulRoaming.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                return true;
            }
        });

        bindPreferenceSummaryToValue(findPreference("backgrounds_wifi_quality_key"));
        bindPreferenceSummaryToValue(findPreference("backgrounds_mobile_data_quality_key"));
        bindPreferenceSummaryToValue(findPreference("backgrounds_roaming_quality_key"));
        bindPreferenceSummaryToValue(findPreference("background_changing_retain_chat_key"));


    }
}

public static void putPref(String key, Object value, Context context) {
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor = prefs.edit();
    if (value instanceof String) {
        editor.putString(key, (String)value);
    } else if (value instanceof Boolean) {
        editor.putBoolean(key, (Boolean) value);
    } else if (value instanceof Integer) {
        editor.putInt(key, (Integer) value);
    }

    editor.apply();
}


public static String getPrefString(String key, Context context) {
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    return preferences.getString(key, null);
}

public static boolean getPrefBool(String key, Context context) {
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    return preferences.getBoolean(key, false);
}

public static int getPrefInteger(String key, Context context) {
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    return preferences.getInt(key, 0);
}

public static void setDefaultSettings(Context context) {
    // General
    putPref("enter_is_send_key", false, context);
    putPref("message_text_size_key", context.getResources().getInteger(R.integer.number_default_value), context);

    // Background settings
    putPref("retain_chat_key", true, context);
    putPref("background_changing_retain_chat_key", context.getString(R.string.medium_speed_change), context);
    putPref("backgrounds_wifi_quality_key", context.getString(R.string.background_quality_high_value), context);
    putPref("backgrounds_mobile_data_quality_key", context.getString(R.string.background_quality_low_value), context);
    putPref("backgrounds_roaming_quality_key", context.getString(R.string.background_quality_low_value), context);

    // Privacy settings
    putPref("show_read_statues_key", true, context);
    putPref("button_show_profile_pic_key", context.getString(R.string.show_profile_pic_my_contacts_value), context);

    // Notifications
    putPref("mute_chat_key", true, context);
    putPref("notifications_new_message_key", "content://settings/system/notification_sound", context);
    putPref("new_message_vibrate_key", false, context);
    putPref("led_color_key", context.getString(R.string.led_color_cyan_value), context);
    putPref("popup_priority_key", String.valueOf(0), context);
}

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(LocaleContextWrapper.wrap(newBase, SharedPrefManager.getUILocale()));
}

}

更新: 我发现这两行用于在OnPostCreate函数中设置设置布局和工具栏会导致设置崩溃。我在OnCreate上尝试过但仍然遇到了同样的问题。更详细的说,setContentView是主要问题而且只在Oreo !!!

setContentView(R.layout.activity_setting);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));

1 个答案:

答案 0 :(得分:6)

问题原因

出现此问题是因为PreferenceActivity的startPreferencePanel方法假设具有id com.android.internal.R.id.prefs(android:id / prefs) 的布局。它尝试用PreferenceFragment的实例替换此布局。由于您使用setCotentView使用了自定义布局,因此找不到此 com.android.internal.R.id.prefs(android:id / prefs) 并且崩溃了。

当我们不设置自定义布局时,PreferenceActivity使用布局preference_list_content.xml。此布局xml文件具有所需的ID。

为什么这只会在Android Oreo及以上版本中发生?

原因是在Android Oreo之前,PreferenceActivity可以以另一种方式附加新的Fragment(请参阅startPreferencePanel方法下的startWithFragment调用)。

Android 7.1.2:https://android.googlesource.com/platform/frameworks/base/+/android-7.1.2_r36/core/java/android/preference/PreferenceActivity.java#1356

public void startPreferencePanel(String fragmentClass, Bundle args, @StringRes int titleRes,
        CharSequence titleText, Fragment resultTo, int resultRequestCode) {
    if (mSinglePane) {
        startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, 0);
    } else {
        Fragment f = Fragment.instantiate(this, fragmentClass, args);
        if (resultTo != null) {
            f.setTargetFragment(resultTo, resultRequestCode);
        }
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.replace(com.android.internal.R.id.prefs, f);
        if (titleRes != 0) {
            transaction.setBreadCrumbTitle(titleRes);
        } else if (titleText != null) {
            transaction.setBreadCrumbTitle(titleText);
        }
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        transaction.addToBackStack(BACK_STACK_PREFS);
        transaction.commitAllowingStateLoss();
    }
}

Android 8.0.0:https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r1/core/java/android/preference/PreferenceActivity.java#1393

public void startPreferencePanel(String fragmentClass, Bundle args, @StringRes int titleRes,
        CharSequence titleText, Fragment resultTo, int resultRequestCode) {
    Fragment f = Fragment.instantiate(this, fragmentClass, args);
    if (resultTo != null) {
        f.setTargetFragment(resultTo, resultRequestCode);
    }
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(com.android.internal.R.id.prefs, f);
    if (titleRes != 0) {
        transaction.setBreadCrumbTitle(titleRes);
    } else if (titleText != null) {
        transaction.setBreadCrumbTitle(titleText);
    }
    transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    transaction.addToBackStack(BACK_STACK_PREFS);
    transaction.commitAllowingStateLoss();
}

解决方案

如果您要定位Honeycomb及更高版本并为其附加PreferenceFragment,Google建议使用普通的Activity而不是PreferenceActivity。

您可以使用方法addPreferencesFromResource在PreferenceFragment中显示PreferenceScreen。

但是,这会打破UI流程。单击此PreferenceFragment将不会执行任何操作。要解决此问题,您只需在Activity中实现PreferenceFragment.OnPreferenceStartFragmentCallback,并用新片段替换当前添加的片段。

有关详细说明,请参阅我的博文 Fixing IllegalArgumentException: No view found when using PreferenceActivity