前言
如果对自定义ViewGroup感兴趣的,可以去查看第一篇文章:
https://my.oschina.net/u/2547914/blog/810722
1.自定义View的属性
2.在View的构造方法中获取我们自定义的属性
3.重写onMesure方法(有的时候重写)
4.重写onDraw方法
下面是一个完成的代码,自定义的View,上面有很想详细的注释,这边就不过多的详解了,我这边只是为了记录一下自己的笔记,如果看到对你有帮助,欢迎留言和关注交流,如若没有,请忽略。
完整代码:
package com.zhjy.hxf.hzcustomview.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import com.zhjy.hxf.hzcustomview.R;
/**
* @author :huangxianfeng on 2016/12/26.
* 实现自定义ImageView的属性等
* 1.自定义View的属性
* 2.在View的构造方法中获取我们自定义的属性
* 3.重写onMesure方法(有的时候重写)
* 4.重写onDraw方法
*/
public class CustomImageView extends View {
private final static String TAG = "CustomImageView";
/**
* 控件的宽
*/
private int mWidth;
/**
* 控件的高
*/
private int mHeight;
/**
* 控件中的图片
*/
private Bitmap mImage;
/**
* 图片的缩放模式
*/
private int mImageScale;
private static final int IMAGE_SCALE_FITXY = 0;
private static final int IMAGE_SCALE_CENTER = 1;
/**
* 图片的介绍
*/
private String mTitle;
/**
* 字体的颜色
*/
private int mTextColor;
/**
* 字体的大小
*/
private int mTextSize;
private Paint mPaint;
/**
* 对文本的约束
*/
private Rect mTextBound;
/**
* 控制整体布局
*/
private Rect rect;
public CustomImageView(Context context) {
super(context);
Log.e(TAG, "n = 1");
}
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.e(TAG, "n = 2");
}
public CustomImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Log.e(TAG, "n = 3");
/**
* TypedArray:获取TypedArray对象来对属性进行操作设置。
*/
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView,defStyle,0);
int n = typedArray.getIndexCount();
Log.e(TAG,"n = "+n);
for (int i = 0; i < n; i++) {
int attr = typedArray.getIndex(i);
switch (attr){
case R.styleable.CustomImageView_image:
Log.e(TAG,"执行了CustomImageView_image");
mImage = BitmapFactory.decodeResource(getResources(),typedArray.getResourceId(attr,0));
break;
case R.styleable.CustomImageView_imageScaleType:
mImageScale = typedArray.getInt(attr,0);
Log.e(TAG,"执行了CustomImageView_imageScaleType");
break;
case R.styleable.CustomImageView_titleText:
mTitle = typedArray.getString(attr);
Log.e(TAG,"执行了CustomImageView_titleText");
break;
case R.styleable.CustomImageView_titleTextColorss:
mTextColor = typedArray.getColor(attr, Color.BLACK);
Log.e(TAG,"执行了CustomImageView_titleTextColorss");
break;
case R.styleable.CustomImageView_titleTextSize:
mTextSize = typedArray.getDimensionPixelSize(attr,(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
16, getResources().getDisplayMetrics()));
Log.e(TAG,"执行了CustomImageView_titleTextSize");
break;
}
}
typedArray.recycle();
typedArray.recycle();
rect = new Rect();
mPaint = new Paint();
mTextBound = new Rect();//对文本的约束
mPaint.setTextSize(mTextSize);
// 计算了描绘字体需要的范围
mPaint.getTextBounds(mTitle, 0, mTitle.length(), mTextBound);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置宽度
* 1.首先要查看测量模式
* 2.查看测量的宽度大小
*/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if (specMode == MeasureSpec.EXACTLY ){
Log.e("xxx", "EXACTLY");
mWidth = specSize;
}else {
/**
* 由图片决定的宽
*/
Log.e(TAG,"onMeasure 执行了 = "+(mImage ==null));
int desireByImg = getPaddingLeft() + getPaddingRight() + mImage.getWidth();
/**
* 由字体决定的宽
*/
int desireByTitle = getPaddingLeft() + getPaddingRight() + mTextBound.width();
if (specMode == MeasureSpec.AT_MOST){
int desire = Math.max(desireByImg,desireByTitle);
mWidth = Math.min(desire,specSize);
Log.e("xxx", "AT_MOST");
}
}
/**
* 设置高度
* 1.首先要查看测量模式
* 2.查看测量的高度大小
*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if (specMode == MeasureSpec.EXACTLY){
mHeight = specSize;
}else{
int desire = getPaddingTop() + getPaddingBottom() + mTextBound.height();
if (specMode == MeasureSpec.AT_MOST){
mHeight = Math.min(desire, specSize);
}
}
//设置宽度和高度给View
setMeasuredDimension(mWidth,mHeight);
}
/**
* 绘制View显示
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 边框
*/
mPaint.setStrokeWidth(4);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.CYAN);
//把画笔设置给画布开始绘画
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint);
/**
* rect是绘制整体布局使用的
*/
rect.left = getPaddingLeft();
rect.right = mWidth - getPaddingRight();
rect.top = getPaddingTop();
rect.bottom = mHeight - getPaddingBottom();
mPaint.setColor(mTextColor);
mPaint.setStyle(Paint.Style.FILL);
/**
* 当前设置的宽度小于字体需要的宽度,将字体改为xxx...
*/
if (mTextBound.width() > mWidth){
TextPaint paint = new TextPaint();
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,null);
}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);
}
}
}
其中牵扯到了自定义属性,这也是在我们编程中有时候经常遇到的问题:
首先要在values文件中新建一个attrs.xml布局文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 关于 attr属性自定义详解
(1)attr的节点有两个属性,name 和format,name是属性的名称,format是属性自定的一个类型
(2)format:的类型有以下几种
1.reference:这个是引用类型比如<attr name="icon" format="reference"/>
2.color:是颜色的类型。
3.String:是字符串的类型
4.dimension:是尺寸的类型,主要是用于尺寸的大小设置
-->
<attr name="titleText" format="string"/>
<attr name="titleTextSize" format="dimension"/>
<attr name="titleTextColorss" format="color"/>
<attr name="image" format="reference"/>
<attr name="imageScaleType">
<enum name="fillXY" value="0"></enum>
<enum name="center" value="1"></enum>
</attr>
<!--这个位置只要attr属性的name,而不需要format属性,这里只是为了加强-->
<declare-styleable name="CustomImageView">
<attr name="titleText" />
<attr name="titleTextSize" />
<attr name="titleTextColorss" />
<attr name="image" />
<attr name="imageScaleType" />
</declare-styleable>
</resources>
然后在自定义的View中找到attrs中定义的属性:
/**
* TypedArray:获取TypedArray对象来对属性进行操作设置。
*/
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView,defStyle,0);
int n = typedArray.getIndexCount();
Log.e(TAG,"n = "+n);
for (int i = 0; i < n; i++) {
int attr = typedArray.getIndex(i);
switch (attr){
case R.styleable.CustomImageView_image:
Log.e(TAG,"执行了CustomImageView_image");
mImage = BitmapFactory.decodeResource(getResources(),typedArray.getResourceId(attr,0));
break;
case R.styleable.CustomImageView_imageScaleType:
mImageScale = typedArray.getInt(attr,0);
Log.e(TAG,"执行了CustomImageView_imageScaleType");
break;
case R.styleable.CustomImageView_titleText:
mTitle = typedArray.getString(attr);
Log.e(TAG,"执行了CustomImageView_titleText");
break;
case R.styleable.CustomImageView_titleTextColorss:
mTextColor = typedArray.getColor(attr, Color.BLACK);
Log.e(TAG,"执行了CustomImageView_titleTextColorss");
break;
case R.styleable.CustomImageView_titleTextSize:
mTextSize = typedArray.getDimensionPixelSize(attr,(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
16, getResources().getDisplayMetrics()));
Log.e(TAG,"执行了CustomImageView_titleTextSize");
break;
}
在最上面也有很完整的代码,在自定义的属性里面,也有不同表现的使用详解。
欢迎大家留言和关注交流。