自定义组合控件和在自定义控件中使用自定义属性

时间:2022-04-08 20:40:05
今天,整理了一下我平时的笔记,写一个比较简单的自定义组合控件,仅供小白参考,大神请绕道,希望能够对大家有一些帮助
首先,得明白为什么我们需要自定义组合控件,它是因为原有控件并不能满足开发的需求,或者说并不能达到我们想要的一种效果,这个时候,就需要我们自己定义一些控件,以达到目的
![先来看一下效果](http://img.blog.csdn.net/20160716224219109)

个人总结自定义控件的步骤:
1、先写一个布局,这里我用的是一个相对布局,我这里的相对布局就是根布局了

<?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:layout_marginTop="10dp"
android:paddingBottom="10dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingTop="10dp" >


<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="设置自动更新"
android:textColor="#000000"
android:textSize="16sp" />


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


</RelativeLayout>
2、用一个类去继承这个布局(相对布局),注意一下,这里最好继承这个布局的根布局,因为有可能会用到根布局里的一些属性
public class SettingItemView extends RelativeLayout{}
3、继承布局的这个类会实现三个构造方法,一般情况下,用到那个就从写那个,但是,如果你不知道要用到那个,那么,你可以三个都实现,通过有一个参数和两个参数的构造方法去调用第三个构造方法,因为第三个构造方法系统并不会自动帮我们调用
    //这个构造方法系统不会帮我们调用,需要通过另外两个方法来调用这个方法,那么,这时候,初始化的一些数据就可一个在这个方法中实现
public SettingItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
//通过下面这两个构造方法,去调用上面的那个构造方法
public SettingItemView(Context context, AttributeSet attrs) {
this(context, attrs,0);

}

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

}

下面是自定义组合控件的完整代码`,在这里我给它起名为SettingItemView.java

public class SettingItemView extends RelativeLayout{

public static final String NAMESPACE ="http://schemas.android.com/apk/res/com.xxx.myphonesafe";
private View view;
private TextView tv_title;
private ImageView iv_toggle;
boolean isToggle = false; //记录当前开关的状态

public SettingItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
//读取布局文件中给自定义控件使用的属性
//参数1:布局的命名空间
//参数2:布局中控件的属性
//参数3:默认值,如果在读取控件的属性没有读取到时,就返回默认的值
/**
* backaground、title、isShowToggle为控件的自定义属性
* 自定义属性步骤:
* 1、在res/values下新建一个attrs.xml
* 2、在自定义类中通过attrs去获取这些属性,如attrs.getAttributeIntValue(NAMESPACE, "backgroundres", 0);
* 3、在布局文件中使用这些属性时,需要声明命名空间
* 4、 xmlns:abc="http://schemas.android.com/apk/res/com.xxx.myphonesafe"
* 注意以下三点:
* abc:可以随便更换为其他字符,但是在下面使用属性时必须保证和你更换后的字符串相同
* com.xxx.myphonesafe:对应的是你在清单文件中的包名,必须和清单文件一致,不能随意更改
* NAMESPACE:命名空间必须和在布局中使用的命名空间一致
*/

//获取布局中的属性值
int backaground = attrs.getAttributeIntValue(NAMESPACE, "backgroundres", 0);
String title = attrs.getAttributeValue(NAMESPACE, "title");
boolean isShowToggle = attrs.getAttributeBooleanValue(NAMESPACE, "isshowtoggle", true);


//加载布局文件
initView();
//设置标题
tv_title.setText(title);
//根据属性值设置条目
if(isShowToggle){
iv_toggle.setVisibility(View.VISIBLE);
}else {
iv_toggle.setVisibility(View.INVISIBLE);
}

//设置背景颜色
setBackGround(backaground);

}



private void setBackGround(int backaground) {
switch (backaground) {
case 0: //first
setBackgroundResource(R.drawable.first_normal);
break;

case 1: //middle
setBackgroundResource(R.drawable.middle_normal);
break;
case 2: //last
setBackgroundResource(R.drawable.last_normal);
break;
}
}


public SettingItemView(Context context, AttributeSet attrs) {
this(context, attrs,0);

}

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

}

/**
* 初始化布局文件
*/

private void initView() {
view = View.inflate(getContext(), R.layout.setting_item_view, null);
tv_title = (TextView) view.findViewById(R.id.tv_title); //获取自动更新的id
iv_toggle = (ImageView) view.findViewById(R.id.iv_toggle); //获取开关的id
addView(view);
}

/**
* 判断开关 是否打开
* @param isToggle
* @return
*/

public boolean isToggle(){

return isToggle;
}

public void setToggle(boolean toggle){
//记录当前的开关状态
isToggle = toggle;
if(toggle){
//根据toggle的值切换图片
iv_toggle.setImageResource(R.drawable.on);
}else {
iv_toggle.setImageResource(R.drawable.off);
}
}

}

自定义属性如下:
在res/values下新建一个attrs.xml(attrs.xml这个文件的命名最好为attrs),代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 注意 -->
<!-- value:不要写成 values-->
<!-- string:不要写成 String-->
<declare-styleable name="SettingItemView">
<!-- 用来控制显示的背景图 -->
<attr name="backgroundres">
<enum name="first" value="0" />
<enum name="middle" value="1" />
<enum name="last" value="2" />
</attr>

<!-- 用来控制显示的文字 -->
<attr name="title" format="string" />
<!-- 用来控制是否显示开关 -->
<attr name="isshowtoggle" format="boolean" />


</declare-styleable>
</resources>

4、新建一个布局(暂且命名为a.xml),在布局中使用这个自定义的组合控件
注意,要在这里引用命名空间,因为你用到了自定义属性,而且要和自定义组合控件中的命名空间一致

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:abc="http://schemas.android.com/apk/res/com.xxx.myphonesafe"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >


<TextView
style="@style/TitleStyle"
android:text="设置中心" />

<!-- 怎么在自定义组合控件的布局中获取自定义控件中的自定义属性 注意这种方法 -->
<!-- 注意需要声明命名空间 -->


<!-- 要记得声明命名空间 -->
<!-- com.abc.myphonesafe:对应的是清单文件中的包名 -->
<!--
* 自定义属性步骤:
* 1、在res/values下新建一个attrs.xml
* 2、在自定义类中通过attrs去获取这些属性,如attrs.getAttributeIntValue(NAMESPACE, "backgroundres", 0);
* 3、在布局文件中使用这些属性时,需要声明命名空间
* 4、 xmlns:abc="http://schemas.android.com/apk/res/com.abc.myphonesafe"
* 注意以下两点:
* abc:可以随便更换为其他字符,但是在下面使用属性时必须保证和你更换后的字符串相同
* com.abc.myphonesafe:对应的是你在清单文件中的包名,必须和清单文件一致,不能随意更改
-->


<com.xxx.myphonesafe.ui.SettingItemView
android:id="@+id/siv_auto_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@drawable/color_selector"
abc:backgroundres="first"
abc:isshowtoggle="true"
abc:title="设置是否自动更新" />


<com.xxx.myphonesafe.ui.SettingItemView
android:layout_width="match_parent"
android:layout_height="wrap_content"
abc:backgroundres="last"
abc:isshowtoggle="true"
android:background="@drawable/color_selector"
abc:title="设置拦截骚扰" />


</LinearLayout>

最后,新建一个Activity,在Activity使用这个a.xml
希望能对看到此文的小伙伴有所帮助