1. 前言

近期看见XX款平板上, 设置中手势导航和虚拟三键导航的切换选项,觉得效果做的非常好,然后想在源码中倒腾一下,仿照写一个效果图出来,本篇文章在android11 Settings源码中做的功能,主要是做的UI效果图,具体逻辑可以根据项目需要去实现,也是对自定义Preference的一个总结。

2. 原机效果图

3. 实现步骤

3.1 UI效果图,见5实现效果图

从上到下第一个Preference有点类似于设置模块中的RadioButtonPreference, 左边是一个标题,右侧是一个RadioButton, 在android11上面没有这种Preference直接拿来用,所以需要稍微改动下。

第二个Preference 的UI布局:上面是一个导航样式图片,下面是一个文本演示,点击会跳转到另一个演示动画效果的界面。在设置中也没有现成的Preference,所以这个也需要自定义

3.2 UI布局代码

第一个Preference的 layout 布局图, 里面的颜色,字体大小属性值都可以自己去适配

#1. layout布局文件: navigation_title_preference.xml<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent" android:layout_height="58dp"android:background="@drawable/navigation_preference_bg"><TextViewandroid:id="@+id/navigation_type_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_marginLeft="16dp"android:layout_centerInParent="true"android:textSize="16sp"android:textColor="#FF000000"/><RadioButtonandroid:id="@android:id/checkbox"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_marginRight="30dp"android:layout_centerInParent="true"android:focusable="false"android:clickable="false"/></RelativeLayout>#2.drawable navigation_preference_bg<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:state_pressed="false"      android:drawable="@drawable/navi_pressed_false"/><item android:state_pressed="true"
android:drawable="@drawable/navi_pressed_true"/>
</selector>#3. drawable  navi_pressed_false<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle"><solid android:color="@color/search_bar_background" /><corners android:radius="9dp" />
</shape>#4. drawable  navi_pressed_true<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle"><solid android:color="#1a000000" /><corners android:radius="9dp" />
</shape>

第二个Preference的 layout 布局图:

#layout 布局文件:navigation_preference.xml<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent" android:layout_height="155dp"android:background="@drawable/navigation_preference_bg"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/navigation_type_icon"android:layout_marginTop="26dp"android:layout_width="240dp"android:layout_height="70dp"android:layout_centerHorizontal="true"/><TextViewandroid:id="@+id/demonstration_effect"android:layout_width="90dp"android:layout_height="34dp"android:layout_below="@id/navigation_type_icon"android:layout_marginTop="16dp"android:layout_marginBottom="9dp"android:background="@drawable/navigation_preference_bg"android:gravity="center"android:layout_centerHorizontal="true"android:textColor="#268AFF"android:textSize="16sp"/></RelativeLayout></RelativeLayout>

第三,两个Preference需要用到的属性:自定义标题,自定义图片,自定义文本的属性,需要前期声明, 这样子可以在xml文件直接配置 preference:navigationTitle    preference:naviDrawable  preference:naviDetail 的值。

  #第一个Preference的title属性<declare-styleable name="NavigationTitlePreference"><attr name="navigationTitle" format="string" /></declare-styleable>#第二个Preference的图片和文本的属性<declare-styleable name="NavigationPreference"><attr name="naviDrawable" format="reference" /><attr name="naviDetail" format="string" /></declare-styleable>

3.3 自定义Preference 代码

第一个Preference类似于源生的RadioButtonPreference控件,只是布局不一样,通过继承相同的父类CheckBoxPreference,可以实现自定义的Preference,代码如下:

public class NavigationTitlePreference extends CheckBoxPreference {//Preference的点击事件自定义接口public interface OnClickListener {void onNaviPrefernceClicked(NavigationTitlePreference emiter);}private OnClickListener mNaviTitleOnClickListener;public void setNaviTitleOnClickListener(OnClickListener onClickListener) {mNaviTitleOnClickListener = onClickListener;}private TextView mTextView;private String mNavigationTitle;public NavigationTitlePreference(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.NavigationTitlePreference, 0, 0);// if these are already set that means they were set dynamically and don't need// to be loaded from xmlmNavigationTitle = (mNavigationTitle == null) ?attributes.getString(R.styleable.NavigationTitlePreference_navigationTitle) : mNavigationTitle;setLayoutResource(R.layout.navigation_title_preference);attributes.recycle();}public NavigationTitlePreference(Context context, AttributeSet attrs) {this(context, attrs, TypedArrayUtils.getAttr(context,androidx.preference.R.attr.preferenceStyle,android.R.attr.preferenceStyle));}public NavigationTitlePreference(Context context) {this(context, null);}@Overridepublic void onBindViewHolder(PreferenceViewHolder view) {super.onBindViewHolder(view);mTextView = (TextView)view.findViewById(R.id.navigation_type_title);mTextView.setText(mNavigationTitle);}@Overrideprotected void onClick() {if (null != mNaviTitleOnClickListener) {mNaviTitleOnClickListener.onNaviPrefernceClicked(this);}}
}

,         代码解读:

1. 自定义类是继承于CheckBoxPreference,它里面已经对按钮(比如 RadioButton,  SwitchButton)已经做了事件处理,我们只需要设置按钮的id为:android:id="@android:id/checkbox" 即可, 父类代码如下:

# CheckBoxPreference.java @Overrideprotected void onBindView(View view) {super.onBindView(view);View checkboxView = view.findViewById(com.android.internal.R.id.checkbox);if (checkboxView != null && checkboxView instanceof Checkable) {((Checkable) checkboxView).setChecked(mChecked);}.......}

2.  点击Preference的事件自定义接口
                    public interface OnClickListener {
                        void onNaviPrefernceClicked(NavigationTitlePreference emiter);
                    }

第二个Preference的代码,继承Preference,  组成元素为 一张图片和一个文本,点击文本会跳转到相应的动画演示界面, 不需要图片点击事件, 不需要整个Preference的点击事件


public class NavigationPreference extends Preference {private TextView mTextView;private ImageView mImageView;private int mNavigationDrawable;private String mNavigationDetail;public NavigationPreference(Context context, AttributeSet attrs) {super(context, attrs);init(context, attrs);}public NavigationPreference(Context context) {super(context);init(context, null);}private void init(Context context, AttributeSet attrs) {TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.NavigationPreference, 0, 0);// if these are already set that means they were set dynamically and don't need// to be loaded from xmlmNavigationDrawable = (mNavigationDrawable == 0) ?attributes.getResourceId(R.styleable.NavigationPreference_naviDrawable, 0) : mNavigationDrawable;mNavigationDetail = (mNavigationDetail == null) ?attributes.getString(R.styleable.NavigationPreference_naviDetail) : mNavigationDetail;setLayoutResource(R.layout.navigation_preference);attributes.recycle();}@Overridepublic void onBindViewHolder(PreferenceViewHolder view) {super.onBindViewHolder(view);mTextView = (TextView)view.findViewById(R.id.demonstration_effect);mTextView.setText(mNavigationDetail);mTextView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (getIntent() != null) {Context context = getContext();context.startActivity(getIntent());}}});mImageView = (ImageView)view.findViewById(R.id.navigation_type_icon);mImageView.setImageResource(mNavigationDrawable);//点击整个Preference时,无响应事件的关键代码view.itemView.setClickable(false);}
}

代码解读:

1.  点击整个自定义Preference时,无响应事件的关键代码
            view.itemView.setClickable(false);   //设置为不可点击

为什么呢?原理如下:

在 super.onBindViewHolder(view)中

 public void onBindViewHolder(PreferenceViewHolder holder) {View itemView = holder.itemView;Integer summaryTextColor = null;//设置 itemView 的点击事件itemView.setOnClickListener(mClickListener);.............
}

当点击后,会执行父类Preference.java 中的 performClick方法时,直接return了。

 protected void performClick(View view) {performClick();
}public void performClick() {//当设置不可点击,或者不可选择的时候,就直接return了。        if (!isEnabled() || !isSelectable()) {return;}onClick();if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {return;}PreferenceManager preferenceManager = getPreferenceManager();if (preferenceManager != null) {PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager.getOnPreferenceTreeClickListener();if (listener != null && listener.onPreferenceTreeClick(this)) {return;}}if (mIntent != null) {Context context = getContext();context.startActivity(mIntent);}}

2.  点击文本的响应代码:

@Override
            public void onClick(View v) {
                if (getIntent() != null) {
                    Context context = getContext();
                    context.startActivity(getIntent());
                }
           } //这里的intent是通过 xml文件配置的intent参数,从而跳转到目标界面, 如下:

 <Preferenceandroid:key="xxxx"android:title="xxxxx">// 举个例子 通过配置intent参数,跳转到指定的界面<intent android:action="android.credentials.INSTALL_AS_USER"android:targetPackage="com.android.certinstaller"android:targetClass="com.android.certinstaller.CertInstallerMain"><extra android:name="install_as_uid" android:value="1010" /></intent></Preference>

4. 设置界面代码

自定义的Preference代码完成后,接下来就开始写界面加载的xml文件了

布局文件:navigation_type_settings.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"xmlns:preference="http://schemas.android.com/apk/res-auto"android:key="gesture_system_navigation_type"android:title="@string/system_navigation_title"><com.android.settings.gestures.NavigationTitlePreferenceandroid:key="gesture_navi"//自定义的titlepreference:navigationTitle="@string/system_gesture_navigation_title"/><com.android.settings.gestures.NavigationPreferenceandroid:key="gesture_navi_drawable"//自定义的图片preference:naviDrawable="@drawable/ic_gesture_navigation"//自定义文本preference:naviDetail="@string/demonstration_effect"><intentandroid:action="android.intent.action.MAIN"android:targetPackage="com.android.settings"android:targetClass="com.android.settings.Settings$GestureNaviSettingsActivity"/></com.android.settings.gestures.NavigationPreference><com.android.settings.gestures.NavigationTitlePreferenceandroid:key="virtual_navi"preference:navigationTitle="@string/system_virtual_navigation_title"/><com.android.settings.gestures.NavigationPreferenceandroid:key="virtual_navi_drawable"preference:naviDrawable="@drawable/ic_virtual_navigation"preference:naviDetail="@string/demonstration_effect"><intentandroid:action="android.intent.action.MAIN"android:targetPackage="com.android.settings"android:targetClass="com.android.settings.Settings$VirtualNaviSettingsActivity"/></com.android.settings.gestures.NavigationPreference></PreferenceScreen>

界面加载菜单代码:

public class NavigationTypeSettings extends SettingsPreferenceFragment implementsNavigationTitlePreference.OnClickListener{private static final String KEY_GESTURE = "gesture_navi";private static final String KEY_VIRTUAL = "virtual_navi";private NavigationTitlePreference mGestureNaviPref;private NavigationTitlePreference mVirtualNaviPref;@Overridepublic void onCreate(Bundle icicle) {super.onCreate(icicle);//加载界面addPreferencesFromResource(R.xml.navigation_type_settings);PreferenceScreen root = getPreferenceScreen();mGestureNaviPref = (NavigationTitlePreference)root.findPreference(KEY_GESTURE);mVirtualNaviPref = (NavigationTitlePreference)root.findPreference(KEY_VIRTUAL);//自定义Preference的监听事件mGestureNaviPref.setNaviTitleOnClickListener(this);mVirtualNaviPref.setNaviTitleOnClickListener(this);}//点击Preference的回调方法处理@Overridepublic void onNaviPrefernceClicked(NavigationTitlePreference emiter) {..........}

5. 自定义Preference效果图

6. 总结

本篇文章是对自定义Preference的一个小结,只写了UI界面和点击响应事件,具体的逻辑看项目需求,只是一个抛砖引玉的Demo,设置模块已经有比较成熟的Preference控件,当有自定义的Preference的需求,根据情况继承类似的Preference去造轮子。对于UI事件的处理,本文中有响应整个Preference的事件处理,也有只需要响应其中一个子控件的事件处理,关键还是要多看和理解源码,面向对象编程继承和多态的灵活应用, 后续在工作项目中再多多总结吧。

Android 自定义Preference 讲解相关推荐

  1. android自定义更新,Android 完美解决自定义preference与ActivityGroup UI更新的问题

    之前发过一篇有关于自定义preference 在ActivityGroup 的包容下出现UI不能更新的问题,当时还以为是Android 的一个BUG 现在想想真可笑 .其实是自己对机制的理解不够深刻, ...

  2. android 自定义设置界面,Android 设置界面之 Preference

    Android系统为设置界面的UI提供了一系列的接口,设置界面的部分和Activity是分离的,会有一个PreferenceScreen的对象 是根目录,在其中会包含CheckBoxPreferenc ...

  3. android 自定义switchpreference,Android设置选项开发及自定义Preference样式

    一个完整的Android应用程序都应该提供选项(或者叫偏好设置等等)让用户对APP的表现形式能够进行设置,比如说是否加入用户体验计划,或者是否自动升级.定时提醒.开启自启动.后台运行等等.提供一个好的 ...

  4. Preference组件探究之自定义Preference

    上一篇文章中我们从源码入手讲解了Preference画面展示的原理.这篇文章讲述下官方提供的Preference组件是怎么实现的,以及我们自己如何自定义Preference组件. Preference ...

  5. android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...

    转载:http://blog.csdn.net/xiabing082/article/details/48781489 1.  大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...

  6. Android 自定义ScrollView ListView 体验各种纵向滑动的需求

    1.概述 群里的一个哥们有个需求是这样的:问题:主要功能就是:1.循环的一个滑动:2.每次滑动结束,保持每个Item的完整.然后我当时给他写了个Demo,所有代码都在Activity里面,后期看来其太 ...

  7. android 自定义取色器,【Android自定义View】仿Photoshop取色器ColorPicker(二)

    ColorPicker 一款仿Photoshop取色器的Android版取色器. 前言 上一篇已经简单介绍了ColorPicker的项目结构以及两种颜色空间,接下来我们详细解析一下ColorPicke ...

  8. 【我的Android进阶之旅】Android自定义Lint实践

    背景 2017年8月份的时候,我在公司开始推广Lint.FindBugs等静态代码检测工具.然后发现系统自带的Lint检测的Issue不满足我们团队内部的特定需求,因此去自定义了部分Lint规则.这个 ...

  9. android 自定义xml属性

    Android 自定义组件 Android 提供了非常精致的和非常强大的组件化模型,能够更加方便的构建UI,这些UI组件都是基于基本的layout类:View 和 ViewGroup. 部分能够用的w ...

最新文章

  1. 【UWP】使用 Rx 改善 AutoSuggestBox
  2. Ansible自动化运维笔记1(安装配置)
  3. 阿里云 Ubuntu16.04 部署 LAMP
  4. 分布式文件系统研究-fastDFS安装及配置文件说明
  5. Python熊猫– GroupBy
  6. 拉格朗日差值 - 杜教板子
  7. docker mysql命令大全_Docker命令大全
  8. php js下拉框与文本联动,php mysql js 下拉框 二级联动
  9. Go Web 编程--如何确保Cookie数据的安全传输
  10. 2754. [SCOI2012]喵星球上的点名【后缀数组】
  11. 中心/设置地图缩放以覆盖所有可见的标记?
  12. 让程序员崩溃的一句话。。。
  13. python量化交易策略实例_Python写一个量化股票提醒系统实例
  14. Python学习13 ----Seaborn调色板
  15. 如何将图片存进SQL数据库中以及从数据库读取照片(解决办法)
  16. 机器学习深度学习 常用算法推导
  17. 深度之眼 - Python学习笔记——第四章 组合数据类型
  18. Compound原理
  19. 238. 银河英雄传说(并查集,扩展域)
  20. IBM i 7.2 –超越一切

热门文章

  1. 机械臂控制C语言程序,51单片机的6自由度机械臂 16路舵机控制 源码
  2. FOC Simulink仿真 --- 0.FOC概述及组成部分
  3. 使用python对电力故障录波数据进行滤波再分析
  4. c语言中double sper是什么意思啊,C语言的文件操作的使用
  5. Ubuntu 下 FireFox( 火狐 )无法使用HTML5播放器的解决方法
  6. 计算你恋爱谈了多长时间
  7. GUP Net论文精读
  8. github电脑壁纸_小创意--- C#设置电脑壁纸
  9. Linux热插拔硬盘导致盘符漂移,一种解决热插拔时磁盘盘符漂移方法及装置与流程...
  10. 上海工程技术大学本科毕业论文答辩和论文选题PPT模板