1. 重写OnMesure()用于测量view宽度和高度
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
当自定义view设置固定数值或者设置为 MATCH_PARENT时不需要重写OnMesure(),系统测量为屏幕大小。 当设置了WRAP_CONTENT时,我们需要自己进行测量。
1.1 重写前先了解下MeasureSpec的specMode
MeasureSpec的specMode,一共三种类型:
- EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
- AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
- UNSPECIFIED:表示子布局想要多大就多大,很少使用
2. 自定义View
layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.lee.myapp.MyviewActivity">
<com.example.lee.myapp.MyTextView
xmlns:my="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="10dp"
android:text="Title"
android:textColor="@android:color/holo_orange_dark"
android:textSize="16sp"
my:mySubText="subTitle"
my:mySubTextColor="@android:color/darker_gray"
my:subMarginTop="10dp"
my:mySubTextSize="12sp"/>
</RelativeLayout>
attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyTextView">
<!--使用android自带语义明确属性 -->
<attr name="android:text"></attr>
<attr name="android:textColor"></attr>
<attr name="android:textSize"></attr>
<!--自定义个性化属性 -->
<attr name="mySubText" format="string"></attr>
<attr name="mySubTextColor" format="color"></attr>
<attr name="mySubTextSize" format="dimension"></attr>
<attr name="subMarginTop" format="dimension"></attr>
</declare-styleable>
</resources>
MyTextView.java
private String mTitleText, mSubTitleText;//内容文字
private int mTitleTextColor, mSubTitleTextColor;//内容文本颜色
private int mTitleTextSize, mSubTitleTextSize;//内容文本大小
private int subMarginTop;//副标题距主标题margin
private Rect mBounds, mSubBounds;//文本区域范围
private Paint mPaint;//画笔
private Paint mSubPaint;//副标题画笔
public MyTextView(Context context) {
super(context);
init(context, null);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTextView);
//屏幕密度
final float density = getResources().getDisplayMetrics().density;
mTitleText = typedArray.getString(R.styleable.MyTextView_android_text);
mTitleTextColor = typedArray.getColor(R.styleable.MyTextView_android_textColor, Color.BLACK);
mTitleTextSize = typedArray.getDimensionPixelSize(R.styleable.MyTextView_android_textSize, (int) (16 * density));
mSubTitleText = typedArray.getString(R.styleable.MyTextView_mySubText);
mSubTitleTextColor = typedArray.getColor(R.styleable.MyTextView_mySubTextColor, Color.BLACK);
mSubTitleTextSize = typedArray.getDimensionPixelSize(R.styleable.MyTextView_mySubTextSize, (int) (12 * density));
subMarginTop = typedArray.getDimensionPixelSize(R.styleable.MyTextView_subMarginTop, (int) (10 * density));
typedArray.recycle();
mPaint = new Paint();
mPaint.setTextSize(mTitleTextSize);
mBounds = new Rect();
//mBound为主标题范围
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBounds);
mSubPaint = new Paint();
mSubPaint.setTextSize(mSubTitleTextSize);
mSubPaint.setColor(mSubTitleTextColor);
//mSubBounds为副标题范围
mSubBounds = new Rect();
mSubPaint.getTextBounds(mSubTitleText, 0, mSubTitleText.length(), mSubBounds);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//最终测量的宽高
int width;
int height;
//EXACTLY模式下直接使用系统测量值
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
//主标题
float textWidth = mBounds.width();
//副标题
float textSubWidth = mSubBounds.width();
int mWidth = (int) (getPaddingLeft() + Math.max(textWidth, textSubWidth) + getPaddingRight());
width = mWidth;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
//主标题
float textHeight = mBounds.height();
//副标题
float textSubHeight = mSubBounds.height();
//textHeight + subMarginTop + textSubHeight 内容全部高度
int mHeight = (int) (getPaddingTop() + (textHeight + subMarginTop + textSubHeight) + getPaddingBottom());
height = mHeight;
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.YELLOW);
//测量完后再布局
canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
mPaint.setColor(mTitleTextColor);
canvas.drawText(mTitleText, getWidth() / 2 - mBounds.width() / 2, (getHeight() - subMarginTop) / 2 , mPaint);
canvas.drawText(mSubTitleText, getWidth() / 2 - mSubBounds.width() / 2,(getHeight() + subMarginTop) / 2 + mSubBounds.height(), mSubPaint);
}
绘制文本函数:canvas.drawText(“”, x, y, paint);
x:默认是字符的左边在屏幕的位置,如果设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心
y:是指定这个字符baseline在屏幕上的位置
最后
简单的自定义view就这样实现了。
自定义View还一个原因是需要适配屏幕,在不同设备上能自动缩放满足要求,接下来加上缩放。