Android自定义View-练习一

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节

猜你喜欢

转载自my.oschina.net/u/2253892/blog/919949