说起这篇博客和这篇博客的历史渊源,估计有一段“历史”。估计有一段“历史”。这要追溯到我试玩探探APP。第一次进入软件界面,就被滑动卡片选择“喜欢/不喜欢”的设计所吸引。当时很想自己实现这种仿探探的效果,但是没有什么想法。但是毫无疑问,这个效果的原理肯定和ListView/RecyclerView类似,涉及到ItemView的回收再利用,否则就会因为大量的ItemView而导致OOM。后来看到很多大神也推出了同样仿探探效果的博客。从头到尾看完,写的通俗易懂,基本没有问题。于是,实现仿探探效果的想法又重新出现在我的脑海中。所以,还犹豫什么,趁热出发吧!这是一个多么幸福的决定。我们面临的第一个问题是实现View的考虑。毫无疑问。RecyclerView是最好的选择!RecyclerView是最好的选择!RecyclerView是最好的选择!重要的事情说三遍!!!原因是,***,RecyclerView自带ItemView回收再利用的功能,只是我们不需要考虑这个问题;其次,通过设置LayoutManager实现RecyclerView的布局,让布局和RecyclerView完全“解耦”。并且LayoutManager可以以自定义的方式实现。这正是我们想要的!!!此外,这也是不选择ListView的原因之一。接下来,让我们开始吧。带你见证奇迹的时刻。CardLayoutManager创建CardLayoutManager并继承自RecyclerView.LayoutManager。我们需要自己实现generateDefaultLayoutParams()方法:@OverridepublicRecyclerView.LayoutParamsgenerateDefaultLayoutParams(){returnnewRecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);}一般情况下,如上写即可。下面的方法是我们的??重点。onLayoutChildren(finalRecyclerView.Recyclerrecycler,RecyclerView.Statestate)方法用于实现ItemView布局:@OverridepublicvoidonLayoutChildren(finalRecyclerView.Recyclerrecycler,RecyclerView.Statestate){super.onLayoutChildren(recycler,state);//清除所有viewremoveAllViews();//在布局之前,detach所有的子View,放入Scrap缓存中detachAndScrapAttachedViews(recycler);intitemCount=getItemCount();//这里我们默认配置CardConfig.DEFAULT_SHOW_ITEM=3。即屏幕显示的卡片数量为3//当数据源数量大于显示数量时if(itemCount>CardConfig.DEFAULT_SHOW_ITEM){//将数据源倒序循环,使第0条数据为inthetopofthescreenisfor(intposition=CardConfig.DEFAULT_SHOW_ITEM;position>=0;position--){finalViewview=recycler.getViewForPosition(position);//添加ItemView到RecyclerViewaddView(view);//测量ItemViewmeasureChildWithMargins(view,0,0);//getDecoratedMeasuredWidth(view)可以得到ItemView的宽度//所以widthSpace是除了ItemView的剩余值intwidthSpace=getWidth()-getDecoratedMeasuredWidth(view);//同理在theightSpace=getHeight()-getDecoratedMeasuredHeight(view);//将ItemView放入RecyclerView布局//这里默认布局是放在RecyclerView的中心layoutDecoratedWithMargins(view,widthSpace/2,heightSpace/2,widthSpace/2+getDecoratedMeasuredWidth(view),heightSpace/2+getDecoratedMeasuredHeight(view));//其实t这里屏幕上有四张卡片,但是我们把第三张和第四张卡片重叠,所以只有三张。//第四张卡片主要是为了保持动画的连续性if(position==CardConfig.DEFAULT_SHOW_ITEM){//Scale按照一定的规则偏移Y轴。//CardConfig.DEFAULT_SCALE默认为0.1f,CardConfig.DEFAULT_TRANSLATE_Y默认为14view.setScaleX(1-(position-1)*CardConfig.DEFAULT_SCALE);view.setScaleY(1-(position-1)*CardConfig.DEFAULT_SCALE);view.setTranslationY((position-1)*view.getMeasuredHeight()/CardConfig.DEFAULT_TRANSLATE_Y);}elseif(position>0){view.setScaleX(1-position*CardConfig.DEFAULT_SCALE);view.setScaleY(1-position*CardConfig.DEFAULT_SCALE);view.setTranslationY(position*view.getMeasuredHeight()/CardConfig.DEFAULT_TRANSLATE_Y);}else{//设置mTouchListener的意义在于我们希望最上面的卡片能够随意滑动//和第二layer,第三层等是禁止滑动的卡片view.setOnTouchListener(mOnTouchListener);}}}else{//当数据源个数小于或等于显示的个数时,与上述代码类似for(intposition=itemCount-1;position>=0;position--){finalViewview=recycler.getViewForPosition(position);addView(view);measureChildWithMargins(view,0,0);intwidthSpace=getWidth()-getDecoratedMeasuredWidth(view);intheightSpace=getHeight()-getDecoratedMeasuredHeight(view);layoutDecoratedWithMargins(view,widthSpace/2,heightSpace/2,widthSpace/2+getDecoratedMeasuredWidth(view),heightSpace/2+getDecoratedMeasuredHeight(view));如果(position>0){view.setScaleX(1-position*CardConfig.DEFAULT_SCALE);view.setScaleY(1-position*CardConfig.DEFAULT_SCALE);view.setTranslationY(position*view.getMeasuredHeight()/CardConfig.DEFAULT_TRANSLATE_Y);}else{view.setOnTouchListener(mOnTouchListener);}}}}privateView.OnTouchListenermOnTouchListener=newView.OnTouchListener(){@Overridepublic(booleanViewv,MotionEventevent){RecyclerView.ViewHolderchildViewHolder=mRecyclerView.getChildViewHolder(v);//将触摸事件交给mItemTouchHelper处理卡片滑动事件if(MotionEventCompat.getActionMasked(event)==MotionEvent.ACTION_DOWN){mItemTouchHelper.startSwipe(childViewHolder);}returnfalse;}};一般来说,CardLayoutManager主要是对ItemView进行布局,然后根据位置做相应的偏移。缺少的是处理触摸和滑动事件。OnSwipeListener在看滑动事件的代码之前,我们先定义一个监听器。主要用于监听卡片滑动事件,代码如下,同时给出注释。你应该能理解:publicinterfaceOnSwipeListener
