用RecyclerView实现一个卡片滑动效果

首先,我们来看看效果:


1.gif

其实效果还很糙,还需要完善,不过大致的功能就是这样子,如果你看到这里还没放弃的话,那么我们就开始吧!

�1:卡片滑动自然得有卡片,卡片是什么呢?我这里使用的是CardView,简单来说就是一个FrameLayout,但是它可以定义各种样式来实现圆角之类的效果,具体大家可以查查学习一下,我就不讲了,使用方法很简单,就和FrameLayout一样:

<android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="200dp"
        android:layout_height="200dp"
        card_view:cardBackgroundColor="#FFE4B5"
        card_view:cardCornerRadius="50dp"
        card_view:cardPreventCornerOverlap="true"
        card_view:cardUseCompatPadding="true"
        android:id="@+id/card_view">

        <TextView
            android:id="@+id/item_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="25sp" />

    </android.support.v7.widget.CardView>

这里我们放了一个CardView,然后在里面放了一个TextView来显示内容

2:现在卡片有了,我们接着来实现RecyclerView,相信大家既然看这篇文章,对于RecyclerView肯定是使用过的,那我就不具体的讲如何使用RecyclerView了,简单来说就是三步:定义一个RecyclerView,设置Adapter,设置LayoutManager,那我们就可以先写出如下的代码:

recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setAdapter(new MyAdapter(datas,this));
recyclerView.setLayoutManager(new LinearLayoutManager(this));

我们可以运行一下,效果是�一张张卡片的列表:

1.gif

3:那么如何实现卡片重叠的效果呢?这就需要我们自定义LayoutManager了,我们只需要重写里面的onLayoutChildren就行:

@Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        detachAndScrapAttachedViews(recycler);
        for(int i=0;i<getItemCount();i++){
            View child = recycler.getViewForPosition(i);
            addView(child);

            measureChildWithMargins(child,0,0);
            int width = getDecoratedMeasuredWidth(child);
            int height = getDecoratedMeasuredHeight(child);
            layoutDecorated(child,0,0,width,height);

            if(i<getItemCount()-1){
                child.setScaleX(0.8f);
                child.setScaleY(0.8f);
            }        
         }
    }

我们只需将每个Child的 top,left设置为0�即可,这样每个卡片就都重叠在了一起。然后最后的if语句则是将下面的卡片缩小。(有关如何自定义LayoutManager大家�可以百度学习,基本上和自定义ViewGroup类似)

我们做完这些,运行一下,会发现只有一张卡片了,其余卡片都叠在下面,我这里就不贴图了。

4:我们现在要来实现卡片的拖动功能了,卡片拖动的功能需要重写RecyclerView的onTouchEvent,这样我们就需要自定义一个RecyclerView,直接继承自RecyclerView就可以了,onTouchEvent代码如下:

@Override
    public boolean onTouchEvent(MotionEvent e) {
        if (getChildCount() == 0) {
            return super.onTouchEvent(e);
        }
        View view = getChildAt(getChildCount() - 1);
        CardView topView = (CardView) view.findViewById(R.id.card_view);
        CardView nextView = null;

        if(getChildAt(getChildCount() - 2)!=null){
            View view2 = getChildAt(getChildCount() - 2);
            nextView = (CardView) view2.findViewById(R.id.card_view);
        }


        switch (e.getAction()){
            case MotionEvent.ACTION_DOWN:
                touchDownX = (int) e.getX();
                touchDownY = (int) e.getY();

                itemX = (int) topView.getX();
                itemY = (int) topView.getY();

                break;

            case MotionEvent.ACTION_MOVE:
                moveX = (int) e.getX();
                moveY = (int) e.getY();

                dx = moveX-touchDownX;
                dy = moveY-touchDownY;

                topView.setX(itemX+dx);
                topView.setY(itemY+dy);

                if(nextView!=null){
                    ViewGroup.LayoutParams params = topView.getLayoutParams();
                    int width = params.width/6;
                    float fraction = calcuFraction(Math.abs(dx),Math.abs(dy),width);
                    NextItemBig(nextView,fraction);
                }
                break;

            case MotionEvent.ACTION_UP:
                ViewGroup.LayoutParams params = topView.getLayoutParams();
                int width = params.width/6;
                int height = params.height/6;
                Log.d("TAG", "width :"+width+'\n'+"height :"+height);
                if(isOut(width,height)){
                    //Toast.makeText(mContext, "控件移出了", Toast.LENGTH_SHORT).show();
                    OutAnimation();
                }else {
                    //Toast.makeText(mContext, "控件未移出", Toast.LENGTH_SHORT).show();
                    topView.setX(itemX);
                    topView.setY(itemY);
                    BackAnimation(topView);
                }

                break;
        }

        return super.onTouchEvent(e);
    }

因为CardView本身周围会有�一些空白,加上getX获取的是View左上角的坐标,所以拖动我们用偏移量来做�,也就是你move时候坐标减去down时候的坐标,得到一个偏移量,再把控件的坐标加上这个偏移量即可。你会发现我在计算控件中心点位置的时候,将params.width除以了6,因为�我发现你params.width获得的大小是你在xml里面设置的大小的3倍(至于为什么我不清楚...),然后在取一半,就是6了。

5:在顶层卡片拖动的时候,接下来的卡片,�需要慢慢变大。我这里设置的是在偏移量大于卡片尺寸一半的时候就算移出范围了,所以我们用滑动的偏移量除以卡片尺寸的一半得到一个变化的值:

float calcuFraction(int dx,int dy,int width){
        int distance = (int) Math.sqrt(dx*dx+dy*dy);
        float fraction = 1+0.25f *(distance/width);
        if(fraction>=1.25f){
            return 1.25f;
        }else if(fraction<=1){
            return 1;
        }
        else {
            return fraction;
        }
    }

再用这个值去设置�下一张卡片的Scale就可以得到一个缩放的效果(我这种实现缩放�没过渡...需要改进)

6:实现了拖动,我们就需要在卡片移出和未移出时做些�动作。未移出时,我们需要卡片有个回弹动画;移出时,我们就把卡片删掉。

未移出时:

private void BackAnimation(View view){
        int centerX = Util.getScreenWidth(mContext)/2;
        int centerY = Util.getScreenHeight(mContext)/2;

        int curX = (int) (view.getX()+Util.dip2px(mContext,100));
        int curY = (int) (view.getY()+Util.dip2px(mContext,100));

        shakeAnimation(1,centerX,centerY,curX,curY);
    }

    public void shakeAnimation(int counts,int centerX,int centerY,int curX,int curY) {
        Animation translateAnimation = new TranslateAnimation((centerX - curX) / 2, 0, (centerY - curY) / 2, 0);
        translateAnimation.setInterpolator(new CycleInterpolator(counts));
        translateAnimation.setDuration(200);
        startAnimation(translateAnimation);
    }

这里我们用TranslationAnimation就可以(我本意是往滑动的反方向回弹,但实际是只能左上�右下,需要改进...)

移出时:

private void OutAnimation() {
        MyAdapter adapter = (MyAdapter) this.getAdapter();
        adapter.delTopItem();
    }

这里我在Adapter里定义了一个删除函数,可以删除最上面的卡片,直接调用即可

public void delTopItem() {
        int position = getItemCount() - 1;
        mData.remove(position);
        notifyItemRemoved(position);
    }```

7:好了,到这里我们基本的效果就都实现了,不过还是有很多问题的...我想做这个的原因是我早上看了一篇文章http://mp.weixin.qq.com/s/75Jonr8kQb073tLWad396Q
作者就是用RecyclerView实现了一个类似的效果,�于是我也想实现一下,不过思路�都是自己的,没有参考他的代码(所以才这么多问题吗...)

8:完整源码:
https://github.com/ChenTianSaber/AndroidSlideCard

谢谢大家忍受这篇博客

####最后的最后:
######感谢我可爱的女朋友。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容