Android 深入(一)- 自定义View之BottomTitleImageView

来源:互联网 发布:单片机串口通信程序 编辑:程序博客网 时间:2024/06/15 06:24

通过本文你可以了解到自定义View的知识,同时你可以学会如何写一个属于自己的View。同时希望能够通过这个例子重新学习下自定义View的知识。

  • Android 开发到一定程度的时候,可以独立开发一款App,一些基本的知识都有使用。这时候必须进行自我提高,深入的研究下Android的各个方面,不然也只能停留在初级程序员的层次,只能是一个码农啦!

  • 自定义View是我们需要掌握的基本知识,在开发中一些需求使用系统内置的View不好实现,我们经常会自定义我们自己的View,同时在面试中经常被问到。下面我们就通过一个带有底部标题的自定义ImageView来学习下自定义View。

先看看效果图(效果就类似于有标题轮播图片的效果):

屏幕快照 2017-02-18 上午10.24.26.png

1.View的生命周期。

View的生命周期(来自网络)

1)从图中我们View的整个生命周期,主要由三部分操作完成分别是measure()、layout()、draw(),作用如下:

  • measure:计算视图的大小。
  • layout:设置视图在屏幕中显示的位置。
  • draw:绘制视图。其中onDraw()方法会花费大量时间,布局变化会重绘视图,所以在onDraw()中要避免对象分配。

2)invalidate() 和requsetLaytout()作用如下:

  • invalidate():重新绘制view,执行draw()操作。
  • requsetLaytout():重新请求绘制view,执行measure()和layout()过程,但不执行draw()操作。

2. 定义和加载自定义属性。

1)在values文件夹中定义属性文件attrs_wcircle_view.xml。

<resources>    <declare-styleable name="WBottomTitleView">        <attr name="textString" format="string" />        <attr name="textDimension" format="dimension" />        <attr name="textColor" format="color" />        <attr name="mAlpha" format="integer" />        <attr name="mTextBgColor" format="color" />        <attr name="textDrawable" format="color|reference" />    </declare-styleable></resources>

2) 在自定义布局的构造方法中加载自定义属性,根据属性更新画笔。

public WBottomTitleView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init(attrs, defStyle);    }    private void init(AttributeSet attrs, int defStyle) {        //加载 attributes        final TypedArray typedArray = getContext().obtainStyledAttributes(                attrs, R.styleable.WBottomTitleView, defStyle, 0);        mTextString = typedArray.getString(                R.styleable.WBottomTitleView_textString);        mTextColor = typedArray.getColor(                R.styleable.WBottomTitleView_textColor,                mTextColor);        mTextDimension = typedArray.getDimension(                R.styleable.WBottomTitleView_textDimension,                mTextDimension);        mAlpha = typedArray.getInt(R.styleable.WBottomTitleView_mAlpha, mAlpha);        mTextBgColor = typedArray.getInt(R.styleable.WBottomTitleView_mTextBgColor, mTextBgColor);        if (typedArray.hasValue(R.styleable.WBottomTitleView_textDrawable)) {            mTextDrawable = typedArray.getDrawable(                    R.styleable.WBottomTitleView_textDrawable);            mTextDrawable.setCallback(this);        }        typedArray.recycle();        mTextPaint = new TextPaint();        mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);        mTextPaint.setTextAlign(Paint.Align.LEFT);        p = new Paint();        invalidateTextPaintAndMeasurements();    }    //根据attributes更新TextPaint    private void invalidateTextPaintAndMeasurements() {        mTextPaint.setTextSize(mTextDimension);        mTextPaint.setColor(mTextColor);        if (TextUtils.isEmpty(mTextString)) {            mTextString = "";        }        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();        mTextHeight = fontMetrics.bottom;    }
  • TypedArray是一个数组容器,用于存储加载的属性。记住:用完必须recycle(),不然会发生内存泄漏。

  • Paint.FontMetrics解析:

图片来自网络

1)top是一行文字的上边界
2)ascent是文字可视区域的上边界
3)descent是文字可视区域的下边界
4)bottom是一行文字的下边界
5)leading是行与行之间的间距(通常为0,bottom与descent及top与ascent之间的间距足够间隔行行)

从上图中可以发现文字的可视区域在ascent与descent之间,top与bottom见的距离是整个文字所占空间的高度。

3. onDraw()绘制视图。

@Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        int paddingLeft = getPaddingLeft();        int paddingTop = getPaddingTop();        int paddingRight = getPaddingRight();        int paddingBottom = getPaddingBottom();        int contentWidth = getWidth() - paddingLeft - paddingRight;        int contentHeight = getHeight() - paddingTop - paddingBottom;        mTextPaintfontMetrics = mTextPaint.getFontMetrics();        p.setColor(mTextBgColor);// 设置灰色        p.setAlpha(mAlpha);        p.setStyle(Paint.Style.FILL);//设置填满        canvas.drawRect(paddingLeft, contentHeight - (mTextPaintfontMetrics.bottom - mTextPaintfontMetrics.top), contentWidth, contentHeight, p);// 矩形        // Draw the text.        canvas.drawText(mTextString,                paddingLeft,                paddingTop + (contentHeight - mTextHeight),                mTextPaint);        // Draw the text drawable on top of the text.        if (mTextDrawable != null) {            mTextDrawable.setBounds(paddingLeft, paddingTop,                    paddingLeft + contentWidth, paddingTop + contentHeight);            mTextDrawable.draw(canvas);        }    }

4. 完整代码

package cn.studyou.myviewdeep.view;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.drawable.Drawable;import android.text.TextPaint;import android.text.TextUtils;import android.util.AttributeSet;import android.widget.ImageView;import cn.studyou.myviewdeep.R;/** * 基本功能:带有底部标题的ImageView * 创建:王杰 * 创建时间:2017-02-18 */public class WBottomTitleView extends ImageView {    private String mTextString;    private int mTextColor = Color.RED;    private int mAlpha = 150;    private float mTextDimension = 0;    private Drawable mTextDrawable;    private Paint p;    private TextPaint mTextPaint;    private float mTextHeight;    private int mTextBgColor = Color.DKGRAY;    private Paint.FontMetrics mTextPaintfontMetrics;    public WBottomTitleView(Context context) {        super(context);        init(null, 0);    }    public WBottomTitleView(Context context, AttributeSet attrs) {        super(context, attrs);        init(attrs, 0);    }    public WBottomTitleView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init(attrs, defStyle);    }    private void init(AttributeSet attrs, int defStyle) {        //加载 attributes        final TypedArray typedArray = getContext().obtainStyledAttributes(                attrs, R.styleable.WBottomTitleView, defStyle, 0);        mTextString = typedArray.getString(                R.styleable.WBottomTitleView_textString);        mTextColor = typedArray.getColor(                R.styleable.WBottomTitleView_textColor,                mTextColor);        mTextDimension = typedArray.getDimension(                R.styleable.WBottomTitleView_textDimension,                mTextDimension);        mAlpha = typedArray.getInt(R.styleable.WBottomTitleView_mAlpha, mAlpha);        mTextBgColor = typedArray.getInt(R.styleable.WBottomTitleView_mTextBgColor, mTextBgColor);        if (typedArray.hasValue(R.styleable.WBottomTitleView_textDrawable)) {            mTextDrawable = typedArray.getDrawable(                    R.styleable.WBottomTitleView_textDrawable);            mTextDrawable.setCallback(this);        }        typedArray.recycle();        mTextPaint = new TextPaint();        mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);        mTextPaint.setTextAlign(Paint.Align.LEFT);        p = new Paint();        invalidateTextPaintAndMeasurements();    }    //根据attributes更新TextPaint    private void invalidateTextPaintAndMeasurements() {        mTextPaint.setTextSize(mTextDimension);        mTextPaint.setColor(mTextColor);        if (TextUtils.isEmpty(mTextString)) {            mTextString = "";        }        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();        mTextHeight = fontMetrics.bottom;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        int paddingLeft = getPaddingLeft();        int paddingTop = getPaddingTop();        int paddingRight = getPaddingRight();        int paddingBottom = getPaddingBottom();        int contentWidth = getWidth() - paddingLeft - paddingRight;        int contentHeight = getHeight() - paddingTop - paddingBottom;        mTextPaintfontMetrics = mTextPaint.getFontMetrics();        p.setColor(mTextBgColor);// 设置灰色        p.setAlpha(mAlpha);        p.setStyle(Paint.Style.FILL);//设置填满        canvas.drawRect(paddingLeft, contentHeight - (mTextPaintfontMetrics.bottom - mTextPaintfontMetrics.top), contentWidth, contentHeight, p);// 矩形        // Draw the text.        canvas.drawText(mTextString,                paddingLeft,                paddingTop + (contentHeight - mTextHeight),                mTextPaint);        // Draw the text drawable on top of the text.        if (mTextDrawable != null) {            mTextDrawable.setBounds(paddingLeft, paddingTop,                    paddingLeft + contentWidth, paddingTop + contentHeight);            mTextDrawable.draw(canvas);        }    }    public String gettextString() {        return mTextString;    }    public void setTextString(String textString) {        mTextString = textString;        invalidateTextPaintAndMeasurements();    }    public int getTextColor() {        return mTextColor;    }    public void setTextColor(int textColor) {        mTextColor = textColor;        invalidateTextPaintAndMeasurements();    }    public float getTextDimension() {        return mTextDimension;    }    public void setTextDimension(float textDimension) {        mTextDimension = textDimension;        invalidateTextPaintAndMeasurements();    }    public Drawable getTextDrawable() {        return mTextDrawable;    }    public void setTextDrawable(Drawable textDrawable) {        mTextDrawable = textDrawable;    }    public int getmTextBgColor() {        return mTextBgColor;    }    public void setmTextBgColor(int mTextBgColor) {        this.mTextBgColor = mTextBgColor;    }    public int getmAlpha() {        return mAlpha;    }    public void setmAlpha(int mAlpha) {        this.mAlpha = mAlpha;    }}

5. 开始使用自定义的View

1)在布局文件中引入View

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@color/colorAccent"    >    <cn.studyou.myviewdeep.view.WBottomTitleView        android:layout_width="match_parent"        android:layout_height="170dp"        android:id="@+id/wCircleView"        android:scaleType="fitXY"        app:textDimension="18sp"        app:textColor="#ffffff"        /></RelativeLayout>

2) Activity中设置图片和标题

package cn.studyou.myviewdeep;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import com.bumptech.glide.Glide;import butterknife.BindView;import butterknife.ButterKnife;import cn.studyou.myviewdeep.view.WBottomTitleView;public class MainActivity extends AppCompatActivity {    @BindView(R.id.wCircleView)    WBottomTitleView wCircleView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        wCircleView.setTextString("  今天天气不错,感觉挺好的。 ");        Glide.with(this).load("http://img06.tooopen.com/images/20170214/tooopen_sy_198645593736.jpg").into(wCircleView);    }}

6. 项目地址:https://github.com/wjie2014/MyViewDeep

简书地址:http://www.jianshu.com/p/cc0c1e9a836b

0 0
原创粉丝点击