阅读之前,请喝口水,慢慢看,主要看加黑加粗字体
如果心静,15分看懂,30分实现,反之遥遥无期
这篇博客是借鉴的yanzhenjie高级猿的代码,同时是在同事的代码中进行抽离出来的,代码可以直接copy,记得不要漏下代码
Effect (如果我们不仅仅需要删除功能,我们可以在item样式里自己随便添加的!):
准备工作-build导入:
compile'com.yanzhenjie:recyclerview-swipe:1.0.0'
ItemSlideHelper(这是一个监听手势的help-可直接Copy,无需改变) :
package com.example.dow.recyclerdelete;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
/**
* 帮助显示左滑菜单
*/
public class ItemSlideHelper implements RecyclerView.OnItemTouchListener, GestureDetector.OnGestureListener {
private static final String TAG = "ItemSwipeHelper";
public boolean isUpdate= false;
private final int DEFAULT_DURATION = 200;
private View mTargetView;
private int mActivePointerId;
private int mTouchSlop;
private int mMaxVelocity;
private int mMinVelocity;
private int mLastX;
private int mLastY;
private boolean mIsDragging;
private Animator mExpandAndCollapseAnim;
private GestureDetectorCompat mGestureDetector;
private Callback mCallback;
public ItemSlideHelper(Context context, Callback callback) {
this.mCallback = callback;
//手势用于处理fling
mGestureDetector = new GestureDetectorCompat(context, this);
ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
mMaxVelocity = configuration.getScaledMaximumFlingVelocity();
mMinVelocity = configuration.getScaledMinimumFlingVelocity();
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
Log.d(TAG, "onInterceptTouchEvent: " + e.getAction());
int action = MotionEventCompat.getActionMasked(e);
int x = (int) e.getX();
int y = (int) e.getY();
//如果RecyclerView滚动状态不是空闲targetView不是空
if(rv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE){
if(mTargetView != null){
//隐藏已经打开
smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2);
mTargetView = null;
}
return false;
}
//如果正在运行动画 ,直接拦截什么都不做
if(mExpandAndCollapseAnim != null && mExpandAndCollapseAnim.isRunning()){
return true;
}
boolean needIntercept = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = MotionEventCompat.getPointerId(e, 0);
mLastX = (int) e.getX();
mLastY = (int) e.getY();
/*
* 如果之前有一个已经打开的项目,当此次点击事件没有发生在右侧的菜单中则返回TRUE,
* 如果点击的是右侧菜单那么返回FALSE这样做的原因是因为菜单需要响应Onclick
* */
if(mTargetView != null){
return !inView(x, y);
}
//查找需要显示菜单的view;
mTargetView = mCallback.findTargetView(x, y);
break;
case MotionEvent.ACTION_MOVE:
int deltaX = (x - mLastX);
int deltaY = (y - mLastY);
if(Math.abs(deltaY) > Math.abs(deltaX))
return false;
//如果移动距离达到要求,则拦截
needIntercept = mIsDragging = mTargetView != null && Math.abs(deltaX) >= mTouchSlop;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
/*
* 走这是因为没有发生过拦截事件
* */
if(isExpanded()){
if (inView(x, y)) {
// 如果走这那行这个ACTION_UP的事件会发生在右侧的菜单中
Log.d(TAG, "click item");
isUpdate= true;
}else{
//拦截事件,防止targetView执行onClick事件
needIntercept = true;
}
//折叠菜单
smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2);
}
mTargetView = null;
break;
}
return needIntercept ;
}
private boolean isExpanded() {
return mTargetView != null && mTargetView.getScrollX() == getHorizontalRange();
}
private boolean isCollapsed() {
return mTargetView != null && mTargetView.getScrollX() == 0;
}
/*
* 根据targetView的scrollX计算出targetView的偏移,这样能够知道这个point
* 是在右侧的菜单中
* */
private boolean inView(int x, int y) {
if (mTargetView == null)
return false;
int scrollX = mTargetView.getScrollX();
int left = mTargetView.getWidth() - scrollX;
int top = mTargetView.getTop();
int right = left + getHorizontalRange() ;
int bottom = mTargetView.getBottom();
Rect rect = new Rect(left, top, right, bottom);
return rect.contains(x, y);
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
Log.d(TAG, "onTouchEvent: " + e.getAction());
if(mExpandAndCollapseAnim != null && mExpandAndCollapseAnim.isRunning() || mTargetView == null)
return;
//如果要响应fling事件设置将mIsDragging设为false
if (mGestureDetector.onTouchEvent(e)) {
mIsDragging = false;
return;
}
int x = (int) e.getX();
int y = (int) e.getY();
int action = MotionEventCompat.getActionMasked(e);
switch (action) {
case MotionEvent.ACTION_DOWN:
//RecyclerView 不会转发这个Down事件
break;
case MotionEvent.ACTION_MOVE:
int deltaX = (int) (mLastX - e.getX());
if(mIsDragging) {
horizontalDrag(deltaX);
}
mLastX = x;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if(mIsDragging){
if(!smoothHorizontalExpandOrCollapse(0) && isCollapsed())
mTargetView = null;
mIsDragging = false;
}
break;
}
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
/**
*
* 根据touch事件来滚动View的scrollX
*
* @param delta
*/
private void horizontalDrag(int delta) {
int scrollX = mTargetView.getScrollX();
int scrollY = mTargetView.getScrollY();
if ((scrollX + delta) <= 0) {
mTargetView.scrollTo(0, scrollY);
return;
}
int horRange = getHorizontalRange();
scrollX += delta;
if (Math.abs(scrollX) < horRange) {
mTargetView.scrollTo(scrollX, scrollY);
} else {
mTargetView.scrollTo(horRange, scrollY);
}
}
/**
* 根据当前scrollX的位置判断是展开还是折叠
*
* @param velocityX
* 如果不等于0那么这是一次fling事件,否则是一次ACTION_UP或者ACTION_CANCEL
*/
private boolean smoothHorizontalExpandOrCollapse(float velocityX) {
int scrollX = mTargetView.getScrollX();
int scrollRange = getHorizontalRange();
if (mExpandAndCollapseAnim != null)
return false;
int to = 0;
int duration = DEFAULT_DURATION;
if (velocityX == 0) {
//如果已经展一半,平滑展开
if (scrollX > scrollRange / 2) {
to = scrollRange;
}
} else {
if (velocityX > 0)
to = 0;
else
to = scrollRange;
duration = (int) ((1.f - Math.abs(velocityX) / mMaxVelocity) * DEFAULT_DURATION);
}
if(to == scrollX)
return false;
mExpandAndCollapseAnim = ObjectAnimator.ofInt(mTargetView, "scrollX", to);
mExpandAndCollapseAnim.setDuration(duration);
mExpandAndCollapseAnim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
mExpandAndCollapseAnim = null;
if(isUpdate) {
mCallback.updateItem();
}
isUpdate= false;
if (isCollapsed()) {
mTargetView = null;
}
Log.d(TAG, "onAnimationEnd");
}
@Override
public void onAnimationCancel(Animator animation) {
//onAnimationEnd(animation);
mExpandAndCollapseAnim = null;
Log.d(TAG, "onAnimationCancel");
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
mExpandAndCollapseAnim.start();
return true;
}
public int getHorizontalRange( ) {
RecyclerView.ViewHolder viewHolder = mCallback.getChildViewHolder(mTargetView);
return mCallback.getHorizontalRange(viewHolder);
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if(Math.abs(velocityX) > mMinVelocity && Math.abs(velocityX) < mMaxVelocity) {
if(!smoothHorizontalExpandOrCollapse(velocityX) ) {
if(isCollapsed())
mTargetView = null;
return true;
}
}
return false;
}
public interface Callback {
int getHorizontalRange(RecyclerView.ViewHolder holder);
RecyclerView.ViewHolder getChildViewHolder(View childView);
View findTargetView(float x, float y);
void updateItem();
}
}
DividerItemDecoration(添加的间隔线条-可直接Copy,无需改变):
package com.example.dow.recyclerdelete;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
MyViewHolder(item我们复用的layout-这里我们需要根据实际情况设置对应的item的id) :
package com.example.dow.recyclerdelete;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
/**
* ViewHolder复用的layout id
*/
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView textView,del;
public MyViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.tv_text);
del = (TextView) itemView.findViewById(R.id.tv_delete);
}
}
**RecyclerAdapter(这个要想实现自己的效果,必须修改! ) :
注意:
-其一:onCreateViewHolder中item的layout
-其二:onBindViewHolder中的数据设置
代码如下:
package com.example.dow.recyclerdelete;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.util.List;
/**
*
*/
public class RecyclerAdapter extends RecyclerView.Adapter implements ItemSlideHelper.Callback {
private List mData;
private Context mContext;
private RecyclerView mRecyclerView;
public RecyclerAdapter(List mData, Context context) {
this.mData = mData;
this.mContext = context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gx_list_item, parent, false);
MyViewHolder holder = new MyViewHolder(view);
setListener(parent, holder, viewType);
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
MyViewHolder viewHolder = (MyViewHolder) holder;
viewHolder.textView.setText(mData.get(position).toString());
viewHolder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "textView-onClick->>>" + mData.get(position).toString(), Toast.LENGTH_LONG).show();
}
});
/*viewHolder.del.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mData.remove(position);
RecyclerAdapter.this.remove(position);
//RecyclerAdapter.this.notifyDataSetChanged();
//Toast.makeText(mContext, "del-onClick->>>" + mData.get(position).toString(), Toast.LENGTH_LONG).show();
}
});*/
}
@Override
public int getItemCount() {
return mData.size();
}
public void remove(int pos) {
this.notifyItemRemoved(pos);
}
protected void setListener(final ViewGroup parent, final MyViewHolder viewHolder, int viewType) {
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
int position = viewHolder.getAdapterPosition();
mOnItemClickListener.onItemClick(v, viewHolder, mData.get(position), position);
}
}
});
viewHolder.del.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
int position = viewHolder.getAdapterPosition();
mOnItemClickListener.delView(v, viewHolder, mData.get(position), position);
}
}
});
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnItemClickListener != null) {
int position = viewHolder.getAdapterPosition();
return mOnItemClickListener.onItemLongClick(v, viewHolder, mData.get(position), position);
}
return false;
}
});
}
public OnItemClickListener mOnItemClickListener;
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
mRecyclerView.addOnItemTouchListener(new ItemSlideHelper(mRecyclerView.getContext(), this));
}
@Override
public int getHorizontalRange(RecyclerView.ViewHolder holder) {
if(holder.itemView instanceof LinearLayout){
ViewGroup viewGroup = (ViewGroup) holder.itemView;
if(viewGroup.getChildCount() == 2){
return viewGroup.getChildAt(1).getLayoutParams().width;
}
}
return 0;
}
@Override
public RecyclerView.ViewHolder getChildViewHolder(View childView) {
return mRecyclerView.getChildViewHolder(childView);
}
@Override
public View findTargetView(float x, float y) {
return mRecyclerView.findChildViewUnder(x, y);
}
@Override
public void updateItem() {
Log.e("666666","7777777777777");
this.notifyDataSetChanged();
}
public interface OnItemClickListener {
void onItemClick(View view, RecyclerView.ViewHolder holder, T o, int position);
boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, T o, int position);
void delView(View view, RecyclerView.ViewHolder holder, T o, int position);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.mOnItemClickListener = onItemClickListener;
}
}
MainActivity(这里必须要修改,因为我们有自己的item样式,还有对应的Bean-其中动画用的是默认动画,间隔线条用的是DividerItemDecoration) :
package com.example.dow.recyclerdelete;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerAdapter adapter;
private RecyclerView recyclerView;
private List list = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.id_rv);
initData();
adapter = new RecyclerAdapter(list, this);
// recyclerView.setLayoutManager(new GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false));
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
adapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, RecyclerView.ViewHolder holder, Object o, int position) {
Toast.makeText(MainActivity.this, "onItemClick-->>>"+list.get(position), Toast.LENGTH_LONG).show();
}
@Override
public boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, Object o, int position) {
Toast.makeText(MainActivity.this, "onItemLongClick-->>>"+list.get(position), Toast.LENGTH_LONG).show();
return true;//!!!!!!!!!!!!
}
@Override
public void delView(View view, RecyclerView.ViewHolder holder, Object o, int position) {
list.remove(position);
//adapter.remove(position);
//adapter.notifyDataSetChanged();
}
});
}
private void initData() {
for (int i = 1; i < 20; i++) {
list.add("第 " + i + " 项数据");
}
}
}
MainActivity Xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.dow.recyclerdelete.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/id_rv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
RelativeLayout>
gx_list_item Xml(以后写自己的item):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="50dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
>
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
RelativeLayout>
<LinearLayout
android:layout_width="60dp"
android:orientation="horizontal"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_delete"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="删除"
android:background="@android:color/holo_red_light"
android:gravity="center"
android:textColor="@android:color/white"
/>
LinearLayout>
LinearLayout>