Android View体系(九)自定义View

前言

学习了以上的文章后,接下来我们来讲讲自定义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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值