仿多点ScrollView下拉回弹遮住卡片的自定义控件,巴拉巴拉我也不知道说了个啥。

今天是周三,打开多点领取免邮券,然后就看到了他一个很好玩的控件。就是这个效果:在这里插入图片描述
ScrollVIew下拉回弹,还遮住半张卡片,就不让我看,就不让我看,有趣,作为一个自身的A货达人,这不学习模仿一下不舒服啊,然后经过奋战一下午,咳咳,效果如下:
在这里插入图片描述
咦,暴露了我晚上十二点还制作Gif哎。
好了废话不多说,思路加代码:
1.看那个效果往上滑能带动卡片,这是一个整体的ScrollView
2.下拉能改变View形状,好里边在加个白色View,这里为了里边好添加东西就加个线性布局吧先
嗯!开码:
布局文件:

<?xml version="1.0" encoding="utf-8"?>

<com.example.renyuhang.fangduodian.DuoDianScrollView
    android:id="@+id/main_scroll"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:overScrollMode="never">  //这个never是必须的,有的手机自带能下拉一部分,不符合我的设定,我不让他动他居然动,砍掉

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.CardView
            android:layout_width="300dp"
            android:layout_height="150dp"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="150dp"
            android:elevation="1dp"
            app:cardBackgroundColor="#292421"
            app:cardCornerRadius="10dp" />

        <com.example.renyuhang.fangduodian.DuoDianLayout
            android:id="@+id/change_lin"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="190dp"      //这里随便写的,盖住了190-150,40dp,本来我是写的50后台因为加上弧度就又缩小了
            android:background="#00000000"   //这个也是必须的,不然绘制完形状背景不透明
            android:elevation="2dp"
            android:minHeight="1000dp"></com.example.renyuhang.fangduodian.DuoDianLayout>

    </RelativeLayout>
</com.example.renyuhang.fangduodian.DuoDianScrollView>

布局完之后:
在这里插入图片描述
然后加下拉回弹:
重写ScrollView:

           @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (contentView == null) {
        return super.dispatchTouchEvent(ev);
    }
    int action = ev.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            canPullUp = isCanPullUp();
            canPullDown = isCanPullDown();
            startY = ev.getY();
            break;
        case MotionEvent.ACTION_UP:
            if (!isMove) break;//没有移动过 不用回弹
            //View回弹
            ValueAnimator valueAnimator = ValueAnimator.ofInt(changeView.getTop(), originalRect.top);
            valueAnimator.setDuration(300);
            valueAnimator.start();
             valueAnimator.setInterpolator(new OvershootInterpolator(4f)); //
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    changeView.layout(originalRect.left, (int) valueAnimator.getAnimatedValue(), originalRect.right, changeView.getBottom() + (int) valueAnimator.getAnimatedValue() - changeView.getTop());
                    Log.i("ssssssssssssssssss", valueAnimator.getAnimatedValue() + "===" + originalRect.top);
                    //小于10是想让他回弹差10的时候开启动画,为了视觉效果好一点
                    if (originalRect.top - (int) valueAnimator.getAnimatedValue() <= 10) {
                        //当回到原位置时曲线跳动
                        profileAnimator();
                    }
                }
            });
            // 设置回到正常的布局位置
            //抬起手了就重置
            canPullDown = false;
            canPullUp = false;
            isMove = false;

            break;
        case MotionEvent.ACTION_MOVE:
            //在移动的过程中, 既没有滚动到可以上拉的程度, 也没有滚动到可以下拉的程度
            if (!canPullDown && !canPullUp) {
                startY = ev.getY();
                canPullDown = isCanPullDown();
                canPullUp = isCanPullUp();
                break;
            }

            //计算手指移动的距离
            float nowY = ev.getY();
            int deltaY = (int) (nowY - startY);

            //是否应该移动布局
            boolean shouldMove =
                    (canPullDown && deltaY > 0)    //可以下拉, 并且手指向下移动
                            || (canPullUp && deltaY < 0)    //可以上拉, 并且手指向上移动
                            || (canPullUp && canPullDown); //既可以上拉也可以下拉(这种情况出现在ScrollView包裹的控件比ScrollView还小)

            if (shouldMove) {
                //计算偏移量
                int offset = (int) (deltaY * 0.3);
                //随着手指的移动而移动布局
                if (deltaY < 150 * 5) {//防止下拉太多,随便给个距离,我demo里没加px2dp转换工具类
                    changeView.layout(originalRect.left, originalRect.top + offset, originalRect.right, originalRect.bottom + offset);
                }
                isMove = true;  //记录移动了布局
            }
    }
    return super.dispatchTouchEvent(ev);
}

//判断是否滚动到顶部
private boolean isCanPullDown() {
    return getScrollY() == 0 ||
            contentView.getHeight() < getHeight() + getScrollY();
}

本来想解释一下,一看写的时候都已经备注了,算了。现在有了滑动回弹,哦哦对注意一点, valueAnimator.setInterpolator(new OvershootInterpolator(4f));
这个回弹时动画的差值器就是原生的,如果觉得效果不太好可以重写一下,我懒得写了,我今天太累了,最近在辟谷,没什么力气。接下来绘制弧度:
也就是我的DuoDianeLayout里,oh,请原谅我的拼音起名,太随意了不会被多点律师函警告吧。

@Override
protected void onDraw(Canvas c) {
    super.onDraw(c);
    Paint paint = new Paint();
    //设置抗锯齿
    paint.setAntiAlias(true);
    //获得画布宽高
    int canvasWidth = c.getWidth();
    int canvasHeight = c.getHeight();
    //创建一个空布局的画布
    Bitmap bitmap = Bitmap.createBitmap(canvasWidth, canvasHeight, Bitmap.Config.ARGB_8888);
    bitmap.eraseColor(Color.argb(0, 0, 0, 0));
    Canvas canvas = new Canvas(bitmap);
    //绘制第一层长方形也就是整个的背景
    paint.setColor(Color.parseColor("#ffffff"));
    canvas.drawRect(0, 0, canvasWidth, canvasHeight, paint);
    //绘制第二层加入贝塞尔曲线,也就是和第一层交集去掉的部分,
    paint.setColor(Color.parseColor("#000000"));//颜色无所谓因为不显示
    Path pp = new Path();
    pp.moveTo(0, 0);
    pp.moveTo(x1, y1);
    pp.quadTo(x2, y2, x3, y3);
    pp.lineTo(x3, y3);
    pp.lineTo(x3, 0);
    pp.lineTo(0, 0);
    //取两层交集显示先绘制的
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
    canvas.drawPath(pp, paint);
    //最后将画笔去除Xfermode
    paint.setXfermode(null);
    c.drawBitmap(bitmap, 0, 0, null);

}

ps:
这段比较费点时间,主要是我忘了PorterDuffXfermode这个写法了,上网现学一会。然后出现canvas不透明,又重新建了一个空布局,才搞定以上。
接下来就是改变以上贝塞尔曲线的动画了,哎?我刚才说了贝塞尔曲线?好高级,其实这个曲线是网上找的公式,ahaha
好了,动画就是:

   private void profileAnimator() {
    //曲线回弹
    ay1 = changeView.y1;
    ValueAnimator valueAnimator1 = ValueAnimator.ofInt(ly, ly / 2);
    valueAnimator1.setDuration(100);
   // valueAnimator1.setInterpolator(new OvershootInterpolator(2f));
    valueAnimator1.start();
    valueAnimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            int yy = (int) valueAnimator.getAnimatedValue();
            Log.i("ttttt", yy + "=" + changeView.y1 + "=" + changeView.y3);
            changeView.setxyr(0, ay1, pingWith / 2, yy, pingWith, ay1);
            if (yy == ly / 2) {
                Log.i("ttttttt", "start");
                ValueAnimator valueAnimator2 = ValueAnimator.ofInt(ly / 2, ly);
                valueAnimator2.setDuration(100);
                valueAnimator2.setInterpolator(new OvershootInterpolator(1f));
                valueAnimator2.start();
                valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        int yy = (int) valueAnimator.getAnimatedValue();
                        Log.i("ttttt", yy + "=" + changeView.y1 + "=" + changeView.y3);
                        changeView.setxyr(0, ay1, pingWith / 2, yy, pingWith, ay1);
                    }
                });
            }
        }
    });

}

两个属性动画,上弹结束嵌套下弹,调节了半天差值器和回弹弧度,这还是比较满意吧。如果你们觉得不满意就自定义差值器吧,哈哈哈,终于写完了,凌晨一点,oh,最近在辟谷,第二天了,比较萎靡。睡觉。
oh,不!忘了上传demo,骚等骚等。。

在这里插入图片描述
怎么上传的都是5分,没积分评论,私发。

ps:z z Z…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值