仿荷包投资的滑块

遇到个需要仿荷包这个滑块的需求

第一想法使用seekbar来实现,结果android:tickMark属性需要API24以上,瞬间无语了,API24的用户目前又有几个呢?

然后想自定义seekbar,结果做出来后拖动的时候一闪一闪的,没有中间过程。

突然想起鸿神的一篇文章里有个拖动控件的ViewDragHelperhttp://blog.csdn.net/lmj623565791/article/details/46858663

稍微修改一下不就是我所需要的吗,我把它里面的多余的去掉,就留了一个回弹的view,然后修改了下回弹的位置,并且用canvas画出几个圆点,大功告成。

先贴上我的布局文件,中间那条线是单独的一个view

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">

    <View
        android:layout_width="match_parent"
        android:layout_height="3dp"
        android:layout_centerVertical="true"
        android:background="@color/colorAccent" />

    <com.tshouyi.vdhdemo.VDHLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:orientation="vertical">

        <TextView
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_gravity="left"
            android:background="@mipmap/bg"
            android:clickable="true"
            android:gravity="center"
            android:text="back" />

    </com.tshouyi.vdhdemo.VDHLayout>
</RelativeLayout>

代码注释也很详细了,没注释的地方我也不知道

package com.tshouyi.vdhdemo;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

/**
 * Created by zxy on 2016/11/11.
 */

public class VDHLayout extends LinearLayout {
    private ViewDragHelper mDragger;
    private View mAutoBackView;
    private Point[] mAutoBackOriginPos =
            {
                    new Point(0, 0),
                    new Point(0, 0),
                    new Point(0, 0),
                    new Point(0, 0),
                    new Point(0, 0),
            };//5个原点坐标数组初始值

    public VDHLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setWillNotDraw(false);//没这个方法就不执行onDraw,我也不知道为什么
        //1、创建实例
        mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback()//3、实现ViewDragHelper.CallCack相关方法
        {
            /**
             * 如果子View不消耗事件(clickable=false),那么整个手势(DOWN-MOVE*-UP)都是直接进入onTouchEvent,在onTouchEvent的DOWN的时候就确定了captureView。
             * 如果消耗事件,那么就会先走onInterceptTouchEvent方法,判断是否可以捕获,而在判断的过程中会去判断另外两个回调的方法:
             * getViewHorizontalDragRange和getViewVerticalDragRange,只有这两个方法返回大于0的值才能正常的捕获。
             */
            @Override
            public boolean tryCaptureView(View child, int pointerId) {//tryCaptureView如果返回ture则表示可以捕获该view
                //mEdgeTrackerView禁止直接移动
                return child == mAutoBackView;
            }

            /**
             * clampViewPositionHorizontal,clampViewPositionVertical可以在该方法中对child移动的边界进行控制,
             * left , top 分别为即将移动到的位置,以下代码表示内部移动效果
             */
            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                final int leftBound = getPaddingLeft();
                final int rightBound = getWidth() - mAutoBackView.getWidth() - leftBound;
                final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
                return newLeft;
            }

            @Override
            public int clampViewPositionVertical(View child, int top, int dy) {
                return mAutoBackOriginPos[0].y;//水平滑动,y是固定的
            }

            //手指释放的时候回调
            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {

                //mAutoBackView手指释放时可以自动回去
                if (releasedChild == mAutoBackView) {
                    //判断不同范围回到最近的圆点
                    if (mAutoBackView.getLeft() < mAutoBackOriginPos[1].x / 2) {
                        mDragger.settleCapturedViewAt(mAutoBackOriginPos[0].x, mAutoBackOriginPos[0].y);
                    } else if (mAutoBackView.getLeft() < 3 * mAutoBackOriginPos[1].x / 2) {
                        mDragger.settleCapturedViewAt(mAutoBackOriginPos[1].x, mAutoBackOriginPos[0].y);
                    } else if (mAutoBackView.getLeft() < 5 * mAutoBackOriginPos[1].x / 2) {
                        mDragger.settleCapturedViewAt(mAutoBackOriginPos[2].x, mAutoBackOriginPos[0].y);
                    } else if (mAutoBackView.getLeft() < 7 * mAutoBackOriginPos[1].x / 2) {
                        mDragger.settleCapturedViewAt(mAutoBackOriginPos[3].x, mAutoBackOriginPos[0].y);
                    } else {
                        mDragger.settleCapturedViewAt(mAutoBackOriginPos[4].x, mAutoBackOriginPos[0].y);
                    }
                    invalidate();
                }
            }

            @Override
            public int getViewHorizontalDragRange(View child) {
                return getMeasuredWidth() - child.getMeasuredWidth();
            }

            @Override
            public int getViewVerticalDragRange(View child) {
                return getMeasuredHeight() - child.getMeasuredHeight();
            }

        });

    }

    //    2、触摸相关方法
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return mDragger.shouldInterceptTouchEvent(event);//决定我们是否应该拦截当前的事件
    }

    int position;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getX() > mAutoBackView.getLeft() && event.getX() < mAutoBackView.getRight()) {//按下时在滑块范围内说明要拖动
            mDragger.processTouchEvent(event);//处理触摸事件
        } else {//else滑动滑块到触摸位置最近的圆点
            if (event.getX() - 60 < mAutoBackOriginPos[1].x / 2) {
                //点击该范围移动到第一个点,60是什么我也不知道,不要这个就点不准
                position = 0;
            } else if (event.getX() - 60 < 3 * mAutoBackOriginPos[1].x / 2) {
                position = mAutoBackOriginPos[1].x;
            } else if (event.getX() - 60 < 5 * mAutoBackOriginPos[1].x / 2) {
                position = 2 * mAutoBackOriginPos[1].x;
            } else if (event.getX() - 60 < 7 * mAutoBackOriginPos[1].x / 2) {
                position = 3 * mAutoBackOriginPos[1].x;
            } else {
                position = 4 * mAutoBackOriginPos[1].x;
            }
            //1.调用ofInt(int...values)方法创建ValueAnimator对象
            int pNow = mAutoBackView.getLeft();
            ValueAnimator mAnimator = ValueAnimator.ofInt(pNow, position);
            //2.为目标对象的属性变化设置监听器
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // 3.为目标对象的属性设置计算好的属性值
                    int animatorValue = (int) animation.getAnimatedValue();
                    MarginLayoutParams marginLayoutParams = (MarginLayoutParams) mAutoBackView.getLayoutParams();
                    marginLayoutParams.leftMargin = animatorValue;
                    mAutoBackView.setLayoutParams(marginLayoutParams);
                }
            });
            //4.设置动画的持续时间、是否重复及重复次数等属性
            mAnimator.setDuration(Math.abs(pNow - position) * 2);
            mAnimator.setRepeatCount(0);
            mAnimator.setRepeatMode(ValueAnimator.REVERSE);
            //5.为ValueAnimator设置目标对象并开始执行动画
            mAnimator.setTarget(mAutoBackView);
            mAnimator.start();
            invalidate();
        }
        return true;
    }


    @Override
    public void computeScroll() {
        if (mDragger.continueSettling(true)) {
            invalidate();
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        //保存5个原点的位置信息(左边缘坐标),不懂的参考图片
        for (int i = 0; i < 5; i++) {
            mAutoBackOriginPos[i].x = i * ((r - l) - (mAutoBackView.getRight() - mAutoBackView.getLeft())) / 4;
            mAutoBackOriginPos[i].y = mAutoBackView.getTop();
        }
    }


    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mAutoBackView = getChildAt(0);//加载view
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setAntiAlias(true);
        //画圆点
        for (int i = 0; i < 5; i++) {
            canvas.drawCircle(mAutoBackOriginPos[i].x + (mAutoBackView.getRight() - mAutoBackView.getLeft()) / 2,
                    mAutoBackOriginPos[i].y + (mAutoBackView.getBottom() - mAutoBackView.getTop()) / 2, 15, paint);
        }
    }
}


不懂的再看一下这张图吧

好了,就这样吧。

顺便把另外一种自定义seek利用seek的进度的方式贴上,原理就是通过改变seekbar的进度来滑动到指定位置。

先看xml文件,两个seekbar重合,上面seekbar个仅仅是为了得到圆点,也就是将Base.Widget.AppCompat.SeekBar.Discrete重新定义了一下,第二个的最大值设置为200

 <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true">
        <SeekBar
            android:id="@+id/seekBar2"
            style="@style/Base.Widget.AppCompat.SeekBar.Seek"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:max="4"
            android:thumb="@null" />
        <SeekBar
            android:id="@+id/seekBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerVertical="true"
            android:max="200"
            android:progress="0"
            android:thumb="@mipmap/ic_launcher" />
    </RelativeLayout>

接下来是自定义的样式
    <style name="Base.Widget.AppCompat.SeekBar.Seek">
        <item name="tickMark">@drawable/seekbar</item>
    </style>
引用的shape文件
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size
        android:width="12dp"
        android:height="12dp" />
    <solid android:color="@android:color/holo_red_dark" />
</shape>
activity也很简单,自己看吧
public class MainActivity extends AppCompatActivity {

    private SeekBar seekBar;
    boolean check = false;
    int pro;//用来控制滑动时触发的onProgressChanged不进行滑动

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        seekBar = (SeekBar) findViewById(R.id.seekBar);
        SeekBar seekBar2 = (SeekBar) findViewById(R.id.seekBar2);
        seekBar2.setEnabled(false);//去除重叠的后面个seekbar的影响
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(final SeekBar seekBar, int i, boolean b) {
                if (pro==0||pro == seekBar.getProgress() || pro - 1  == seekBar.getProgress()|| pro+ 1 == seekBar.getProgress() ) {
                 //变化值为1的一般不是滑动触发的
                    final int progress = seekBar.getProgress();
                    pro = progress;
                    if (progress < 25) {
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                if (progress != 0)
                                    seekBar.setProgress(progress - 1);
                            }
                        }, 10);

                    } else if (progress < 50) {
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                seekBar.setProgress(progress + 1);
                            }
                        }, 10);

                    } else if (progress < 75) {
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                if (progress != 50)
                                    seekBar.setProgress(progress - 1);
                            }
                        }, 10);

                    } else if (progress < 100) {
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                seekBar.setProgress(progress + 1);
                            }
                        }, 10);

                    } else if (progress < 125) {
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                if (progress != 100)
                                    seekBar.setProgress(progress - 1);
                            }
                        }, 10);
                    } else if (progress < 150) {
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                seekBar.setProgress(progress + 1);
                            }
                        }, 10);
                    } else if (progress < 175) {
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                if (progress != 150)
                                    seekBar.setProgress(progress - 1);
                            }
                        }, 10);
                    } else if (progress < 200) {
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                seekBar.setProgress(progress + 1);
                            }
                        }, 10);
                    }
                }
            }
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }
            @Override
            public void onStopTrackingTouch(final SeekBar seekBar) {
                pro=seekBar.getProgress();
                seekBar.setProgress(seekBar.getProgress() - 1);
            }
        });
    }
}

最后效果

没怎么写博客,里面问题很多,欢迎指正



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值