Android中的自定义view和自定义属性TypedArray的使用

先总结下自定义View的步骤:
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
[ 3、重写onMesure ]
4、重写onDraw

我把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>

CustomView03.java

/**
 * 
 */
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>

2,在布局文件中加入自定义属性,加上命名空间

<?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);  
	     }  
}

关于TypedArray和AttributeSet

       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/

你可能感兴趣的:(Android中的自定义view和自定义属性TypedArray的使用)