我把3用[]标出了,所以说3不一定是必须的,当然了大部分情况下还是需要重写的。
具体理解参考:http://blog.csdn.net/lmj623565791/article/details/24252901/
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- 自定义属性名和属性类型 --> <attr name="text" format="string"/> <attr name="textcolor" format="color"/> <attr name="textsize" format="dimension"/> <!-- 声明属性 --> <declare-styleable name="customview03_attrs"> <attr name="text"></attr> <attr name="textcolor"></attr> <attr name="textsize"></attr> </declare-styleable> </resources>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:tuke="http://schemas.android.com/apk/res/com.example.customview03" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.customview03.CustomView03 android:id="@+id/view03" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="20dp" tuke:text="4327" tuke:textcolor="#ff0000" tuke:textsize="40sp" /><!-- 对于继承的view没有的属性,可以使用自定义属性,加命名空间 --> </RelativeLayout>
/** * */ package com.example.customview03; import java.util.HashSet; import java.util.Random; import java.util.Set; import android.R.integer; 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.util.AttributeSet; import android.view.View; import android.view.View.MeasureSpec; import android.widget.TextView; /** * @author tuke * */ public class CustomView03 extends View { String text; int textcolor; int textsize; /** *绘制时控制文本绘制的范围 */ private Rect mBound; private Paint mPaint; public CustomView03(Context context) { this(context,null); // 代码初始化调用的构造函数 } public CustomView03(Context context, AttributeSet attrs) { this(context, attrs,0); // xml文件初始化调用的构造函数 } public CustomView03(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // xml文件初始化调用的构造函数,获得我们所定义的自定义样式属性,类型数组,把自定义属性存放到当地变量 TypedArray array=context.getTheme().obtainStyledAttributes(attrs, R.styleable.customview03_attrs, defStyleAttr, 0); int count=array.getIndexCount(); for(int i=0;i<count;i++){ int index=array.getIndex(i); switch (index) { case R.styleable.customview03_attrs_text: text=array.getString(index); break; case R.styleable.customview03_attrs_textcolor: textcolor=array.getInt(index, Color.BLACK); break; case R.styleable.customview03_attrs_textsize: textsize=array.getDimensionPixelSize(index, 16); break; default: break; } } array.recycle();//typearray要回收 // 获得绘制文本的宽和高 mPaint=new Paint(); mPaint.setTextSize(textsize); mBound = new Rect(); mPaint.getTextBounds(text, 0, text.length(), mBound); this.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { text=getRamdomText(); postInvalidate(); } }); } //获得随机4位数 private String getRamdomText() { Random random = new Random(); Set<Integer> set = new HashSet<Integer>(); while (set.size() < 4){ int randomInt = random.nextInt(10); set.add(randomInt); } StringBuffer sb = new StringBuffer(); for (Integer i : set){ sb.append("" + i); } return sb.toString(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ //super.onMeasure(widthMeasureSpec, heightMeasureSpec);//xml中设置为wrap_content的话,系统测量会是满屏 /* * 注意: * 重写之前先了解MeasureSpec的specMode,一共三种类型: * EXACTLY:一般是设置了布局文件设置的是明确的值或者是MATCH_PARENT * AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT * UNSPECIFIED:表示子布局想要多大就多大,很少使用 * 因为正是不知道内容大小才去测量 * */ int width=0,height=0; //测量宽 int specMode=MeasureSpec.getMode(widthMeasureSpec); int specSize=MeasureSpec.getSize(widthMeasureSpec);//主要是得到View的内容的宽度 switch (specMode) { case MeasureSpec.EXACTLY://1073741824 width = getPaddingLeft() + getPaddingRight() + specSize;//内容的宽度+内边距=View的宽度 break; case MeasureSpec.AT_MOST://-2147483648 width = getPaddingLeft() + getPaddingRight() + mBound.width(); break; } //测量高 specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec);//主要是得到View的内容的高度 switch (specMode) { case MeasureSpec.EXACTLY: height = getPaddingTop() + getPaddingBottom() + specSize;//内容的高度+内边距=View的高度 break; case MeasureSpec.AT_MOST: height = getPaddingTop() + getPaddingBottom() + mBound.height(); break; } //这个方法在OnMesure中必须被调用,来存储已经测量的宽和高 setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas){ mPaint.setColor(Color.YELLOW); canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint); mPaint.setColor(Color.BLACK); for(int i=0;i<400;i++){ Random randomx=new Random(); int rx=randomx.nextInt(getWidth()); int ry=randomx.nextInt(getHeight()); canvas.drawCircle(rx, ry, 3, mPaint); } mPaint.setColor(textcolor); canvas.drawText(text, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint); } }关于自定义属性
1,先定义属性文件attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="myView"> <attr name="textColor" format="color"/> <attr name="textSize" format="dimension"/> </declare-styleable> </resources>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:test="http://schemas.android.com/apk/res/com.example.customview04" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="helloworld" /> <com.example.customview04.CustomView04 android:layout_width="fill_parent" android:layout_height="fill_parent" test:textSize="10px" test:textColor="#fff" /> <!-- test是命名空间 --> </LinearLayout>3,在自定义View的构造函数中通过TypedArray获取自定义属性值
package com.example.customview04; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; public class CustomView04 extends View { private Paint myPaint; private static final String myString = "Welcome to our Zoon!"; public CustomView04(Context context) { super(context); // TODO Auto-generated constructor stub } public CustomView04(Context context, AttributeSet attr) { super(context, attr); myPaint = new Paint(); //获取自定义的属性 TypedArray a = context.obtainStyledAttributes(attr, R.styleable.myView);//TypedArray是一个数组容器 float textSize = a.getDimension(R.styleable.myView_textSize, 30);//防止在XML文件里没有定义,就加上了默认值30 int textColor = a.getColor(R.styleable.myView_textColor, 0xFFFFFFFF);//同上,这里的属性是:名字_属性名 myPaint.setTextSize(textSize); myPaint.setColor(textColor); a.recycle();//我的理解是:返回以前取回的属性,供以后使用。以前取回的可能就是textSize和textColor初始化的那段 } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); //myPaint = new Paint(); myPaint.setColor(Color.RED); myPaint.setStyle(Style.FILL); canvas.drawRect(new Rect(10,10,100,100), myPaint); myPaint.setColor(Color.WHITE); canvas.drawText(myString, 10, 100, myPaint); } }
AttributeSet的作用就是在控件进行初始化的时候,解析布局文件中该控件的属性(keyeg:background)与该值(valueeg:@drawable/icon)的信息封装在AttributeSet中,传递给该控件(View)的构造函数。
对于非Android自带的属性,在View类中处理时是无法识别的,因此需要我们自己解析。所以这就要用到另外一个类TypedArray。在AttributeSet中我们有属性名称,有属性值,但是控件如何知道哪个属性代表什么意思呢?这个工作就由TypedArray来做了。
TypedArray对象封装了/values/attrs.xml中的styleable里定义的每个属性的类型信息,通过TypedArray我们就可以知道AttributeSet中封装的值到底是干什么的了,从而可以对这些数据进行应用。
具体理解参考http://blog.csdn.net/lmj623565791/article/details/45022631/