自定义View 之 图标&文本 标题信息小控件

文本主要说明两点:
1,Android自定义View(继承View)的基本流程。
2,图标&文本 标题信息小控件的制作。

前言:
1,Android自定义View可以分别继承View,ViewGroup以及View控件(例如ImageView)来实现。本文中只说明继承View的方式来自定义View。
2,项目中经常使用到图标来描述或表示一种操作,而且经常会在图标的底部配上简单的文字说明,以更加清楚的表达图标所代表的意思。如微信主界面底部的4个小图标。
这里写图片描述
他们的格式都是图标+文本,像笔者这种新手可能就会在XML中使用ImageView+TextView(咳咳,以前真是这样做的,很明显坚持两个月这样做,XML布局就会很牛逼)。言归正传,现在知道使用一个自定义View就能比较简单的完成这个事情。

正文:
先说明继承View自定义控件的流程,一切都在图中。有三大部分,定义属性,继承View重写一些重要的方法,项目中引用。
这里写图片描述
其中第二部分又分为4个小部分,抽象的来说就是,获取值,根据值测量视图大小,更具视图以及资源完成绘制的过程。待会详讲。
那么接下来就按照这个大体的流程制作上面描述的这个小控件。

一,定义属性
先上代码
这里写图片描述

    <!-- Defines the custom XML attributes supported for a ImageTextView-->
    <declare-styleable name="ImageTextView">
        <attr name="text_info" format="string"/><!--文本类容-->
        <attr name="text_info_size" format="dimension"/><!--文本字体大小-->
        <attr name="text_info_color" format="color"/><!--文本颜色-->
        <attr name="image_content" format="reference"/><!--图标资源值-->
        <attr name="image_content_height" format="dimension"/><!--图标高度-->
    </declare-styleable>

定义属性首先要在valuse文件夹下建立attrs.xml文件,用来存放属性表述项。然后再定义declare-styleable以及attr项。
属性定义就是说明自定义控件需要那些新的属性,比如说上面定义了text_info属性就是表示ImageTextView图标下方文本类容。
其中attr name项为属性名,可随意定义。format是说明了该属性值的类型。详情对应方式参照http://blog.csdn.net/fanxl10/article/details/41316013。其中特别要说的是dp,sp等长度计量单位都是dimension表示。
二,继承View
a,定义类继承View
定义自己的类继承View,并重写3个构造方法,以及在代码中定义相关成员变量(该类名要与attrs文件中declare-styleable name属性名一致)

public class ImageTextView extends View {
    private int mImage_src;//图标资源
    private int mImage_size = 0;//图标大小
    private String mText_info = "";//文本类容
    private int mText_info_color = Color.BLACK;//文本颜色
    private int mText_info_size = 0;//文本大小
    private Rect mRect;//绘制文本矩形区,用于测绘文本高度
    private Paint mPaint;//画笔对象

    public ImageTextView(Context context) {
        this(context, null);
    }

    public ImageTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ImageTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

}

这里需要主要的是重写4个方法时如果使用IDE直接生产的其构造方法中是super(context),这里需要改为 this(context, null)。由于第三个构造方式是用于XML构造时使用的,xml中有部分属性是要交给View父类处理(整体View的高度),故要调用super方法。这里我们只处理我们自定义的属性。也就是接下来要将的获取属性值。

开始我们在attrs中定义了5个属性值,如果我们将该控件在XML布局文件中使用后,在构造该控件时,我们能在ImageTextView(Context context, AttributeSet attrs, int defStyleAttr)这个构造方法中通过attrs 参数获取到我们定义的值。

    public ImageTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取XML 定义的样式
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ImageTextView, defStyleAttr, 0);
        int n = typedArray.getIndexCount();
        //对其进行遍历 从第0项节点开始寻找
        for(int i = 0;i<n;i++)
        {
            int attr = typedArray.getIndex(i);
            switch (attr)
            {
                case R.styleable.ImageTextView_text_info://如果找到了定义本文类容的属性
                    mText_info = typedArray.getString(attr);//就将属性值获取出来,赋值给本类中的成员变量,待会作显示使用
                    break;

                case R.styleable.ImageTextView_text_info_color:
                    mText_info_color = typedArray.getColor(attr, Color.BLACK);//获取颜色值
                    break;

                case R.styleable.ImageTextView_image_content:
                    mImage_src = typedArray.getResourceId(attr,0);//获取资源值
                    break;
                case R.styleable.ImageTextView_image_content_height:
                    mImage_size = (int)typedArray.getDimension(attr,20);//获取图标高度 这里默认单位是dp

                    break;
                case R.styleable.ImageTextView_text_info_size:
                    mText_info_size = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));//获取文本大小 这里要将sp单位转为px
                    break;
            }
        }
        typedArray.recycle();//回收资源

        mPaint = new Paint();//初始化画笔
        mRect = new Rect();//初始化矩形区
    }

b,重写onMeasure 定义控件大小
Android View 机制中onMeasure就是用来测绘控件大小的,这里我们要算出各个控件需要的大小以及整体控件的大小,计算完成后将整体控件的大小通过setMeasuredDimension函数通知给系统,让系统知道该控件占用了整个布局中多大的位置。
这里写图片描述

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        int width;
        int height ;
        if (widthMode == MeasureSpec.EXACTLY)//xml中给定了控件的具体宽度的情况下
        {
            width = widthSize;//这里我们不用计算,直接使用给定的值
        } else//xml中没给定具体宽度值,即使用wrap_content的情况下
        {
            //这里没有指定宽度,故我们需要更具子控件的大小计算出整个控件需要的大小(宽度)
            int desired = getPaddingLeft() + mImage_size + getPaddingRight();//这里本人定义控件宽度就是图标的宽度,并且规定图标为正方形。大家完全可更具自己的需求定义。
            width = desired;
        }

        //下面是高度的计算 和上面同理
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        if (heightMode == MeasureSpec.EXACTLY)
        {
            height = heightSize;
        } else
        {
            mPaint.setTextSize(mText_info_size);
            mPaint.getTextBounds(mText_info, 0, mText_info.length(), mRect);
            float textHeight = mRect.height();
            int desired = (int) (getPaddingTop() + textHeight + mImage_size + getPaddingBottom());
            height = desired;
        }
        //完成测绘告知系统
        setMeasuredDimension(width, height);
    }

c,重写onDraw完成绘制
在本项目中onDraw方法很简单,只需要绘制图片和文本即可,而且没有动画什么的。如果遇到复杂的控件canvas绘制方面就要多了解些。

@Override
    protected void onDraw(Canvas canvas) {

        Bitmap bitmap = ((BitmapDrawable)(ContextCompat.getDrawable(getContext(),mImage_src))).getBitmap();//获取图片资源
        drawImage(canvas,bitmap,0,0,mImage_size,mImage_size,0,0);//绘制图片

        mPaint.setColor(mText_info_color);//设置文本颜色
        mPaint.setTextSize(mText_info_size);//设置文本大小

        if(mText_info.length()>=5)//设置不能超过5个字 
        {
            mText_info = mText_info.substring(0,5);
        }
        canvas.drawText(mText_info,5,mImage_size+mText_info_size,mPaint);//绘制文本
    }
    /*---------------------------------
         * 绘制图片
         * @param       x屏幕上的x坐标
         * @param       y屏幕上的y坐标
         * @param       w要绘制的图片的宽度
         * @param       h要绘制的图片的高度
         * @param       bx图片上的x坐标
         * @param       by图片上的y坐标
         *
         * @return      null
         ------------------------------------*/
    public static void drawImage(Canvas canvas, Bitmap blt, int x, int y,
                                 int w, int h, int bx, int by) {
        Rect src = new Rect();// 图片 >>原矩形
        Rect dst = new Rect();// 屏幕 >>目标矩形

        src.left = bx;
        src.top = by;
        src.right = bx + w;
        src.bottom = by + h;

        dst.left = x;
        dst.top = y;
        dst.right = x + w;
        dst.bottom = y + h;
        // 画出指定的位图,位图将自动--》缩放/自动转换,以填补目标矩形
        // 这个方法的意思就像 将一个位图按照需求重画一遍,画后的位图就是我们需要的了
        canvas.drawBitmap(blt, null, dst, null);
        src = null;
        dst = null;
    }

drawImage方法是从网上一小哥博客中直接拿过来的,抱歉当时没有记录下链接地址。但是亲测可用,谢谢小哥^.^

d,提供接口
这里当然就是大家公布想要对外提供的接口,我这里就提供个更换图片,更换文本的接口。这就属于很简单的内容了。

    public void setInfo(String text)
    {
        this.mText_info = text;
        invalidate();//刷新界面 会重新调用onDraw方法
    }

    public void setImageSrc(int src)
    {
        this.mImage_src = src;
        invalidate();
    }

然后在项目代码中就可以动态的改变这些属性了。

三,xml中使用

<com.ws.coyc.wsnote.UI.Layout.ImageTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:image_content="@mipmap/map"
        app:image_content_height="50dp"
        app:text_info=" 地图"
        app:text_info_size="18sp"
        app:text_info_color="@android:color/holo_blue_dark"
        />
    <com.ws.coyc.wsnote.UI.Layout.ImageTextView
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        app:image_content="@mipmap/search"
        app:image_content_height="50dp"
        app:text_info=" 搜索"
        app:text_info_size="18sp"
        android:layout_marginLeft="70dp"
        app:text_info_color="@android:color/holo_blue_dark"
        />
    <com.ws.coyc.wsnote.UI.Layout.ImageTextView
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        app:image_content="@mipmap/c"
        app:image_content_height="50dp"
        app:text_info="风景名胜"
        app:text_info_size="12sp"
        android:layout_marginLeft="140dp"
        app:text_info_color="@android:color/holo_blue_dark"
        />

在代码中就和常规的View一样 findViewbyId即可初始化。
效果展示:
这里写图片描述
可以看到第三张图的文本还可以向下来一些的,其实实在绘制文本的时候居中绘制就行了。这里我就偷偷懒不重新截图,给大家推荐些博客关于自定义view的。
http://blog.csdn.net/lmj623565791/article/details/24252901/
http://blog.csdn.net/huachao1001/article/details/51577291

后语:本来是打算前几天晚上写的,只怪电视太好看,连着看了两部谍战片。哈哈,中秋节后还是逼着自己先写完,不能拖了。个人写博客时间不长,文中定有很多不足之处甚至错误,大家如有发现问题,还请大家多多包含并及时指出,希望能和网友们一并进步,谢谢大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值