二阶贝塞尔曲线之波浪图(二)

昨天我写了二阶贝塞尔曲线之波浪图之后,结果装逼失败。理由是大佬说这个波浪图的效果很生硬,一般情况下波浪图是配合手势使用的。因此决定改写该控件,实现利用手势实现波浪图。先来一张效果图:

这里写图片描述

实现思路如下:

  1. 在RecyclerView的onTouch事件中,获取当前页面加载的最后一个可见item的下标Position,确认RecyclerView是否加载到底部?
  2. 上述确认加载到到底部后,处理onTouch事件(判断是长按事件还是点击事件)
  3. 在长按事件中将滑动过程中的X值传给自定义控件,然后自定义控件根据x值绘制贝塞尔曲线并显示控件;
  4. 在手势擡起后调用自定义控件方法,重置参数并隐藏控件

一、我本来打算在RecyclerView的item布局文件中自定义控件,但是发现获取不到控件,后来想了一下,这样会把最后一项Item布局撑大,于是还是加在了根布局上。
RecyclerView获取最后一个可见Item有两个方法,但是都需要实现同一个抽象类RecyclerView.OnScrollListener,然后我们可以使用一个boolean的标志位来记录RecyclerView是否加载到底部?代码如下:

 mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    //当滚动结束
                    if(newState == RecyclerView.SCROLL_STATE_IDLE){
                         //当前分页加载的demo数据最多只有41条,所以下标值是40
                        check =((LinearLayoutManager)mRecyclerView.getLayoutManager()).findLastVisibleItemPosition() == 40;
                    }
                }

                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    //得到当前显示的最后一个item的view
                    View lastChildView = recyclerView.getLayoutManager().getChildAt(recyclerView.getLayoutManager().getChildCount()-1);
                    //当前分页加载的demo数据最多只有41条,所以下标值是40
                    check  = recyclerView.getLayoutManager().getPosition(lastChildView) == 40;
                }
            });

二、在onTouch事件判断当前事件为触摸还是还是点击、长按等其它事件,其实这个方法一般就两种:

  1. 通过触摸的时间来判断
  2. 通过滑动的距离来判断

由于为了不影响长按事件,这里选择通过滑动距离来判断,代码如下:

   @Override
    public boolean onTouch(View v, MotionEvent event) {

        if(check){
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    downX = event.getX();
                    downY = event.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float moveX = event.getX();
                    float moveY = event.getY();
                    if (Math.abs(moveX - downX) > dp5 && Math.abs(moveY - downY) > dp5) {
                        waveView.setPoint(moveX);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    float upX = event.getX();
                    float upY = event.getY();
                    //加载到顶部后的点击或者长按事件
                    if (Math.abs(upX - downX) < dp5 && Math.abs(upY - downY) < dp5) {
                       return false;
                    }

                    break;
            }
            return true;
        }
        return false;
    }

三、在长按事件中将滑动过程中的X值传给自定义控件,然后自定义控件根据x值绘制贝塞尔曲线并显示控件。整个过程就是自定义控件有一个公共方法接收参数,并刷新视图。虽然自定义控件的核心还是绘制贝塞尔曲线,但是一些参数有了变化,这里做一个说明。

这里写图片描述
这里写图片描述
制图技术实在不行,因此将就看吧,看不懂就看源码:


/**
 * Created by 魏兴 on 2017/6/12.
 */

public class WaveView extends View  {

    private static final String TAG = "WaveView";
    //波浪画笔
    private Paint mPaint;


    //波浪Path类
    private Path mPath;

    private float controlpoint;


    //波纹的中间轴
    private int mCenterY;

    //屏幕高度
    private int mScreenHeight;
    //屏幕宽度
    private int mScreenWidth;

    public WaveView(Context context) {
        super(context);
        init();
    }

    public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public WaveView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mPath = new Path();
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.LTGRAY);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);



    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mScreenHeight = h;
        mScreenWidth = w;

        mCenterY = mScreenHeight * 3/ 5;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPath.reset();

        int start;
        int end;
        boolean isLeft = true;
        if(controlpoint>=mScreenWidth/2){

            isLeft = false;
        }
        start = isLeft ? mCenterY/3:mCenterY;
        end =  isLeft ? mCenterY:mCenterY/3;

        mPath.moveTo(0, start);
        mPath.quadTo(controlpoint,mCenterY/3, mScreenWidth,end);

        //填充矩形
        mPath.lineTo(mScreenWidth, mScreenHeight);
        mPath.lineTo(0, mScreenHeight);
        mPath.close();
        canvas.drawPath(mPath, mPaint);
    }

    public void setPoint(float x ){

        this.controlpoint = x;
        setVisibility(VISIBLE);
        invalidate();
    }

    public void rest(){
        this.controlpoint = 0;
        setVisibility(GONE);
    }

}

感觉手势擡起以后贝塞尔曲线的图形直接隐藏还是很生硬,加一个动画:

     @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPath.reset();

        int startY;
        int endY;
        boolean isLeft = true;
        if(controlpoint>=mScreenWidth/2){

            isLeft = false;
        }

        startY = isLeft ? mCenterY/3:mCenterY;
        endY =  isLeft ? mCenterY:mCenterY/3;

        mPath.moveTo(0, startY+offset);
        mPath.quadTo(controlpoint,0+offset, mScreenWidth,endY+offset);
        Log.e(TAG, "onDraw: "+(startY)+"   " +0+"   "+endY);
        //填充矩形
        mPath.lineTo(mScreenWidth, mScreenHeight);
        mPath.lineTo(0, mScreenHeight);
        mPath.close();
        canvas.drawPath(mPath, mPaint);
    }

    public void setPoint(float x ){
        if(!isRestting){
            this.controlpoint = x;
            setVisibility(VISIBLE);
            invalidate();
        }

    }
    private boolean isRestting = false;
    public void rest(){
//        setVisibility(GONE);
//        controlpoint = 0;
        isRestting = true;
        ValueAnimator animator = ValueAnimator.ofInt(0, mScreenHeight);
        animator.setDuration(400);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                offset = (int) animation.getAnimatedValue();
                postInvalidate();
            }
        });

        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                setVisibility(GONE);
                controlpoint = 0;
                mScreenHeight = height;
                mCenterY = mScreenHeight * 3/ 5;
                offset = 0;
                isRestting = false;
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        animator.start();
    }

再看看效果:

这里写图片描述

源码

参考资料:

Recyclerview中最后一个可见Item的位置以及是否滑动到底部

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章