复合控件
其实,就是自定义view的简单版。
把几个控件拖到一起,形成一个新的控件,下次要用的时候直接使用新控件,而不用重新拖了。好处除了方便之外还可以保证修改起来方便呀,视图风格统一呀之类的。
比如各大app的头部:基本上都是左边一个按钮,右边一个按钮这种~
一、步骤
1、首先定义你的复合控件有哪些属性: 这样是为了让使用控件的人来操作你的控件,设置颜色啊,文字啊等等。
2、新建一个类,就是你的控件的类,完成对刚才的属性的读入和写入,以及一些其他功能,包括按钮点击功能等等。
3、其实复合控件的编写到这里差不多就结束了,剩下最后一步是使用控件,包括命名空间等等。
二、定义属性
1、在values文件夹下新建一个xml文件,用来配置你的属性值。
2、在这个xml里面写上属性值的代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="topBar">
<attr name="leftTextColor" format="color"/>
<attr name="leftBackground" format="reference|color"/>
<attr name="leftText" format="string"/>
<attr name="rightTextColor" format="color"/>
<attr name="rightBackground" format="reference|color"/>
<attr name="rightText" format="string"/>
</declare-styleable>
</resources>
其中declare-styleable的name属性是在后面的类文件里用来找到这个xml文件的。
下面的attr标签就是你定义的属性的名字和它的格式,倒数第二个属性的格式可以用两种,用“|”连接。
为了简单起见,我只定义了左右两个按钮和六个属性,毕竟目的不是好看,是弄懂用法。
到这里,第一步关于属性的配置就完成了。
三、完成控件类
在这里,首先当然是新建一个类,类名我取成TopBar。
接下来就是在这个类里面进行操作了。
同样分为下面几步:
1、读出属性值。
//取出在xml里面用户定义的属性值
TypedArray ta=context.obtainStyledAttributes(attrs,
R.styleable.topBar);//这里的第二个参数就是attrs里面的name属性
//左侧
mleftTextColor=ta.getColor(R.styleable.topBar_leftTextColor, 0x00ffffff);
mleftBackground=ta.getColor(R.styleable.topBar_leftBackground, 0x00000000);
mleftText=ta.getString(R.styleable.topBar_leftText);
//右侧
mrightTextColor=ta.getColor(R.styleable.topBar_rightTextColor, 0x00ffffff);
mrightBackground=ta.getColor(R.styleable.topBar_rightBackground, 0x00000000);
mrightText=ta.getString(R.styleable.topBar_rightText);
//回收ta
ta.recycle();
不知道为什么,感觉这个代码片的颜色很丑。。
一句一句来,首先是第一句,TypedArray里面就有刚才写的属性值,先得到这样一个对象,然后慢慢取就好了。
2、将ta里面的值取出来存放在各个变量里面,代码里面的变量都是在前面声明过,应该也看得懂。
3、然后就是操作你的自定义控件里面的各个小控件
//使用取出来的值为控件赋值
//左侧
mleftButton=new Button(context);
mleftButton.setText(mleftText);
mleftButton.setBackgroundColor(mleftBackground);
mleftButton.setTextColor(mleftTextColor);
//右侧
mrightButton=new Button(context);
mrightButton.setText(mrightText);
mrightButton.setBackgroundColor(mrightBackground);
mrightButton.setTextColor(mrightTextColor);
如上所示,对左右的button进行属性的设置,使用我们得到的值。
4、和自定义view一样,对里面的控件调整一下布局
//对控件进行布局设置
//左侧
mleftParams=new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT); mleftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
addView(mleftButton,mleftParams);
//右侧
mrightParams=new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
mrightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
addView(mrightButton,mrightParams);
以上面的代码为例,就是左右按钮宽高都是wrap_content,一个靠左,一个靠右。
四、使用你的复合控件
难点在于如何在xml文件中使用曾经定义过的那些属性,也就是attrs.xml里面的那些。
只需要在布局文件中加上这句话:
xmlns:custom="http://schemas.android.com/apk/res-auto"
其中custom是自定义的,在下面使用那些属性的时候前面要加上custom,就好像android:id=”@+id/topBar”前面的android一样。
后面的res-auto也可以换成androidmanifest.xml里面的包名,具体的原因我不是很懂,命名空间什么的,我感觉就像是导入包的import一样吧。
然后就可以使用自定义的属性了:
<com.example.compositeview.TopBar
android:id="@+id/topBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
custom:leftTextColor="#ff0000ff"
custom:leftBackground="#ffffffff"
custom:leftText="左侧按钮"
custom:rightTextColor="#ff00ffff"
custom:rightBackground="#ff000000"
custom:rightText="右侧按钮"
/>
就像上面最后几排那样。
到这里,简单的复合控件就搞定了,不过其实还有几个遗留问题。
为什么TypedArray最后一定要recycle();
如何为复合控件里面的按钮或者其他控件添加事件响应或者一些其他复杂的操作,并且允许使用者自定义这些事件动作。
五、其他问题
1、TypedArray为什么一定要recycle();
其实我也不是很懂,不过我去百度了一下,现在算是半懂吧。
就是下面这个回答http://blog.csdn.net/Monicabg/article/details/45014327
和我之前看到的那个Message和obtainMessage类似,就是这些东西不是你自己创建出来的,是从一个池子里面取出来的。
这个池子大家都要用,一方面因为公用,节省了开销;一方面如果你长期霸占着,让管理员来亲自收回,又会增大开销。所以要合理的使用这些池子里的东西,才可以更有效率。
2、如何允许用户添加自定义的事件响应
之前在一本书上看到这样一句话:
没有一件事是一个中间层不能解决的,如果有,那就两个。
说白了,用在这里就是接口的思想:
有一个接口类,控件类里面添加一个方法setListener(),让控件类里面的接口类的对象和用户自定义的接口类的对象相等。
对象里面有什么呢,就是有那些按钮的事件响应代码。
此时,不要忘了在复合控件里面为单独的原始控件添加事件响应,响应后调用接口里面的方法就好:只要用户实现了这个接口,那么就会调用用户重新填写的方法。具体代码就是这样的:
接口类:
package com.example.compositeview;
public interface TopbarClickListener {
void leftClick();
void rightClick();
}
自定义控件里面的setListener方法
public void setOnTopbarClickListener(TopbarClickListener listener){
this.mListener=listener;
}
为左右按钮添加事件监听:
mleftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
mListener.leftClick();
}
});
mrightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
mListener.rightClick();
}
});
最后,就差使用者实现这个接口了:
mTopBar.setOnTopbarClickListener(new TopbarClickListener() {
@Override
public void rightClick() {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "右侧按钮被点了",Toast.LENGTH_LONG).show();
}
@Override
public void leftClick() {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "左侧按钮被点了", Toast.LENGTH_LONG).show();
}
});
最后最后附上效果图