前言:
本篇文章我们结合一个Demo学习自定义View的基本用法
一:自定义View的绘制流程
1:自定义View的属性
自定义View属性在哪里写?
我们要在在/res/values目录下新建一个attrs.xml文件,在attrs文件写自定义View
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DeclareViewOne">
<attr name="mText" format="string" />
<attr name="mTextColor" format="color" />
<attr name="mTextSize" format="dimension" />
</declare-styleable>
</resources>
declare-styleable:给自定义控件添加自定义属性用的。
name:输性名
format:属性取值类型,有以下10种
string:字符串
color:颜色
demension:尺寸值
integer:整型
enum:枚举型
reference:参考指定Theme中资源ID
float:浮点型
boolean:布尔型
fraction:百分数型
flag:位或运算
自定义View属性怎么用?
来看看代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<myview.csdn.com.view.one.ViewOne
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:mText="测试文本"
app:mTextColor="#ff0000"
app:mTextSize="40sp" />
</LinearLayout>
一定要引入xmlns:app="http://schemas.android.com/apk/res-auto"
2:获得我们自定义View的属性
自定义View的属性在自定义View的构造方法中获取。
3:重写onMeasure方法
计算View的高度和宽度
4:重写onDraw方法
绘制View的内容
二 来一个Demo
第一步 自定义View的属性(attrs.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DeclareViewOne">
<attr name="mText" format="string" />
<attr name="mTextColor" format="color" />
<attr name="mTextSize" format="dimension" />
</declare-styleable>
</resources>
第二步 自定义View(ViewOne.class)
package myview.csdn.com.view.one;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import myview.csdn.com.R;
/**
* Created by Administrator on 2017/6/14.
*/
public class ViewOne extends View {
/**
* 内容
*/
private String mText;
/**
* 颜色
*/
private int mTextColor;
/**
* 大小
*/
private int mTextSize;
/**
* 绘制时控制文本绘制的范围
*/
private Rect mRect;
private Paint mPaint;
public ViewOne(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public ViewOne(Context context)
{
this(context, null);
}
/**
* 获得自定义的样式属性
*
* @param context
* @param attrs
* @param defStyle
*/
public ViewOne(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
/**
* 所定义的自定义样式属性
*/
TypedArray mType = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DeclareViewOne, defStyle, 0);
int count = mType.getIndexCount();
for (int i = 0; i < count; i++)
{
int attr = mType.getIndex(i);
switch (attr)
{
case R.styleable.DeclareViewOne_mText:
mText = mType.getString(attr);
break;
case R.styleable.DeclareViewOne_mTextColor:
// 默认颜色设置为黑色
mTextColor = mType.getColor(attr, Color.BLACK);
break;
case R.styleable.DeclareViewOne_mTextSize:
// 默认设置为16sp,TypeValue也可以把sp转化为px
mTextSize = mType.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
mType.recycle();
/**
* 获得绘制文本的宽和高
*/
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
mRect = new Rect();
mPaint.getTextBounds(mText, 0, mText.length(), mRect);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas)
{
mPaint.setColor(Color.BLUE);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
mPaint.setColor(mTextColor);
canvas.drawText(mText, getWidth() / 2 - mRect.width() / 2, getHeight() / 2 + mRect.height() / 2, mPaint);
}
}
这里面有什么,哈哈 获得自定义View的属性,重写onMeasure方法,重写onDraw方法这些都在里面了
第三步 使用 aty_my_view1.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<myview.csdn.com.view.one.ViewOne
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:mText="测试文本"
app:mTextColor="#ff0000"
app:mTextSize="40sp" />
</LinearLayout>
走完三步大功告成,来可以运行了,来一张截图
效果不对我们不是用了wrap_content吗。好下一节我们来完善它
三 修改onMeasure
ViewOne.class
package myview.csdn.com.view.one;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import myview.csdn.com.R;
/**
* Created by Administrator on 2017/6/14.
*/
public class ViewOne extends View {
/**
* 内容
*/
private String mText;
/**
* 颜色
*/
private int mTextColor;
/**
* 大小
*/
private int mTextSize;
/**
* 绘制时控制文本绘制的范围
*/
private Rect mRect;
private Paint mPaint;
public ViewOne(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewOne(Context context) {
this(context, null);
}
/**
* 获得自定义的样式属性
*
* @param context
* @param attrs
* @param defStyle
*/
public ViewOne(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
/**
* 所定义的自定义样式属性
*/
TypedArray mType = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DeclareViewOne, defStyle, 0);
int count = mType.getIndexCount();
for (int i = 0; i < count; i++) {
int attr = mType.getIndex(i);
switch (attr) {
case R.styleable.DeclareViewOne_mText:
mText = mType.getString(attr);
break;
case R.styleable.DeclareViewOne_mTextColor:
// 默认颜色设置为黑色
mTextColor = mType.getColor(attr, Color.BLACK);
break;
case R.styleable.DeclareViewOne_mTextSize:
// 默认设置为16sp,TypeValue也可以把sp转化为px
mTextSize = mType.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
mType.recycle();
/**
* 获得绘制文本的宽和高
*/
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
mRect = new Rect();
mPaint.getTextBounds(mText, 0, mText.length(), mRect);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 设置宽度、设置高度
*/
initSpec(widthMeasureSpec,heightMeasureSpec);
}
/**
* 设置宽度、设置高度
*/
private void initSpec(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
int height = 0;
/**************************宽***************************/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
switch (specMode) {
case MeasureSpec.EXACTLY:
width = getPaddingLeft() + getPaddingRight() + specSize;
break;
case MeasureSpec.AT_MOST:
width = getPaddingLeft() + getPaddingRight() + mRect.width();
break;
}
/**************************高***************************/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
switch (specMode) {
case MeasureSpec.EXACTLY:// 明确指定了
height = getPaddingTop() + getPaddingBottom() + specSize;
break;
case MeasureSpec.AT_MOST:// 一般为WARP_CONTENT
height = getPaddingTop() + getPaddingBottom() + mRect.height();
break;
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
mPaint.setColor(Color.BLUE);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
mPaint.setColor(mTextColor);
canvas.drawText(mText, getWidth() / 2 - mRect.width() / 2, getHeight() / 2 + mRect.height() / 2, mPaint);
}
}
在onMeasure中对代码进行了修改
aty_mv_view1.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<myview.csdn.com.view.one.ViewOne
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="20dp"
app:mText="测试文本"
app:mTextColor="#ff0000"
app:mTextSize="40sp" />
</LinearLayout>
布局也做了小小的修改,设置了padding,grvity居中
再来一张截图
OK 任务完成 我们的第一个问题这些组件是如何实现的解决了。
当然我们不能仅仅满足此,来看看这篇文章中还有哪些问题?
这里我们对onMeasure和onDraw只是简单的使用,下一篇我们将对它们进行深入分析。