package com.example.myapplication;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* 支持侧滑删除的RecyclerView
* <p>
* Created by DavidChen on 2018/5/29.
*/
public class SlideRecyclerView extends RecyclerView {
private static final String TAG = "SlideRecyclerView";
private static final int INVALID_POSITION = -1; // 触摸到的点不在子View范围内
private static final int INVALID_CHILD_WIDTH = -1; // 子ItemView不含两个子View
private static final int SNAP_VELOCITY = 600; // 最小滑动速度
private VelocityTracker mVelocityTracker; // 速度追踪器
private int mTouchSlop; // 认为是滑动的最小距离(一般由系统提供)
private Rect mTouchFrame; // 子View所在的矩形范围
private Scroller mScroller;
private float mLastX; // 滑动过程中记录上次触碰点X
private float mFirstX, mFirstY; // 首次触碰范围
private boolean mIsSlide; // 是否滑动子View
private ViewGroup mFlingView; // 触碰的子View
private int mPosition; // 触碰的view的位置
private int mMenuViewWidth; // 菜单按钮宽度
public SlideRecyclerView(Context context) {
this(context, null);
}
public SlideRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mScroller = new Scroller(context);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int x = (int) e.getX();
int y = (int) e.getY();
obtainVelocity(e);
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) { // 如果动画还没停止,则立即终止动画
mScroller.abortAnimation();
}
mFirstX = mLastX = x;
mFirstY = y;
mPosition = pointToPosition(x, y); // 获取触碰点所在的position
if (mPosition != INVALID_POSITION) {
View view = mFlingView;
// 获取触碰点所在的view
mFlingView = (ViewGroup) getChildAt(mPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition());
// 这里判断一下如果之前触碰的view已经打开,而当前碰到的view不是那个view则立即关闭之前的view,此处并不需要担动画没完成冲突,因为之前已经abortAnimation
if (view != null && mFlingView != view && view.getScrollX() != 0) {
view.scrollTo(0, 0);
}
// 这里进行了强制的要求,RecyclerView的子ViewGroup必须要有2个子view,这样菜单按钮才会有值,
// 需要注意的是:如果不定制RecyclerView的子View,则要求子View必须要有固定的width。
// 比如使用LinearLayout作为根布局,而content部分width已经是match_parent,此时如果菜单view用的是wrap_content,menu的宽度就会为0。
if (mFlingView.getChildCount() == 2) {
mMenuViewWidth = mFlingView.getChildAt(1).getWidth();
} else {
mMenuViewWidth = INVALID_CHILD_WIDTH;
}
}
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.computeCurrentVelocity(1000);
// 此处有俩判断,满足其一则认为是侧滑:
// 1.如果x方向速度大于y方向速度,且大于最小速度限制;
// 2.如果x方向的侧滑距离大于y方向滑动距离,且x方向达到最小滑动距离;
float xVelocity = mVelocityTracker.getXVelocity();
float yVelocity = mVelocityTracker.getYVelocity();
if (Math.abs(xVelocity) > SNAP_VELOCITY && Math.abs(xVelocity) > Math.abs(yVelocity)
|| Math.abs(x - mFirstX) >= mTouchSlop
&& Math.abs(x - mFirstX) > Math.abs(y - mFirstY)) {
mIsSlide = true;
return true;
}
break;
case MotionEvent.ACTION_UP:
releaseVelocity();
break;
}
return super.onInterceptTouchEvent(e);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if (mIsSlide && mPosition != INVALID_POSITION) {
float x = e.getX();
obtainVelocity(e);
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: // 因为没有拦截,所以不会被调用到
break;
case MotionEvent.ACTION_MOVE:
// 随手指滑动
if (mMenuViewWidth != INVALID_CHILD_WIDTH) {
float dx = mLastX - x;
if (mFlingView.getScrollX() + dx <= mMenuViewWidth
&& mFlingView.getScrollX() + dx > 0) {
mFlingView.scrollBy((int) dx, 0);
}
mLastX = x;
}
break;
case MotionEvent.ACTION_UP:
if (mMenuViewWidth != INVALID_CHILD_WIDTH) {
int scrollX = mFlingView.getScrollX();
mVelocityTracker.computeCurrentVelocity(1000);
// 此处有两个原因决定是否打开菜单:
// 1.菜单被拉出宽度大于菜单宽度一半;
// 2.横向滑动速度大于最小滑动速度;
// 注意:之所以要小于负值,是因为向左滑则速度为负值
if (mVelocityTracker.getXVelocity() < -SNAP_VELOCITY) { // 向左侧滑达到侧滑最低速度,则打开
mScroller.startScroll(scrollX, 0, mMenuViewWidth - scrollX, 0, Math.abs(mMenuViewWidth - scrollX));
} else if (mVelocityTracker.getXVelocity() >= SNAP_VELOCITY) { // 向右侧滑达到侧滑最低速度,则关闭
mScroller.startScroll(scrollX, 0, -scrollX, 0, Math.abs(scrollX));
} else if (scrollX >= mMenuViewWidth / 2) { // 如果超过删除按钮一半,则打开
mScroller.startScroll(scrollX, 0, mMenuViewWidth - scrollX, 0, Math.abs(mMenuViewWidth - scrollX));
} else { // 其他情况则关闭
mScroller.startScroll(scrollX, 0, -scrollX, 0, Math.abs(scrollX));
}
invalidate();
}
mMenuViewWidth = INVALID_CHILD_WIDTH;
mIsSlide = false;
mPosition = INVALID_POSITION;
releaseVelocity(); // 这里之所以会调用,是因为如果前面拦截了,就不会执行ACTION_UP,需要在这里释放追踪
break;
}
return true;
} else {
// 此处防止RecyclerVie
没有合适的资源?快使用搜索试试~ 我知道了~
Android中的recycleview侧滑按钮的展示和操作处理
共72个文件
xml:21个
webp:10个
properties:7个
需积分: 0 0 下载量 143 浏览量
2023-05-26
17:21:29
上传
评论
收藏 426KB RAR 举报
温馨提示
内容概要:1.自定义SlideRecyclerView继承recycleview; 2.在其onInterceptTouchEvent方法中监听手指滑动的距离,获取触碰点所在的 position,来判断是否触发侧滑操作; 3.在其onTouchEvent方法中监听手指滑动的距离,并结合速度追踪器来处理侧滑 按钮展开和关闭; 4.将获取的结果在SideslipDeletionAdapter通过回调进行事件的处理; 适合人群:具备一定编程基础,工作1-3年的研发人员; 学习收获:对触摸事件和RecyclerView有更深的了解; 阅读建议:此资源有详细的注解,建议结合注解阅读,遇到不懂 的方法需进行网络搜索,阅读完毕之后,建议手写一份,更有助于理解和记忆
资源推荐
资源详情
资源评论
收起资源包目录
MyAppication.rar (72个子文件)
MyAppication
.gradle
buildOutputCleanup
cache.properties 51B
buildOutputCleanup.lock 17B
outputFiles.bin 308KB
7.4
fileChanges
last-build.bin 1B
checksums
md5-checksums.bin 20KB
sha1-checksums.bin 29KB
checksums.lock 17B
executionHistory
executionHistory.bin 1.39MB
executionHistory.lock 17B
dependencies-accessors
gc.properties 0B
dependencies-accessors.lock 17B
fileHashes
fileHashes.lock 17B
resourceHashesCache.bin 21KB
fileHashes.bin 174KB
gc.properties 0B
vcsMetadata
vcs-1
gc.properties 0B
file-system.probe 8B
gradle.properties 1KB
gradle
wrapper
gradle-wrapper.jar 58KB
gradle-wrapper.properties 236B
app
src
androidTest
java
com
example
myapplication
ExampleInstrumentedTest.java 763B
test
java
com
example
myapplication
ExampleUnitTest.java 386B
main
java
com
example
myapplication
SlideRecyclerView.java 10KB
BaseRecyclerViewAdapter.java 3KB
SideslipDeletionAdapter.java 1KB
RecyclerViewHolder.java 1KB
SideslipDeletionActivity.java 2KB
res
mipmap-xxhdpi
ic_default_avatar.png 28KB
ic_launcher_round.webp 6KB
ic_launcher.webp 3KB
mipmap-hdpi
ic_launcher_round.webp 3KB
ic_launcher.webp 1KB
drawable-v24
ic_launcher_foreground.xml 2KB
mipmap-anydpi-v26
ic_launcher.xml 272B
ic_launcher_round.xml 272B
values-night
themes.xml 815B
mipmap-mdpi
ic_launcher_round.webp 2KB
ic_launcher.webp 982B
mipmap-xxxhdpi
ic_refuse.png 2KB
ic_agree.png 2KB
ic_launcher_round.webp 8KB
ic_launcher.webp 4KB
mipmap-xhdpi
ic_launcher_round.webp 4KB
ic_launcher.webp 2KB
xml
data_extraction_rules.xml 551B
backup_rules.xml 478B
values
dimens.xml 638B
colors.xml 894B
strings.xml 283B
styles.xml 475B
themes.xml 815B
layout
league_application_list__item.xml 6KB
activity_sideslip_deletion.xml 534B
drawable
ic_launcher_background.xml 5KB
shape_corner8_ffffff.xml 238B
bg_pay_tip_coner.xml 420B
AndroidManifest.xml 1KB
proguard-rules.pro 750B
libs
build.gradle 1KB
.gitignore 6B
sampledata
gradlew.bat 3KB
build.gradle 229B
.idea
.name 14B
workspace.xml 12KB
misc.xml 477B
compiler.xml 174B
gradle.xml 698B
.gitignore 50B
settings.gradle 559B
local.properties 540B
gradlew 6KB
.gitignore 225B
共 72 条
- 1
资源评论
Android程序猿mz
- 粉丝: 4
- 资源: 5
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功