自定义控件之定义UI模板
前言:
以前学过一点自定义控件,但是好久没有用,早都忘记了,前几天抽空看了个视频,在慕课网上的,链接是http://www.imooc.com/learn/247 就跟着这个视频重新学了一遍,俗话说好记性不如烂笔头。所以就把里面的步骤都一步一步的记录了下来,给自己以后做参考。也希望能给有需要的人一点帮助:
主要包括以下步骤:
一:设计需要的属性
在value文件夹下,建立一个arrts.xml的文件,
声明declare-styleable属性,来告诉系统,这个是我们自定义的属性。
定义attr标签,给每个属性起个名字和对应的格式
name是表示你定义的属性的名字。format表示我们在xml中引用的类型,
例如:
<attr name="title" format="string"/> 就表示定义一个属性名为title,用来表示标题。类型是string类型的,给这个属性赋值的时候,就可以引用string.xml文件中的值了
<attr name="titleTextSize"format="dimension" /> 定义属性为titleTextSize用来表示标题字体的大小,类型是尺寸,给这个属性赋值的时候,就可以使用R.dimen.
<attr name="titleTextColor"format="color" /> 定义属性名为titleTextColor,用来表示标题文字颜色,类型是color,就可以使用color.xml文件中的数据了
自定义的attr.xml文件代码:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--通过声明declare-styleable 属性,来告诉系统,这个是我们自定义的属性,-->
<declare-styleable name="Topbar">
<!--定义attr标签,来定义第一个属性,为名字为title,用来表示标题的文字,-->
<attr name="title"format="string" />
<attrname="titleTextColor" format="color" />
<attrname="leftText" format="string" />
<!--定义背景,不仅仅是color属性,还定义来一个reference属性,因为我们引用背景的时候,不仅仅可以使用十六进制的颜色代码,@color-->
<!--还可以引用资源中的一个文件 @drawbable-->
<attrname="leftBackgroud" format="reference|color" />
<attrname="leftTextColor" format="color" />
<attrname="rightText" format="string" />
<attrname="rightBackgroud" format="reference|color" />
<attrname="rightTextColor" format="color" />
</declare-styleable>
</resources>
二:实现一个我们的View
自定义的类名为TopBar,继承了RelativeLayout ,并重写了其中一个构造方法,RelativeLayout中有三个构造方法的,而且这三个中没有无参构造函数,所以根据java语法,是至少需要重写其中一个的,我们这里必须要重写的就是带有AttributeSet 这个参数的构造函数,因为这个参数就是我们需要的属性
即:
publicTopBar(Context context, AttributeSet attrs) {
super(context,attrs);
}
在构造方法中,我们需要做的又以下几件事情
1.通过obtainStyledAttributes()根据上下文获得TypedArray对象
obtainStyledAttributes()接受两个参数,
第一个参数就是构造函数中的AttributeSet对象,
后一个就是我们在xml文件中定义的属性集合,即attr.xml文件,
系统就是通过这个方法,把我们在xml文件中定义的属性值映射到属性集合中去,返回了一个TyperdArray对象ta,这样,我们就可以通过ta取得相应的属性的值,
2.从TypedArray中取得相应的值赋给相应的变量
其实这一步就可以理解是这样的,定义一个textView,在定义该Textview中,使用到了android:text=“自定义控件”;那么我们通过ta.getString(属性名)就得到了相应的值,还记得我们定义的属性的format格式吗,这个时候就用到了,取出该属性值就可以通过相应的方法了
属性名的规定:R.styleable.属性集合的名_属性名
例如:R.styleable.Topbar_leftTextColor 就表示Topbar下面的 leftTextColor属性
定义的是format类型的变量,就可以通过相应的方法获取
例如:
leftTextColor =ta.getColor(R.styleable.Topbar_leftTextColor, 0);// format类型是color,默认的是0
leftBackground =ta.getDrawable(R.styleable.Topbar_leftBackground);//format类型是Drawable
leftText = ta.getString(R.styleable.Topbar_leftText);//format类型是字符串
titleTextSize =ta.getDimension(R.styleable.Topbar_titleTextSize, 0f);//format类型是float
记得最后要调用TypedArray的recycle()方法,对象进行回收,避免浪费资源,避免由于缓存引起的一些错误
代码如下:
TypedArray ta =context.obtainStyledAttributes(attrs, R.styleable.Topbar);
leftTextColor =ta.getColor(R.styleable.Topbar_leftTextColor, 0);
leftBackground =ta.getDrawable(R.styleable.Topbar_leftBackground);
leftText =ta.getString(R.styleable.Topbar_leftText);
//为左边和右边的button找到了我们自定义的属性的值
rightTextColor = ta.getColor(R.styleable.Topbar_rightTextColor,0);
rightBackground =ta.getDrawable(R.styleable.Topbar_rightBackground);
rightText =ta.getString(R.styleable.Topbar_rightText);
//定义位子的大小,使用getDimension
titleTextSize =ta.getDimension(R.styleable.Topbar_titleTextSize, 0f);
titleTextColor =ta.getColor(R.styleable.Topbar_titleTextColor, 0);
title =ta.getString(R.styleable.Topbar_title);
ta.recycle();//对象进行回收,避免浪费资源,避免由于缓存引起的一些错误
3.实例化控件
自定义的ui模板是什么样的,自己心里肯定有数,有几个TextView,几个Button,等,肯定是事先知道的,name在这个时候,你就需要实例化这些控件
例如在这个布局中,需要两个Button,左边一个,右边一个,中间是一个TextView,那么就需要实例化这三个控件
leftButton = new Button(context);
rightButton = new Button(context);
tvTitle = new TextView(context);
4.给控件赋值,
因为之前已经通过TypedArray得到自定义属性的值了,这个时候就需要给相应的控件进行赋值,这就和我们普通的设置值没有什么两样了,这就没什么好说的了
//对左边button进行一些设置
leftButton.setTextColor(leftTextColor);
leftButton.setText(leftText);
leftButton.setBackground(leftBackground);
//对右边button进行的一些设置
rightButton.setTextColor(rightTextColor);
rightButton.setText(rightText);
rightButton.setBackground(rightBackground);
//对title进行的一些设置
tvTitle.setTextColor(titleTextColor);
tvTitle.setText(title);
tvTitle.setTextSize(titleTextSize);
tvTitle.setGravity(Gravity.CENTER);//设置标题文字居中显示
setBackgroundColor(0xFFFFCC1F);//设置背景颜色
5.排版
控件也定义好了,相应的值也赋上去了,那么现在就需要把这些控件放到自定义UI模板上去了
这就需要用到一个类Layoutparams,设置该控件的相应参数。通过addView方法,把每个控件添加到模板布局上去。
通过Layoutparams 方式把一个控件添加到VIewGroup中
leftParams = new LayoutParams(LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
leftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);//这里的true并不是我们通常说的真假,而是RelativeLayout定义的一个常量,RelativeLayout.TRUE,表示-1,0表示false, 或者其它View 的Id,如果有其他view的id,就表示是相对于该id的布局
//这样我们就定义了一个属性,
addView(leftButton, leftParams);//这样就把leftButton,以leftParams的形式加入到了viewGroup中了
rightParams = new LayoutParams(LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
rightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
addView(rightButton, rightParams);
titleParams = new LayoutParams(LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.MATCH_PARENT);
titleParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
addView(tvTitle, titleParams);
这样我们就初步定义了一个自定义UI模板。定义好,之后,我们就开始使用这个UI模板
三:引用我们的View
1.引用第三方命名空间
怎么使用我们自定义的模板呢?
想想我们在使用Android提供的控件的时候,想定义该控件的某个属性时候,通常就是 android:****=“” 这样的格式的,这个android是什么呢?按住ctrl,鼠标结果指向了布局根节点的 xmlns:android=http://schemas.android.com/apk/res/android
这个xmlns 其实就是xml的命名空间,我理解的是和我们写java类中的导入包是一样的吧,如果想引用我们自己定义的UI模板,那么就需要把我们自定义的模板类引用到该布局中,在Android中引用第三方的命名空间,需要
xmlns:custom="http://schemas.android.com/apk/res-auto"
其中只有custom是可以自定义的,就是你定义的名字,其他都是规定死的,
但是在不同的IDE 上,语法稍有不同:eclipse上最后面需要跟上类全名(没测试,我用的是studio,没有添加类全名,也可以使用)
Android Studio "http://schemas.android.com/apk/res-auto"
eclipse “http://schemas.android.com/apk/res-auto /类全名”
2.使用自定义模板
一切就绪,我们就可以像使用一般的控件那样使用我的自定义模板了,在我们自定义的模板中,也可以使用Android自带的一些属性的,例如id,layout_width,layout_height ,这些是Android自带的属性,开头都是以android开头的,而我们自定义的属性,开头都是之前定义的custom,我们都知道,引用自定义的控件,就需要使用类全名的,整体代码如下:
<com.example.hoyouly.mytopbar.TopBar
android:id="@+id/topbar"
android:layout_width="wrap_content"
android:layout_height="50dp"
custom:leftBackground="#0000ff"
custom:leftText="后退"
custom:leftTextColor="#FFFFFF"
custom:rightBackground="#0000ff"
custom:rightText="测试"
custom:rightTextColor="#FFFFFF"
custom:title="自定义标题"
custom:titleTextColor="#ff0000"
custom:titleTextSize="10sp"
/>
这样,我们就可以在类中像使用一般的控件那样使用我们自定义的模板了
topBar = (TopBar) findViewById(R.id.topbar);
3.更多功能设置
当然,我们还可以对我们的自定义模板进行更多的功能设置。例如设置按钮是否隐藏,等
这样我们就需要对外提供一些方法
例如:设置左按钮是否隐藏:
public void setLeftButtonIsVisable(boolean flag){
if(flag){
leftButton.setVisibility(View.VISIBLE);
}else{
leftButton.setVisibility(View.INVISIBLE);
}
}
这样,就可以在主类中使用了
topBar.setLeftButtonIsVisable(false); 坐按钮隐藏
四:接口回调机制:
为了使我们UI模板更加完美,增加动态响应效果,就需要让我们UI模板设计的像普通的控件能够处理点击,当然,这些可以在我们模板中处理,可是如果每次都再模板中处理,那么就失去了模板的作用,那该怎处理这些点击事件呢,这就需要接口回调机制的帮助了。
在我们定义一个普通Button的点击事件的时候,通常是调用setOnClickListener()方法,里面接受的是一个接口对象,实现里面的方法,从而实现接口回调。虽然我不知道你要做什么事情,但是你只要实现这个接口就行
举个不知道是否合适的例子,你想找朋友借车,朋友说,我不想知道你干嘛去,但是你必须得帮我把这包东西送给某某才行,不让我就不借给你。
同样的,你要像设置点击事件,Button不想知道,也不愿意知道你点击他要干嘛,但是你必须要实现我定义个规则才行
这里定义接口回调机制其实也挺简单的;
只需要三步骤就可以了,
1. 定义一个接口
首先需要定义一个接口,在Topbar中定义一个接口TopBarClickListener
这里我们就定义了两个方法,点击左按钮和右按钮
public interface TopBarClickListener{
public void leftClick();//点击左按钮
public void rightClick();//点击右按钮
}
2. 对外提供方法进行设置
在TopBar中提供设置接口的方法,
public voidsetOnTopbarClickListener(TopBarClickListener topbarClickListener){
this.topBarClickListener=topbarClickListener;
}
3. 对内调用该接口中的方法
在Topbar处理左右按钮的点击事件的时候,直接调用相应的方法就可以
leftButton.setOnClickListener(newOnClickListener() {
@Override
public void onClick(View view) {
topBarClickListener.leftClick();
}
});
rightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(context,"right Button",Toast.LENGTH_LONG).show();
topBarClickListener.rightClick();;
}
});
那么我们在使用点击事件的时候,只需要传递给TopBar一个实现TopBarClickListener接口的对象就可以了,至于这个接口中做的什么事情,就没有必要管了,
topBar.setOnTopbarClickListener(newTopBar.TopBarClickListener() {
@Override
public void leftClick() {
Toast.makeText(MainActivity.this, "leftButton",Toast.LENGTH_LONG).show();
}
@Override
public void rightClick() {
Toast.makeText(MainActivity.this, "leftButton",Toast.LENGTH_LONG).show();
}
});
这样就完成了一个接口回调了。
五:总结
不知道的是,看完这个视频,自己学到的自定义UI模板的步骤:最主要的是又一次加深理解了接口回调机制。记录一下。自己洋洋洒洒写了这么多,很少会有人会认真看完吧。