1. 适配器 RedEnvelopeAdapter.java
package com.example.sliderecyle.adapter;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.example.sliderecyle.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RedEnvelopeAdapter extends RecyclerView.Adapter<RedEnvelopeAdapter.MyViewHolder> {
private Context context;
private View view;
public List<String> topIconData;
public List<Boolean> itemVisibility;
public RedEnvelopeAdapter(Context context, List<String> topIconData) {
this.context = context;
this.topIconData = topIconData;
this.itemVisibility = new ArrayList<>(Collections.nCopies(topIconData.size(), true));
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_rv_red_envelope_test, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
int adapterPosition = holder.getAdapterPosition();
if (itemVisibility.get(adapterPosition)) {
holder.itemView.setVisibility(View.VISIBLE);
} else {
holder.itemView.setVisibility(View.GONE);
}
}
@Override
public int getItemCount() {
return topIconData.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView mTvDelete;
TextView mTvHide;
LinearLayout mLlView;
public MyViewHolder(View itemView) {
super(itemView);
mTvDelete = itemView.findViewById(R.id.tv_delete);
mTvHide = itemView.findViewById(R.id.tv_hide);
mLlView = itemView.findViewById(R.id.ll_view);
mLlView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = getPosition();
if (mItemClickListener != null)
mItemClickListener.onItemClick(position);
}
});
if (!mTvDelete.hasOnClickListeners()) {
mTvDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mDeleteClickListener != null) {
int position = getPosition();
mDeleteClickListener.onDeleteClick(v, position);
}
}
});
}
if (!mTvHide.hasOnClickListeners()) {
mTvHide.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mHideClickListener != null) {
int position = getPosition();
mHideClickListener.onHideClick(v, position);
}
}
});
}
}
}
public void setItemClickListener(MyItemClickListener myItemClickListener) {
this.mItemClickListener = myItemClickListener;
}
public void setCaseDetailsClickListener(MyCaseDetailsClickListener myCaseDetailsClickListener) {
this.mCaseDetailsClickListener = myCaseDetailsClickListener;
}
private MyItemClickListener mItemClickListener;
private MyCaseDetailsClickListener mCaseDetailsClickListener;
private OnDeleteClickListener mDeleteClickListener;
public void setOnDeleteClickListener(OnDeleteClickListener listener) {
this.mDeleteClickListener = listener;
}
public interface OnDeleteClickListener {
void onDeleteClick(View view, int position);
}
private OnHideClickListener mHideClickListener;
public void setOnHideClickListener(OnHideClickListener listener) {
this.mHideClickListener = listener;
}
public interface OnHideClickListener {
void onHideClick(View view, int position);
}
public interface MyItemClickListener {
void onItemClick(int position);
}
public interface MyCaseDetailsClickListener {
void onItemClick(int position);
}
}
2. RecyleView.java
package com.example.sliderecyle.view;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
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;
public class SlideRecyclerView extends RecyclerView {
private static final String TAG = "SlideRecyclerView";
private static final int INVALID_POSITION = -1;
private static final int INVALID_CHILD_WIDTH = -1;
private static final int SNAP_VELOCITY = 600;
private VelocityTracker mVelocityTracker;
private int mTouchSlop;
private Rect mTouchFrame;
private Scroller mScroller;
private float mLastX;
private float mFirstX, mFirstY;
private boolean mIsSlide;
private boolean mIsShowSlideView;
private ViewGroup mFlingView;
private int mPosition;
private int mMenuViewWidth;
private float xVelocityValue;
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);
if (mPosition != INVALID_POSITION) {
View view = mFlingView;
mFlingView = (ViewGroup) getChildAt(mPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition());
if (view != null && mFlingView != view && view.getScrollX() != 0) {
view.scrollTo(0, 0);
}
if (mFlingView.getChildCount() == 2) {
mMenuViewWidth = mFlingView.getChildAt(1).getWidth();
} else {
mMenuViewWidth = INVALID_CHILD_WIDTH;
}
}
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.computeCurrentVelocity(1000);
float xVelocity = mVelocityTracker.getXVelocity();
xVelocityValue = xVelocity;
float yVelocity = mVelocityTracker.getYVelocity();
Log.d(TAG, "onInterceptTouchEvent: xVelocity:"+xVelocity);
Log.d(TAG, "onInterceptTouchEvent: yVelocity:"+yVelocity);
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);
if (mVelocityTracker.getXVelocity() < -SNAP_VELOCITY) {
mIsShowSlideView = true;
mScroller.startScroll(scrollX, 0, mMenuViewWidth - scrollX, 0, Math.abs(mMenuViewWidth - scrollX));
} else if (mVelocityTracker.getXVelocity() >= SNAP_VELOCITY) {
mIsShowSlideView = false;
mScroller.startScroll(scrollX, 0, -scrollX, 0, Math.abs(scrollX));
} else if (scrollX >= mMenuViewWidth / 2) {
mIsShowSlideView = true;
mScroller.startScroll(scrollX, 0, mMenuViewWidth - scrollX, 0, Math.abs(mMenuViewWidth - scrollX));
} else {
mIsShowSlideView = false;
mScroller.startScroll(scrollX, 0, -scrollX, 0, Math.abs(scrollX));
}
invalidate();
}
mMenuViewWidth = INVALID_CHILD_WIDTH;
mIsSlide = false;
mPosition = INVALID_POSITION;
releaseVelocity();
break;
}
return true;
} else {
closeMenu();
releaseVelocity();
}
return super.onTouchEvent(e);
}
private void releaseVelocity() {
if (mVelocityTracker != null) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
private void obtainVelocity(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
public int pointToPosition(int x, int y) {
int firstPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
Rect frame = mTouchFrame;
if (frame == null) {
mTouchFrame = new Rect();
frame = mTouchFrame;
}
final int count = getChildCount();
for (int i = count - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child.getVisibility() == View.VISIBLE) {
child.getHitRect(frame);
if (frame.contains(x, y)) {
return firstPosition + i;
}
}
}
return INVALID_POSITION;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
mFlingView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
public void closeMenu() {
mIsShowSlideView = false;
if (mFlingView != null && mFlingView.getScrollX() != 0) {
mFlingView.scrollTo(0, 0);
}
}
public boolean isShowSlideView() {
return mIsShowSlideView;
}
}
3. Activity.java
package com.example.sliderecyle.view;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.example.sliderecyle.R;
import com.example.sliderecyle.adapter.RedEnvelopeAdapter;
import com.example.sliderecyle.adapter.RedEnvelopeAdapter1;
import java.util.ArrayList;
import java.util.List;
public class SelectRedEnvelopeActivity extends AppCompatActivity implements View.OnClickListener {
private SlideRecyclerView mRvOrder;
private SlideRecyclerView mRvOrder1;
private Button btnAdd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_red_envelope);
initView();
initData();
initListener();
}
private void initView() {
View rootView = findViewById(R.id.root_view);
setStatusBarHeight(rootView);
mRvOrder = findViewById(R.id.rvRedEnvelope);
btnAdd = findViewById(R.id.btn_add);
}
private void initData() {
List<String> list = new ArrayList<>();
for (int i = 0; i < 15; i++) {
list.add("早晨推荐" + i);
}
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
RedEnvelopeAdapter mAdapter = new RedEnvelopeAdapter(this, list);
mRvOrder.setLayoutManager(layoutManager);
mRvOrder.setAdapter(mAdapter);
mAdapter.setItemClickListener(new RedEnvelopeAdapter.MyItemClickListener() {
@Override
public void onItemClick(int position) {
Log.d("SGF", "isShowSlideView: " + mRvOrder.isShowSlideView());
if (mRvOrder.isShowSlideView()) {
mRvOrder.closeMenu();
} else {
mRvOrder.computeScroll();
Toast.makeText(SelectRedEnvelopeActivity.this,"准备跳转",Toast.LENGTH_SHORT).show();
}
}
});
mAdapter.setOnDeleteClickListener(new RedEnvelopeAdapter.OnDeleteClickListener() {
@Override
public void onDeleteClick(View view, int position) {
mRvOrder.closeMenu();
mAdapter.topIconData.remove(position);
mAdapter.notifyItemRemoved(position);
mAdapter.notifyItemRangeChanged(position, mAdapter.topIconData.size()-position);
Toast.makeText(SelectRedEnvelopeActivity.this,"删除了" + (position + 1),Toast.LENGTH_SHORT).show();
}
});
mAdapter.setOnHideClickListener(new RedEnvelopeAdapter.OnHideClickListener() {
@Override
public void onHideClick(View view, int position) {
mRvOrder.closeMenu();
mAdapter.topIconData.remove(position);
mAdapter.notifyItemRemoved(position);
mAdapter.notifyItemRangeChanged(position, mAdapter.topIconData.size()-position);
}
});
btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAdapter.topIconData.add("新添加的item");
mAdapter.notifyItemInserted(mAdapter.topIconData.size() - 1);
mRvOrder.scrollToPosition(mAdapter.topIconData.size() - 1);
}
});
}
private void initListener() {
}
public static int tv_go_follow = 1000006;
@Override
public void onClick(View view) {
}
private void setStatusBarHeight(View rootView) {
}
}
4. 子项文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/ll_view"
android:background="@color/white"
android:orientation="horizontal">
<RelativeLayout
android:visibility="visible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingBottom="12dp"
android:paddingEnd="16dp"
android:paddingStart="16dp"
android:paddingTop="12dp">
<TextView
android:id="@+id/tv_quantity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginLeft="2dp"
android:text="星期三"
android:layout_marginStart="2dp"
android:textColor="#FF34393F"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_item_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Android X"
android:layout_toLeftOf="@id/tv_quantity"
android:layout_toStartOf="@id/tv_quantity"
android:textColor="#FF34393F"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_item_desc"
android:layout_marginTop="8dp"
android:text="杭州"
android:layout_toLeftOf="@+id/tv_volume"
android:layout_toStartOf="@+id/tv_volume"
android:textColor="#FFA3A5A8"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_volume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/tv_detail"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:text="2023-08-09"
android:layout_marginLeft="2dp"
android:layout_marginStart="2dp"
android:textColor="#FFA3A5A8"
android:textSize="12sp" />
</RelativeLayout>
<LinearLayout
android:layout_width="160dp"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_hide"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="@color/blue_500"
android:gravity="center"
android:text="隐藏"
android:textColor="#FFFFFFFF"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="#FFF55030"
android:gravity="center"
android:text="删除"
android:textColor="#FFFFFFFF"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
5. 布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1">
<TextView
android:id="@+id/tv_selected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="32dp"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:text="可选择一张"
android:textColor="@color/gray_999"
android:textSize="12sp" />
<com.example.sliderecyle.view.SlideRecyclerView
android:id="@+id/rvRedEnvelope"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:paddingLeft="32dp"
android:paddingRight="32dp"
android:paddingBottom="16dp" />
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/btn_add"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:background="@drawable/ic_launcher_background"
android:text="添加"
android:textColor="@color/white"
android:textSize="16sp" />
</RelativeLayout>