• 自定义View实战


    PS:上一篇从0开始学自定义View有博友给我留言说要看实战,今天我特意写了几个例子,供大家参考,所画的图案加上动画看着确实让人舒服,喜欢的博友可以直接拿到自己的项目中去使用,由于我这个写的是demo,代码格式写的有些乱,所以,要自己封装一下才可以使用,当然你如果真的不想封装,可以直接使用,也可以给我留言,我封装好放在github上供大家参考,也会做成依赖让大家直接添加即可

    先上图再分析

    可以看出图中有三种样式

    • 第一种是普通的一个label,使用场景:商品过期,促销等展示。
    • 第二种是圆形进度条,      使用场景:下载文件进度,加载视频进度,耗电量进度.....
    • 第三种是条形进度条,      使用场景:滑动调值,手机音效大小...

    上面的三种,均是demo,考虑使用场景并不完善,比如说第三种条形进度条还可以加上刻度,滑动到两边需要判断越界等。那就先拿第三个来吧

    条形进度条-可拖动

    分析:我们想要做一个类似的控件,需要考虑的问题不只是眼睛看的到的,看不到的就好比我只能点击小红球才可以滑动,我点击其他区域是不能有任何操作的,这个时候就要判断手指down的时候是否落在了小球上。

    • 线条 : 渐变颜色,线帽格式,长度,宽度设置,父布局宽高格式设置格式设置,子view宽高格式设置
    • 球 :颜色,起始位置和终止位置要在线上,尺寸

    在做之前我们先一个一个知识点解析,首先是线的渐变颜色,单独拿出

    /**
         * 设置进度圆环颜色(支持渐变色)
         *
         * @param colorArray 渐变色集合
         */
        private int[] mColorArray;  // 圆环渐变色
        public void setProgColor(@ColorRes int[] colorArray) {
            if (colorArray == null || colorArray.length < 2) return;
            mColorArray = new int[colorArray.length];
            for (int index = 0; index < colorArray.length; index++)
                mColorArray[index] = ContextCompat.getColor(getContext(), colorArray[index]);
            paint.setShader(new LinearGradient(0, 0, getMeasuredWidth(),0 , mColorArray, new float[]{0,.3F,.6F,.9F}, Shader.TileMode.MIRROR));
            invalidate();
        }

    我们可以看到我封装成了一个方法,通过paint.setShader进行着色,方法传入的是LinearGradient对象,我们看源码,解释参数

     /**
         * Create a shader that draws a linear gradient along a line.
         *
         * @param x0           The x-coordinate for the start of the gradient line
         * @param y0           The y-coordinate for the start of the gradient line
         * @param x1           The x-coordinate for the end of the gradient line
         * @param y1           The y-coordinate for the end of the gradient line
         * @param colors       The colors to be distributed along the gradient line
         * @param positions    May be null. The relative positions [0..1] of
         *                     each corresponding color in the colors array. If this is null,
         *                     the the colors are distributed evenly along the gradient line.
         * @param tile         The Shader tiling mode
        */
        public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
                @Nullable float positions[], @NonNull TileMode tile) {
            if (colors.length < 2) {
                throw new IllegalArgumentException("needs >= 2 number of colors");
            }
            if (positions != null && colors.length != positions.length) {
                throw new IllegalArgumentException("color and position arrays must be of equal length");
            }
            mType = TYPE_COLORS_AND_POSITIONS;
            mX0 = x0;
            mY0 = y0;
            mX1 = x1;
            mY1 = y1;
            mColors = colors.clone();
            mPositions = positions != null ? positions.clone() : null;
            mTileMode = tile;
        }

    参数

    1. x0,y0着色的起始位置
    2. x1,y1终止位置
    3. colors区域内着色的颜色集
    4. positions区域内部划分模块,逐一着色,如:区域1-100划分为4块,第一块1-25红色,26-50蓝色..。系统会默认在两种颜色不一样的情况下进行颜色过度渲染,达到渐变的效果,所以我们不用担心出现红蓝划分明显的情况。
    5. TileMode 模式选择
      1.   CLAMP:当图片小于绘制尺寸时要进行边界拉伸来填充
      2.   REPEAT:当图片小于绘制尺寸时重复平铺
      3.   MIRROR:当图片小于绘制尺寸时镜像平铺

    好了,下面我们就先画线和红点,如果有看不懂的博友,可以先看上一篇从0开始学自定义View

    public void init(){
            paint = new Paint();
            paint.setStrokeWidth(20);//画笔宽度
            paint.setStyle(Paint.Style.FILL);//填充类型
            paint.setAntiAlias(true);
            paint.setStrokeCap(Paint.Cap.ROUND);//线帽,半圆
            paintCircle2 = new Paint();
            paintCircle2.setColor(Color.RED);
            paintCircle2.setStrokeWidth(5);
            paintCircle2.setStyle(Paint.Style.FILL);
            paintCircle2.setAntiAlias(true);
        }
     @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            width = MeasureSpec.getSize(widthMeasureSpec);
            heigth =  MeasureSpec.getSize(heightMeasureSpec);
            Log.e("heigth -- width ",heigth+"  -- "+width);
            circleX=width/2;//初始化红点坐标位置
            circleY=20;
        }
     int[] colors={R.color.colorPrimary,R.color.colorAccent,R.color.color_environment_serious,R.color.color_environment_mild};
        @Override
        protected void onDraw(Canvas canvas) {
    
            super.onDraw(canvas);
            setProgColor(colors);
            //绘制一条线,线帽为半圆
            canvas.drawLine(50,20,width-50,20,paint);
            canvas.drawCircle(circleX,circleY,15,paintCircle2);
        }

    到这里,线和点就已经做好了,只是静态的,下面是如何拖动,就要在onTouchEvent方法中去写了,代码都已经添加了注释  Math.abs(dhx)<50&&Math.abs(dhy)<50  是证明down的坐标点和原始球的坐标点相差范围在50内,如果小球在屏幕200,200的位置,而我们手指down的点在800,800,那么相差如此巨大,肯定不是我们想要的结果,所以,我们就认为down的坐标减去球的坐标差值最小(50内)才是我们想要的结果,这个时候我们再设置小球move的坐标(让小球跟随手指移动)。

    /**
         * 判断所按压的坐标和红点坐标的关系
         * 如果手指按在了红点的下方,那么down-红点y坐标的绝对值如果等于或者小于半径,也就是说目前按压的就是红点,
         *
         * */
        private boolean clickCircle(int downHeigth,int downWidth){
            int dhy = downHeigth - circleY;
            int dhx = downWidth - circleX;
            //证明按压的是原点
            if(Math.abs(dhx)<50&&Math.abs(dhy)<50){
                return true;
            }
            return false;
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            return super.dispatchTouchEvent(event);
        }
        boolean isFlag=false;
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int rawX = (int) event.getX();
            int rawY = (int) event.getY();//获取到视图坐标,想对于外部viewgroup来说。
            Log.e("raw",rawX+"  ---  "+rawY);
            Log.e("rawwww",circleX+"  ---  "+circleY);
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    //如果是手指down在了红点上,move时可重绘
                    if(clickCircle(rawY,rawX)) {
                        circleX = rawX;
                        isFlag=true;
                    }else{
                        isFlag=false;
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    if(isFlag){
                        circleX = rawX;
                        invalidate();
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    isFlag=false;//抬起手指,状态恢复
                    break;
            }
            return true;//自己消费
        }

    下一篇是圆形进度条

  • 相关阅读:
    Python
    C#中读写INI文件
    C#函数式编程
    TypeScript安装
    finally是否执行?finally何时执行?
    JavaScript对象、JSON对象、JSON字符串的区别
    webjars-jquery的引用
    spring boot 笔记--第三章
    两个常见tomcat警告分析
    JSch 实现 SSH 端口转发
  • 原文地址:https://www.cnblogs.com/cmusketeer/p/12843367.html
Copyright © 2020-2023  润新知