效果图:
凑合着看吧,达到的效果就是测划出现Delete后,触碰任何地方都是会自动的滑回去
是一个自定义控件和RecycleView合用。
activity:
public class TestActivity2 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);
RecyclerView recyclerView = findViewById(R.id.RecyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
SlideAdapter slideAdapter = new SlideAdapter();
recyclerView.setAdapter(slideAdapter);
slideAdapter.setNewInstance(getDatas());
findViewById(R.id.rel).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
slideAdapter.mSlideHelper.closeAllUpSide();
return false;
}
});
}
private List<Bean> getDatas() {
List<Bean> list = new ArrayList<>();
for (int i = 0; i < 6; i++) {
Bean b = new Bean("" + i, false);
list.add(b);
}
return list;
}
}
Activity的布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rel"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/RecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</RelativeLayout>
用到的recycleview的adapter我用的是BRVAH的BaseQuickAdapter
public class SlideAdapter extends BaseQuickAdapter<Bean, BaseViewHolder> {
public final SlideHelper mSlideHelper = new SlideHelper();
public SlideAdapter() {
super(R.layout.adapter_slide);
}
@Override
protected void convert(@NotNull BaseViewHolder holder, Bean item) {
holder.setText(R.id.tv_content, item.content);
final SlideLayout sl_slide = holder.getView(R.id.sl_slide);
sl_slide.setOpen(false, false);
sl_slide.setOnStateChangeListener(new SlideLayout.OnStateChangeListener() {
@Override
public boolean onInterceptTouchEvent(SlideLayout layout) {
boolean result = mSlideHelper.closeAll(layout);
return false;
}
@Override
public void onStateChanged(SlideLayout layout, boolean open) {
mSlideHelper.onStateChanged(layout, open);
}
});
}
}
recycleview的item的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Just contain two view -->
<com.example.fragmentadaptertest.slide.SlideLayout
android:id="@+id/sl_slide"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Content view -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="65dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="CONTENT"
android:textColor="#000000"
android:textSize="14dp" />
</LinearLayout>
<!-- Slide view -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_stick"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="#C7C7CD"
android:gravity="center"
android:text="STICK"
android:textColor="#000000"
android:textSize="14dp" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="#FF3A30"
android:gravity="center"
android:text="DELETE"
android:textColor="#000000"
android:textSize="14dp" />
</LinearLayout>
</com.example.fragmentadaptertest.slide.SlideLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#e3e4e5" />
</LinearLayout>
然后用到的自定义View:
public class SlideLayout extends ViewGroup {
private int mWidth;
private int mHeight;
private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
private Scroller mScroller;
private int mLeftBorder;
private int mRightBorder;
private int mTouchSlop;
private int mSlideSlop;
private int mDuration;
// TouchEvent_ACTION_DOWN coordinates (x, y)
private float mTouchX, mTouchY;
// TouchEvent last coordinate (x, y)
private float mLastTouchX;
private boolean mIsDragging;
private boolean mIsOpen;
private boolean mIsEnable;
private OnStateChangeListener mOnStateChangeListener;
public SlideLayout(Context context) {
this(context, null);
}
public SlideLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initTypedArray(context, attrs);
init(context);
}
private void initTypedArray(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SlideLayout);
mSlideSlop = (int) typedArray.getDimension(R.styleable.SlideLayout_sl_slideSlop,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 45, getResources().getDisplayMetrics()));
mDuration = typedArray.getInteger(R.styleable.SlideLayout_sl_duration, 250);
mIsEnable = typedArray.getBoolean(R.styleable.SlideLayout_sl_enable, true);
typedArray.recycle();
}
private void init(Context context) {
mScroller = new Scroller(context);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
return new LayoutParams(lp);
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final SlideLayout.LayoutParams lp = (SlideLayout.LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
if (measureMatchParentChildren) {
if (lp.width == SlideLayout.LayoutParams.MATCH_PARENT ||
lp.height == SlideLayout.LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
} else {
setMeasuredDimension(mWidth, mHeight);
}
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == SlideLayout.LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth()
- lp.leftMargin - lp.rightMargin);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
lp.leftMargin + lp.rightMargin,
lp.width);
}
final int childHeightMeasureSpec;
if (lp.height == SlideLayout.LayoutParams.MATCH_PARENT) {
final int height = Math.max(0, getMeasuredHeight()
- lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
height, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
lp.topMargin + lp.bottomMargin,
lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
if (count <= 0) {
return;
}
final int parentLeft = getPaddingLeft();
final int parentRight = r - l - getPaddingRight();
final int parentTop = getPaddingTop();
final int parentBottom = b - t - getPaddingBottom();
int left = 0, top = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
childTop = parentTop + lp.topMargin;
childLeft = parentLeft + lp.leftMargin;
// Layout horizontally for each child view in the ViewGroup
child.layout(left + childLeft, childTop, left + childLeft + width, childTop + height);
left += childLeft + width + lp.rightMargin + getPaddingRight();
}
}
// Initialize left and right boundary values
mLeftBorder = getChildAt(0).getLeft();
mRightBorder = getChildAt(count - 1).getRight();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
final boolean intercepted = mOnStateChangeListener != null
&& mOnStateChangeListener.onInterceptTouchEvent(this);
if (intercepted) {
return false;
}
final float x = ev.getRawX();
final float y = ev.getRawY();
mLastTouchX = mTouchX = x;
mTouchY = y;
mIsDragging = false;
super.dispatchTouchEvent(ev);
return true;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!mIsEnable) {
return super.onInterceptTouchEvent(ev);
}
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
final float x = ev.getRawX();
final float y = ev.getRawY();
// Intercept child event when horizontal ACTION_MOVE value is greater than TouchSlop
if (Math.abs(x - mTouchX) > mTouchSlop
&& Math.abs(x - mTouchX) > Math.abs(y - mTouchY)) {
return true;
}
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mIsEnable) {
return super.onTouchEvent(event);
}
final float x = event.getRawX();
final float y = event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (!mIsDragging
&& Math.abs(x - mTouchX) > mTouchSlop
&& Math.abs(x - mTouchX) > Math.abs(y - mTouchY)) {
// Disable parent view interception events
requestDisallowInterceptTouchEvent(true);
mIsDragging = true;
mLastTouchX = x;
return super.onTouchEvent(event);
}
if (mIsDragging) {
final int offset = (int) (mLastTouchX - x);
if (getScrollX() + offset < 0) {
setOpen(false, false);
mTouchX = x; // Reset touch x
} else if (getScrollX() + offset > mRightBorder - mWidth) {
setOpen(true, false);
mTouchX = x; // Reset touch x
} else {
scrollBy(offset, 0);
}
mLastTouchX = x;
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mIsDragging) {
if (x - mTouchX < -mSlideSlop) {
setOpen(true, true);
} else if (x - mTouchX > mSlideSlop) {
setOpen(false, true);
} else {
setOpen(mIsOpen, true);
}
event.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(event);
return true;
}
break;
}
return super.onTouchEvent(event);
}
private void smoothScrollTo(int dstX, int duration) {
int offset = dstX - getScrollX();
mScroller.startScroll(getScrollX(), 0, offset, 0, duration);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
public boolean isEnable() {
return mIsEnable;
}
public void setEnable(boolean enable) {
this.mIsEnable = enable;
}
public boolean isOpen() {
return mIsOpen;
}
public void open() {
setOpen(true, true);
}
public void close() {
setOpen(false, true);
}
/**
* Set on or off status
*
* @param open Open or close
* @param withAnim Whether with animation effect
*/
public void setOpen(boolean open, boolean withAnim) {
if (mIsOpen != open && mOnStateChangeListener != null) {
mOnStateChangeListener.onStateChanged(this, open);
}
mIsOpen = open;
int x = mIsOpen ? mRightBorder - mWidth : 0;
int y = 0;
if (withAnim) {
smoothScrollTo(x, mDuration);
} else {
scrollTo(x, y);
}
}
public void setOnStateChangeListener(OnStateChangeListener listener) {
this.mOnStateChangeListener = listener;
}
public abstract static class OnStateChangeListener {
/**
* Implement this method to intercept all touch screen motion events.
*
* @param layout This layout
*/
public boolean onInterceptTouchEvent(SlideLayout layout) {
return false;
}
public abstract void onStateChanged(SlideLayout layout, boolean open);
}
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
}
和自定义属性:放在values下的attrs.xml中,没有就创建一个
<declare-styleable name="SlideLayout">
<attr name="sl_enable" format="boolean" />
<attr name="sl_slideSlop" format="dimension" />
<attr name="sl_duration" format="integer" />
</declare-styleable>
会用到一个工具类:
public class SlideHelper {
private final List<SlideLayout> mSlides = new ArrayList<>();
public SlideHelper() {
}
public void onStateChanged(SlideLayout layout, boolean open) {
if (open) {
mSlides.add(layout);
} else {
mSlides.remove(layout);
}
}
public boolean closeAll(SlideLayout layout) {
if (mSlides.size() <= 0) {
return false;
}
boolean result = false;
for (int i = 0; i < mSlides.size(); i++) {
SlideLayout slide = mSlides.get(i);
if (slide != null && slide != layout) {
slide.close();
mSlides.remove(slide); // Unnecessary
result = true;
i--;
}
}
return result;
}
public boolean closeAllUpSide() {
if (mSlides.size() <= 0) {
return false;
}
boolean result = false;
for (int i = 0; i < mSlides.size(); i++) {
SlideLayout slide = mSlides.get(i);
if (slide != null) {
slide.close();
mSlides.remove(slide); // Unnecessary
result = true;
i--;
}
}
return result;
}
}