美文网首页Android开发Android开发经验谈技术干货
Android自定义View-渐变的温度指示器

Android自定义View-渐变的温度指示器

作者: 4e70992f13e7 | 来源:发表于2018-10-15 17:36 被阅读11次

    废话少说,先上图


    ED9EF9312D01ADABDFADF481CF32A26C.jpg

    1、自定义View的分类

    image.png

    2、自定义View要点

    1. View需要支持wrap_content
    2. View需要支持padding
    3. 尽量不要再View中使用Handler,View已经有post系列方法
    4. View如果有线程或者动画,需要及时停止(onDetachedFromWindow会在View被remove时调用)——避免内存泄露
    5. View如果有滑动嵌套情形,需要处理好滑动冲突

    3、直接继承自View的实现步骤和方法

    1. 重写onDraw,在onDraw中处理padding
    2. 重写onMeasure,额外处理wrap_content的情况
    3. 设定自定义属性attrs(属性相关xml文件,以及在onDraw中进行处理)

    4、实现效果图步骤

    1. 重写onMeasure,额外处理wrap_content的情况
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    
            mHeight = mDefaultTextSize + mDefaultTempHeight + mDefaultIndicatorHeight + textSpace;
    
            if (widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {
                setMeasuredDimension(mWidth, mHeight);
            } else if (widthSpecMode == MeasureSpec.AT_MOST) {
                setMeasuredDimension(mWidth, heightSpecSize);
            } else if (heightSpecMode == MeasureSpec.AT_MOST) {
                setMeasuredDimension(widthSpecSize, mHeight);
            }
        }
    
    1. 重写onDraw,在onDraw中处理padding,并画出温度计及指针
    @Override
    protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            final int paddingLeft = getPaddingLeft();
            final int paddingTop = getPaddingTop();
            final int paddingRight = getPaddingRight();
            final int paddingBottom = getPaddingBottom();
    
            //确定圆角矩形的范围,在TmepView的最底部,top位置为总高度-圆角矩形的高度
            rectProgressBg = new RectF();
            rectProgressBg.left = 0 + paddingLeft;
            rectProgressBg.top = mHeight - mDefaultTempHeight;
            rectProgressBg.right = mWidth - paddingRight;
            rectProgressBg.bottom = mHeight - paddingBottom;
    
            shader = new LinearGradient(0, mHeight - mDefaultTempHeight, mWidth, mHeight, SECTION_COLORS, null, Shader.TileMode.MIRROR);
            mPaint.setShader(shader);
            //绘制圆角矩形 mDefaultTempHeight / 2确定圆角的圆心位置
            canvas.drawRoundRect(rectProgressBg, mDefaultTempHeight / 2, mDefaultTempHeight / 2, mPaint);
    
            //当前位置占比
            selction = currentCount / maxCount;
            //绘制指针 指针的位置在当前温度的位置 也就是三角形的顶点落在当前温度的位置
            //定义三角形的左边点的坐标 x= tempView的宽度*当前位置占比-三角形的宽度/2  y=tempView的高度-圆角矩形的高度
            indexPath.moveTo(mWidth * selction - mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
            //定义三角形的右边点的坐标 = tempView的宽度*当前位置占比+三角形的宽度/2  y=tempView的高度-圆角矩形的高度
            indexPath.lineTo(mWidth * selction + mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
            //定义三角形的左边点的坐标 x= tempView的宽度*当前位置占比  y=tempView的高度-圆角矩形的高度-三角形的高度
            indexPath.lineTo(mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight + paddingTop);
            indexPath.close();
            indexPaint.setShader(shader);
            canvas.drawPath(indexPath, indexPaint);
    
            //绘制文本
            String text = (int) currentCount + "°c";
            //确定文本的位置 x=tempViwe的宽度*当前位置占比 y=tempView的高度-圆角矩形的高度-三角形的高度-文本的间隙
            canvas.drawText(text, mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight - textSpace, textPaint);
        }
    

    ps:绘制三角形指针,由于位置会变 所以要确定绘制的位置如图


    image

    代码还算比较好理解,详细代码如下:

    package androidtest.project.com.customview;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.LinearGradient;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.RectF;
    import android.graphics.Shader;
    import android.support.annotation.Nullable;
    import android.text.TextPaint;
    import android.util.AttributeSet;
    import android.view.View;
    
    /**
     * Created by liuboyu on 2018/10/15.
     */
    
    public class TemperatureView extends View {
    
        private Context mContext;
    
        /**
         * 分段颜色
         */
        private static final int[] SECTION_COLORS = {Color.GREEN, Color.YELLOW, Color.RED};
    
        /**
         * 默认宽度
         */
        private int mWidth = 1000;
    
        /**
         * 默认高度
         */
        private int mHeight = 200;
    
        /**
         * 设置温度的最大范围
         */
        private float maxCount = 100f;
    
        /**
         * 设置当前温度
         */
        private float currentCount = 50f;
    
        /**
         * 当前刻度位置
         */
        private float selction;
    
        /**
         * 主画笔,画刻度尺
         */
        private Paint mPaint;
    
        /**
         * 文字画笔
         */
        private Paint textPaint;
    
        /**
         * 当前刻度指针
         */
        private Path indexPath;
        private Paint indexPaint;
    
        /**
         * 画圆柱
         */
        private RectF rectProgressBg;
        private LinearGradient shader;
    
        /**
         * 指针的宽高
         */
        private int mDefaultIndicatorWidth = dipToPx(10);
        private int mDefaultIndicatorHeight = dipToPx(8);
        /**
         * 圆角矩形的高度
         */
        private int mDefaultTempHeight = dipToPx(20);
        /**
         * 默认字体大小
         */
        private int mDefaultTextSize = 30;
        private int textSpace = dipToPx(5);
    
    
        public TemperatureView(Context context) {
            super(context);
            init(context);
        }
    
        public TemperatureView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public TemperatureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
        }
    
        /**
         * 初始化各种画笔
         *
         * @param context
         */
        private void init(Context context) {
            this.mContext = context;
            //圆角矩形paint
            mPaint = new Paint();
            //防止边缘的锯齿
            mPaint.setAntiAlias(true);
    
            //文本paint
            textPaint = new TextPaint();
            textPaint.setAntiAlias(true);
            textPaint.setTextSize(mDefaultTextSize);
            textPaint.setTextAlign(Paint.Align.CENTER);
            textPaint.setColor(mContext.getResources().getColor(R.color.colorAccent));
    
            //三角形指针paint
            indexPath = new Path();
            indexPaint = new Paint();
            indexPaint.setAntiAlias(true);
            indexPaint.setStyle(Paint.Style.FILL);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    
            mHeight = mDefaultTextSize + mDefaultTempHeight + mDefaultIndicatorHeight + textSpace;
    
            if (widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {
                setMeasuredDimension(mWidth, mHeight);
            } else if (widthSpecMode == MeasureSpec.AT_MOST) {
                setMeasuredDimension(mWidth, heightSpecSize);
            } else if (heightSpecMode == MeasureSpec.AT_MOST) {
                setMeasuredDimension(widthSpecSize, mHeight);
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            final int paddingLeft = getPaddingLeft();
            final int paddingTop = getPaddingTop();
            final int paddingRight = getPaddingRight();
            final int paddingBottom = getPaddingBottom();
    
            //确定圆角矩形的范围,在TmepView的最底部,top位置为总高度-圆角矩形的高度
            rectProgressBg = new RectF();
            rectProgressBg.left = 0 + paddingLeft;
            rectProgressBg.top = mHeight - mDefaultTempHeight;
            rectProgressBg.right = mWidth - paddingRight;
            rectProgressBg.bottom = mHeight - paddingBottom;
    
            shader = new LinearGradient(0, mHeight - mDefaultTempHeight, mWidth, mHeight, SECTION_COLORS, null, Shader.TileMode.MIRROR);
            mPaint.setShader(shader);
            //绘制圆角矩形 mDefaultTempHeight / 2确定圆角的圆心位置
            canvas.drawRoundRect(rectProgressBg, mDefaultTempHeight / 2, mDefaultTempHeight / 2, mPaint);
    
            //当前位置占比
            selction = currentCount / maxCount;
            //绘制指针 指针的位置在当前温度的位置 也就是三角形的顶点落在当前温度的位置
            //定义三角形的左边点的坐标 x= tempView的宽度*当前位置占比-三角形的宽度/2  y=tempView的高度-圆角矩形的高度
            indexPath.moveTo(mWidth * selction - mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
            //定义三角形的右边点的坐标 = tempView的宽度*当前位置占比+三角形的宽度/2  y=tempView的高度-圆角矩形的高度
            indexPath.lineTo(mWidth * selction + mDefaultIndicatorWidth / 2, mHeight - mDefaultTempHeight);
            //定义三角形的左边点的坐标 x= tempView的宽度*当前位置占比  y=tempView的高度-圆角矩形的高度-三角形的高度
            indexPath.lineTo(mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight + paddingTop);
            indexPath.close();
            indexPaint.setShader(shader);
            canvas.drawPath(indexPath, indexPaint);
    
            //绘制文本
            String text = (int) currentCount + "°c";
            //确定文本的位置 x=tempViwe的宽度*当前位置占比 y=tempView的高度-圆角矩形的高度-三角形的高度-文本的间隙
            canvas.drawText(text, mWidth * selction, mHeight - mDefaultTempHeight - mDefaultIndicatorHeight - textSpace, textPaint);
        }
    
    
        /**
         * 单位转换
         *
         * @param dip
         * @return
         */
        private int dipToPx(int dip) {
            float scale = getContext().getResources().getDisplayMetrics().density;
            return (int) (dip * scale + 0.5f * (dip >= 0 ? 1 : -1));
        }
    
    
        /***
         * 设置最大的温度值
         * @param maxCount
         */
        public void setMaxCount(float maxCount) {
            this.maxCount = maxCount;
        }
    
        /***
         * 设置当前的温度
         * @param currentCount
         */
        public void setCurrentCount(float currentCount) {
            if (currentCount > maxCount) {
                this.currentCount = maxCount - 5;
            } else if (currentCount < 0f) {
                currentCount = 0f + 5;
            } else {
                this.currentCount = currentCount;
            }
            invalidate();
        }
    
        /**
         * 设置温度指针的大小
         *
         * @param width
         * @param height
         */
        public void setIndicatorSize(int width, int height) {
            this.mDefaultIndicatorWidth = width;
            this.mDefaultIndicatorHeight = height;
        }
    
        /**
         * 设置温度计厚度
         *
         * @param height
         */
        public void setTempHeight(int height) {
            this.mDefaultTempHeight = height;
        }
    
        /**
         * 设置文字大小
         *
         * @param textSize
         */
        public void setTextSize(int textSize) {
            this.mDefaultTextSize = textSize;
        }
    
        /**
         * 获取温度计最大刻度
         *
         * @return
         */
        public float getMaxCount() {
            return maxCount;
        }
    
        /**
         * 获取当前刻度
         *
         * @return
         */
        public float getCurrentCount() {
            return currentCount;
        }
    }
    
    

    相关文章

      网友评论

        本文标题:Android自定义View-渐变的温度指示器

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