android 自定义view起步之一

来源:互联网 发布:JVM1099端口被占用 编辑:程序博客网 时间:2024/04/29 20:22

    • 一代码解析
    • 二疑问解答
    • 未完待续

本文中用到的例子是来自于http://blog.csdn.net/lmj623565791/article/details/24300125,只是为了方便更多的人了解自定义view的过程对其中的代码进行详细的解释,如果给原作者带来的不便还请谅解,如果本文中,有什么说的不对的地方,还请指正,谢谢。
对于自定义view来说我们通常走的步骤大概分为:

1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
[ 3、重写onMesure ]
4、重写onDraw

我们以上面提到的例子源码进行详细的分析:

一、代码解析

<?xml version="1.0" encoding="utf-8"?><resources>    <!-- 自定义属性 -->    <attr name="titleText" format="string" />    <!-- 前面是自定义属性的名称,后面是可使用的类型,注意大小写 -->    <attr name="titleSize" format="dimension" />    <!-- 这个表示只要是颜色都可以 -->    <attr name="titleColor" format="color" />    <!-- 这个表示只要是资源文件即可 -->    <attr name="image" format="reference" />    <!-- 这种类似于填写相对位置信息时的选项下面的枚举类型就是可选项 -->    <attr name="imageScaleType">        <enum name="fillXY" value="0" />        <enum name="center" value="1" />    </attr>    <!-- 此处用于说明CustomImageView 有哪些属性 -->    <declare-styleable name="customImageView">        <attr name="titleText" />        <attr name="titleSize" />        <attr name="titleColor" />        <attr name="image" />        <attr name="imageScaleType" />    </declare-styleable></resources>

这个是属性文件的内容,在 res/valuse/attr.xml 设置此属性,代码中已经做出了详细的解释,在此先不赘诉。

下面的是整个view的编写代码,也有了详细的注释,稍后对其中的代码片段进行解释。

public class customImageView extends View {    private Bitmap mImage;    private int mImageScale;    private String mTitle;    private int mTextColor;    private int mTextSize;    private Rect rect;    private Paint mPaint;    private Rect mTextBound;    private int mWidth;    private int mHeight;    private static int IMAGE_SCALE_FITXY = 0;    public customImageView(Context context, AttributeSet attrs) {        this(context, attrs, 0);// 此处一定要写成this                                // ,因为你除非设置样式否则都会执行第一或者第二个构造函数,不会经过第三个,所以第一第二个要调用第三个        // TODO Auto-generated constructor stub    }    public customImageView(Context context) {        this(context, null);        // TODO Auto-generated constructor stub    }    public customImageView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        // 获取我们自定义的属性        // 得到属性数组 //attrs 属性集合(哪一个对应那些值) 属性集(前面的那个的哪一个) 默认样式        TypedArray array = context.getTheme().obtainStyledAttributes(attrs,                R.styleable.customImageView, defStyleAttr, 0);// 这个地方传入的R.styleable.customImageView                                                                // 就是需要筛选的集合        int n = array.getIndexCount();        for (int i = 0; i < n; i++) {            int attr = array.getIndex(i); // 获取其中的一个属性            switch (attr) {            case R.styleable.customImageView_image:                // array 通过get 方法,通过传入的标识(attr),获取对应的属性的内容,此处的就是图片资源                mImage = BitmapFactory.decodeResource(getResources(),                        array.getResourceId(attr, 0));                // bitmap工厂类生成bitmap                break;            case R.styleable.customImageView_imageScaleType:                mImageScale = array.getInt(attr, 0);// 此处是整数 //放大倍数                break;            case R.styleable.customImageView_titleText:                mTitle = array.getString(attr);                break;            case R.styleable.customImageView_titleColor:                mTextColor = array.getColor(attr, 0);                break;            case R.styleable.customImageView_titleSize:                mTextSize = array.getDimensionPixelSize(attr, (int) TypedValue                        .applyDimension(TypedValue.COMPLEX_UNIT_SP, 16,                                getResources().getDisplayMetrics()));                break;            default:                break;            }        }        array.recycle(); // 释放内存资源        rect = new Rect(); // 矩形画布        mPaint = new Paint();// 新建画笔        mTextBound = new Rect();// 文字的矩形画布        // 画笔根据文字内容测算文字矩形画布的宽高并赋值给mTextBound(此时是包裹文字的最小矩形)        mPaint.getTextBounds(mTitle, 0, mTitle.length(), mTextBound);    }    // 测量函数    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 此处传入的值由父容器决定        // ---设置控件宽度---        int specMode = MeasureSpec.getMode(widthMeasureSpec); // 获取宽度的设置类型        int specSize = MeasureSpec.getSize(widthMeasureSpec);// 获取宽度大小        if (specMode == MeasureSpec.EXACTLY) {// fill_parent,或者具体值 --type : 精确模式            mWidth = specSize;        } else {//非精确模式            // 由图片决定的宽            int desireByImg = getPaddingLeft() + getPaddingRight()                    + mImage.getWidth();            // 由文字决定的宽            int desireByTitle = getPaddingLeft() + getPaddingRight()                    + mTextBound.width();            if (specMode == MeasureSpec.AT_MOST) {// wrap_content                int desire = Math.max(desireByImg, desireByTitle); // 获取文字和图片之间的最大值,保证能正常显示                mWidth = Math.min(specSize, desire); // 此处的specSize 是父布局剩余的大小                                                        // 选择能正常显示和剩余大小的最小值//也就是说如果剩余值大于最小值此时图片会显示不完整,下面的代码会详细标明            }        }        // ---设置高度---        specMode = MeasureSpec.getMode(heightMeasureSpec);        specSize = MeasureSpec.getSize(heightMeasureSpec);        if (specMode == MeasureSpec.EXACTLY) {            mHeight = specSize;        } else {            int desire = getPaddingTop() + getPaddingBottom()                    + mImage.getHeight() + mTextBound.height();            if (specMode == MeasureSpec.AT_MOST) { // 同上理解                mHeight = Math.min(desire, specSize);            }        }        setMeasuredDimension(mWidth, mHeight);// 设置当前控件的宽高    }    @Override    protected void onDraw(Canvas canvas) {        // ---------绘制一个宽度为4的矩形边框----------------        mPaint.setStrokeWidth(4);// 设置画笔宽度        mPaint.setStyle(Paint.Style.STROKE); // 设置画笔填充样式        mPaint.setColor(Color.CYAN);// 设置画笔颜色        // 绘制一个矩形,左上坐标为0,0 右下为getMeasuredWidth(), getMeasuredHeight()        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);        // --------设置这个控件的4个位置信息--------------------------------------------        rect.left = getPaddingLeft(); // 左上的x 坐标        rect.right = mWidth - getPaddingRight();// 右下的x 坐标 ,看mWidth 的组成                                                // 减去这个getPaddingRight等同于getPaddingLeft()+控件自身所用到的宽度        rect.top = getPaddingTop();// 左上的y        rect.bottom = mHeight - getPaddingBottom(); // 右下的y ,解释同上        // ---------开始绘制文字----------------------        mPaint.setColor(mTextColor);        mPaint.setStyle(Style.FILL);        // 当设置的宽度小于字体需要的宽度,将内容更改为xxx... 后面为省略号的形式        if (mTextBound.width() > mWidth) {            TextPaint paint = new TextPaint(mPaint);            String msg = TextUtils.ellipsize(mTitle,                    paint, // 参数含义 原始内容,画笔,真实宽度,出现“...”的方式(中间或者结尾)                    (float) mWidth - getPaddingLeft() - getPaddingRight(),                    TextUtils.TruncateAt.END).toString();            canvas.drawText(msg, getPaddingLeft(),                    mHeight - getPaddingBottom(), mPaint); // 特别指出        } else {            // 正常情况,将字体居中            canvas.drawText(mTitle, mWidth / 2 - mTextBound.width() * 1.0f / 2,                    mHeight - getPaddingBottom(), mPaint);        }        // 去掉使用掉的高度        rect.bottom -= mTextBound.height();        if (mImageScale == IMAGE_SCALE_FITXY) {            canvas.drawBitmap(mImage, null, rect, mPaint);        } else {            rect.left = mWidth / 2 - mImage.getWidth() / 2; // 图片大于控件宽度的时候会是负值,图片会被裁剪、、也就是说会有一部分显示不出来            rect.right = mWidth / 2 + mImage.getWidth() / 2;            rect.top = (mHeight - mTextBound.height()) / 2 - mImage.getHeight()                    / 2;            rect.bottom = (mHeight - mTextBound.height()) / 2                    + mImage.getHeight() / 2;            canvas.drawBitmap(mImage, null, rect, mPaint);        }    }}

二、疑问解答

下面对其中出现的不太容易理解的地方进行分析:

1. 下面这行代码的含义是什么呢?

    mTextSize = array.getDimensionPixelSize(attr, (int) TypedValue                        .applyDimension(TypedValue.COMPLEX_UNIT_SP, 16,                                getResources().getDisplayMetrics()));

getDimensionPixelSize 这个方法的功能是获取像素值,TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_SP, 16,
getResources().getDisplayMetrics()) 这个代码的含义是将16转化为sp 格式时int 的值。也就是16sp 的int 值。

2. 这个将文字居中怎么理解?

    // 正常情况,将字体居中            canvas.drawText(mTitle, mWidth / 2 - mTextBound.width() * 1.0f / 2,                    mHeight - getPaddingBottom(), mPaint);

首先我们要理解一下canvas.drawText(text, x, y, paint) 这个函数的含义:首先第一个和第四个参数的含义很容易理解,第一个是需要绘制的文本,最后一个是所使用的画笔,x,y 的话按照一般的规律来说应该是文本的位置信息,但是他的x,y 并不是一般的理解思路, 首先y 的确定上跟baseline 有关,不清楚的可以百度一下,这个就不解释了,x 和设置的文字的居中方式有关,如果设置了paint.setTextAlign(Paint.Align.CENTER),则x 的起点默认是字符的中心位置,默认是最左侧。
所以上面的y 的计算其实是计算baseline ,x的位置参考下图:
x的位置理解

未完待续…

0 0
原创粉丝点击