Android 自定义控件学习之一 基础知识


基本实现 步骤
             

1、自定义View的属性

2、在View的构造方法中获得我们自定义的属性

[ 3、重写onMesure ]

4、重写onDraw

在最新的andriod studio 中,选择自定义空间,它会生成相应的attr 文件,布局文件,属性实现文件,通过这三个文件,我们就可以设计自己的控件。


  1. <resources>  
  2.     <declare-styleable name="MyTextView">  
  3.         <attr name="codeString" format="string" />  
  4.         <attr name="codeDimension" format="dimension" />  
  5.         <attr name="codeColor" format="color" />  
  6.         <attr name="codeDrawable" format="color|reference" />  
  7.     </declare-styleable>  
  8. </resources>  
<resources>
    <declare-styleable name="MyTextView">
        <attr name="codeString" format="string" />
        <attr name="codeDimension" format="dimension" />
        <attr name="codeColor" format="color" />
        <attr name="codeDrawable" format="color|reference" />
    </declare-styleable>
</resources>

我们定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:

一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;不清楚的可以google一把。

然后在布局中声明我们的自定义View

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.   
  8.     tools:context="com.selftextview.MainActivity">  
  9.   
  10.     <LinearLayout  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="wrap_content"  
  13.         android:orientation="horizontal"  
  14.         android:paddingTop="@dimen/activity_vertical_margin"  
  15.         android:id="@+id/codes_linear">  
  16.   
  17.         <LinearLayout  
  18.             android:layout_width="match_parent"  
  19.             android:layout_height="wrap_content"  
  20.             android:layout_weight="1">  
  21.   
  22.             <EditText  
  23.                 android:layout_width="match_parent"  
  24.                 android:layout_height="match_parent"  
  25.                 android:id="@+id/codes"  
  26.                 android:hint="@string/codes"  
  27.                 android:textSize="21sp"/>  
  28.   
  29.         </LinearLayout>  
  30.   
  31.         <LinearLayout  
  32.             android:layout_width="match_parent"  
  33.             android:layout_height="wrap_content"  
  34.             android:layout_weight="1"  
  35.             android:weightSum="1">  
  36.   
  37.             <com.selftextview.MyTextView  
  38.                 android:layout_width="match_parent"  
  39.                 android:layout_height="58dp"  
  40.                 android:background="#c1c7c3"  
  41.                 app:codeColor="#33b5e5"  
  42.                 app:codeDimension="24sp"  
  43.                 app:codeString="@string/codes"  
  44.              />  
  45.   
  46.         </LinearLayout>  
  47.   
  48.     </LinearLayout>  
  49.   
  50.     <Button  
  51.         android:layout_below="@id/codes_linear"  
  52.         android:layout_width="wrap_content"  
  53.         android:layout_height="wrap_content"  
  54.         android:id="@+id/sureBtn"  
  55.         android:text="@string/sure"  
  56.         />  
  57.   
  58. </RelativeLayout>  
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context="com.selftextview.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:id="@+id/codes_linear">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1">

            <EditText
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/codes"
                android:hint="@string/codes"
                android:textSize="21sp"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:weightSum="1">

            <com.selftextview.MyTextView
                android:layout_width="match_parent"
                android:layout_height="58dp"
                android:background="#c1c7c3"
                app:codeColor="#33b5e5"
                app:codeDimension="24sp"
                app:codeString="@string/codes"
             />

        </LinearLayout>

    </LinearLayout>

    <Button
        android:layout_below="@id/codes_linear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/sureBtn"
        android:text="@string/sure"
        />

</RelativeLayout>
2、在View的构造方法中,获得我们的自定义的样式

  
  
  1. <span style="color:#555555;">    private Paint paint = new Paint();  
  2.     /** 
  3.      * 验证码内容 
  4.      */  
  5.     private String[] content = null;  
  6.     /** 
  7.      * 验证码图片 
  8.      */  
  9.     private Bitmap bitmap = null;  
  10.   
  11.   
  12.     private String mCodeString;  
  13.     private int mCodeColor = Color.RED;  
  14.     private float mCodeDimension = 0;  
  15.     private Drawable mCodeDrawable;  
  16.   
  17.     private TextPaint mTextPaint;  
  18.     private float mTextWidth;  
  19.     private float mTextHeight;  
  20.   
  21.     public MyTextView(Context context) {  
  22.         super(context);  
  23.         init(null0);  
  24.     }  
  25.   
  26.     public MyTextView(Context context, AttributeSet attrs) {  
  27.         super(context, attrs);  
  28.         init(attrs, 0);  
  29.     }  
  30.   
  31.     public MyTextView(Context context, AttributeSet attrs, int defStyle) {  
  32.         super(context, attrs, defStyle);  
  33.         init(attrs, defStyle);  
  34.     }  
  35.   
  36.     private void init(AttributeSet attrs, int defStyle) {  
  37.         // Load attributes  
  38.         final TypedArray a = getContext().obtainStyledAttributes(  
  39.                 attrs, R.styleable.MyTextView, defStyle, 0);  
  40.   
  41.         //mCodeString = a.getString(  
  42.         //        R.styleable.MyTextView_codeString);  
  43.   
  44.         mCodeString = randomText();  
  45.   
  46.         mCodeColor = a.getColor(  
  47.                 R.styleable.MyTextView_codeColor,  
  48.                 mCodeColor);  
  49.         // Use getDimensionPixelSize or getDimensionPixelOffset when dealing with  
  50.         // values that should fall on pixel boundaries.  
  51.         mCodeDimension = a.getDimension(  
  52.                 R.styleable.MyTextView_codeDimension,  
  53.                 mCodeDimension);  
  54.   
  55.         if (a.hasValue(R.styleable.MyTextView_codeDrawable)) {  
  56.             mCodeDrawable = a.getDrawable(  
  57.                     R.styleable.MyTextView_codeDrawable);  
  58.             mCodeDrawable.setCallback(this);  
  59.         }  
  60.   
  61.         a.recycle();  
  62.   
  63.         // Set up a default TextPaint object  
  64.         mTextPaint = new TextPaint();  
  65.         mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);  
  66.         mTextPaint.setTextAlign(Paint.Align.LEFT);  
  67.   
  68.         invalidateTextPaintAndMeasurements();  
  69.   
  70.         this.setOnClickListener(new OnClickListener() {  
  71.             @Override  
  72.             public void onClick(View v) {  
  73.                 mCodeString = randomText();  
  74.                 postInvalidate();  /</span><span style="color:#ff6666;">/通过这个方法重新绘制</span><span style="color:#555555;">  
  75.             }  
  76.         });  
  77.     }</span>  
<span style="color:#555555;">    private Paint paint = new Paint();
    /**
     * 验证码内容
     */
    private String[] content = null;
    /**
     * 验证码图片
     */
    private Bitmap bitmap = null;


    private String mCodeString;
    private int mCodeColor = Color.RED;
    private float mCodeDimension = 0;
    private Drawable mCodeDrawable;

    private TextPaint mTextPaint;
    private float mTextWidth;
    private float mTextHeight;

    public MyTextView(Context context) {
        super(context);
        init(null, 0);
    }

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public MyTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    private void init(AttributeSet attrs, int defStyle) {
        // Load attributes
        final TypedArray a = getContext().obtainStyledAttributes(
                attrs, R.styleable.MyTextView, defStyle, 0);

        //mCodeString = a.getString(
        //        R.styleable.MyTextView_codeString);

        mCodeString = randomText();

        mCodeColor = a.getColor(
                R.styleable.MyTextView_codeColor,
                mCodeColor);
        // Use getDimensionPixelSize or getDimensionPixelOffset when dealing with
        // values that should fall on pixel boundaries.
        mCodeDimension = a.getDimension(
                R.styleable.MyTextView_codeDimension,
                mCodeDimension);

        if (a.hasValue(R.styleable.MyTextView_codeDrawable)) {
            mCodeDrawable = a.getDrawable(
                    R.styleable.MyTextView_codeDrawable);
            mCodeDrawable.setCallback(this);
        }

        a.recycle();

        // Set up a default TextPaint object
        mTextPaint = new TextPaint();
        mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextAlign(Paint.Align.LEFT);

        invalidateTextPaintAndMeasurements();

        this.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mCodeString = randomText();
                postInvalidate();  /</span><span style="color:#ff6666;">/通过这个方法重新绘制</span><span style="color:#555555;">
            }
        });
    }</span>

3.绘制该View
  
  
  1. private void invalidateTextPaintAndMeasurements() {  
  2.      mTextPaint.setTextSize(mCodeDimension);  
  3.      mTextPaint.setColor(mCodeColor);  
  4.      mTextWidth = mTextPaint.measureText(mCodeString);  
  5.   
  6.      Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();  
  7.      mTextHeight = fontMetrics.bottom;  
  8.  }  
  9.   
  10.  @Override  
  11.  protected void onDraw(Canvas canvas) {  
  12.      super.onDraw(canvas);  
  13.   
  14.      // allocations per draw cycle.  
  15.      int paddingLeft = getPaddingLeft();  
  16.      int paddingTop = getPaddingTop();  
  17.      int paddingRight = getPaddingRight();  
  18.      int paddingBottom = getPaddingBottom();  
  19.   
  20.      int contentWidth = getWidth() - paddingLeft - paddingRight;  
  21.      int contentHeight = getHeight() - paddingTop - paddingBottom;  
  22.   
  23.      // Draw the text.  
  24.      canvas.drawText(mCodeString,  
  25.              paddingLeft + (contentWidth - mTextWidth) / 2,  
  26.              paddingTop + (contentHeight + mTextHeight) / 2,  
  27.              mTextPaint);  
  28.   
  29.      // Draw the example drawable on top of the text.  
  30.      if (mCodeDrawable != null) {  
  31.          mCodeDrawable.setBounds(paddingLeft, paddingTop,  
  32.                  paddingLeft + contentWidth, paddingTop + contentHeight);  
  33.          mCodeDrawable.draw(canvas);  
  34.      }  
  35.   
  36.   
  37.  }  
   private void invalidateTextPaintAndMeasurements() {
        mTextPaint.setTextSize(mCodeDimension);
        mTextPaint.setColor(mCodeColor);
        mTextWidth = mTextPaint.measureText(mCodeString);

        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        mTextHeight = fontMetrics.bottom;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // allocations per draw cycle.
        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int contentWidth = getWidth() - paddingLeft - paddingRight;
        int contentHeight = getHeight() - paddingTop - paddingBottom;

        // Draw the text.
        canvas.drawText(mCodeString,
                paddingLeft + (contentWidth - mTextWidth) / 2,
                paddingTop + (contentHeight + mTextHeight) / 2,
                mTextPaint);

        // Draw the example drawable on top of the text.
        if (mCodeDrawable != null) {
            mCodeDrawable.setBounds(paddingLeft, paddingTop,
                    paddingLeft + contentWidth, paddingTop + contentHeight);
            mCodeDrawable.draw(canvas);
        }


    }

4.关于onmeasure 的相关介绍
     把布局文件的宽和高写成wrap_content ,会发生铺满全屏的现象。

这里是鸿洋大神写的解释

系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。

所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:

重写之前先了解MeasureSpec的specMode,一共三种类型:

EXACTLY:一般是设置了明确的值或者是MATCH_PARENT

AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT

UNSPECIFIED:表示子布局想要多大就多大,很少使用

下面是我们重写onMeasure代码:

  1. @Override  
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
  3. {  
  4.     int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  5.     int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  6.     int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  7.     int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  8.     int width;  
  9.     int height ;  
  10.     if (widthMode == MeasureSpec.EXACTLY)  
  11.     {  
  12.         width = widthSize;  
  13.     } else  
  14.     {  
  15.         mPaint.setTextSize(mTitleTextSize);  
  16.         mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);  
  17.         float textWidth = mBounds.width();  
  18.         int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());  
  19.         width = desired;  
  20.     }  
  21.   
  22.     if (heightMode == MeasureSpec.EXACTLY)  
  23.     {  
  24.         height = heightSize;  
  25.     } else  
  26.     {  
  27.         mPaint.setTextSize(mTitleTextSize);  
  28.         mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);  
  29.         float textHeight = mBounds.height();  
  30.         int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());  
  31.         height = desired;  
  32.     }  
  33.       
  34.       
  35.   
  36.     setMeasuredDimension(width, height);  
  37. }  
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);
		int width;
		int height ;
		if (widthMode == MeasureSpec.EXACTLY)
		{
			width = widthSize;
		} else
		{
			mPaint.setTextSize(mTitleTextSize);
			mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
			float textWidth = mBounds.width();
			int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
			width = desired;
		}

		if (heightMode == MeasureSpec.EXACTLY)
		{
			height = heightSize;
		} else
		{
			mPaint.setTextSize(mTitleTextSize);
			mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
			float textHeight = mBounds.height();
			int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
			height = desired;
		}
		
		

		setMeasuredDimension(width, height);
	}

通过上面的实现可以实现文字的wrap_conent
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值