标签:
当我们开发中遇到原生的组件无法满足需求时,我们这时候就应该写自定义View来满足一些特殊的组件需求。自定义View
个人总结自定义View的概念分为两种:
1、在同一个样式的控件组合多处要使用到,我们可以采用原生控件组合一个View供其他地方调用,减少重复代码。
举个栗子:最常见的空白提示页面,一般都是上面是图标下面是文字就可以考虑以上方式。下面直接上代码:
/** *我们这里因为是空白提示页面,上下结构,所以我们选用的是LinearLayout */ <span style="font-size:18px;">public class NavigationView extends LinearLayout { private ImageView iv_nva; private TextView tv_nva_title; public NullDateView(Context context) { super(context); LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.view_nulldate, this);//引用你自己写的xml布局文件 tv_nva_title = (TextView) findViewById(R.id.tv_nva_title); iv_nva = (ImageView) findViewById(R.id.iv_nva); } /* *这样我们就可以传入不同的参数,实现不同显示的空白提示页面了 */ public void setData(int ivID,String title){ tv_nva_title.setText(title); iv_nva.set.setImageResource(ivID); } }</span>
还得写至少写2个构造函数:
<span style="font-size:18px;">public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); }</span>
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)参数中的widthMeasureSpec和heightMeasureSpec包含宽和高的信息、还包含测量模式。
int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec);我们在设置宽高时有3个选择:wrap_content、match_parent以及指定固定尺寸。
例子:
如果我们要自定义一个View,并且是一个正方形的长宽都是100:
<span style="font-size:18px;">private int getMySize(int defaultSize, int measureSpec) { int mySize = defaultSize; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); switch (mode) { case MeasureSpec.UNSPECIFIED: {//如果没有指定大小,就设置为默认大小 mySize = defaultSize; break; } case MeasureSpec.AT_MOST: {//如果测量模式是最大取值为size //我们将大小取最大值,你也可以取其他值 mySize = size; break; } case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改变它 mySize = size; break; } } return mySize; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMySize(100, widthMeasureSpec); int height = getMySize(100, heightMeasureSpec); if (width < height) { height = width; } else { width = height; } setMeasuredDimension(width, height); }</span>
<span style="font-size:18px;"><com.ljw.MyView android:layout_width="match_parent" android:layout_height="100dp" android:background="#ff0000" /></span>如果我们不重写OnMeasure那么他就会是宽铺满整个屏幕高为100dp,但是我们重写了OnMeasure,并且设置了其宽高为100dp,所以你不居中设置的android:layout_width="match_parent"是没用的。
重写onDraw()
看方法名就知道,是绘制View的形状。我们可以直接在画板Canvas对象上绘制。
例子:
假设我们需要一个圆形View,结合我们上面重写的onMeasure方法设置的宽高尺寸相等。
<span style="font-size:18px;">@Override protected void onDraw(Canvas canvas) { //调用父View的onDraw函数,因为View这个类帮我们实现了一些 // 基本的而绘制功能,比如绘制背景颜色、背景图片等 super.onDraw(canvas); int r = getMeasuredWidth() / 2;//也可以是getMeasuredHeight()/2,本例中我们已经将宽高设置相等了 //圆心的横坐标为当前的View的左边起始位置+半径 int centerX = getLeft() + r; //圆心的纵坐标为当前的View的顶部起始位置+半径 int centerY = getTop() + r; Paint paint = new Paint(); paint.setColor(Color.GREEN); //开始绘制 canvas.drawCircle(centerX, centerY, r, paint); }</span>小结:以上两个方法就可以实现自定义大小、自定义形状的View了,当然我们真实的需求不仅仅是这些,我们还需要在布局文件中加入自定义的属性例如(android:layout_width)这样的属性。
自定义View的属性attr
首先需要声明自定义属性,在res/values/styles.xml文件里面声明(没有该文件请自行新建):
<span style="font-size:18px;"><resources> <!--name为声明的"属性集合"名,可以随便取,但是最好是设置为跟我们的View一样的名称--> <declare-styleable name="MyView"> <!--声明我们的属性,名称为default_size,取值类型为尺寸类型(dp,px等)--> <attr name="default_size" format="dimension" /> </declare-styleable> </resources></span>属性定义好,我们就可以直接在布局里面直接使用了:
<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:jw="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.ljw.MyView android:layout_width="match_parent" android:layout_height="100dp" jw:default_size="100dp" /> </LinearLayout></span>注意:需要在根标签(LinearLayout)里面设定命名空间,命名空间名称可以随便取,比如"jw",命名空间后面取得值是固定的:"http://schemas.android.com/apk/res-auto"
private int defalutSize; public MyView(Context context, AttributeSet attrs) { super(context, attrs); //第二个参数就是我们在styles.xml文件中的<declare-styleable>标签,即属性集合的标签,在R文件中名称为R.styleable+name TypedArray tArr = context.obtainStyledAttributes(attrs, R.styleable.MyView); //第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称 //第二个参数为默认值 defalutSize = tArr.getDimensionPixelSize(R.styleable.MyView_default_size, 100); //最后记得将TypedArray对象释放回收 a.recycle(); }总结:综合自定义View三点要素(重写onMeasure()、onDraw()、自定义布局属性)基本可以完成自定义View了!
/** * @Arthur Jovial * 2016-06-15 */ public class MyView extends View { private int defalutSize; public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); //第二个参数就是我们在styles.xml文件中的<declare-styleable>标签,即属性集合的标签,在R文件中名称为R.styleable+name TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView); //第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称 //第二个参数为默认值 defalutSize = a.getDimensionPixelSize(R.styleable.MyView_default_size, 100); //将TypedArray对象释放回收 a.recycle(); } private int getMySize(int defaultSize, int measureSpec) { int mySize = defaultSize; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); switch (mode) { case MeasureSpec.UNSPECIFIED: {//如果没有指定大小,就设置为默认大小 mySize = defaultSize; break; } case MeasureSpec.AT_MOST: {//如果测量模式是最大取值为size //我们将大小取最大值,你也可以取其他值 mySize = size; break; } case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改变它 mySize = size; break; } } return mySize; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMySize(defalutSize, widthMeasureSpec); int height = getMySize(defalutSize, heightMeasureSpec); if (width < height) { height = width; } else { width = height; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas);//调用父类的onDraw函数,父类已经帮我们实现了一些比如绘制背景颜色、背景图片等 //也可以是getMeasuredHeight()/2,本例中我们已经将宽高设置相等了 int r = getMeasuredWidth() / 2; //圆心的横坐标为当前的View的左边起始位置+半径 int centerX = getLeft() + r; //圆心的纵坐标为当前的View的顶部起始位置+半径 int centerY = getTop() + r; Paint paint = new Paint(); paint.setColor(Color.GREEN); //开始绘制 canvas.drawCircle(centerX, centerY, r, paint); } }
自定义View了解了,下一篇我们将讲解一下自定义ViewGroup!
标签:
原文地址:http://blog.csdn.net/caihongdao123/article/details/51690285