美文网首页
Androd自定义view--简单实战练习

Androd自定义view--简单实战练习

作者: 谢尔顿 | 来源:发表于2018-01-26 14:40 被阅读15次

    参考文章:
    Android 自定义 View(2) -- 使用

    1.渐变的SeekBar

    地址:https://github.com/gaojuanjuan/CustomViewDemo
    效果图:

    渐变的SeekBar
    核心代码:
    public class GradientSeekBarView extends View {
        private static final int[] SECTION_COLORS = {0xffCC3399, 0xFF00CCFF, 0xff339999};
        private  Context mContext;
        private Paint mPaint;
        private RectF rfBase;       //底层圆角矩形
        private RectF rfCover;      //覆盖层圆角矩形,底层圆角矩形和覆盖层圆角矩形形成一个线框
        private RectF rfContent;    //内容圆角矩形
        private int mWidth;
        private int mHeight;
        private float currentCount;
        private float maxCount;
    
        public GradientSeekBarView(Context context) {
            this(context,null);
        }
    
        public GradientSeekBarView(Context context, @Nullable AttributeSet attrs) {
            this(context,attrs,0);
        }
    
        public GradientSeekBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mContext = context;
            init();
        }
    
        private void init() {
            mPaint = new Paint();
            rfBase = new RectF();
            rfCover = new RectF();
            rfContent = new RectF();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int wMode = MeasureSpec.getMode(widthMeasureSpec);
            int wSize = MeasureSpec.getSize(widthMeasureSpec);
            int hMode = MeasureSpec.getMode(heightMeasureSpec);
            int hSize = MeasureSpec.getSize(heightMeasureSpec);
    
            if (wMode == MeasureSpec.EXACTLY || wMode ==MeasureSpec.AT_MOST){
                mWidth = wSize;
            }else {
                mWidth = 0;
            }
            if (hMode == MeasureSpec.AT_MOST || hMode ==MeasureSpec.UNSPECIFIED){
                mHeight = dpToPx(15);
            }else {
                mHeight = hSize;
            }
            setMeasuredDimension(mWidth,mHeight);
        }
    
        private int dpToPx(int dp){
            float scale = mContext.getResources().getDisplayMetrics().density;
            return (int)(dp * scale + 0.5f * (dp >= 0 ? 1:-1));
        }
        @SuppressLint("DrawAllocation")
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setAntiAlias(true);
            int round = mHeight /2;
    
            //绘制底层的圆角矩形
            mPaint.setColor(Color.GRAY);
            rfBase.set(0,0,mWidth,mHeight);
            canvas.drawRoundRect(rfBase,round,round,mPaint);
    
            //绘制内层的圆角矩形,和底层的圆角矩形形成线框
            mPaint.setColor(Color.WHITE);
            rfCover.set(2,2,mWidth-2,mHeight-2);
            canvas.drawRoundRect(rfCover,round,round,mPaint);
    
            //得到当前位置占总宽度的比例
            float section = currentCount / maxCount;
            
            //创建内容圆角矩形
            rfContent.set(2,2,(mWidth - 2)*section,mHeight - 2);
            
            //如果当前值的比例小于等于1/3,设置颜色为颜色数组中的第一个值
            if (section <= 1.0f/3.0f){
                if (section != 0.0f){
                    mPaint.setColor(SECTION_COLORS[0]);                
                }else {
                    mPaint.setColor(Color.TRANSPARENT);
                }
            }else{
                int count = (section <= 1.0f / 3.0f * 2) ? 2 : 3;
                int[] colors = new int[count];
                System.arraycopy(SECTION_COLORS,0,colors,0,count);
                LinearGradient shader = new LinearGradient(3, 3, (mWidth - 3) * section, mHeight - 3, colors, null, Shader.TileMode.MIRROR);
                mPaint.setShader(shader);
            }
            canvas.drawRoundRect(rfContent,round,round,mPaint);
            //一次绘制完成之后应该重置一下画笔
            mPaint.reset();
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
            //下面这句话的作用:表示子组件要自己消费这次事件,告诉父组件不要拦截(抢走)这次的事件
            getParent().requestDisallowInterceptTouchEvent(true);
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    moved(x);
                    break;
                case MotionEvent.ACTION_MOVE:
                    moved(x);
                    break;
                case MotionEvent.ACTION_UP:
                    moved(x);
                    break;
            }
            return true;
        }
    
        private void moved(float x) {
            if (x > mWidth){
                return;
            }
            currentCount = maxCount * (x / mWidth);
            invalidate();
        }
    
        public void setMaxCount(float maxCount){
            this.maxCount = maxCount;
        }
    
        public void setCurrentCount(float currentCount){
            this.currentCount = currentCount > maxCount ? maxCount :currentCount;
            invalidate();
        }
    
        public float getMaxCount() {
            return maxCount;
        }
    
        public float getCurrentCount() {
            return currentCount;
        }
    }
    

    Activity

            mGradientSeekBar.setMaxCount(100);
            mGradientSeekBar.setCurrentCount(100);
    

    2.自定义闪烁文本的TextView

    地址:https://github.com/gaojuanjuan/CustomViewDemo

    效果图:


    闪烁文本的TextView

    核心代码:

    public class TwinklingTextView extends TextView {
        private int mViewWidth;                 //用于获取整个View的宽度
        private LinearGradient mLinearGradient;
        private Matrix mGradientMatrix;
        private int mTranslate;                 //用于记录渲染的偏移量
    
        public TwinklingTextView(Context context) {
            this(context,null);
        }
    
        public TwinklingTextView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs,0);
        }
    
        public TwinklingTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            //通过这个条件判断,可以保证只在初始化的时候调用一次
            if (mViewWidth == 0){
                mViewWidth = getMeasuredWidth();
            }
            if (mViewWidth > 0){
                //创建渐变渲染器
                mLinearGradient = new LinearGradient(0,0,mViewWidth,0,new int[]{0x33e20b6c, 0xffe20b6c, 0x33e20b6c},
                        null, Shader.TileMode.CLAMP);
                //对当前view的paing设置渲染
                getPaint().setShader(mLinearGradient);
                mGradientMatrix = new Matrix();
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (mGradientMatrix != null){
                mTranslate += mViewWidth / 10;
                if (mTranslate > 2*mViewWidth){
                    mTranslate = -mViewWidth;
                }
                mGradientMatrix.setTranslate(mTranslate,0);
                mLinearGradient.setLocalMatrix(mGradientMatrix);
                postInvalidateDelayed(50);
            }
    
        }
    }
    
    

    3.自定义颜色选择器

    地址:https://github.com/gaojuanjuan/CustomViewDemo

    效果图:


    自定义颜色选择器

    核心代码:

    public class ColorSelectorView extends View {
        private static final int CENTER_RADIUS = 50;
        private int[] mColors;
        private Paint mColorPaint;
        private Paint mCenterPaint;
    
        private OnColorChangedListener mListener;
        private static final float PI = 3.1415926f;
        private int viewWidth;
        private int viewHeight;
    
        public ColorSelectorView(Context context) {
            this(context,null);
        }
    
        public ColorSelectorView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs,0);
        }
    
        public ColorSelectorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            mColors = new int[]{0xFFFF0000, 0xFFFF00FF,
                    0xFF0000FF, 0xFF00FFFF, 0xFF00FF00,
                    0xFFFFFF00, 0xFFFF0000};
            SweepGradient s = new SweepGradient(0, 0, mColors, null);
    
            //画外部颜色选择圆环
            mColorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mColorPaint.setShader(s);
            mColorPaint.setStyle(Paint.Style.STROKE);
            mColorPaint.setStrokeWidth(50);
    
            //画中心的圆
            mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mCenterPaint.setColor(Color.RED);
            mCenterPaint.setStrokeWidth(15);
    
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            viewWidth = w;
            viewHeight = h;
        }
    
    //    @Override
    //    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //        setMeasuredDimension(CENTER_X * 2,CENTER_Y * 2);
    //    }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.translate(viewWidth/2,viewHeight/2);
            canvas.drawCircle(0,0,CENTER_RADIUS * 6,mColorPaint);
            canvas.drawCircle(0,0,CENTER_RADIUS,mCenterPaint);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX() - viewWidth/2;
            float y = event.getY() - viewHeight/2;
            //判断当前触摸的位置是否在圆环内部
    
            boolean isInRing = Math.sqrt(x * x + y * y) <= (CENTER_RADIUS*6);
            Log.e(Constants.TAG,"onTouchEvent,x = "+x+",y = "+y+",CENTER_RADIUS = "+CENTER_RADIUS+
                    ",Math.sqrt(x * x + y * y) = "+Math.sqrt(x * x + y * y));
    
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_MOVE:
                    Log.e(Constants.TAG,"ACTION_DOWN");
                    if (isInRing){
                        setCenterPaintColor(x,y);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (isInRing&&mListener!=null){
                        mListener.colorChanged(mCenterPaint.getColor());
                    }
                    invalidate();
                    break;
    
            }
            return true;
        }
    
        private void setCenterPaintColor(float x, float y) {
            float angle = (float) Math.atan2(y, x);
            //计算得到江都相对于整个圆的比例
            float unit = angle / (2 * PI);
            if (unit < 0){
                unit += 1;
            }
            mCenterPaint.setColor(interpColor(mColors,unit));
            invalidate();
        }
    
        /**
         * 根据颜色数组和当前触摸点所在的比例计算得到颜色值
         * @param colors    颜色数组
         * @param unit      当前触摸点的比例
         * @return
         */
        private int interpColor(int[] colors, float unit) {
            if (unit <= 0){
                return colors[0];
            }
            if (unit >= 1){
                return colors[colors.length - 1];
            }
            float p = unit * (colors.length -1);
            int i = (int)p;
            p -= i;
            int c0 = colors[i];
            int c1 = colors[i + 1];
            int a = ave(Color.alpha(c0),Color.alpha(c1),p);
            int r = ave(Color.red(c0),Color.red(c1),p);
            int g = ave(Color.green(c0),Color.green(c1),p);
            int b = ave(Color.blue(c0),Color.blue(c1),p);
            return Color.argb(a,r,g,b);
        }
    
        /**
         * 计算两个值之间的指定比例的值
         * @param s 区域的开始值
         * @param d 区域的结束值
         * @param p 比例
         * @return
         */
        private int ave(int s, int d, float p) {
            return s + Math.round(p * (d-s));
        }
    
        public interface OnColorChangedListener{
            void colorChanged(int color);
        }
    
        public void setListener(OnColorChangedListener listener) {
            mListener = listener;
        }
    }
    

    4.自定义圆形颜色渐变的SeekBar

    地址:https://github.com/gaojuanjuan/CustomViewDemo

    效果图:


    圆形颜色渐变的SeekBar

    核心代码:

    public class CircleColorGradientSeekBarView extends View {
        private static final int[] SECTION_COLORS = {0xffffd300, Color.GREEN, 0xff319ed4, 0xffffd300};
        private Paint baseRingPaint;
        private RectF rect;
        private Paint colorfulRingPaint;
        private float ringWidth = 60;
        private int cx;
        private int cy;
        private int outerRadius;
        private float innerRadius;
        private float ringRadius;
    
        private int angle = 0;//弧度值
        private float mMaxProgress = 100;//最大进度值
        private boolean CALLED_FROM_ANGLE = false;
        private int mProgress;
        private int mProgressPercent;
        private Paint mTextPaint;
    
        public CircleColorGradientSeekBarView(Context context) {
            this(context, null);
        }
    
        public CircleColorGradientSeekBarView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CircleColorGradientSeekBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            baseRingPaint = new Paint();
            baseRingPaint.setColor(Color.GRAY);
            baseRingPaint.setAntiAlias(true);
            baseRingPaint.setStrokeWidth(ringWidth);
            baseRingPaint.setStyle(Paint.Style.STROKE);
            rect = new RectF();
    
            colorfulRingPaint = new Paint();
            colorfulRingPaint.setColor(Color.parseColor("#ff33b5e5"));
            colorfulRingPaint.setAntiAlias(true);
            colorfulRingPaint.setStrokeWidth(ringWidth);
            colorfulRingPaint.setStyle(Paint.Style.STROKE);
    
            setBackgroundColor(Color.TRANSPARENT);
            mTextPaint = new TextPaint();
            mTextPaint.setAntiAlias(true);
            mTextPaint.setDither(true);
            mTextPaint.setTextSize(100);
            mTextPaint.setColor(Color.RED);
    
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            Log.e(Constants.TAG, "w = " + w + "h = " + h);
            Log.e(Constants.TAG, "width = " + getWidth() + "height = " + getHeight());
            Log.e(Constants.TAG, "MeasuredWidth = " + getMeasuredWidth() + ",MeasuredHeight = " + getMeasuredHeight());
            //选择最小的值作为圆环视图的直径
            int size = (w > h) ? h : w;
    
            cx = w / 2;
            cy = h / 2;
    
            outerRadius = size / 2;//圆环外部半径
            ringRadius = outerRadius - ringWidth / 2;//圆环的半径
            innerRadius = outerRadius - ringWidth;//圆环内部半径
    
            float left = cx - ringRadius;   //渐变圆环外接矩形左边坐标
            float right = cx + ringRadius;  //渐变圆环外接矩形右边坐标
            float top = cy - ringRadius;    //渐变圆环外接矩形顶部坐标
            float bottom = cy + ringRadius; //渐变圆环外接矩形底部坐标
            rect.set(left, top, right, bottom);//设置渐变圆环的位置
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        @SuppressLint("DrawAllocation")
        @Override
        protected void onDraw(Canvas canvas) {
            SweepGradient shader = new SweepGradient(cx, cy, SECTION_COLORS, null);
            Matrix matrix = new Matrix();
            matrix.setRotate(-90, cx, cy);
            shader.setLocalMatrix(matrix);
            colorfulRingPaint.setShader(shader);
            //画背景圆环;
            canvas.drawCircle(cx, cy, ringRadius, baseRingPaint);
            String text = mProgressPercent + "%";
            //获取text的高度和宽度
            Rect textRect = new Rect();
            mTextPaint.getTextBounds(text,0,text.length(),textRect);
            canvas.drawText(text,cx-(textRect.width()/2),cy+(textRect.height()/2),mTextPaint);
            //画渐变圆环,每次刷新界面主要是改变这里的angle的值
            Log.e(Constants.TAG, "onDraw,rect = " + this.rect.toString());
            canvas.drawArc(this.rect, 270, angle, false, colorfulRingPaint);
            super.onDraw(canvas);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
            this.getParent().requestDisallowInterceptTouchEvent(true);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    moved(x, y, false);
                    break;
                case MotionEvent.ACTION_MOVE:
                    moved(x, y, false);
                    break;
                case MotionEvent.ACTION_UP:
                    moved(x, y, true);
                    break;
            }
            return true;
        }
    
        private void moved(float x, float y, boolean up) {
            //计算圆心到触摸点的直线距离,使用数学中的勾股定理
            float distance = (float) Math.sqrt(Math.pow((x - cx), 2) + Math.pow((y - cy), 2));
    
            //如果触摸点在外圆半径的一个适配区域内
            if (distance < outerRadius + 100 && distance > innerRadius - 100 && !up) {
    
                //将角度转换成弧度
                float degrees = (float) ((float) ((Math.toDegrees(Math.atan2(x - cx, cy - y)) + 360.0)) % 360.0);
    
                //使弧度值永远为正
                if (degrees < 0) {
                    degrees += 2 * Math.PI;
                }
    
                setAngle(Math.round(degrees));
                invalidate();
    
            } else {
                invalidate();
            }
        }
    
        public void setAngle(int angle) {
            this.angle = angle;
            float donePercent = (((float) this.angle) / 360) * 100;
            float progress = (donePercent / 100) * getMaxProgress();
            setProgressPercent(Math.round(donePercent));
            CALLED_FROM_ANGLE = true;
            setProgress(Math.round(progress));
        }
    
        public float getMaxProgress() {
            return mMaxProgress;
        }
    
        public void setProgress(int progress) {
            mProgress = progress;
        }
    
        public int getProgressPercent() {
            return mProgressPercent;
        }
    
        public void setProgressPercent(int progressPercent) {
            mProgressPercent = progressPercent;
        }
    
        public void setRingBackgroundColor(int color) {
            baseRingPaint.setColor(color);
        }
    
    }
    

    4.自定义折线图

    地址:https://github.com/gaojuanjuan/CustomViewDemo

    效果图:


    折线图

    核心代码:

    
    

    相关文章

      网友评论

          本文标题:Androd自定义view--简单实战练习

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