1、实现一个简单的类似于TextView的自定义View
1.1 自定义属性
在attrs.xml文件中添加了如下的内容
<declare-styleable name="SimpleApiTextView">
<attr name="android:text"/>
<attr name="titleColor" format="color"/>
<attr name="titleTextSize" format="dimension"/>
</declare-styleable>
1.2 在布局xml文件中的使用
引入了如下包名:
xmlns:zhy="http://schemas.android.com/apk/res-auto"
具体的代码如下:
<com.self.view.view.SimpleApiTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="5678"
zhy:titleColor="@color/colorPrimary"
zhy:titleTextSize="20sp"/>
1.3 自定义一个类extends view类
自定义一个类,继承android.view.View类,重写其中的构造函数,在构造函数中,获得自定义参数的属性。并重写onDraw方法,绘制对应的文字以及背景色。具体的代码如下所示:
package com.self.view.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import com.self.view.R;
import com.self.view.common.GraphicsUtil;
import com.self.view.common.L;
/**
* Graphics中简单Api的练习
* 模仿TextView
* Created by Administrator on 2017/6/7 0007.
*/
public class SimpleApiTextView extends View {
private String titleText;
private int titleColor;
private int titleTextSize;
private Paint paint;
private float textHeight = 0;
public SimpleApiTextView(Context context) {
this(context,null);
L.i("一个参数的构造函数");
}
public SimpleApiTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
L.i("俩个参数的构造函数");
}
public SimpleApiTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
L.i("三个参数的构造函数");
init(context, attrs,defStyleAttr);
}
private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
//获取自定义属性的值
if(null != attrs){
int count = attrs.getAttributeCount();
for(int i =0;i<count;i++){
L.i("key:"+attrs.getAttributeName(i)+" value:"+attrs.getAttributeValue(i));
}
}
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SimpleApiTextView);
// TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SimpleApiTextView, defStyleAttr, 0);
if(null != typedArray){
titleText = typedArray.getString(R.styleable.SimpleApiTextView_android_text);
titleColor = typedArray.getColor(R.styleable.SimpleApiTextView_titleColor, Color.RED);
titleTextSize = typedArray.getDimensionPixelSize(R.styleable.SimpleApiTextView_titleTextSize, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 10, getResources().getDisplayMetrics()));
L.i("titleText="+titleText+" titleColor="+titleColor+" titleTextSize="+titleTextSize);
}
typedArray.recycle();
//获得当前Paint对应的文字的长宽
paint = new Paint();
paint.setTextSize(titleTextSize);
textHeight = GraphicsUtil.measureTextHeight(paint);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.YELLOW);
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint);
paint.setColor(titleColor);
// canvas.drawText(titleText,0,textHeight,paint);
//计算文字的起始位置
float width = getWidth();
float height = getHeight();
float textWidth = paint.measureText(titleText);
canvas.drawText(titleText,(width-textWidth)/2,(height+textHeight)/2,paint);
}
}
2、给自定义View添加click事件
2.1 在构造函数中添加对应的click处理事件
最终构造函数中调用的init方法,变成如下所示的代码:
private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
//获取自定义属性的值
if(null != attrs){
int count = attrs.getAttributeCount();
for(int i =0;i<count;i++){
L.i("key:"+attrs.getAttributeName(i)+" value:"+attrs.getAttributeValue(i));
}
}
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SimpleApiTextView);
// TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SimpleApiTextView, defStyleAttr, 0);
if(null != typedArray){
titleText = typedArray.getString(R.styleable.SimpleApiTextView_android_text);
titleColor = typedArray.getColor(R.styleable.SimpleApiTextView_titleColor, Color.RED);
titleTextSize = typedArray.getDimensionPixelSize(R.styleable.SimpleApiTextView_titleTextSize, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 10, getResources().getDisplayMetrics()));
L.i("titleText="+titleText+" titleColor="+titleColor+" titleTextSize="+titleTextSize);
}
typedArray.recycle();
//获得当前Paint对应的文字的长宽
paint = new Paint();
paint.setTextSize(titleTextSize);
textHeight = GraphicsUtil.measureTextHeight(paint);
//设置Onclick方法
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
titleText = getRandowText();
postInvalidate();
}
});
}
private String getRandowText(){
String text = "";
int temp = (int)(Math.random()*10000);
text = temp+"";
if(text.length() < 4){
String tempStr = "";
for(int i=0;i<4-text.length();i++){
tempStr+="0";
}
text = tempStr+text;
}
return text;
}
3、关于自定义View长宽的自定义
原来的代码,如果你在布局文件中,将对应的长宽设置成wrap_content的时候,会发现没有达到自己想要的结果。主要是因为没有重写onMeasure方法进行重新测量长宽。
3.1 关于MeasureSpec类的理解
MeasureSpec主要用于自定义view的测量。MeasureSpec是一个32位的int值,其中高2位是测量的模式,低30位为测量的大小。其中测量的模式可以分为:EXACTLY、AT_MOST、UNSPECIFIED.这三种模式的说明如下:
EXACTLY:默认值,对应的长宽的设置:具体值或者是match_parent
AT_MOST:对应的长宽的设置:wrap_content
UNSPECIFIED:这个属性比较奇怪,它不指定其大小测量模式,View想多大就多大,通常情况下在绘制自定义View时才会使用。
所以,在自定义View的时候,如果没有重写onMeasure方法的话,默认的长宽模式就是EXACTLY.如果需要实现wrap_content的效果,就需要重写onMeasure方法。
3.2 关于onMeasure
该方法的定义如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}
查看该方法的源码,最终会调用setMeasuredDimension(int measuredWidth,int measuredHeight)方法,将测量后的宽高值设置进去。所以在重写onMeasure方法以后,必须调用此方法,将最终的结果设置进去。
文中例子中的onMeasure方法,如下所示:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
float viewWidth,viewHeight;//自定义View的宽、高
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if(widthMode == MeasureSpec.EXACTLY){
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
}else{
viewWidth = paint.measureText(titleText)+getPaddingLeft()+getPaddingRight();
}
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if(heightMode == MeasureSpec.EXACTLY){
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
}else{
viewHeight = textHeight+getPaddingBottom()+getPaddingTop();
}
setMeasuredDimension((int)viewWidth,(int)viewHeight);
}
文章写的并不是特别的好,可以查看下面的参考地址以及参考书籍
参考地址:http://blog.csdn.net/lmj623565791/article/details/24252901
参考书籍:《Android群英传》第三章第3.2节