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)在res\values目录下新建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)通过一个单独的类,去加载此段布局文件

Android自定义控件宽高 android自定义组合控件_xml

获取自定义的属性值,并将属性值的内容赋值给自定义控件;

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.效果图

 

Android自定义控件宽高 android自定义组合控件_Android自定义控件宽高_02

       

Android自定义控件宽高 android自定义组合控件_Android自定义控件宽高_03