CircleTextView的实现

来源:互联网 发布:python的位运算作用 编辑:程序博客网 时间:2024/06/06 03:36

“CircleTextView”顾名思义就是带圆边框的TextView,先来看效果图:

这里写图片描述

之前的项目中如果有带圆边框的需求,基本是都是在drawable中创建一个shape,然后将它设置为TextView的background,但是这样有个弊端,就是每次只要边框的颜色、边框的宽度或者填充颜色不同,就要创建一个新的shape文件,这样做显然不是一个有长久之计,想到CircleImageView的效果受到大家的认可,就产生了自己写一个CircleTextView的想法。

废话少说,开始分析。从需求上来说,至少要支持以下几点:

  • 支持圆形的背景
  • 支持外加边框的宽度和颜色自定义
  • 支持填充颜色自定义

Ok,需求分析完毕,开始动工。

声明自定义属性

要想满足以上几点需求,原生Android肯定是不支持了,因此,我们首先要为支持上面的需求,自定义属性。这个很简单,只需要在values/attr中创建自定义属性即可。

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="border_width" format="dimension" />  <!-- 边框的宽度 -->    <attr name="border_color" format="color" />      <!-- 边框的颜色 -->    <attr name="fill_color" format="color" />        <!-- 填充颜色 -->    <declare-styleable name="CircleTextView">        <attr name="border_width" />        <attr name="border_color" />        <attr name="fill_color" />    </declare-styleable></resources>

创建CircleTextView

创建一个类,我们就命名为“CircleTextView”,并且让它继承TextView,并实现它的构造方法。

public class CircleTextView extends TextView {    public CircleTextView(Context context) {        this(context, null);    }    public CircleTextView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public CircleTextView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }

可以看到这里实现了3个构造方法,第一个构造方法调用第二个构造方法,第二个构造方法调用第三个构造方法,而第三个构造方法调用父类的构造方法。也就是说,无论我们调用哪个构造方法,最后都会调用第三个构造方法。所以我们把自定义属性的解析,放在第三个构造方法中。解析属性之前,首先声明变量。

    private int borderWidth;    private int borderColor;    private int fillColor;

接下来解析自定义属性的值。

public CircleTextView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleTextView);        borderWidth = typedArray.getDimensionPixelSize(R.styleable.CircleTextView_border_width, 0);        borderColor = typedArray.getColor(R.styleable.CircleTextView_border_color, Color.WHITE);        fillColor = typedArray.getColor(R.styleable.CircleTextView_fill_color, Color.WHITE);        typedArray.recycle();        setGravity(Gravity.CENTER);        setPadding(Utils.dip2px(context,10), Utils.dip2px(context,10), Utils.dip2px(context,10), Utils.dip2px(context,10));    }

代码中原理都很简单,就是取出用户自定义属性的值,如果没有定义该属性,并赋予默认值。另外,为了美观,默认将gravity属性设置为居中,最后一行setPadding是为了避免边框与文字内容重叠所以设置padding默认为10dp,用到了dp转px的方法,可以移步https://github.com/CodeXiaoMai/MyProject/中查看具体实现。

测量重写onMeasure方法

属性的值我们都得到了,接下来就该根据得到的值去给TextView加上圆形的背景和边框。那么这个圆形该画多大呢,如果需要用户手动设置圆形的大小,那么圆太大太小都不合适,显然这种方法太low了。可是,一般我们设置textview的宽度与高度时,基本上都是wrap_content就行了,但是如果这里还用wrap_content的话,那么宽和高是不一定相等的,应该说是极大可能是不相等的。

我们的TextView中的文字内容可能是这样的:
这里写图片描述
第一种宽明显比高的值要大,第二种高明显比宽的值要大,第三种,恩~,看着好像差不多了,不过你仔细一看,还是宽高不一样的。这时候,你可能迷茫了,尼玛,我到底要画多大的圆才行。。。哈哈,接下来,就来通过重写onMeasuer方法,让它的文字内容无论是怎样排列,都会宽高相同。

 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int measuredWidth = getMeasuredWidth();        int measuredHeight = getMeasuredHeight();        int max = Math.max(measuredWidth, measuredHeight);        setMeasuredDimension(max, max);    }

其实原理还是很简单的,就是获取TextView的宽和高,然后取出最大值,重新设置TextView的宽和高都为这个最大值,这样就实现了宽和高一致了。

画圆

终于到了最后一步,也是效果出现的时候了。

@Override    protected void onDraw(Canvas canvas) {        int width = getWidth();        int height = getWidth();        // 半径        radius = Math.min(width, height) / 2;        if (borderWidth > 0) {            //创建绘制边框的画笔            Paint borderPaint = new Paint();            //设置画笔的颜色            borderPaint.setColor(borderColor);            //在画布上用绘制边框的笔,画出边框            canvas.drawCircle(getWidth() / 2, getWidth() / 2, radius, borderPaint);        }        //创建绘制背景圆的画笔        Paint fillPaint = new Paint();        fillPaint.setColor(fillColor);        //在画布上画出背景圆,圆的大小为TextView的宽度减去边框的宽度        canvas.drawCircle(getWidth() / 2, getWidth() / 2, radius - borderWidth, fillPaint);        super.onDraw(canvas);    }

Ok,到这里CircleTextView就已经全部实现了。最后,为了方便代码的阅读,贴出完整的代码:

package com.xiaomai.myproject.view;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.Gravity;import android.widget.TextView;import com.xiaomai.myproject.R;import com.xiaomai.myproject.utils.Utils;/** * Created by XiaoMai */public class CircleTextView extends TextView {    private int radius;    private int borderWidth;    private int borderColor;    private int fillColor;    public CircleTextView(Context context) {        this(context, null);    }    public CircleTextView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public CircleTextView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleTextView);        borderWidth = typedArray.getDimensionPixelSize(R.styleable.CircleTextView_border_width, 0);        borderColor = typedArray.getColor(R.styleable.CircleTextView_border_color, Color.WHITE);        fillColor = typedArray.getColor(R.styleable.CircleTextView_fill_color, Color.WHITE);        typedArray.recycle();        setGravity(Gravity.CENTER);        setPadding(Utils.dip2px(context, 10), Utils.dip2px(context, 10), Utils.dip2px(context, 10),                Utils.dip2px(context, 10));    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int measuredWidth = getMeasuredWidth();        int measuredHeight = getMeasuredHeight();        int max = Math.max(measuredWidth, measuredHeight);        setMeasuredDimension(max, max);    }    @Override    protected void onDraw(Canvas canvas) {        int width = getWidth();        int height = getWidth();        // 半径        radius = Math.min(width, height) / 2;        if (borderWidth > 0) {            Paint borderPaint = new Paint();            borderPaint.setColor(borderColor);            canvas.drawCircle(getWidth() / 2, getWidth() / 2, radius, borderPaint);        }        Paint fillPaint = new Paint();        fillPaint.setColor(fillColor);        canvas.drawCircle(getWidth() / 2, getWidth() / 2, radius - borderWidth, fillPaint);        super.onDraw(canvas);    }}
0 0
原创粉丝点击