如何实现 Android 5.0 多任务界面中的安卓卡片式多任务层叠效果

安卓L/安卓5.0新特性再爆:分屏多任务
  【PConline 资讯】安卓L很快就会发布正式版,之前有传言称安卓L会正式命名为安卓5.0。不过名字并不特别重要,大家主要还是关心新系统会带来怎样的功能特性。现在,安卓L/安卓5.0的一个新功能曝光了!一些来自Google的概念图显示,安卓L将会带有分屏多任务功能,用户可以在一个屏幕上同时处理两个任务,样式和Windows 8的分屏多任务有点类似。  根据介绍,用户可以从最近任务列表中选择一个App,将其拖放到屏幕一侧,这个App就会自动占据一半的屏幕空间。分别拖移两个App到屏幕两侧,则可以实现两个App同屏显示。两个并排App的屏幕占据比例可以作调整,比如说拖放一个占据25%屏幕,另外一个就会自动放大到75%。一个缩到0%的时候,也就退居幕后了,另一个随之全屏。  和Windows 8的分屏多任务不同,Google会让某一个App成为焦点,这个App可以单独操作和退出。比如说当发起&OK Google&语音命令时,只会占据非焦点App的一侧。在排版方面,安卓App很早之前就可以适应各种比例,安卓L的Material Design对横竖比例的内容都可以很好地展示,并排App的面积改变并不会让排版错乱。值得一提的是,安卓L的这个分屏多任务还支持两个App间的内容交换,比如说可以将图片、文字从一个App拖到另一个App。  总的来说,这个还是比较值得期待的。虽然说有一些Windows 8的味道,但Google也作了一些创新。不过,现在安卓L的分屏多任务只停留在比较初期的阶段,只有大致的框架图,是否可以伴随着安卓L正式版一同出现还尚未可知。相关阅读:Google欲收购CyanogenMod:竟被CM拒绝?涵盖高低端!华为EMUI 3.0升级计划大公开国庆出境游风险高!8款必备APP保平安买别只看性能!教你测试手机信号好坏&
软件论坛帖子排行
最新资讯离线随时看
聊天吐槽赢奖品
相关软件:
大小:1.93 MB
授权:免费
大小:62.51 MB
授权:免费探探首页的图片堆叠效果如何实现
签到天数: 208 天连续签到: 1 天[LV.7]常住居民III主题帖子e币
QQ巧遇你知道吗,和那个效果一模一样的
一模一样的效果很难找的啦&&只能根据已有的修改了~
好吧 不过还是谢谢你 分享了&
签到天数: 17 天连续签到: 1 天[LV.4]偶尔看看III主题帖子e币
一模一样的效果很难找的啦&&只能根据已有的修改了~
好吧 不过还是谢谢你 分享了
客气客气!&
签到天数: 208 天连续签到: 1 天[LV.7]常住居民III主题帖子e币
好吧 不过还是谢谢你 分享了
客气客气!
签到天数: 1 天连续签到: 1 天[LV.1]初来乍到主题帖子e币
是啊 我也需要 那个效果啊 !&&要疯掉了。&&怎么搞啊。
签到天数: 1 天连续签到: 1 天[LV.1]初来乍到主题帖子e币
看看是不是需要的,求解
签到天数: 3 天连续签到: 2 天[LV.2]偶尔看看I主题帖子e币
楼主有了吗,我也需要用到。
签到天数: 3 天连续签到: 2 天[LV.2]偶尔看看I主题帖子e币
/kikoso/Swipeable-Cards&&我在网上找到有这个,类似了不过我看有一些bug
该用户从未签到主题帖子e币
rrfewr我去而且她日前4f
签到天数: 71 天连续签到: 1 天[LV.6]常住居民II主题帖子e币
楼主实现这个功能没? 求分享
该用户从未签到主题帖子e币
我也在搞这个效果,求指教
签到天数: 1 天连续签到: 1 天[LV.1]初来乍到主题帖子e币
22楼的也不错& && && &
推荐阅读热门话题
61887420384328283281261252226218208204201192715
2&小时前昨天&23:48昨天&23:39昨天&23:30昨天&23:27昨天&22:49昨天&22:28昨天&22:26前天&23:52前天&11:503&天前3&天前3&天前3&天前3&天前4&天前
Powered by
扫一扫 关注eoe官方微信下次自动登录
现在的位置:
& 综合 & 正文
Android开发学习之卡片式布局的简单实现
GoogleNow是Android4.1全新推出的一款应用他,它可以全面了解你的使用习惯,并为你提供现在或者未来可能用到的各种信息,GoogleNow提供的信息关联度较高,几乎是瞬间返回答案,总而言之,GoogleNow是Google提出的全新搜索概念。当然,GoogleNow最为引人注目的当属它的卡片式设计。我们首先来看几张GoogleNow的图片:
我们可以看出,这种卡片式的界面设计更为简洁,可以将用户需要的信息直接地呈现在用户面前,简洁而不失美观。那么现在我们就来一起做一个这样的卡片式界面吧!
首先我们先来建立一个布局:
&?xml version="1.0" encoding="utf-8"?&
&LinearLayout xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/radius_bg"
android:padding="15dp"&
android:id="@+id/Card_Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textIsSelectable="true"/&
&ImageView
android:id="@+id/Card_Pic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/Description"
android:scaleType="center" /&
&/LinearLayout&
通过这个布局我们将完成一个圆角卡片的设计,其中圆角的实现是定义了一个Shape,具体如下:
&?xml version="1.0" encoding="utf-8"?&
&shape xmlns:android="/apk/res/android" &
&corners android:radius="8dp"/&
&solid android:color="#ffffff"/&
下面我们定义一个GoogleCard的类:
package com.Android.GoogleC
public class GoogleCard
private String mD
private int mD
public GoogleCard(String mDescription,int mDrawable)
this.mDescription=mD
this.mDrawable=mD
public String getDescription()
public void setDescription(String mDescription)
this.mDescription = mD
public int getDrawable()
public void setDrawable(int mDrawable)
this.mDrawable = mD
接下里我们定义一个适配器类GoogleCardAdapter
package com.Android.GoogleC
import java.util.L
import android.content.C
import android.view.LayoutI
import android.view.V
import android.view.ViewG
import android.widget.BaseA
import android.widget.ImageV
import android.widget.TextV
public class GoogleCardAdapter extends BaseAdapter
private List&GoogleCard& mC
private Context mC
public GoogleCardAdapter(Context mContext,List&GoogleCard& mCards)
this.mContext=mC
this.mCards=mC
public int getCount()
return mCards.size();
public Object getItem(int Index)
return mCards.get(Index);
public long getItemId(int Index)
public View getView(int Index, View mView, ViewGroup mParent)
ViewHolder mHolder=new ViewHolder();
mView=LayoutInflater.from(mContext).inflate(R.layout.layout_item, null);
mHolder.Card_Title=(TextView)mView.findViewById(R.id.Card_Title);
mHolder.Card_Title.setText(mCards.get(Index).getDescription());
mHolder.Card_Pic=(ImageView)mView.findViewById(R.id.Card_Pic);
//记住啊,这里是setImageResource()方法,不是setBackgroundResource(),否则图像会变形啊
mHolder.Card_Pic.setImageResource(mCards.get(Index).getDrawable());
private static class ViewHolder
TextView Card_T
ImageView Card_P
最后我们来看主界面的布局和主要代码:
&LinearLayout xmlns:android="/apk/res/android"
xmlns:tools="/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" &
android:id="@+id/ListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:dividerHeight="15dp"
android:scrollbarStyle="outsideOverlay" &
&/ListView&
&/LinearLayout&
package com.Android.GoogleC
import java.util.ArrayL
import java.util.L
import android.os.B
import android.app.A
import android.view.M
import android.view.W
import android.widget.ListV
public class MainActivity extends Activity {
private ListView mListV
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.layout_main);
mListView=(ListView) findViewById(R.id.ListView);
GoogleCardAdapter mAdapter=new GoogleCardAdapter(this, getItems());
mListView.setAdapter(mAdapter);
private List&GoogleCard& getItems()
List&GoogleCard& mCards=new ArrayList&GoogleCard&();
for(int i=0;i&20;i++)
GoogleCard mCard=new GoogleCard("这是第"+(i+1)+"张卡片", getResource(i));
mCards.add(mCard);
private int getResource(int Index)
int mResult=0;
switch(Index%2)
mResult=R.drawable.card_0;
mResult=R.drawable.card_1;
public boolean onCreateOptionsMenu(Menu menu) {
// I this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
最终运行效果如图所示:
&&&&推荐文章:
【上篇】【下篇】Android手把手教你实现卡片式瀑布流效果(RecyclerView+CardView,附源码) - 简书
Android手把手教你实现卡片式瀑布流效果(RecyclerView+CardView,附源码)
卡片式的瀑布流效果是一种非常美观的UI设计,自从Android 5.0的 RecyclerView出现以后,实现瀑布流变得十分简单,本文将一步步带领读者去实现一个卡片式的瀑布流布局。
一、先上效果图
源码上传在git上上,下载完成后直接用Android Studio打开即可
二、代码实现卡片式瀑布流
1. 涉及到要使用的类和控件
RecyclerView
5.0出现的类似于ListView的控件
用它的布局管理器实现两列“ListView”效果
StaggeredGridLayoutManager
交错布局管理器
实现两列“ListView”效果
RecyclerView.Adapter
数据适配器
与ListView的Adapter类似,写法上不同。
同样是5.0新出的控件
实现圆角效果,省去了自己写Drawble,这个可要可不要。
图片开源框架
为了简化从网上下载图片的过程就使用了它做快速演示,如想了解Fresco的详情,请看本人另一篇博客:
2. 在Gradle中添加RecyclerView、Fresco和CardView需要的依赖
compile 'com.android.support:recyclerview-v7:24.2.0'
compile 'com.facebook.fresco:fresco:0.14.1'
compile 'com.android.support:cardview-v7:25.0.0'
3. 在Activity的布局文件中使用RecyclerView
&?xml version="1.0" encoding="utf-8"?&
&RelativeLayout
xmlns:android="/apk/res/android"
xmlns:tools="/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F0F0F0"
tools:context="com.testwaterfall.MainActivity"
&android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
&&/android.support.v7.widget.RecyclerView&
&/RelativeLayout&
4. 构建RecyclerView的子Item View,命名为recyclerview_item.xml,这个布局里面主要是一张明星的图片和明星的名字。(这里的嵌套层数有点多,不是个好案例,为了省事就这样写了。)
&?xml version="1.0" encoding="utf-8"?&
&LinearLayout xmlns:android="/apk/res/android"
xmlns:app="/apk/res-auto"
xmlns:fresco="/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:background="#F0F0F0"
android:orientation="vertical"
android:padding="5dp"
&android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
app:cardCornerRadius="5dp"
&LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="10dp"
android:orientation="vertical"
&com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/user_avatar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
fresco:actualImageScaleType="centerCrop"
android:id="@+id/user_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
&/LinearLayout&
&/android.support.v7.widget.CardView&
&/LinearLayout&
5. 编写RecyclerView的数据适配器WaterFallAdapter
public class WaterFallAdapter extends RecyclerView.Adapter {
private Context mC
private List&PersonCard& mD //定义数据源
//定义构造方法,默认传入上下文和数据源
public WaterFallAdapter(Context context, List&PersonCard& data) {
mContext =
//将ItemView渲染进来,创建ViewHolder
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.recyclerview_item, null);
return new MyViewHolder(view);
//将数据源的数据绑定到相应控件上
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
MyViewHolder holder2 = (MyViewHolder)
PersonCard personCard = mData.get(position);
Uri uri = Uri.parse(personCard.avatarUrl);
holder2.userAvatar.setImageURI(uri);
holder2.userAvatar.getLayoutParams().height = personCard.imgH //从数据源中获取图片高度,动态设置到控件上
holder2.userName.setText(personCard.name);
public int getItemCount() {
if (mData != null) {
return mData.size();
//定义自己的ViewHolder,将View的控件引用在成员变量上
public class MyViewHolder extends RecyclerView.ViewHolder {
public SimpleDraweeView userA
public TextView userN
public MyViewHolder(View itemView) {
super(itemView);
userAvatar = (SimpleDraweeView) itemView.findViewById(R.id.user_avatar);
userName = (TextView) itemView.findViewById(R.id.user_name);
6. 构建一个数据Model,明星,以便于传递数据。
public class PersonCard implements Serializable{
public String avatarU //明显头像的Url
//明显的名字
public int imgH
//头像图片的高度
7. 在Activity里面初始化界面和生成、绑定数据
public class MainActivity extends Activity {
private RecyclerView mRecyclerV
private RecyclerView.LayoutManager mLayoutM
private WaterFallAdapter mA
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
private void init() {
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
//设置布局管理器为2列,纵向
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mAdapter = new WaterFallAdapter(this, buildData());
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
//生成6个明星数据,这些Url地址都来源于网络
private List&PersonCard& buildData() {
String[] names = {"邓紫棋","范冰冰","杨幂","Angelababy","唐嫣","柳岩"};
String[] imgUrs = {"/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b&sec=&di=21924aeef8fbf00a28f49&src=/file/upload//.jpg",
"/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b&sec=&di=f740bdbcb0cafe454a6465a2&src=http://tpic./xhCloudNewsPic/xhpic/06/wKhTlVfs1h2EBoQfAAAAAF479OI749.jpg",
"/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=&fm=111&gp=0.jpg",
"/94o3dSag_xI4khGko9WTAnF6hhy/image/h%3D200/sign=fd90a83e900a304e4d22a7fae1c9a7c3/daafa480a2f1fcedabd.jpg",
"/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=,&fm=111&gp=0.jpg",
"/-Po3dSag_xI4khGko9WTAnF6hhy/image/h%3D200/sign=b191df4e0e1/c2fdfc27b453e7a3c27d1ed21b243b.jpg",
List&PersonCard& list = new ArrayList&&();
for(int i=0;i&6;i++) {
PersonCard p = new PersonCard();
p.avatarUrl = imgUrs[i];
p.name = names[i];
p.imgHeight = (i % 2)*100 + 400; //偶数和奇数的图片设置不同的高度,以到达错开的目的
list.add(p);
瀑布流实现的关键有3点:
使用RecyclerView和它的StaggeredGridLayoutManager布局管理器,
其次在于列表中的Item View的高度并不一样,这样才能达到交错开来的效果
子Item的布局,让两列之间,上下之间能有一定的“空隙”,这才能达成视觉上的效果。
纸上得来终觉浅,绝知此事要躬行。
CSDN博客:http://blog.csdn.net/a2947人阅读
Android实战项目学习(12)
转载请标明出处:&
本文出自:()&
代码传送门:喜欢的话,随手点个star。多谢&
前几天看有人实现了仿人人美剧的订阅界面,不过在细节之处以及实现方式我个人认为都不是最佳的姿势。&
于是我也动手撸了一个,还顺带撸了个探探的界面,先看GIF:&
这里吐个槽,探探这种设计真的像皇帝翻牌子的感觉,不喜欢左滑,喜欢右滑。
人人影视版特点(需求):
动画:最多可见的这四层,在顶层卡片滑动时,每一层都会位移&放大动画,有种补充到顶层的感觉。动画:松手时,如果未被判定为删除,则会有顶层以下每一层卡片收缩回原位的动画。无限循环:模仿人人影视,顶层卡片被删除后,补充到最底层。
除上述动画特点,探探版特点(需求):
Roate的变化:左右滑动时,顶层卡片会慢慢旋转,到阈值max大概十五度。Alpha的变化:左滑时顶层卡片的删除按钮会慢慢显现,右滑时爱心按钮会慢慢显现。显然,松手时,以上动画也需要复位。
我们的效果,基本上和原版一致了,写起来怎么样呢?&
我不是标题党,如标题所说:
简单:思路简单清晰易理解优雅:性能没有任何隐患,LayoutManager只会加载显示屏幕上可见的数量的View。快速:利用ItemTouchHelper处理拖拽&滑动删除逻辑,核心代码不超过50行。且经过封装,四行代码就可以用。
伸手党福利:
如果懒得看这么多文字只想用,直接移步gayhub,gradle导入相关文件or复制。然后如下,搞定。
mRv.setLayoutManager(new OverLayCardLayoutManager());
CardConfig.initConfig(this);
ItemTouchHelper.Callback callback = new RenRenCallback(mRv, mAdapter, mDatas);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(mRv);1234512345
而且我将一些参&
数都以变量形式计算,这样就做到了可配置,假如老板让你一开始多显示几层卡片,例如6层,你只需要修改一个参数即可,效果如图:&
正确的姿势
正确的姿势就是:
利用LayoutManager实现卡片层叠布局,值得注意的是,只layout出界面上可能会看见的那些View。搭配ItemTouchHelper,它本身实现了拖拽&滑动删除逻辑,我们只需要在onChildDraw()中绘制动画和onSwiped()中处理数据集(循环or删除)。
所以本文也算是填了的坑,实现了一个酷炫效果的布局。&
LayoutManager的实现卡片层叠
其实本例中的LayoutManager十分简单,因为ItemTouchHelper的存在,LayoutManager根本不需要处理它的滑动事件,而LayoutManager中最难写的就是在滑动时的View回收和复用,以及layout新View的处理。
关于LayoutManager的基础知识和铺垫,我就不再赘述,可参考我以前的文章:
唯一注意事项
但是即便如此,还是有一个唯一的注意事项。我们只layout出界面上可能会看见的那些View即可。&
因为考虑到动画,所以是可能会看见。&
我们看人人美剧的界面:&
初始化时,界面上可见三个View,我们分别起名:TopView,Top-1View,Top-2View。其中TopView完全可见,Top-1View,Top-2View只有下边缘可见。
如文首GIF,滑动TopView时,Top-1View,Top-2View开始慢慢放大,并且向上位移,直至填充至它们各自上层的View。这时候露出了Top-3View。
所以我们在书写LayoutManager的onLayoutChildren()方法时,只要layout出当前数据集最后四个View即可。
前文提到的参数配置如下:&
包括一些配置
界面最多显示几个View每一级View之间的Scale差异、translationY等等
public class CardConfig {
public static int MAX_SHOW_COUNT;
public static float SCALE_GAP;
public static int TRANS_Y_GAP;
public static void initConfig(Context context) {
MAX_SHOW_COUNT = 6;
SCALE_GAP = 0.05f;
TRANS_Y_GAP = (int) TypedValue.PLEX_UNIT_DIP, 15, context.getResources().getDisplayMetrics());
}1234567891011121312345678910111213
LayoutManager全部代码如下,布满注释,如果看不懂,建议阅读前置文章:
public class OverLayCardLayoutManager extends RecyclerView.LayoutManager {
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
detachAndScrapAttachedViews(recycler);
int itemCount = getItemCount();
if (itemCount &= MAX_SHOW_COUNT) {
for (int position = itemCount - MAX_SHOW_COUNT; position & itemC position++) {
View view = recycler.getViewForPosition(position);
addView(view);
measureChildWithMargins(view, 0, 0);
int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);
layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2,
widthSpace / 2 + getDecoratedMeasuredWidth(view),
heightSpace / 2 + getDecoratedMeasuredHeight(view));
* TopView的Scale 为1,translationY 0
* 每一级Scale相差0.05f,translationY相差7dp左右
* 观察人人影视的UI,拖动时,topView被拖动,Scale不变,一直为1.
* top-1View 的Scale慢慢变化至1,translation也慢慢恢复0
* top-2View的Scale慢慢变化至 top-1View的Scale,translation 也慢慢变化只top-1View的translation
* top-3View的Scale要变化,translation岿然不动
int level = itemCount - position - 1;
if (level & 0 ) {
view.setScaleX(1 - SCALE_GAP * level);
if (level & MAX_SHOW_COUNT - 1) {
view.setTranslationY(TRANS_Y_GAP * level);
view.setScaleY(1 - SCALE_GAP * level);
view.setTranslationY(TRANS_Y_GAP * (level - 1));
view.setScaleY(1 - SCALE_GAP * (level - 1));
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
撸到这里,我们的静态界面已经成型,下面让我们动起来:&
ItemTouchHelper实现炫动滑动:
ItemTouchHelper的基础知识,建议大家自行学习,网上文章很多,我简单介绍一下,
This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.&
It works with a RecyclerView and a Callback class, which configures what type of interactions&
are enabled and also receives events when user performs these actions.&
Depending on which functionality you support, you should override&
{@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)} and / or&
{@link Callback#onSwiped(ViewHolder, int)}.
翻译 + 总结:
这货是一个工具类,为RecyclerView扩展滑动消失(删除)和drag & drop效果的。&
它需要和RecyclerView、Callback 一起工作。Callback 类里定义了 允许哪些交互,并且会接收到对应的交互事件&
根据你需要哪种功能(滑动消失(删除)和drag & drop),你需要重写&
Callback#onMove(RecyclerView, ViewHolder, ViewHolder)—–drag & drop&
Callback#onSwiped(ViewHolder, int) 方法。 —–滑动消失(删除)
总结一下入门级用法如下,三个步骤:
定义一个Callback:ItemTouchHelper.Callback callback = new ItemTouchHelper.SimpleCallback(int,int),这两个int分别代表要&监听哪几个方向上的拖拽、滑动事件。
常用:ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT
将Callback传给ItemTouchHelper:ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
关联ItemTouchHelper和RecyclerView:itemTouchHelper.attachToRecyclerView(mRv)
这三个步骤做完后,ItemTouchHelper就会自动帮我们完成&滑动消失(删除)和drag & drop&的功能。
我们本例中,需要的是滑动消失(删除)&,所以我们的Callback不需要关注onMove()方法。&
且我们需要上下左右滑动都可以删除的效果。&
则如下构造Callback,传入上下左右:
ItemTouchHelper.Callback callback = new ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT)1212
onSwiped()方法,是滑动删除动作已经发生后回调的,即,我们先滑动卡片,然后松手,此时ItemTouchHelper判断我们的手势是删除手势,会自动对这个卡片执行丢出屏幕外的动画,同时回调onSwiped()方法。&
所以我们需要在其中如下写:
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
SwipeCardBean remove = mDatas.remove(viewHolder.getLayoutPosition());
mDatas.add(0, remove);
mAdapter.notifyDataSetChanged();
}12345671234567
在这里我们完成了循环的操作:
利用当前被删除的View的ViewHolder拿到Position删除数据集中对应Position的数据源同时将该数据源插入数据集中的首位。调用notifyDataSetChanged(),通知列表刷新&
如此我们便完成了,循环列表的需求。
这里提一下为什么我们要调用notifyDataSetChanged()。&
看官方文档:
ItemTouchHelper moves the items’ translateX/Y properties to reposition them
即ItemTouchHelper实现的滑动删除,其实只是隐藏了这个滑动的View。并不是真的删除了。
在一文第五节中,我们已经提到,notifyDataSetChanged()会回调onLayoutChildren()这个函数,而在这个函数中,我们会重新布局,即真正的移除(不再layout)滑动掉的View,同时会补充进新的最底层的View。
嗯,JavaBean也看一眼吧,没亮点:
public class SwipeCardBean {
private int
}1234512345
我们写到这里已经完成了滑动删除的功能,其实我们什么都没有写是吧,复杂的判断都由ItemTouchHelper帮我们处理掉了,例如速度、滑动距离是否到达删除阈值,删除成功移除的动画、取消删除复位的动画等等。&
所以我说利用ItemTouchHelper才是正确的姿势,因为很简单&快速。&
下面我们来实现滑动时的动画。
滑动时动画
我们需要重写Callback的onChildDraw()方法,这个方法参数较多:
* @param c
The canvas which RecyclerView is drawing its children
* @param recyclerView
The RecyclerView to which ItemTouchHelper is attached to
* @param viewHolder
The ViewHolder which is being interacted by the User or it was
interacted and simply animating to its original position
* @param dX
The amount of horizontal displacement caused by user's action
* @param dY
The amount of vertical displacement caused by user's action
* @param actionState
是拖拽还是滑动事件
The type of interaction on the View. Is either {@link #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.
* @param isCurrentlyActive 事件是用户产生还是动画产生的 True if this view is currently being controlled by the user or false it is simply animating back to its original state.
对我们比较有用的有dX dX,可以判断滑动方向,以及计算滑动的比例,从而控制缩放、位移动画的程度。
本文如下编写,对View的缩放、位移,其实是对LayoutManager里的操作的逆操作,值得注意的是最后一层,即top-3View在Y轴上是保持不变的:
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 swipValue = Math.sqrt(dX * dX + dY * dY);
double fraction = swipValue / getThreshold(viewHolder);
if (fraction & 1) {
fraction = 1;
int childCount = recyclerView.getChildCount();
for (int i = 0; i & childC i++) {
View child = recyclerView.getChildAt(i);
int level = childCount - i - 1;
if (level & 0) {
child.setScaleX((float) (1 - SCALE_GAP * level + fraction * SCALE_GAP));
if (level & MAX_SHOW_COUNT - 1) {
child.setScaleY((float) (1 - SCALE_GAP * level + fraction * SCALE_GAP));
child.setTranslationY((float) (TRANS_Y_GAP * level - fraction * TRANS_Y_GAP));
}12345678910111213141516171819202122232425261234567891011121314151617181920212223242526
getThreshold(viewHolder)函数,返回是否可以被回收掉的阈值,关于它为什么这么写,我是从源码里找到的,本末会讲解:
public float getThreshold(RecyclerView.ViewHolder viewHolder) {
return mRv.getWidth() * getSwipeThreshold(viewHolder);
探探效果的实现
一开始文章撸到这里应该结束了,群里出来一个马小跳,告诉我探探和这略有不同,希望我一并实现。&
嗯,好吧。表示没听说过探探,那我先去下载一个看看吧。&
loading-install-open……..&
哎哟呵,十分钟过去了,我还在滑动看美女 忘记了要干什么,被女票看到胖揍了我一顿。&
好的,我捂着脸继续分析。
探探和人人影视有两点不同:
Roate的变化:左右滑动时,顶层卡片会慢慢旋转,到阈值max大概十五度。Alpha的变化:左滑时顶层卡片的删除按钮会慢慢显现,右滑时爱心按钮会慢慢显现。
感觉也是炒鸡简单,来吧。五分钟撸完吃外卖。修改点:
在layout布局添加『 X 』&『 爱心 』。在onChildDraw()里,按比例修改TopView的Rotate & Alpha
还有一点小不同,上滑下滑不再能删除,所以我们构造时只传入左右即可:
ItemTouchHelper.Callback callback = new ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) 1212
布局添加两个按钮
onChildDraw()
在上文人人影视的基础上扩展,上文的效果,对TopView是不做任何操作的。这里只需要再对TopView做额外操作即可:
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
for (int i = 0; i & childC i++) {
View child = recyclerView.getChildAt(i);
int level = childCount - i - 1;
if (level & 0) {
float xFraction = dX / getThreshold(viewHolder);
if (xFraction & 1) {
xFraction = 1;
} else if (xFraction & -1) {
xFraction = -1;
child.setRotation(xFraction * MAX_ROTATION);
if (viewHolder instanceof ViewHolder) {
ViewHolder holder = (ViewHolder) viewH
if (dX & 0) {
holder.setAlpha(R.id.iv_love, xFraction);
holder.setAlpha(R.id.iv_del, -xFraction);
}123456789101112131415161718192021222324252627282930313233343536123456789101112131415161718192021222324252627282930313233343536
实现完后,我以为结束了,结果比我们想象的还要复杂一丢丢。因为此时删除后,notifyDataSetChanged()刷新界面,而TopView还是倾斜的,爱心、删除图标也是出现的。这显然与预期不符。所以我们需要在onSwiped()里将其复位:
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
viewHolder.itemView.setRotation(0);
if (viewHolder instanceof ViewHolder) {
ViewHolder holder = (ViewHolder) viewH
holder.setAlpha(R.id.iv_love, 0);
holder.setAlpha(R.id.iv_del, 0);
}1234567891011121312345678910111213
Ok,大功告成。效果和文首一样,尽情去跟产品UI嘚瑟吧。
阈值的寻找之路
阈值的寻找,花费了我一些时间,因为我想做到和系统的行为保持一致。&
即,当删除、喜欢图标全显,当Top-1View显示完毕时,松手&TopView会回收。&
这就决定了我们的缩放、位移的阈值不能随便定,所以我们必须去源代码里找答案。
public float getThreshold(RecyclerView.ViewHolder viewHolder) {
return mRv.getWidth() * getSwipeThreshold(viewHolder);
1234512345
因为滑动删除操作是touch事件导致的,且应该是ACTION_UP时,触发的,&
所以在ItemTouchHelper&源码里,搜索onTouch字样:&
定位到:mOnItemTouchListener,-&&
继续定位其中的onTouchEvent(),-&&
case MotionEvent.ACTION_UP:,-&&
void select(ViewHolder selected, int actionState)-&&
在这里我注意到有一句代码:animationType = ANIMATION_TYPE_SWIPE_SUCCESS;&
这说明删除成功,它的触发条件是:if (swipeDir & 0)-&&
swipeDir的值:&final
int swipeDir = prevActionState == ACTION_STATE_DRAG ? 0&
: swipeIfNecessary(prevSelected);&-&&
int swipeIfNecessary(ViewHolder viewHolder)-&
if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) & 0) {
return swipeD
如此返回1的话,则-&checkHorizontalSwipe(viewHolder, flags)-&&
在其中终于找到源码里阈值的获取之处:
final float threshold = mRecyclerView.getWidth() * mCallback
.getSwipeThreshold(viewHolder);1212
于是我就直接复制出来。
代码传送门:喜欢的话,随手点个star。多谢&
本文利用LayoutManager加载显示屏幕上可见的数量的View,搭配ItemTouchHelper处理拖拽&滑动删除逻辑,核心代码不超过50行。且经过封装,四行代码就可以用。
记住LayoutManager,我们写,只layout出界面上可能会看见的那些View即可。
关于ItemTouchHelper,它本身实现了拖拽&滑动删除逻辑,我们只需要在onChildDraw()中绘制动画和onSwiped()中处理数据集(循环or删除)即可。
以后老板让你做这种效果,你只需要:
mRv.setLayoutManager(new OverLayCardLayoutManager());
CardConfig.initConfig(this);
ItemTouchHelper.Callback callback = new RenRenCallback(mRv, mAdapter, mDatas);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(mRv);1234512345
如果需要定制特殊的参数,例如显示6层:
CardConfig.MAX_SHOW_COUNT = 6;
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:52923次
积分:1196
积分:1196
排名:千里之外
原创:50篇
转载:91篇
评论:11条}

我要回帖

更多关于 xposed卡片式多任务 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信