1.需求介绍
将已经编写好的布局文件,抽取到一个类中去做管理,下次还需要使用类似布局时,直接使用该组合控件的对象。
优点:可复用。
例如要重复利用以下布局:
<RelativeLayout android:padding="5dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tvSet_title" android:textSize="18dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000" android:text="自动更新设置" /> <TextView android:id="@+id/tvSet_def" android:textSize="18dp" android:textColor="#000" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/tvSet_title" android:text="自动更新已关闭" /> <!--android:layout_alignParentRight="true" 设置控件在父控件的最右边--> <!--android:layout_centerVertical="true" 垂直方向居中--> <CheckBox android:id="@+id/cbSet_1" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <!--实现分割线的效果--> <View android:background="#000" android:layout_marginTop="5dp" android:layout_below="@id/tvSet_def" android:layout_width="match_parent" android:layout_height="1dp"/> </RelativeLayout>
2.事件传递规则与相应规则
SettingActivity对应布局文件的根布局获取点击事件
此事件传递给SettingItemView控件
(1)点击在SettingItemView非CheckBox区域,事件就由SettingItemView去做响应
(2)点击在SettingItemView中CheckBox区域,事件就由SettingItemView传递给CheckBox,由CheckBox去做响应
CheckBox响应当前的点击事件,则SettingItemView就不能再去响应此事件,不能调用onClick方法,去改变状态
解决此问题的方案为:不让checkBox响应点击事件
3.自定义属性
在复用SettingItemView的时候,每一个条目对应的标示,描述内容都不一致。
解决方案:给自定义控件SettingItemView定义属性
(1)在resvalues目录下新建attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="SetttingItemView"> <attr name="destitle" format="string"/> <attr name="desoff" format="string"/> <attr name="deson" format="string"/> </declare-styleable> </resources>
(2)自定义属性的使用
后3个属性就是自定义属性
safeguard替换掉原有android
com.example.administrator.test62360safeguard(工程的包名)必须这样编写,替换掉了android,代表当前应用自定义属性
<com.example.administrator.test62360safeguard.Utils.SetttingItemView xmlns:safeguard="http://schemas.android.com/apk/res/com.example.administrator.test62360safeguard" android:id="@+id/siv_update" android:layout_width="match_parent" android:layout_height="wrap_content" safeguard:destitle="自动更新设置" safeguard:desoff="自动更新已关闭" safeguard:deson="自动更新已开启" >
(3)获取属性值
在自定义控件类(SetttingItemView)中创建获取属性值的方法
/** * @param attrs 自定义属性值 */ private void initAttrs(AttributeSet attrs) { destitle=attrs.getAttributeValue(NAMESPACE,"destitle"); desoff=attrs.getAttributeValue(NAMESPACE,"desoff"); deson=attrs.getAttributeValue(NAMESPACE,"deson"); }
4.实现步骤
(1)将组合控件的布局,抽取到一个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="wrap_content"> <RelativeLayout android:padding="5dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tvSet_title" android:textSize="18dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000" android:text="自动更新设置" /> <TextView android:id="@+id/tvSet_def" android:textSize="18dp" android:textColor="#000" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tvSet_title" android:text="自动更新已关闭" /> <!--android:layout_alignParentRight="true" 设置控件在父控件的最右边--> <!--android:layout_centerVertical="true" 垂直方向居中--> <!--android:clickable="false"--> <!--android:focusable="false"--> <!--android:focusableInTouchMode="false" 让当前的CheckBox不能被点击,即不能相应事件--> <CheckBox android:id="@+id/cbSet_1" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <!--实现分割线的效果--> <View android:background="#000" android:layout_marginTop="5dp" android:layout_below="@id/tvSet_def" android:layout_width="match_parent" android:layout_height="1dp"/> </RelativeLayout> </RelativeLayout>
(2)通过一个单独的类,去加载此段布局文件
获取自定义的属性值,并将属性值的内容赋值给自定义控件;
package com.example.administrator.test62360safeguard.Utils; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.CheckBox; import android.widget.RelativeLayout; import android.widget.TextView; import com.example.administrator.test62360safeguard.R; //通过一个单独的类,去加载此段布局文件,由于布局采用相对布局,所以此类需继承RelativeLayout public class SetttingItemView extends RelativeLayout { CheckBox cbSet_1; TextView tvSet_def; String destitle; String desoff; String deson; private static final String NAMESPACE="http://schemas.android.com/apk/res/com.example.administrator.test62360safeguard"; //使得该方法调用第二个构造方法 public SetttingItemView(Context context) { this(context,null); } //使得该方法调用第三个构造方法 public SetttingItemView(Context context, AttributeSet attrs) { this(context, attrs,0); } public SetttingItemView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //将xml转化为一个view对象,直接添加到当前的SetttingItemView对应的view中 View.inflate(context, R.layout.setting_item_view,this); TextView tvSet_title=findViewById(R.id.tvSet_title); tvSet_def=findViewById(R.id.tvSet_def); cbSet_1=findViewById(R.id.cbSet_1); //获取自定义以及原生属性 initAttrs(attrs); //获取布局文件中定义的字符串,赋值给自定义组合控件的标题 tvSet_title.setText(destitle); tvSet_def.setText(desoff); } /** * @param attrs 自定义属性值 */ private void initAttrs(AttributeSet attrs) { destitle=attrs.getAttributeValue(NAMESPACE,"destitle"); desoff=attrs.getAttributeValue(NAMESPACE,"desoff"); deson=attrs.getAttributeValue(NAMESPACE,"deson"); } //判断SetttingItemView组件是否被选中,true(选中),false(未选中) public boolean isChecked(){ //SetttingItemView组件是否被选中与该自定义组件内部的checkbox的状态绑定 return cbSet_1.isChecked(); } //切换选中状态 public void setChecked(boolean isCheck){ if(isCheck){ cbSet_1.setChecked(isCheck); tvSet_def.setText(deson); }else { cbSet_1.setChecked(isCheck); tvSet_def.setText(desoff); } } }
(3)自定义组合控件在xml使用
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".SettingActivity"> <!--调用可复用的样式--> <TextView style="@style/TitleStyle" android:text="设置中心"/> <!--使用自定义的组合控件--> <com.example.administrator.test62360safeguard.Utils.SetttingItemView xmlns:safeguard="http://schemas.android.com/apk/res/com.example.administrator.test62360safeguard" android:id="@+id/siv_update" android:layout_width="match_parent" android:layout_height="wrap_content" safeguard:destitle="自动更新设置" safeguard:desoff="自动更新已关闭" safeguard:deson="自动更新已开启" > </com.example.administrator.test62360safeguard.Utils.SetttingItemView> <com.example.administrator.test62360safeguard.Utils.SetttingItemView xmlns:safeguard="http://schemas.android.com/apk/res/com.example.administrator.test62360safeguard" android:layout_width="match_parent" android:layout_height="wrap_content" safeguard:destitle="电话归属地显示设置" safeguard:desoff="归属地显示已关闭" safeguard:deson="归属地显示已开启" > </com.example.administrator.test62360safeguard.Utils.SetttingItemView> </LinearLayout>
(4)UI界面应用
package com.example.administrator.test62360safeguard; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import com.example.administrator.test62360safeguard.ViewUtil.SetttingItemView; public class SettingActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_setting); initUpdate(); } private void initUpdate() { final SetttingItemView siv_update=findViewById(R.id.siv_update); siv_update.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //获取之前的选中状态 boolean isCheck=siv_update.isChecked(); //将原有状态取反,设置为当前状态 siv_update.setChecked(!isCheck); } }); } }
5.效果图