学习记录中
本来是用PreferenceFragment,发现这东西过期了,提示要用v7下的,然后搜下用哪个包
参考这里:
因为新工程都用的androidx的,所以这个也换成androidx的
迁移到 AndroidX新老库查询
https://blog.csdn.net/qq_26914291/article/details/79917097
androidx.preference:preference:1.0.0
使用
class SettingFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
}
1.简单看下PreferenceFragmentCompat的源码,看下数据的加载过程
private int mLayoutResId = R.layout.preference_list_fragment;//默认布局,下边有
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//省略,就是应用下主题
//然后就是下边这个抽象方法,我们到时候在这里加载xml即可
onCreatePreferences(savedInstanceState, rootKey);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//...可以自定义布局,设置divider,divider高度,最后一个item是否添加divider
mLayoutResId = a.getResourceId(R.styleable.PreferenceFragmentCompat_android_layout,
mLayoutResId);
final Drawable divider = a.getDrawable(
R.styleable.PreferenceFragmentCompat_android_divider);
final int dividerHeight = a.getDimensionPixelSize(
R.styleable.PreferenceFragmentCompat_android_dividerHeight, -1);
final boolean allowDividerAfterLastItem = a.getBoolean(
R.styleable.PreferenceFragmentCompat_allowDividerAfterLastItem, true);
//...后边就是加载布局了,获取id是android.R.id.list_container的容器,
//然后是创建RecyclerView
//省略,设置divider的代码
// If mList isn't present in the view hierarchy, add it. mList is automatically inflated
// on an Auto device so don't need to add it.
if (mList.getParent() == null) {
listContainer.addView(mList);
}
mHandler.post(mRequestFocus);
return view;
}
//另外还有一些可以用到的方法
//在当前的选项里添加新的
public void addPreferencesFromResource(@XmlRes int preferencesResId)
//替换新的
public void setPreferencesFromResource(@XmlRes int preferencesResId, @Nullable String key)
默认布局preference_list_fragment.xml
里边一个FrameLayout容器,到时候会add一个RecyclerView进去,然后还有一个empty的textview
2. 开始写xml
系统提供的还不少,一个一个学习
常用的preference的层级结构
//A container for multiple {@link Preference} objects
public abstract class PreferenceGroup extends Preference
public final class PreferenceScreen extends PreferenceGroup
//Used to group {@link Preference} objects and provide a disabled title above the group.
public class PreferenceCategory extends PreferenceGroup
3.边学边记录
写了个简单的,完事发现没有divider,看源码里有divider啊
仔细看下DividerDecoration的源码
看完发现,要显示divider有两个条件
①非最后一个item的情况,需要同时满足自己isDividerAllowedBelow为true,完事下边紧挨着的那个item的isDividerAllowedAbove为true;
②至于最后一个item,则需要满足isDividerAllowedBelow和mAllowDividerAfterLastItem这两个条件;
这些默认都是false的
private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
final boolean dividerAllowedBelow = holder instanceof PreferenceViewHolder
&& ((PreferenceViewHolder) holder).isDividerAllowedBelow();
if (!dividerAllowedBelow) {
return false;
}
boolean nextAllowed = mAllowDividerAfterLastItem;
int index = parent.indexOfChild(view);
if (index < parent.getChildCount() - 1) {
final View nextView = parent.getChildAt(index + 1);
final RecyclerView.ViewHolder nextHolder = parent.getChildViewHolder(nextView);
nextAllowed = nextHolder instanceof PreferenceViewHolder
&& ((PreferenceViewHolder) nextHolder).isDividerAllowedAbove();
}
return nextAllowed;
}
如何修改默认的divider了
看下PreferenceFragmentCompat的默认值哪来的
getActivity().getTheme().resolveAttribute(R.attr.preferenceTheme, tv, true);
int theme = tv.resourceId;
if (theme == 0) {
// Fallback to default theme.
theme = R.style.PreferenceThemeOverlay;
}
//我们没有设置主题,默认用的就是PreferenceThemeOverlay这个主题,然后我们去这个fragment所在的包的res下找
TypedArray a = mStyledContext.obtainStyledAttributes(null,
R.styleable.PreferenceFragmentCompat,
R.attr.preferenceFragmentCompatStyle,
0);
mLayoutResId = a.getResourceId(R.styleable.PreferenceFragmentCompat_android_layout,
mLayoutResId);
final Drawable divider = a.getDrawable(
R.styleable.PreferenceFragmentCompat_android_divider);
final int dividerHeight = a.getDimensionPixelSize(
R.styleable.PreferenceFragmentCompat_android_dividerHeight, -1);
final boolean allowDividerAfterLastItem = a.getBoolean(
R.styleable.PreferenceFragmentCompat_allowDividerAfterLastItem, true);
这是找到的默认的style
我们可以修改这个,系统的如下
修改后的,放到我们app的style.xml下即可
当然了,这里也可以修改fragment默认加载的布局,不过里边必须有一个id如下的ViewGroup,
还可以有个id是recycler_view的RecyclerView,没有的话代码里会add一个进去
PreferenceCategory
category的title颜色,默认使用的是colorAccent
当然,这些都是可以改的,你可以修改默认的布局,修改默认布局里TextView的style

ListPreference
单选弹框
系统没有提供页面内的单选功能,只有这种弹框,如果需要的话得自定义了
比如需要这种页面内的单选
系统有checkbox的preference,没有radiobutton的,我们可以稍微修改下
看下checkbox的style,最右边的widget替换的是个checkbox
而CheckBoxPreference 里并没有强求是checkbox,判断的都是Checkable,而radioButton也有这属性
public class CheckBoxPreference extends TwoStatePreference
private void syncCheckboxView(View view) {
if (view instanceof CompoundButton) {
((CompoundButton) view).setOnCheckedChangeListener(null);
}
if (view instanceof Checkable) {
((Checkable) view).setChecked(mChecked);
}
if (view instanceof CompoundButton) {
((CompoundButton) view).setOnCheckedChangeListener(mListener);
}
}
如下简单修改即可
//preference_widget_radiobutton布局如下,原本是个CheckBox,我们这里替换成RadioButton
替换完发现没啥太大意义,这玩意和radioButton有区别,这个可以取消选择,RadioButton选中的再点还是选中状态
自定义单选功能
我们自定义一下页面内单选功能,用同一个key,给个默认的value
代码
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Checkable;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
public class RadioButtonPreference extends Preference implements SharedPreferences.OnSharedPreferenceChangeListener{
public RadioButtonPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override//给每个选项设置一个defaultValue,用来判断选中的是哪个
protected String onGetDefaultValue(TypedArray a, int index) {
value=a.getString(index);
return value;
}
private String value;
public String getValue(){
return getPreferenceManager().getSharedPreferences().getString(getKey(),"0");
}
public void saveValue(){
getPreferenceManager().getSharedPreferences().edit().putString(getKey(),value).commit();
}
@Override
public void onAttached() {
super.onAttached();
getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onDetached() {
super.onDetached();
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (TextUtils.equals(getKey(),key)&&view!=null&&view instanceof Checkable) {
((Checkable) view).setChecked(TextUtils.equals(getValue(),value));
}
//单选的preference大家的key都一样,所以监听下改变,修改自己的radioButton的状态即可
}
@Override
protected void onClick() {
super.onClick();//点击的时候还继续是选中状态
saveValue();
if (view!=null&&view instanceof Checkable) {
((Checkable) view).setChecked(true);
}
}
View view;
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
view=holder.findViewById(android.R.id.checkbox);
if (view instanceof Checkable) {
((Checkable) view).setChecked(TextUtils.equals(getValue(),value));
}
}
}
这里点击以后页面不消失的,如果你点击以后想退回上个页面,那么在fragment里处理即可,
如下每个preference点击都会走这里,可以判断下然后做处理
override fun onPreferenceTreeClick(preference: Preference?): Boolean
前边的icon不设置,在pad上也占位的问题
新的这种preference,前边的icon,就算不设置它也会占位的,代码如下
if (mIcon != null) {
imageView.setVisibility(View.VISIBLE);
} else {
imageView.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
}
查下默认值iconSpaceReserved在pad上默认是true,我测试机就是pad
你可以在自定义style里改,也可以在xml里改
暂时不研究这个了,先把已有的代码贴下
class SettingFragment : PreferenceFragmentCompat() ,SharedPreferences.OnSharedPreferenceChangeListener{
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
//加载xml文件
addPreferencesFromResource(R.xml.setting_preference)
}
override fun onResume() {
super.onResume()
preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
preferenceManager.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
println("change==========$key=====${findPreference(getString(R.string.ID_SETTINGS_KEY_MENU_LAYOUT)).title}")
if(TextUtils.equals(key,getString(R.string.ID_SETTINGS_KEY_MENU_LAYOUT))){ findPreference(getString(R.string.ID_SETTINGS_KEY_MENU_LAYOUT)).setSummary(sharedPreferences.getString(key,"-1"))
}
}
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
val result=super.onPreferenceTreeClick(preference);
println("onPreferenceTreeClick====$result==========$preference")
return result
}
//对于Preference下边有子元素的,点击的时候会走这里,然后我们就可以设置跳转【其实就是加载新的preference】
override fun onNavigateToScreen(preferenceScreen: PreferenceScreen?) {
super.onNavigateToScreen(preferenceScreen)
println("onNavigateToScreen============$preferenceScreen====${preferenceScreen?.preferenceCount}")
setPreferenceScreen(preferenceScreen)
(activity as SettingActivity).changeTitle(preferenceScreen?.title?:"")//修改acitivity的标题
}
//activity的onbackPress调用这个方法判断是否需要关闭页面
fun handledBack():Boolean{
println("handledBack==================${preferenceScreen.parent}")
return handleParent(preferenceScreen.parent)
}
//根据当前展示的preference,判断parent是否是PreferenceScreen,是的话展示parent
fun handleParent(preferenceGroup: PreferenceGroup?):Boolean{
if(preferenceGroup==null) return false
if(preferenceGroup is PreferenceScreen){
preferenceScreen=preferenceGroup;
(activity as SettingActivity).changeTitle(preferenceScreen?.title?:"")
return true;
}
if(preferenceGroup is PreferenceCategory){
return handleParent(preferenceGroup.parent)
}
return false;
}
}
xml文件,主要就写了第一个,下边还有2层可以点,其他几个没写