Android View体系(九)自定义View

来源:互联网 发布:qt 编程 编辑:程序博客网 时间:2024/06/05 18:39

前言

学习了以上的文章后,接下来我们来讲讲自定义View,自定义View一直被认为是高手掌握的技能,因为情况太多,想实现的效果又变化多端,但它也要遵循一定的规则,我们要讲的就是这个规则,至于那些变化多端的酷炫的效果就由各位来慢慢发挥了。但是需要注意的是凡事都要有个度,自定义View毕竟不是规范的控件,如果不设计好不考虑性能反而会适得其反,另外适配起来可能也会产生问题,笔者的建议是如果能用系统控件的还是尽量用系统控件。

1.自定义View简介

自定义View按照笔者的划分,分为两大类,一种是自定义View,一种是自定义ViewGroup;其中自定义View又分为继承View和继承系统控件两种。这篇文章首先先了解下两大类的其中一种:自定义View。

2.继承系统控件的自定义View

这种自定义View在系统控件的基础上进行拓展,一般是添加新的功能或者修改显示的效果,一般情况下我们在onDraw()方法中进行处理。这里举一个简单的例子:

<code class="hljs java">publicclass InvalidTextView extends TextView {    privatePaint mPaint = newPaint(Paint.ANTI_ALIAS_FLAG);    publicInvalidTextView(Context context) {        super(context);        initDraw();    }    publicInvalidTextView(Context context, AttributeSet attrs) {        super(context, attrs);        initDraw();    }    publicInvalidTextView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initDraw();    }    privatevoid initDraw() {        mPaint.setColor(Color.RED);        mPaint.setStrokeWidth((float)1.5);    }    @Override    protectedvoid onDraw(Canvas canvas) {        super.onDraw(canvas);        intwidth = getWidth();        intheight = getHeight();        canvas.drawLine(0, height /2, width, height /2, mPaint);    }}</code>

这个自定义View继承TextView,并且在onDraw()方法中画了一条红色的横线,接下来在布局中引用这个InvalidTextView:

1

<code class="hljs avrasm">   <com.example.liuwangshu.mooncustomview.invalidtextview android:id="@+id/iv_text" android:textsize="16sp" android:layout_width="200dp" android:layout_height="100dp" android:layout_centerhorizontal="true" android:gravity="center" android:background="@android:color/holo_blue_light"></com.example.liuwangshu.mooncustomview.invalidtextview></code>

这里写图片描述
2.继承系统控件的自定义View
这种自定义View在系统控件的基础上进行拓展,一般是添加新的功能或者修改显示的效果,一般情况下我们在onDraw()方法中进行处理。这里举一个简单的例子:
?

<code class="hljs java">publicclass InvalidTextView extends TextView {    privatePaint mPaint = newPaint(Paint.ANTI_ALIAS_FLAG);    publicInvalidTextView(Context context) {        super(context);        initDraw();    }    publicInvalidTextView(Context context, AttributeSet attrs) {        super(context, attrs);        initDraw();    }    publicInvalidTextView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initDraw();    }    privatevoid initDraw() {        mPaint.setColor(Color.RED);        mPaint.setStrokeWidth((float)1.5);    }    @Override    protectedvoid onDraw(Canvas canvas) {        super.onDraw(canvas);        intwidth = getWidth();        intheight = getHeight();        canvas.drawLine(0, height /2, width, height /2, mPaint);    }}</code>

3.继承View的自定义View
与上面的继承系统控件的自定义View不同,继承View的自定义View实现起来要稍微复杂一些,不只是要实现onDraw()方法,而且在实现过程中还要考虑到wrap_content属性以及padding属性的设置;为了方便配置自己的自定义View还会对外提供自定义的属性,另外如果要改变触控的逻辑,还要重写onTouchEvent()等触控事件的方法。
简单实现继承View的自定义View
按照上面的例子我们再写一个RectView类继承View来画一个正方形:
?

<code class="hljs java">publicclass RectView extends View {    privatePaint mPaint = newPaint(Paint.ANTI_ALIAS_FLAG);    privateint mColor=Color.RED;    publicRectView(Context context) {        super(context);        initDraw();    }    publicRectView(Context context, AttributeSet attrs) {        super(context, attrs);        initDraw();    }    publicRectView(Context context, AttributeSet attrs, intdefStyleAttr) {        super(context, attrs, defStyleAttr);        initDraw();    }    privatevoid initDraw() {        mPaint.setColor(mColor);        mPaint.setStrokeWidth((float)1.5);    }    @Override    protectedvoid onDraw(Canvas canvas) {        super.onDraw(canvas);        intwidth = getWidth();        intheight = getHeight();        canvas.drawRect(0,0, width, height, mPaint);    }}</code>

在布局中引用RectView:

这里写图片描述
1

<code class="hljs avrasm">  <com.example.liuwangshu.mooncustomview.rectview android:id="@+id/rv_rect" android:layout_width="200dp" android:layout_height="200dp" android:layout_centerhorizontal="true" android:layout_margintop="50dp" android:layout_below="@id/iv_text"></com.example.liuwangshu.mooncustomview.rectview></code>

对padding属性进行处理
如果我在布局文件中设置pading属性,发现没有任何的作用,看来还得对padding属性进行处理,只需要在onDraw()方法中稍加修改就可以了,在绘制正方形的时候考虑到padding属性就可以了:
?

<code class="hljs java">@Override    protectedvoid onDraw(Canvas canvas) {        super.onDraw(canvas);        intpaddingLeft=getPaddingLeft();        intpaddingRight=getPaddingRight();        intpaddingTop=getPaddingTop();        intpaddingBottom=getPaddingBottom();        intwidth = getWidth()-paddingLeft-paddingRight;        intheight = getHeight()-paddingTop-paddingBottom;        canvas.drawRect(0+paddingLeft,0+paddingTop, width+paddingRight, height+paddingBottom, mPaint);    }</code>

修改布局文件加入padding属性:

<code class="hljs avrasm">    <com.example.liuwangshu.mooncustomview.rectview android:id="@+id/rv_rect" android:layout_width="200dp" android:layout_height="200dp" android:layout_centerhorizontal="true" android:layout_margintop="50dp" android:layout_below="@id/iv_text" android:padding="10dp"></com.example.liuwangshu.mooncustomview.rectview></code>

这里写图片描述

对wrap_content属性进行处理
修改布局文件,让RectView的宽度分别为wrap_content和match_parent效果都是一样的:

导致这种情况的原因请查看Android View体系(七)从源码解析View的measure流程这篇文章。对于这种情况需要我们在onMeasure()方法中指定一个默认的宽和高,在设置wrap_content属性时设置此默认的宽和高就可以了 @Override

protectedvoid onMeasure(intwidthMeasureSpec, intheightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    intwidthSpecMode = MeasureSpec.getMode(widthMeasureSpec);    intheightSpecMode = MeasureSpec.getMode(heightMeasureSpec);    intwidthSpecSize=MeasureSpec.getSize(widthMeasureSpec);    intheightSpecSize=MeasureSpec.getSize(heightMeasureSpec);    if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){        setMeasuredDimension(400,400);    }elseif(widthSpecMode==MeasureSpec.AT_MOST){        setMeasuredDimension(400,heightSpecSize);    }elseif(heightSpecMode==MeasureSpec.AT_MOST){        setMeasuredDimension(widthSpecSize,400);    }}</code>

需要注意的是setMeasuredDimeon()方法接数的单位是px,来看看效果:
这里写图片描述

自定义属性
android系统的控件以android开头的比如android:layout_width,这些都是系统自带的属性,为了方便配置RectView的属性,我们也可以自定义属性,首先在values目录下创建 attrs.xml





这个配置义了名为RectView的自定义属性组合,我们定义了rect_color属性,它的格式为color,接下来在RectView的构造函数中解析自定义属性的值public RectView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray mTypedArray=context.obtainStyledAttributes(attrs,R.styleable.RectView);
//提取RectView属性集合的rect_color属性,如果没设置默认值为Color.RED
mColor=mTypedArray.getColor(R.styleable.RectView_rect_color,Color.RED);
//获取资源后要及时回收
mTypedArray.recycle();
initDraw();
}

最后修改布局文

<code class="hljs java">packagecom.example.liuwangshu.mooncustomview;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;public class RectView extendsView {    privatePaint mPaint = newPaint(Paint.ANTI_ALIAS_FLAG);    privateint mColor=Color.RED;    publicRectView(Context context) {        super(context);        initDraw();    }    publicRectView(Context context, AttributeSet attrs) {        super(context, attrs);        TypedArray mTypedArray=context.obtainStyledAttributes(attrs,R.styleable.RectView);        //提取RectView属性集合的rect_color属性,如果没设置默认值为Color.RED        mColor=mTypedArray.getColor(R.styleable.RectView_rect_color,Color.RED);        //获取资源后要及时回收        mTypedArray.recycle();        initDraw();    }    publicRectView(Context context, AttributeSet attrs, intdefStyleAttr) {        super(context, attrs, defStyleAttr);        initDraw();    }    privatevoid initDraw() {        mPaint.setColor(mColor);        mPaint.setStrokeWidth((float)1.5);    }    @Override    protectedvoid onMeasure(intwidthMeasureSpec, intheightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        intwidthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        intheightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        intwidthSpecSize=MeasureSpec.getSize(widthMeasureSpec);        intheightSpecSize=MeasureSpec.getSize(heightMeasureSpec);        if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){            setMeasuredDimension(400,400);        }elseif(widthSpecMode==MeasureSpec.AT_MOST){            setMeasuredDimension(400,heightSpecSize);        }elseif(heightSpecMode==MeasureSpec.AT_MOST){            setMeasuredDimension(widthSpecSize,400);        }    }    @Override    protectedvoid onDraw(Canvas canvas) {        super.onDraw(canvas);        intpaddingLeft = getPaddingLeft();        intpaddingRight = getPaddingRight();        intpaddingTop = getPaddingTop();        intpaddingBottom = getPaddingBottom();        intwidth = getWidth() - paddingLeft - paddingRight;        intheight = getHeight() - paddingTop - paddingBottom;        canvas.drawRect(0+ paddingLeft, 0 + paddingTop, width + paddingRight, height + paddingBottom, mPaint);    }}</code>
原创粉丝点击