Android自定義View-練習一

一、實現一個簡單的相似於TextView的自定義View

1.1  自定義屬性

        在attrs.xml文件中添加了以下的內容java

<declare-styleable name="SimpleApiTextView">
        <attr name="android:text"/>
        <attr name="titleColor" format="color"/>
        <attr name="titleTextSize" format="dimension"/>
    </declare-styleable>

1.2   在佈局xml文件中的使用

        引入了以下包名:android

xmlns:zhy="http://schemas.android.com/apk/res-auto"

        具體的代碼以下:canvas

<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方法,繪製對應的文字以及背景色。具體的代碼以下所示:app

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


二、給自定義View添加click事件

2.1  在構造函數中添加對應的click處理事件

        最終構造函數中調用的init方法,變成以下所示的代碼:dom

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

     
三、關於自定義View長寬的自定義

       原來的代碼,若是你在佈局文件中,將對應的長寬設置成wrap_content的時候,會發現沒有達到本身想要的結果。主要是由於沒有重寫onMeasure方法進行從新測量長寬。ide

3.1  關於MeasureSpec類的理解

        MeasureSpec主要用於自定義view的測量。MeasureSpec是一個32位的int值,其中高2位是測量的模式,低30位爲測量的大小。其中測量的模式能夠分爲:EXACTLY、AT_MOST、UNSPECIFIED.這三種模式的說明以下:函數

EXACTLY:默認值,對應的長寬的設置:具體值或者是match_parent佈局

AT_MOST:對應的長寬的設置:wrap_contentpost

UNSPECIFIED:這個屬性比較奇怪,它不指定其大小測量模式,View想多大就多大,一般狀況下在繪製自定義View時纔會使用。this

        因此,在自定義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節

相關文章
相關標籤/搜索