自定义组合控件SettingItemView的简单实现

package com.loaderman.settingitemviewdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private SettingItemView siv1;
    private SettingItemView siv2;
    private SettingItemView siv3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        siv1 = (SettingItemView) findViewById(R.id.siv_test1);
        siv2 = (SettingItemView) findViewById(R.id.siv_test2);
        siv3 = (SettingItemView) findViewById(R.id.siv_test3);
        siv1.setTitle("测试1");
        siv1.setToggleOn(true);
        siv2.setToggleOn(false);

        siv1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                siv1.toggle();
            }
        });
        siv2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (siv2.isToggleOn()) {
                    siv2.setToggleOn(false);
                } else {
                    siv2.setToggleOn(true);
                }

            }
        });
        siv3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    }
}
package com.loaderman.settingitemviewdemo;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

/**
 *
 * <p/>
 * 如果一个控件A包含了控件B, 那么A就是B的父控件, B是A的子控件; 和继承没有任何关系, 只是一种称谓而已.
 * <p/>
 * 自定义组合控件:
 * <p/>
 * 1. 写一个类继承ViewGroup(LinearLayout, RelativeLayout)
 * 2. 给当前空布局添加布局对象View.inflate(getContext(), R.layout.setting_item_view, this);
 * 3. 添加方法, 修改标题和背景
 * 4. 维护开关状态, boolean isOpen, 点击控件,切换开关状态,并更新开关图片
 *
 * 自定义属性:
 * 1. 在values中创建attrs.xml文件, 配置自定义属性
 * 2. 在布局文件中声明命名空间, 并给相关控件配置自定义属性
 * 3. 在自定义控件中, 从属性集合中获取自定义属性,并更新相关控件
 */
public class SettingItemView extends RelativeLayout {

    private TextView  tvTitle;
    private ImageView ivToggle;

    private boolean isOpen = false;//标记当前开关状态

    private static final String NAMESPACE = "http://schemas.android.com/apk/res-auto";

    public SettingItemView(Context context) {
        this(context, null);
    }

    public SettingItemView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public SettingItemView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();

        //1.从AttributeSet中取出自定义的属性
        String title = attrs.getAttributeValue(NAMESPACE, "title");
        int bgId = attrs.getAttributeIntValue(NAMESPACE, "bg", 0);//0,1,2
        boolean showToggle = attrs.getAttributeBooleanValue(NAMESPACE, "show_toggle", true);

        //2.根据自定义属性的值更新相关控件
        setTitle(title);//修改标题

        //修改背景
        switch (bgId) {
            case 0:
                setBackgroundResource(R.drawable.first_selector);
                break;
            case 1:
                setBackgroundResource(R.drawable.middle_selector);
                break;
            case 2:
                setBackgroundResource(R.drawable.last_selector);
                break;
        }

        //控制开关的显示和隐藏
        ivToggle.setVisibility(showToggle ? VISIBLE : GONE);

    }

    private void initView() {
        //给当前空布局添加布局对象
        //A view group that will be the parent
        //参3: 如果是null, 表示加载出的布局对象没有父控件, 绝大部分情况下都传null
        //如果是this,代表在加载布局对象时, 以当前的SettingItemView为父控件; 这样写也可以实现给SettingItemView填充布局的效果
        View view = View.inflate(getContext(), R.layout.setting_item_view, this);
        //addView(view);

        tvTitle = (TextView) view.findViewById(R.id.tv_title);
        ivToggle = (ImageView) view.findViewById(R.id.iv_toggle);
    }

    //公开一个方法,供外界修改标题
    public void setTitle(String title) {
        tvTitle.setText(title);
    }

    //获取当前开关状态
    public boolean isToggleOn() {
        return isOpen;
    }

    //修改当前开关状态
    public void setToggleOn(boolean isOpen) {
        this.isOpen = isOpen;
        System.out.println("当前开关状态:" + isOpen);
        //修改开关图片
        ivToggle.setImageResource(isOpen ? R.drawable.on : R.drawable.off);
    }

    //如果开,则关; 如果关,则开
    public void toggle() {
        setToggleOn(!isOpen);
    }
}

 自定义属性.在values/下新建attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SettingItemView">
        <!--自定义标题属性-->
        <attr name="title" format="string"/>
        <!--自定义背景属性, 枚举-->
        <attr name="bg">
            <enum name="first" value="0" />
            <enum name="middle" value="1" />
            <enum name="last" value="2" />
        </attr>

        <!--自定义是否显示开关的属性-->
        <attr name="show_toggle" format="boolean"/>

    </declare-styleable>

</resources>

 activtity_main.xml,使用自定义属性记得添加命名空间

 xmlns:YOU_NAME="http://schemas.android.com/apk/res-auto"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:loaderman="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp"
    tools:context="com.loaderman.settingitemviewdemo.MainActivity">

    <com.loaderman.settingitemviewdemo.SettingItemView
        android:id="@+id/siv_test1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        loaderman:bg="first"
        />
    <com.loaderman.settingitemviewdemo.SettingItemView
        android:id="@+id/siv_test2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        loaderman:title="测试2"
        loaderman:bg="middle"
        />

    <com.loaderman.settingitemviewdemo.SettingItemView
        android:id="@+id/siv_test3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        loaderman:bg="last"
        loaderman:title="测试3"
        loaderman:show_toggle="false"
        />
</LinearLayout>

 setting_item_view.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"
    android:paddingBottom="10dp"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    android:paddingTop="10dp"
    >

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:textColor="@color/black"
        android:textSize="18sp"
        />

    <ImageView
        android:id="@+id/iv_toggle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:src="@drawable/off"
        />

</RelativeLayout>

实现效果:


原文地址:https://www.cnblogs.com/loaderman/p/6491132.html