美文网首页Android应用开发连载自定义ViewAndroid第一行代码
Android 深入(一)- 自定义View之BottomTit

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

作者: JeenWang | 来源:发表于2017-02-23 14:38 被阅读388次

    通过本文你可以了解到自定义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>
    
    
    1. 在自定义布局的构造方法中加载自定义属性,根据属性更新画笔。
    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>
    
    
    1. 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

    相关文章

      网友评论

      本文标题:Android 深入(一)- 自定义View之BottomTit

      本文链接:https://www.haomeiwen.com/subject/mfprwttx.html