android UI自定义组件

来源:互联网 发布:收看电视直播的软件 编辑:程序博客网 时间:2024/05/20 16:40

转载: http://blog.csdn.net/yjp19871013/article/details/54849308

本文主要讲讲如何派生View类,实现Android自定义控件。通过派生View实现自定义控件首先要清楚两个步骤:测量与绘制。


自定义控件首先要为Android框架提供其内容的测量值,这个阶段在onMeasure方法中进行,我们可以重写该方法,提供测量值。测量阶段发生在绘制之前,在测量期间,视图还没有确切的大小,只有侧量尺寸。如果在分配大小后还需要做处理,那就要重写onSizeChanged方法。


测量阶段onMeasure方法会传入两个参数,分别是宽和高对应的MeasureSpec的整型值,它有三个值:

AT_MOST:当我们的布局参数使用match_parent或者其他给出大小上限的参数值时,会传入该模式,我们的onMeasure应当返回对应的上限值。

EXACTLY:当布局参数是固定值时,会传入该模式,我们的onMeasure应当返回对应的宽和高的确切值。

UNSPECIFIED:当视图无约束时,会传入该模式,onMeasure应当返回宽和高都为0.


完成测量阶段后,会进入绘制阶段,这个阶段需要重写onDraw方法,传入的参数是Android框架按照测量阶段的数值创建的Canvas实例,我们基于该实例进行绘制即可。


下面是一个例子,实现了一个倾斜45度的TextView,它可以按照文本大小调整自己的宽高,然后和父视图边框接合,效果如图


下面看看代码,首先是我们的视图类

[java] view plain copy
print?
  1. package com.yjp.rotationtextview;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Canvas;  
  5. import android.graphics.Paint;  
  6. import android.graphics.Path;  
  7. import android.graphics.RectF;  
  8. import android.graphics.drawable.ColorDrawable;  
  9. import android.graphics.drawable.Drawable;  
  10. import android.util.AttributeSet;  
  11. import android.widget.TextView;  
  12.   
  13. public class RotationTextView extends TextView {  
  14.   
  15.     private Drawable mBackground;  
  16.   
  17.     public RotationTextView(Context context) {  
  18.         //调用自己的构造函数  
  19.         this(context, null);  
  20.     }  
  21.   
  22.     public RotationTextView(Context context, AttributeSet attrs) {  
  23.         //调用自己的构造函数  
  24.         this(context, attrs, 0);  
  25.     }  
  26.   
  27.     public RotationTextView(Context context, AttributeSet attrs, int defStyle) {  
  28.         //调用超类TextView的构造函数  
  29.         super(context, attrs, defStyle);  
  30.     }  
  31.   
  32.     @Override  
  33.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  34.         //获取文本的宽度,以计算倾斜45度后的长度,注意计算宽度和高度的代码不同  
  35.         int textWidth = (int) getPaint().measureText(getText().toString(), 0, getText().length());  
  36.         int min = (int) (textWidth / Math.cos(Math.PI / 4));  
  37.   
  38.         //调用resolveSizeAndState()快捷方法计算尺寸  
  39.         int w = resolveSizeAndState(min, widthMeasureSpec, 0);  
  40.         int h = resolveSizeAndState(min, heightMeasureSpec, 0);  
  41.         int result = Math.max(w, h);  
  42.   
  43.         //必须调用  
  44.         setMeasuredDimension(result, result);  
  45.     }  
  46.   
  47.     @SuppressWarnings(“deprecation”)  
  48.     @Override  
  49.     protected void onDraw(Canvas canvas) {  
  50.         //设置绘制背景使用的Paint  
  51.         Paint backgroundPaint = new Paint();  
  52.         backgroundPaint.setAntiAlias(true);  
  53.         backgroundPaint.setStyle(Paint.Style.FILL);  
  54.   
  55.         if (mBackground != null) {  
  56.             backgroundPaint.setColor(((ColorDrawable)mBackground).getColor());  
  57.         } else {  
  58.             backgroundPaint.setColor(getContext().getResources().getColor(android.R.color.white));  
  59.         }  
  60.   
  61.         //获取字符串高度,注意计算宽度和高度的代码不同  
  62.         Paint.FontMetrics fm = getPaint().getFontMetrics();  
  63.         double textHeight = Math.ceil(fm.descent - fm.top);  
  64.   
  65.         //绘制梯形外边框,并填充颜色  
  66.         Path backgroundPath = new Path();  
  67.         backgroundPath.moveTo(0, getHeight() - (int) textHeight - 20);  
  68.         backgroundPath.lineTo(0, getHeight());  
  69.         backgroundPath.lineTo(getWidth(), 0);  
  70.         backgroundPath.lineTo(getWidth() - (int) textHeight - 200);  
  71.         backgroundPath.close();  
  72.         canvas.drawPath(backgroundPath, backgroundPaint);  
  73.   
  74.         //绘制文本  
  75.         RectF backgroundRectF = new RectF();  
  76.         backgroundPath.computeBounds(backgroundRectF, false);  
  77.         int textWidth = (int) getPaint().measureText(getText().toString(), 0, getText().length());  
  78.         double delta = textWidth / 2 * Math.cos(Math.PI / 4);  
  79.         Path textPath = new Path();  
  80.         textPath.moveTo((float)(backgroundRectF.centerX() - delta - 5),  
  81.                 (float)(backgroundRectF.centerY() + delta - 5));  
  82.         textPath.lineTo((float)(backgroundRectF.centerX() + delta - 5),  
  83.                 (float)(backgroundRectF.centerY() - delta - 5));  
  84.         canvas.drawTextOnPath(getText().toString(), textPath, 00, getPaint());  
  85.     }  
  86.   
  87.     //需要重载一系列background相关的函数,否则TextView默认用该background填充整个正方形视图  
  88.     //所有函数最终调用的都是setBackgroundDrawable()  
  89.     @SuppressWarnings(“deprecation”)  
  90.     @Override  
  91.     public void setBackgroundResource(int resid) {  
  92.         Drawable d = null;  
  93.         if (resid != 0) {  
  94.             d = getContext().getResources().getDrawable(resid);  
  95.         }  
  96.         this.setBackground(d);  
  97.     }  
  98.   
  99.     @Override  
  100.     public void setBackgroundColor(int color) {  
  101.         if (mBackground instanceof ColorDrawable) {  
  102.             ((ColorDrawable) mBackground.mutate()).setColor(color);  
  103.         } else {  
  104.             this.setBackground(new ColorDrawable(color));  
  105.         }  
  106.     }  
  107.   
  108.     @Override  
  109.     public void setBackground(Drawable background) {  
  110.         this.setBackgroundDrawable(background);  
  111.     }  
  112.   
  113.     @SuppressWarnings(“deprecation”)  
  114.     @Override  
  115.     public void setBackgroundDrawable(Drawable background) {  
  116.         mBackground = background;  
  117.     }  
  118. }  
package com.yjp.rotationtextview;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Path;import android.graphics.RectF;import android.graphics.drawable.ColorDrawable;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.widget.TextView;public class RotationTextView extends TextView {    private Drawable mBackground;    public RotationTextView(Context context) {        //调用自己的构造函数        this(context, null);    }    public RotationTextView(Context context, AttributeSet attrs) {        //调用自己的构造函数        this(context, attrs, 0);    }    public RotationTextView(Context context, AttributeSet attrs, int defStyle) {        //调用超类TextView的构造函数        super(context, attrs, defStyle);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //获取文本的宽度,以计算倾斜45度后的长度,注意计算宽度和高度的代码不同        int textWidth = (int) getPaint().measureText(getText().toString(), 0, getText().length());        int min = (int) (textWidth / Math.cos(Math.PI / 4));        //调用resolveSizeAndState()快捷方法计算尺寸        int w = resolveSizeAndState(min, widthMeasureSpec, 0);        int h = resolveSizeAndState(min, heightMeasureSpec, 0);        int result = Math.max(w, h);        //必须调用        setMeasuredDimension(result, result);    }    @SuppressWarnings("deprecation")    @Override    protected void onDraw(Canvas canvas) {        //设置绘制背景使用的Paint        Paint backgroundPaint = new Paint();        backgroundPaint.setAntiAlias(true);        backgroundPaint.setStyle(Paint.Style.FILL);        if (mBackground != null) {            backgroundPaint.setColor(((ColorDrawable)mBackground).getColor());        } else {            backgroundPaint.setColor(getContext().getResources().getColor(android.R.color.white));        }        //获取字符串高度,注意计算宽度和高度的代码不同        Paint.FontMetrics fm = getPaint().getFontMetrics();        double textHeight = Math.ceil(fm.descent - fm.top);        //绘制梯形外边框,并填充颜色        Path backgroundPath = new Path();        backgroundPath.moveTo(0, getHeight() - (int) textHeight - 20);        backgroundPath.lineTo(0, getHeight());        backgroundPath.lineTo(getWidth(), 0);        backgroundPath.lineTo(getWidth() - (int) textHeight - 20, 0);        backgroundPath.close();        canvas.drawPath(backgroundPath, backgroundPaint);        //绘制文本        RectF backgroundRectF = new RectF();        backgroundPath.computeBounds(backgroundRectF, false);        int textWidth = (int) getPaint().measureText(getText().toString(), 0, getText().length());        double delta = textWidth / 2 * Math.cos(Math.PI / 4);        Path textPath = new Path();        textPath.moveTo((float)(backgroundRectF.centerX() - delta - 5),                (float)(backgroundRectF.centerY() + delta - 5));        textPath.lineTo((float)(backgroundRectF.centerX() + delta - 5),                (float)(backgroundRectF.centerY() - delta - 5));        canvas.drawTextOnPath(getText().toString(), textPath, 0, 0, getPaint());    }    //需要重载一系列background相关的函数,否则TextView默认用该background填充整个正方形视图    //所有函数最终调用的都是setBackgroundDrawable()    @SuppressWarnings("deprecation")    @Override    public void setBackgroundResource(int resid) {        Drawable d = null;        if (resid != 0) {            d = getContext().getResources().getDrawable(resid);        }        this.setBackground(d);    }    @Override    public void setBackgroundColor(int color) {        if (mBackground instanceof ColorDrawable) {            ((ColorDrawable) mBackground.mutate()).setColor(color);        } else {            this.setBackground(new ColorDrawable(color));        }    }    @Override    public void setBackground(Drawable background) {        this.setBackgroundDrawable(background);    }    @SuppressWarnings("deprecation")    @Override    public void setBackgroundDrawable(Drawable background) {        mBackground = background;    }}
重点在onMeasure和onDraw方法上,另外为了能够支持视图设置background属性,我们重写了setBackgroundxxx的一系列方法。下面是布局

[html] view plain copy
print?
  1. <?xml version=“1.0” encoding=“utf-8”?>  
  2. <FrameLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     xmlns:tools=“http://schemas.android.com/tools”  
  4.     android:layout_width=“match_parent”  
  5.     android:layout_height=“match_parent”  
  6.     android:background=“@android:color/holo_green_light”  
  7.     tools:context=“com.yjp.rotationtextview.MainActivity”>  
  8.   
  9.     <com.yjp.rotationtextview.RotationTextView  
  10.         android:layout_width=“wrap_content”  
  11.         android:layout_height=“wrap_content”  
  12.         android:background=“@android:color/holo_orange_light”  
  13.         android:text=“测试文本1”  
  14.         android:textSize=“20sp”  
  15.         android:textColor=“@android:color/holo_red_dark”/>  
  16.   
  17.     <com.yjp.rotationtextview.RotationTextView  
  18.         android:layout_width=“wrap_content”  
  19.         android:layout_height=“wrap_content”  
  20.         android:background=“@android:color/holo_blue_bright”  
  21.         android:text=“测试文本2”  
  22.         android:textSize=“40sp”  
  23.         android:textColor=“@android:color/holo_red_dark”/>  
  24.   
  25.     <com.yjp.rotationtextview.RotationTextView  
  26.         android:layout_width=“wrap_content”  
  27.         android:layout_height=“wrap_content”  
  28.         android:background=“@android:color/holo_purple”  
  29.         android:text=“测试文本3”  
  30.         android:textSize=“60sp”  
  31.         android:textColor=“@android:color/holo_red_dark”/>  
  32.   
  33. </FrameLayout>  
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@android:color/holo_green_light"    tools:context="com.yjp.rotationtextview.MainActivity">    <com.yjp.rotationtextview.RotationTextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:background="@android:color/holo_orange_light"        android:text="测试文本1"        android:textSize="20sp"        android:textColor="@android:color/holo_red_dark"/>    <com.yjp.rotationtextview.RotationTextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:background="@android:color/holo_blue_bright"        android:text="测试文本2"        android:textSize="40sp"        android:textColor="@android:color/holo_red_dark"/>    <com.yjp.rotationtextview.RotationTextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:background="@android:color/holo_purple"        android:text="测试文本3"        android:textSize="60sp"        android:textColor="@android:color/holo_red_dark"/></FrameLayout>

谢谢观看!



原创粉丝点击