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的位置参考下图:
未完待续…
- android 自定义view起步之一
- Android自定义View之一
- Android自定义View之一
- Android 自定义View起步(转载)
- Android自定义View之一:自定义属性
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- ****Android自定义组件之一:View详解
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- Android自定义View之一:初探实例
- (矩阵快速幂)HDU5667
- 1007 of search
- PHP中use分析
- 关于Windows和Linux的那些事儿
- SVM完全解析
- android 自定义view起步之一
- Android开发 判断长按和点击事件
- 链式异步任务类,解决迷之缩进
- FZU Problem 2227 邮票
- 八大排序算法
- 二叉树先序建树及先序遍历
- Mysql修改root用户密码 For Mac
- 【新版20160418后】校园网实现一键登陆ajax
- 1008 of search