BMoveView,RadioGroup添加移动的特效View

作者: 不识水的鱼 | 来源:发表于2017-04-28 14:07 被阅读195次

    google也曾推荐使用底部导航栏的方式进行切换,使用RadioButton可完成切换的动作

    RadioButton 除了变颜色,添加图片显示外,我们还可以添加如下的特定效果.动画可以增加APP的美感,提升用户体验度

    先上图:

    1493335479979.mp4_1493336353.gif

    很多属性可以自定义

    我的github 源码使用链接
    BMoveView链接
    很多的自定义View

    欢迎点个Star
    属性 含义
    circleColor 圆环的颜色
    lineColor 下面的线条的颜色
    lineDuration 线条头的移动时间(单位ms)
    lineWidth 线条的宽度
    circleDuration 圆圈的动画时间(单位ms)
    circleCenterColor 圆圈中心的颜色(可以不和背景一样)
    circleRadio 圆圈的半径

    以上就是所有的属性

    可以设置不同的移动效果,根据个人需求来实现

    使用

    在布局文件XML里

    <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="60dp">
        <com.yk.myselfview.views.BMoveView
            android:id="@+id/bmoveview"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            yk:circleColor="#fd4040"
            yk:lineColor="#fd4040"
            yk:lineDuration="150"
            yk:lineWidth="3"
            yk:circleDuration="500"
            yk:circleCenterColor="#FFFFFF"
            yk:circleRadio="25"
            />
        <RadioGroup
            android:id="@+id/rg_group"
            android:gravity="center"
            android:weightSum="3"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="60dp">
            <RadioButton
                android:id="@+id/rb_first"
                android:button="@null"
                android:text="索引"
                android:layout_weight="1"
                android:textColor="@drawable/rb_button"
                android:textSize="18sp"
                android:gravity="center"
                android:layout_width="0dp"
                android:layout_height="match_parent"/>
            <RadioButton
                android:id="@+id/rb_second"
                android:button="@null"
                android:text="热门"
                android:layout_weight="1"
                android:textColor="@drawable/rb_button"
                android:textSize="18sp"
                android:gravity="center"
                android:layout_width="0dp"
                android:layout_height="match_parent"/>
            <RadioButton
                android:id="@+id/rb_third"
                android:button="@null"
                android:text="我的"
                android:layout_weight="1"
                android:textColor="@drawable/rb_button"
                android:textSize="18sp"
                android:gravity="center"
                android:layout_width="0dp"
                android:layout_height="match_parent"/>
        </RadioGroup>
    </RelativeLayout>
    

    其中

      <com.yk.myselfview.views.BMoveView
          android:id="@+id/bmoveview"
          android:layout_width="match_parent"
          android:layout_height="60dp"
          yk:circleColor="#fd4040"
          yk:lineColor="#fd4040"
          yk:lineDuration="150"
          yk:lineWidth="3"
          yk:circleDuration="500"
          yk:circleCenterColor="#FFFFFF"
          yk:circleRadio="25"/>
    

    为主要的布局,是我们的自定义的BMoveView

    在Activity里,如下:

      public class BMoveViewActivity extends Activity {
        private int mFirstPos; //上一次的radiobutton位置
        private int mLastPos;  //点击的radiobutton位置
        private BMoveView mBMoveView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_bmove_view);
            bMoveInit();
        }
    
        private void bMoveInit() {
            mBMoveView = (BMoveView) findViewById(R.id.bmoveview);
            RadioGroup radioGroup= (RadioGroup) findViewById(R.id.rg_group);
            ((RadioButton) (radioGroup.getChildAt(0))).setChecked(true);
            mFirstPos = 0;
            mBMoveView.startAnim();
            radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(RadioGroup group, int checkedId) {
                    for (int i = 0; i < group.getChildCount(); i++) {
                        boolean checked = ((RadioButton) (group.getChildAt(i))).isChecked();
                        if(checked){
                            mLastPos = i; //当前的点击位置
                            mBMoveView.setTwoPos(mFirstPos, mLastPos);
                            mFirstPos = mLastPos; //记录上一次的点击位置,更新位置
                        }
                    }
                }
            });
        }
    }
    

    是不是很简单,主要是记录两次的位置,就能实现这个效果了

    使用方法讲完了,下面介绍是如何实现的

    主要是在onDraw里,绘制我们的view,分析动画和过程

    • 一个圆圈的动画,就是旋转
    • 下面一个线条,添加移动效果
    • 线条移动头和尾的移动时间不同
    • 移动的方向和位置

    主要代码如下

        public class BMoveView extends View {
        private int mWidth;
        private int mHeight;
        private Paint mPaint=new Paint();
        private RectF mRectF;
        private int boardWidth=50;
        private int maxCircle=360;
        private int firstPos;  //第一次点击位置
        private int lastPos;  //第二次点击位置
        private int lineControl; //下面控制线条的起始位置
    
        private int mRoationx=0;
    
        private int radio=5;
        private int position=0;//点击到了那个button
        private int mLineEndLength;
        private int mLineLength;
        private int mCircleColor;
        private int mLineColor;
        private int mLineDuration;
        private int mLineWidth;
        private int mCircleDuration;
        private int mCircleCenterColor;
        private int mCircleRadio;
    
        public void setTwoPos(int firstPos,int lastPos) {
            this.firstPos = firstPos;
            this.lastPos=lastPos;
            devidePos();
        }
    
        //判断两次的位置,选择不同动画
        private void devidePos() {
            setRoationx(0);
            if(firstPos==0){
                if(lastPos==1){
                    leftToRigth(lastPos, 1);
                } else if(lastPos==2){
                    leftToRigth(lastPos, 2);
                }
            }else if(firstPos==1){
                if(lastPos==0){
                    leftToRigth(lastPos, -1);
                } else if(lastPos==2){
                    leftToRigth(lastPos, 1);
                }
            }
            else if(firstPos==2){
                if(lastPos==0){
                    leftToRigth(lastPos, -2);
                } else if(lastPos==1){
                    leftToRigth(lastPos, -1);
                }
            }
        }
    
        /**
         *
         * @param lastPos 上一次的位置
         * @param startLineineLastPosition 正为向右,负为想左,如果是1.则跨度为一,如果是2,则跨度为2;
         */
        private void leftToRigth(int lastPos, int startLineineLastPosition) {
            setPosition(lastPos);
            startAnim();
            lineControl=lastPos-startLineineLastPosition;//下面控制线条的起始位置
            startLineAnim(startLineineLastPosition);
            startLineEndAnim(startLineineLastPosition);
        }
    
        public void setRoationx(int roationx) {
            mRoationx = roationx;
        }
    
        public void setPosition(int position) {
            this.position = position;
        }
    
        public BMoveView(Context context) {
            super(context);
            init(context,null,0);
        }
    
        public BMoveView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context,attrs,0);
        }
    
        public BMoveView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context,attrs,defStyleAttr);
        }
    
        private void init(Context context, AttributeSet attrs, int defStyleAttr) {
    
            TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.BMoveView, defStyleAttr, 0);
    
            int n = a.getIndexCount();
    
            for (int i = 0; i < n; i++) {
                int attr = a.getIndex(i);
                switch (attr) {
                    case R.styleable.BMoveView_circleColor:
                        mCircleColor = a.getColor(attr, Color.WHITE);
                        break;
                    case R.styleable.BMoveView_lineColor:
                        mLineColor = a.getColor(attr, Color.GRAY);
                        break;
                    case R.styleable.BMoveView_circleCenterColor:
                        mCircleCenterColor = a.getColor(attr, Color.GRAY);
                        break;
                    case R.styleable.BMoveView_lineDuration:
                        mLineDuration = a.getInt(attr,500);
                        break;
                    case R.styleable.BMoveView_lineWidth:
                        mLineWidth = a.getInt(attr, 5);
                        break;
                    case R.styleable.BMoveView_circleDuration:
                        mCircleDuration = a.getInt(attr,500);
                        break;
                    case R.styleable.BMoveView_circleRadio:
                        mCircleRadio = a.getInt(attr,500);
                        break;
                }
            }
            a.recycle();
            boardWidth=dip2px(context,mCircleRadio);
            radio=dip2px(context,mLineWidth);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mWidth = w;
            mHeight = h;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //画弧度
            mPaint.setColor(mCircleColor);
            mPaint.setAntiAlias(true);
            mPaint.setStyle(Paint.Style.FILL);
            //位置计算比较麻烦,用比例
            mRectF=new RectF(mWidth/6-boardWidth+position*mWidth/3,mHeight/2-boardWidth,mWidth/6+boardWidth+position*mWidth/3,mHeight/2+boardWidth);
            canvas.drawArc(mRectF,90,mRoationx,true,mPaint);
            //画圆覆盖
            mPaint.reset();
            mPaint.setColor(mCircleCenterColor);
            mPaint.setAntiAlias(true);
            mPaint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(mWidth/6+(position)*mWidth/3,mHeight/2,boardWidth-radio,mPaint);
            //画线条
            mPaint.setColor(mLineColor);
            mPaint.setStrokeWidth(radio);
            //起始和结束不同,每次动画结束位置是相同的,控制起始点和结束点
            canvas.drawLine(mWidth/6+lineControl*mWidth/3+mLineEndLength,mHeight/2+boardWidth-radio/2,mWidth/6+lineControl*mWidth/3+mLineLength,mHeight/2+boardWidth-radio/2, mPaint);
        }
    
        //圆圈的动画
        public void startAnim(){
            ValueAnimator animator = ValueAnimator.ofInt(0,maxCircle);
            animator.setDuration(mCircleDuration);
            animator.setStartDelay(mLineDuration);
            animator.setInterpolator(new LinearInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mRoationx = (int)animation.getAnimatedValue();
                    postInvalidate();
                }
            });
            animator.start();
        }
        //线条开始的动画
        public void startLineAnim(int startLineineLastPosition){
            ValueAnimator animator = ValueAnimator.ofInt(0,(mWidth/3)*startLineineLastPosition);
            animator.setDuration(mLineDuration);
            animator.setInterpolator(new LinearInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mLineLength = (int)animation.getAnimatedValue();
                    postInvalidate();
                }
            });
            animator.start();
        }
        //线条结束的动画
        public void startLineEndAnim(int endLineineLastPosition){
            ValueAnimator animator = ValueAnimator.ofInt(0,(mWidth/3)*endLineineLastPosition);
            animator.setDuration(mCircleDuration);
            animator.setInterpolator(new LinearInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mLineEndLength = (int)animation.getAnimatedValue();
                    postInvalidate();
                }
            });
            animator.start();
        }
        public static int dip2px(Context context, float dpValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
        }
    

    主要是在onDraw里的绘制

            @Override
            protected void onDraw(Canvas canvas) {
                super.onDraw(canvas);
                //画弧度
                mPaint.setColor(mCircleColor);
                mPaint.setAntiAlias(true);
                mPaint.setStyle(Paint.Style.FILL);
                //位置计算比较麻烦,用比例
                mRectF=new RectF(mWidth/6-boardWidth+position*mWidth/3,mHeight/2-boardWidth,mWidth/6+boardWidth+position*mWidth/3,mHeight/2+boardWidth);
                canvas.drawArc(mRectF,90,mRoationx,true,mPaint);
                //画圆覆盖
                mPaint.reset();
                mPaint.setColor(mCircleCenterColor);
                mPaint.setAntiAlias(true);
                mPaint.setStyle(Paint.Style.FILL);
                canvas.drawCircle(mWidth/6+(position)*mWidth/3,mHeight/2,boardWidth-radio,mPaint);
                //画线条
                mPaint.setColor(mLineColor);
                mPaint.setStrokeWidth(radio);
                //起始和结束不同,每次动画结束位置是相同的,控制起始点和结束点
                canvas.drawLine(mWidth/6+lineControl*mWidth/3+mLineEndLength,mHeight/2+boardWidth-radio/2,mWidth/6+lineControl*mWidth/3+mLineLength,mHeight/2+boardWidth-radio/2, mPaint);
            }
    

    如上所示,选好了点的位置就是添加动画,一共写了三个动画,圆圈的动画,线条头的动画,线条尾的动画

         //圆圈的动画
         public void startAnim(){
             ValueAnimator animator = ValueAnimator.ofInt(0,maxCircle);
             animator.setDuration(mCircleDuration);
             animator.setStartDelay(mLineDuration);
             animator.setInterpolator(new LinearInterpolator());
             animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
                     mRoationx = (int)animation.getAnimatedValue();
                     postInvalidate();
                 }
             });
             animator.start();
         }
         //线条开始的动画
         public void startLineAnim(int startLineineLastPosition){
             ValueAnimator animator = ValueAnimator.ofInt(0,(mWidth/3)*startLineineLastPosition);
             animator.setDuration(mLineDuration);
             animator.setInterpolator(new LinearInterpolator());
             animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
                     mLineLength = (int)animation.getAnimatedValue();
                     postInvalidate();
                 }
             });
             animator.start();
         }
         //线条结束的动画
         public void startLineEndAnim(int endLineineLastPosition){
             ValueAnimator animator = ValueAnimator.ofInt(0,(mWidth/3)*endLineineLastPosition);
             animator.setDuration(mCircleDuration);
             animator.setInterpolator(new LinearInterpolator());
             animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
                     mLineEndLength = (int)animation.getAnimatedValue();
                     postInvalidate();
                 }
             });
             animator.start();
         }
    

    逻辑判断,通过两次的位置,来判断该使用哪一个动画,同时配合Radiobutton完成移动的效果

        //判断两次的位置,选择不同动画
        private void devidePos() {
            setRoationx(0);
            if(firstPos==0){
                if(lastPos==1){
                    leftToRigth(lastPos, 1);
                } else if(lastPos==2){
                    leftToRigth(lastPos, 2);
                }
            }else if(firstPos==1){
                if(lastPos==0){
                    leftToRigth(lastPos, -1);
                } else if(lastPos==2){
                    leftToRigth(lastPos, 1);
                }
            }
            else if(firstPos==2){
                if(lastPos==0){
                    leftToRigth(lastPos, -2);
                } else if(lastPos==1){
                    leftToRigth(lastPos, -1);
                }
            }
        }
    
        /**
         *
         * @param lastPos 上一次的位置
         * @param startLineineLastPosition 正为向右,负为想左,如果是1.则跨度为一,如果是2,则跨度为2;
         */
        private void leftToRigth(int lastPos, int startLineineLastPosition) {
            setPosition(lastPos);
            startAnim();
            lineControl=lastPos-startLineineLastPosition;//下面控制线条的起始位置
            startLineAnim(startLineineLastPosition);
            startLineEndAnim(startLineineLastPosition);
        }
    

    再绘制view的时候,主要也使用了常用的一些方法,加上动画组合成的view

    gif图太小,找点大图镇楼

    S70427-19180549.jpg
    S70427-19175462.jpg
    S70427-19170761.jpg

    相关文章

      网友评论

        本文标题:BMoveView,RadioGroup添加移动的特效View

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