记录一下:
ViewDragHelper可以实现侧滑删除。所以自定义一个布局,放入上面的布局和底部的删除布局就能凭借ViewDragHelper侧滑了,很简单。但是有几点记录一下,怕以后用得到。
1、列表中使用侧滑,会和recycleview的上下滑动冲突,那么怎么办?很简单,我们只要知道ViewDragHelper啥时候在滑动,停掉父布局的事件分发,然后在停止滑动重新开启就行。
@Override
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
mHorizontalDragLayout.getParent().requestDisallowInterceptTouchEvent(true);
}
@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
mHorizontalDragLayout.getParent().requestDisallowInterceptTouchEvent(false);
}
2、上面的布局点击事件无效,也就是itemclick失效了,怎么办?
追踪源码发现,原来我们是没有设置拖拽范围
@Override
public int getViewHorizontalDragRange(View child) {
return mHorizontalDragLayout.mTopView == child ? child.getWidth() : 0;
}
@Override
public int getViewVerticalDragRange(View child) {
return mHorizontalDragLayout.mTopView == child ? child.getHeight() : 0;
}
把范围就设置成上面布局大小就行
3、案例中我们设置的是往左边滑,但是当我们往右边滑动时,居然触发了点击事件,那么怎么处理?很简单,在自定义view中监听滑动距离是不是超出了最小距离,超出了就在onInterceptTouchEvent中返回true。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = mViewDragHelper.shouldInterceptTouchEvent(ev);
float curX = ev.getX();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = curX;
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(curX - startX) > mViewDragHelper.getTouchSlop()){
result = true;
}
break;
}
return result;
}
全部代码
自定义view HorizontalDragLayout
public class HorizontalDragLayout extends RelativeLayout {
public ViewDragHelper mViewDragHelper;
public View mTopView;
public View mBgView;
public int mBgWidth;
public int mTopWidth;
/**
* 默认右滑菜单
*/
public int mTrackingEdges = EDGE_RIGHT;
/**
* 左滑菜单
*/
public static final int EDGE_LEFT = 1;
/**
* 右滑菜单
*/
public static final int EDGE_RIGHT = 2;
private HorizontalDragHelper mDragHelper;
public boolean isOpen = false;
private int roundSize = 0;//圆角
private int bgViewId;
private int topViewId;
private float startX;
public HorizontalDragLayout(Context context) {
this(context,null);
}
public HorizontalDragLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public HorizontalDragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//自定义属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HorizontalDragLayout);
topViewId = ta.getResourceId(R.styleable.HorizontalDragLayout_topViewId, 0);
bgViewId = ta.getResourceId(R.styleable.HorizontalDragLayout_bgViewId, 0);
roundSize = (int) ta.getDimension(R.styleable.HorizontalDragLayout_roundSize, roundSize);
ta.recycle();
mDragHelper = new HorizontalDragHelper(this);
mViewDragHelper = ViewDragHelper.create(this, mDragHelper);
//圆角处理
if(roundSize != 0){
RoundViewOutlineProvider outlineProvider = new RoundViewOutlineProvider(roundSize);
setOutlineProvider(outlineProvider);
setClipToOutline(true);
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mBgView = findViewById(bgViewId);//底部删除按钮view
mBgView.setEnabled(false);
//根据左滑右滑放置背景view位置
setBgLocation();
mTopView = findViewById(topViewId);//上面的布局view
}
private void setBgLocation() {
try {
RelativeLayout.LayoutParams layoutParams = (LayoutParams) mBgView.getLayoutParams();
if(mTrackingEdges == EDGE_LEFT){
//靠左边放
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
}else{
//靠右边放
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
}
mBgView.setLayoutParams(layoutParams);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//此时已测量完成,可获取抽屉的宽度
if(mBgView != null){
mBgWidth = mBgView.getMeasuredWidth();
}
if(mTopView != null){
mTopWidth = mTopView.getMeasuredWidth();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = mViewDragHelper.shouldInterceptTouchEvent(ev);
float curX = ev.getX();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = curX;
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(curX - startX) > mViewDragHelper.getTouchSlop()){
result = true;
}
break;
}
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
//如果滑动还未结束
if (mViewDragHelper.continueSettling(true)) {
//继续调用computeScroll
ViewCompat.postInvalidateOnAnimation(this);
}
}
public void open() {
mDragHelper.open();
}
public void close() {
mDragHelper.close();
}
/**
* 圆角ViewOutlineProvider
*/
private static class RoundViewOutlineProvider extends ViewOutlineProvider {
private final int roundSize;
public RoundViewOutlineProvider(int roundSize) {
this.roundSize = roundSize;
}
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), roundSize);
}
}
}
ViewDragHelper的实现类
public class HorizontalDragHelper extends ViewDragHelper.Callback {
private HorizontalDragLayout mHorizontalDragLayout;
public HorizontalDragHelper(HorizontalDragLayout horizontalDragLayout) {
this.mHorizontalDragLayout = horizontalDragLayout;
}
@Override
public boolean tryCaptureView(@NonNull View child, int pointerId) {
if(mHorizontalDragLayout != null && mHorizontalDragLayout.mTopView != null){
return child == mHorizontalDragLayout.mTopView;
}else{
return false;
}
}
@Override
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
//处理水平滑动(子view左边界距离当前framelayout左边界距离),返回滑动位置
if(mHorizontalDragLayout == null){
return 0;
}else{
mHorizontalDragLayout.getParent().requestDisallowInterceptTouchEvent(true);
if (mHorizontalDragLayout.mTrackingEdges == HorizontalDragLayout.EDGE_LEFT) {
if (left > 0) {
return left>mHorizontalDragLayout.mBgWidth ? mHorizontalDragLayout.mBgWidth : left;
} else {
return 0;
}
}else{
if (left < 0) {
return -left > mHorizontalDragLayout.mBgWidth ? -mHorizontalDragLayout.mBgWidth : left;
} else {
return 0;
}
}
}
}
@Override
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
//处理垂直滑动,返回垂直滑动位置,这里不需要
return 0;
}
@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
mHorizontalDragLayout.getParent().requestDisallowInterceptTouchEvent(false);
try {
if(mHorizontalDragLayout != null){
if (mHorizontalDragLayout.mTrackingEdges == HorizontalDragLayout.EDGE_LEFT) {
resetView(mHorizontalDragLayout.mTopView.getLeft());
}else{
resetView((mHorizontalDragLayout.mTopWidth - mHorizontalDragLayout.mTopView.getRight()));
}
}
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public int getViewHorizontalDragRange(View child) {
return mHorizontalDragLayout.mTopView == child ? child.getWidth() : 0;
}
@Override
public int getViewVerticalDragRange(View child) {
return mHorizontalDragLayout.mTopView == child ? child.getHeight() : 0;
}
/**
* 手指抬起的回弹效果
* @param currentMargin
*/
private void resetView(int currentMargin){
//手指抬起时处理
try {
if (currentMargin > mHorizontalDragLayout.mBgWidth/2) {
open();
} else {
//让底部菜单栏隐藏
close();
}
}catch (Exception e){
e.printStackTrace();
}
}
public void open(){
try {
int smoothSlideViewToX = 0;
if(mHorizontalDragLayout.mTrackingEdges == HorizontalDragLayout.EDGE_LEFT){
smoothSlideViewToX = mHorizontalDragLayout.mBgWidth;
}else{
smoothSlideViewToX = -mHorizontalDragLayout.mBgWidth;
}
//让底部菜单栏完全展示
mHorizontalDragLayout.mViewDragHelper.smoothSlideViewTo(mHorizontalDragLayout.mTopView,smoothSlideViewToX,0);
//它执行后会调用到computerScroll()
ViewCompat.postInvalidateOnAnimation(mHorizontalDragLayout);
mHorizontalDragLayout.isOpen = true;
//打开才能点击底部控件
mHorizontalDragLayout.mBgView.setEnabled(true);
}catch (Exception e){
e.printStackTrace();
}
}
public void close(){
try {
mHorizontalDragLayout.mViewDragHelper.smoothSlideViewTo(mHorizontalDragLayout.mTopView,0,0);
ViewCompat.postInvalidateOnAnimation(mHorizontalDragLayout);
mHorizontalDragLayout.isOpen = false;
mHorizontalDragLayout.mBgView.setEnabled(false);
}catch (Exception e){
e.printStackTrace();
}
}
}
attrs
<declare-styleable name="HorizontalDragLayout">
<attr name="topViewId" format="reference"/>
<attr name="bgViewId" format="reference"/>
<attr name="roundSize" format="dimension"/>
</declare-styleable>
adapter
public class TestAdapter extends BaseQuickAdapter<String, BaseViewHolder> {
public TestAdapter(@Nullable List<String> data) {
super(R.layout.horizontal_item_layout,data);
}
@Override
protected void convert(@NonNull BaseViewHolder helper, String item) {
helper.getView(R.id.mTvDelete).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(helper.itemView.getContext(), "删除 item = "+item, Toast.LENGTH_SHORT).show();
}
});
helper.getView(R.id.topView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(helper.itemView.getContext(), "item = "+item, Toast.LENGTH_SHORT).show();
}
});
}
}
条目布局horizontal_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.example.demo.dragView.view.HorizontalDragLayout
android:id="@+id/mHorizontalDragLayout"
android:layout_width="match_parent"
android:layout_height="128dp"
app:roundSize="8dp"
app:topViewId="@id/topView"
app:bgViewId="@id/mTvDelete">
<TextView
android:id="@+id/mTvDelete"
android:layout_width="56dp"
android:layout_height="match_parent"
android:background="@color/mainColor"
android:textSize="12sp"
android:textColor="@color/white"
android:text="删除"
android:gravity="center"/>
<TextView
android:id="@+id/topView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/ff999999"/>
</com.example.demo.dragView.view.HorizontalDragLayout>
</RelativeLayout>
activity
public class DragViewActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private TestAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag_view);
mRecyclerView = findViewById(R.id.mRecyclerView);
mAdapter = new TestAdapter(null);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter);
initData();
}
private void initData() {
List<String> list = new ArrayList<>();
for (int x = 0; x < 10; x++) {
list.add(x + "");
}
mAdapter.setNewData(list);
}
}
activity_drag_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".dragView.DragViewActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/mRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"/>
</RelativeLayout>