【Android开源项目解析】RecyclerView侧滑删除粒子效果实现——初探Android开源粒子库 Leonids
来源:互联网 发布:windows sudo rm -rf 编辑:程序博客网 时间:2024/04/30 14:43
前两天在微博上看到了这个侧滑删除的粒子效果,但是只有IOS的,所以心血来潮,写了个玩玩,下面简单介绍下实现的思路
项目简介
先不废话,上效果图
项目地址:https://github.com/ZhaoKaiQiang/ParticleLayout
实现原理解析
其实看了那么多的关于侧滑删除的项目,再来思考这个问题,就so easy了!
咱们先分析下需求:
- 侧滑手势检测
- 粒子跟手效果
- 删除状态判断
- 数据源刷新
ok,知道需求了,咱们看对策
- 手势检测可以重写onTouch,判断移动方向和距离
- 粒子效果使用第三方的开源项目leonids,跟手效果就是简单的触摸位置的更新
- 假定滑动距离超过item的宽度一半,就代表删除
- 添加回调接口,完成数据源刷新
代码实现
知道了咱们的需求,并且每一个需求都有了解决方案,那么剩下的问题其实就是如何写代码的问题了。
下面这部分,最好参考这个项目的源码进行阅读~
首先,这肯定是属于自定义控件,那么咱们继承谁呢?我选择继承FrameLayout,为啥呢?因为在FrameLayout里面咱们可以控制遮罩效果。
其实完成遮罩效果,也有两种方案,
- 在FrameLayout中放置一个和背景色相同的布局,然后再onTouch中控制宽度,来模拟遮罩效果
- 直接重写dispatchDraw(Canvas canvas) ,使用Canvas.clipRect()控制绘制区域,模拟遮罩效果
其实这两种效果我都做过,在第一个版本中使用的是方案一,可以完成这个效果,但是不知道怎么回事,在5.0以上系统中,遮罩层的层级关系和5.0以下不一致,因此导致在5.0以上不能使用。除此之外, 使用第一种效果需要多一层布局,效率低,而且通用性不好,所以在这里我选择第二种方案。
咱们开始看代码~
public ParticleLayout(Context context) { this(context, null); } public ParticleLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ParticleLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); backLayoutRect = new Rect(); backLocation = new int[2]; }
构造函数非常简单,在三个参数构造函数中,初始化两个变量,backLayoutRect用于控制内容区域,用于后面的触摸边界检测,backLocation则用于存储布局位置,粒子效果需要用坐标。
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (getChildCount() != 1) { throw new IllegalArgumentException("the count of child view must be one !"); } backLayout = (ViewGroup) getChildAt(0); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); backLayout.getLocationInWindow(backLocation); backLayoutRect.set(backLocation[0], backLocation[1], backLocation[0] + backLayout.getMeasuredWidth(), backLocation[1] + backLayout.getMeasuredHeight()); }
上面的代码很好理解,在onSizeChange()里面对子View数量进行强制规定,必须为一个,方便获取到内容区域,而在onLayout()则在测量、布局之后,获取到布局所在位置和初始化内容区域backLayoutRect。
到现在位置,就初始化完毕了,下面其实就是触摸事件的写法。
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { return true; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: if (event.getX() > backLayoutRect.width() * 4 / 5) { isSwape = true; startX = event.getX(); if (bitmapArrays == null || bitmapArrays.length == 0) { particleSystem = new ParticleSystem((Activity) getContext(), COUNT_OF_PARTICAL_BITMAP, DEFAULT_PARTICLE_BITMAP, TIME_TO_LIVE); } else { Random random = new Random(); int resId = bitmapArrays[random.nextInt(bitmapArrays.length)]; particleSystem = new ParticleSystem((Activity) getContext(), COUNT_OF_PARTICAL_BITMAP, resId, TIME_TO_LIVE); } particleSystem.setAcceleration(0.00013f, 90) .setSpeedByComponentsRange(0f, 0.3f, 0.05f, 0.3f) .setFadeOut(TIME_TO_FADE_OUT, new AccelerateInterpolator()) .emitWithGravity(backLayout, Gravity.RIGHT, COUNT_OF_PARTICAL_BITMAP); } break; case MotionEvent.ACTION_MOVE: clipWidth = (int) (startX - event.getX()); if (isSwape && clipWidth > 0) { requestLayout(); particleSystem.updateEmitVerticalLine(backLayoutRect.right - clipWidth, backLayoutRect.top - getStatuBarHeight(), backLayoutRect.bottom - getStatuBarHeight()); } else { particleSystem.stopEmitting(); } getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: startX = 0; clipWidth = 0; invalidate(); isSwape = false; particleSystem.stopEmitting(); getParent().requestDisallowInterceptTouchEvent(false); if (event.getX() >= getWidth() / 2) { isDelete = false; } else { isDelete = true; } if (isDelete) { if (mDeleteListener != null) { mDeleteListener.onDelete(); } } break; } if (isSwape) { return true; } return super.onTouchEvent(event); }
虽然代码长了点,但是也不难啊,onInterceptTouchEvent返回true,就是为了这个区域内的任何触摸事件,我都要拦截一下,至于到底处不处理,看爷心情~
在onTouchEvent()里面实现了核心的代码逻辑,咱们一起看一下~
首先ACTION_DOWN的时候,如果触摸的位置是宽度的4/5处,则认为想要侧滑啦,记下开始的X坐标startX,然后在下面初始化了ParticleSystem对象。这个ParticleSystem对象是粒子库leonids里面的主要业务类,在这里实现了诸如粒子图片、存活时间、加速插值器、渐变消失时间等等参数。
到了ACTION_MOVE,如果x的移动距离是正数,也就是往左滑,并且isSwape为true,就 requestLayout()一下,这个是为啥呢?这是因为如果不 requestLayout(),那么布局的位置就还是初始化时候的位置,从而导致粒子效果位置不对,所以重新计算一下现在的位置,然后调用
particleSystem.updateEmitVerticalLine(backLayoutRect.right - clipWidth, backLayoutRect.top - getStatuBarHeight(), backLayoutRect.bottom - getStatuBarHeight());
当然,这个方法在原先的粒子库是没有的,我自己添加上去的,就是为了能顺着一条竖线往外发送粒子,感兴趣的可以去看源码。
下面这句代码是为了只要处于触摸模式,那么外面的父控件,也就是RecyclerView就不会劫持触摸事件了。
getParent().requestDisallowInterceptTouchEvent(true);
最后,ACTION_UP和ACTION_CANCEL,在这里完成数据的初始化和删除状态的判断,并且如果有监听器,则调用。
当然,如果isSwape为true,那么触摸时间就被消耗了,否则,默认处理即可。
这个时候跟手粒子已经实现了,那么遮罩咋办?
简单~
@Override protected void dispatchDraw(Canvas canvas) { canvas.clipRect(0, 0, backLayoutRect.right - clipWidth, getHeight()); super.dispatchDraw(canvas); }
在画子View之前,clipRect一下,这样,就只会绘制范围内的布局,遮罩效果也就算是实现了。
如何使用
使用也非常简单,首先看布局文件
<?xml version="1.0" encoding="utf-8"?><com.socks.library.ParticleLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/root_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="2dp" app:cardBackgroundColor="@android:color/white" app:cardCornerRadius="4dp"> <TextView android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="48dp" android:gravity="center" android:text="测试" android:textColor="@android:color/primary_text_light" android:textSize="20sp" /> </android.support.v7.widget.CardView></com.socks.library.ParticleLayout>
就和平常的FrameLayout一样使用即可,再看下MainActivity
public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private ParticleAdapter mAdapter; private LinearLayoutManager layoutManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); mAdapter = new ParticleAdapter(); recyclerView.setAdapter(mAdapter); } private class ParticleAdapter extends RecyclerView.Adapter<ParticleAdapter.ParticleViewHolder> { private ArrayList<String> strings; ParticleAdapter() { strings = new ArrayList<>(); for (int i = 0; i < 20; i++) { strings.add("POSITION = " + i); } } @Override public ParticleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = getLayoutInflater().inflate(R.layout.item_layout, parent, false); return new ParticleViewHolder(view); } @Override public void onBindViewHolder(final ParticleViewHolder holder, final int position) { holder.tv.setText(strings.get(position)); holder.root_layout.setDeleteListener(new ParticleLayout.DeleteListener() { @Override public void onDelete() { Toast.makeText(MainActivity.this, "DETELE", Toast.LENGTH_SHORT).show(); strings.remove(position); mAdapter.notifyItemRemoved(position); mAdapter.notifyItemRangeChanged(position, strings.size() - position); } }); holder.root_layout.setBitmapArrays(R.drawable.ic_star, R.drawable.ic_partical, R.drawable.ic_boom); } @Override public int getItemCount() { return strings.size(); } class ParticleViewHolder extends RecyclerView.ViewHolder { TextView tv; ParticleLayout root_layout; public ParticleViewHolder(View itemView) { super(itemView); tv = (TextView) itemView.findViewById(R.id.tv); root_layout = (ParticleLayout) itemView.findViewById(R.id.root_layout); } } }}
代码非常简单,不需要我再说了吧?
唯一需要注意的是,在删除之后,需要刷新数据源,否则就会删除错误位置的数据,
下面的代码是为了添加粒子图片,随机显示。
holder.root_layout.setBitmapArrays(R.drawable.ic_star, R.drawable.ic_partical, R.drawable.ic_boom);
是不是很简单?so easy~
拜拜~
原文链接:http://www.jianshu.com/p/e9c4a053dbde
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
- 【Android开源项目解析】RecyclerView侧滑删除粒子效果实现——初探Android开源粒子库 Leonids
- 【Android开源项目解析】RecyclerView侧滑删除粒子效果实现——初探Android开源粒子库 Leonids
- Android粒子破碎效果(1)——开源项目ExplosionField代码分析
- Android 爆炸粒子动画-参考ExplosionField开源项目
- Android粒子效果
- Android 粒子效果
- Android粒子破碎效果(2)——实现多种破碎效果之ParticleSmasher
- Android粒子系统库——DroidParticle
- android Button粒子化效果
- Android粒子效果之雨
- android Button粒子化效果
- Android碎裂的粒子效果
- Leonids 粒子系统源码分析
- android通过自定义view实现粒子效果展示
- android使用粒子动画实现炊烟袅袅的效果
- Android基础控件——RecyclerView实现拖拽排序侧滑删除效果
- Swift——粒子效果
- CAEmitterLayer实现粒子效果
- Andriod性能优化之列表卡顿——以“简书”APP为例
- 阿里音乐流行趋势预测大赛一起做-(2)weka初识
- PAT (Advanced Level) Practise1002. A+B for Polynomials (25)
- SQL Server移植到MySQL(利用工具Sqlyog)
- activiti 流程超时自动发送邮件
- 【Android开源项目解析】RecyclerView侧滑删除粒子效果实现——初探Android开源粒子库 Leonids
- 我们再来玩游戏(博弈)
- 话说ReferenceQueue
- 终于能轻松一阵了
- 常用框架总结
- 山东省第四届 A Rescue The Princess
- 转载:HTTP和HTTPS详解
- Android面试题汇总:
- Linux下查看某程序的端口占用情况