一,上班闲来无聊,撸发代码。实现Android层叠卡片效果如下图!
1.我们可以随意方向的拖动这个卡片。
2.我们可以将卡片放在一起显示且有层叠式效果。
3.我们可以将最上层的卡片移除,当移除最底层之后又继续循环开始。
4.我们拖缀图片时候下面图片有起伏动画效果。
二,分析如何实现:
1.很多人会想到viewPager+setPageTransformer+属性动画
2.也有人会想到手势。
3.自定义控件+viewGroup+拖动事件。
有没有更简单的呢?
很多人估计都用过RecylerView吧!
RecylerView里面就有滑动删除效果+ItemTouchHelper不知道
的同学们可以查查资料哦![TtemTouchHelper](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0630/3123.html)
三,准备工作:
(1.)首先我们完成并显示一个RecylerView列表:代码如下*
新建一个SwipeCardBean实体类对象来储存信息:
SwipeCardBean.java
public class SwipeCardBean {
public String title;
public int resoutimage;
}
(2)写一个适配器用来显示列表信息(相信这个都会把。不会来那就看看别人写的自己动手练习几个,这里的recylerview_item.xml我就不写了吧!):
UniversalAdapter.java
package com.example.ls.dn_swipecard.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.ls.dn_swipecard.R;
import com.example.ls.dn_swipecard.SwipeCardBean;
import java.util.ArrayList;
/**
* Created by 路很长~ on 2017/7/3.
*/
public class UniversalAdapter extends RecyclerView.Adapter<UniversalAdapter.UniversalViewHolder> {
public ArrayList mData;
public Context context;
public UniversalAdapter(ArrayList mData, Context context) {
this.mData = mData;
this.context = context;
}
@Override
public UniversalViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.recylerview_item, null);
UniversalViewHolder holder = new UniversalViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(UniversalViewHolder holder, int position) {
UniversalViewHolder holder1=holder;
holder1.recy_item_im.setBackgroundResource(mData.get(position).resoutimage);
holder1.recy_item_tv.setText(mData.get(position).title);
}
@Override
public int getItemCount() {
return mData == null ? 0 : mData.size();
}
public class UniversalViewHolder extends RecyclerView.ViewHolder {
public TextView recy_item_tv;
public ImageView recy_item_im;
public UniversalViewHolder(View itemView) {
super(itemView);
recy_item_im=itemView.findViewById(R.id.recy_item_im);
recy_item_tv=itemView.findViewById(R.id.recy_item_tv);
}
}
}
(3.)我们先写好MainActivity来试试运行效果呗:
public class MainActivity extends AppCompatActivity {
private RecyclerView mActivity_review;
private UniversalAdapter mAdatper;
private ArrayList mList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
setData();
}
private void initView() {
mList = new ArrayList<>();
mActivity_review = (RecyclerView) findViewById(R.id.activity_review);
}
private void initData() {
/**这里的图片就上网百度了8张。本人比较懒就用本地图片代替。
当然你要有现成的接口也可以网络加载解析。*/
int[] intimage = {R.drawable.png1, R.drawable.png2, R.drawable.png3, R.drawable.png4,
R.drawable.png5, R.drawable.png6, R.drawable.png7, R.drawable.png8};
for (int i = 0; i < 8; i++) {
SwipeCardBean swpe = new SwipeCardBean();
swpe.resoutimage = intimage[i];
swpe.title = "美丽" + i;
mList.add(swpe);
}
}
private void setData() {
SwipeCardLayoutManager swmanamger = new SwipeCardLayoutManager(this);
mActivity_review.setLayoutManager(new LinearLayoutManager(this,1,false));
mAdatper = new UniversalAdapter(mList, this);
mActivity_review.setAdapter(mAdatper);
}
}
运行完后结果如下:
四,实现过程
(1.)看着这个列表也就是正常的RecylerView来展示数据,如何任意拖缀呢,而且让他动呢?:
首先我们来实现任意拖动。这里我们需要了解一下ItemTouchHelper:
ItemTouchHelper是一个强大的工具,它处理好了关于在RecyclerView上添加拖动排序与滑动删除的所有事情。我们点击ItemTouchHelper源码可以看到他继承了RecyclerView.ItemDecoration,我们可以发现ItemTouchHelper源码构造函数传递一个CallBack这个参数源码告诉如果用户需要控制视图的动作行为那么就需要重新定义这个CallBack。
下面我们来自定一个CallBack类并继承ItemTouchHelper.CallBack代码如下:这里我们只是设置了构造方法super(0,LEFT|RIGHT|UP|DOWN);就可以实现拖动效果。
public class SwipeCardCallBack extends ItemTouchHelper.SimpleCallback {
public SwipeCardCallBack(int dragDirs, int swipeDirs) {
super(dragDirs, swipeDirs);
}
public SwipeCardCallBack() {
/*
* 即我们对哪些方向操作关心。如果我们关心用户向上拖动,可以将
填充swipeDirs参数为LEFT | RIGHT 。0表示从不关心。
* */
super(0,
ItemTouchHelper.LEFT | ItemTouchHelper.UP |
ItemTouchHelper.RIGHT | ItemTouchHelper.DOWN
);
}
@Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder,
int direction) {
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX,
dY, actionState, isCurrentlyActive);
}
}
实现效果图如下:
(2.)我们实现层叠效果 :
我们要实现层叠效果首先要讲item叠在一起。我们如何处理呢?
我们知道可以通过RecyclerView的LayoutManager来控制布局的
显示方式,实现想要的效果。我们在类继承关系上可以看出LayoutManager是个抽象类,LinearLayoutManager,SwipeCardLayoutManager,StaggeredCridLayoutManager实现与它。
这里我们要实现图片层叠在一起。那么我们就需要从写LayoutManager类:代码如下
generateDefaultLayoutParams()这个使我们必须要写的。这个方法是给RecyclerView的子View创建一个默认的LayoutParams。
public class SwipeCardLayoutManager extends RecyclerView.LayoutManager {
Context context;
int TRANS_Y_GAP;
SwipeCardLayoutManager(Context context){
TRANS_Y_GAP= (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,15,
context.getResources().getDisplayMetrics());
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
//1.如何实现层叠效果--cardView.layout(l,t,r,b)
//2.如何让8个条目中的4个展示在RecylerView里面
//1在布局layout之前,将所有的子View先全部detach掉,然后放到Scrap集合里面缓存。
detachAndScrapAttachedViews(recycler);
//2)只将最上面4个view添加到RecylerView容器里面
int itemCount=getItemCount();//8个
int bottomPosition;
if(itemCount<4){
bottomPosition=0;
}else{
bottomPosition=itemCount-4;
}
for(int i=bottomPosition;i0,0);
int widthSpace=getWidth()-getDecoratedMeasuredWidth(view);
int heightSpace=getWidth()-getDecoratedMeasuredHeight(view);
//摆放cardView
layoutDecorated(view,
widthSpace/2,
heightSpace/2,
widthSpace/2+getDecoratedMeasuredWidth(view),
heightSpace/2+getDecoratedMeasuredHeight(view));
//层叠效果--Scale+TranslationY
//层级的位置关系1/2/3/4
int level=itemCount-i-1;
if(level>0){
if(level1-CardConfig.SCALE_GAP*level);
view.setScaleY(1-CardConfig.SCALE_GAP*level);
}
}else {
view.setTranslationY(TRANS_Y_GAP*(level-1));
view.setScaleX(1-CardConfig.SCALE_GAP*(level-1));
view.setScaleY(1-CardConfig.SCALE_GAP*(level-1));
}
}
}
}
这部分代码也不是很难理解需要自己画图分析如图所示:
看图看着不部分代码理解很简单啦:
layoutDecorated(view,
widthSpace/2,
heightSpace/2,
widthSpace/2+getDecoratedMeasuredWidth(view),
heightSpace/2+getDecoratedMeasuredHeight(view));
setTranslationY将子View网上移动距离。
view.setScaleX放大多少倍。
为了方便这里写了一个CardConfing配置文件。
配置文件如下代码:
public class CardConfig {
//屏幕最对同时显示几个item
public static int MAX_SHOW_COUNT=4;
//没一级Scale相差0.05f,translation相差7dp左右
public static float SCALE_GAP;
public static int TRANS_V_GAP;
public static void initConfig(Context context){
MAX_SHOW_COUNT=4;
SCALE_GAP=0.05f;
TRANS_V_GAP=(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,15,context.getResources().getDisplayMetrics());
}
}
我们如何解决移除并且移出最后一个之后循环呢?
这时候我们回到自定义的SwipeCardCallBack中onSwiped()方法里面进行处理:
我们需要手指滑动结束之后进行删除这个数据,然后添加这个数据到集合的最低端。
来实现循环。加代码如下:
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
//当已经滑动删除了的时候会被回掉--删除数据,循环的效果
SwipeCardBean remove = mDatas.remove(viewHolder.getLayoutPosition());
mDatas.add(0, remove);
adapter.notifyDataSetChanged();
}
这时候基本完成任务。也实现了移除循环效果。但是我们没有动画效果。
我们在SwipeCardCallBack的onChildDraw里面可以进行计算并设置代码如下:
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
//监听话滑动的距离--控制动画的执行程度
//灵界点
double maxDistance = recyclerView.getWidth() * 0.5f;
double distance = Math.sqrt(dX * dX);
//动画执行的百分比
double fraction = distance / maxDistance;
if (fraction > 1) {
fraction = 1;
}
int itemcount = recyclerView.getChildCount();
for (int i = 0; i < itemcount; i++) {
//执行
View view = recyclerView.getChildAt(i);
//几个view层叠的效果,错开的效果--便宜动画+缩放动画
int level = itemcount - i - 1;
if (level > 0) {
if (level < CardConfig.MAX_SHOW_COUNT- 1) {
view.setTranslationY((float)(1-CardConfig.TRANS_V_GAP*level+fraction*CardConfig.TRANS_V_GAP));
view.setScaleX((float)(1-CardConfig.SCALE_GAP*level+fraction*CardConfig.SCALE_GAP));
view.setTranslationY((float)(1-CardConfig.SCALE_GAP*level+fraction*CardConfig.SCALE_GAP));
}
}
}
}
最终结果如图一。曹下班了。我得跑路了。
忘记帖代码了写的都找不到了。这里再写一遍github下载地址:https://github.com/luhenchang/RecylerViewSwper.git