文/程序员男神
前言
今天这个天气就像星期一上班的情绪一样,阴沉沉的。最讨厌下雨天,也许,文艺男屌丝都喜欢有阳光的日子吧!一整天都在搞这个需求,一天除了调试接口,就是在弄它。赶紧记录一下,让寒冷的心情有点温暖。
概述
先上需求图:Recycleview左滑删除的需求。
实现的功能细节:
- 滑动时删除按钮显示在item底下,非后方平移出现
- 内容项和删除按钮都可以点击
- 当有滑动菜单显示时,点击任意项关闭滑动菜单
- 当有滑动菜单显示时,滑动别的项关闭之前的滑动菜单
- 删除的视觉动画效果(偷懒直接用了默认的)
实现步骤
首先是引用:
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.android.support:recyclerview-v7:25.0.1'
自定义删除按钮的实现,自定义View继承水平滚动条,参考别人的代码,学以致用。
代码如下:
/**
* desc: 侧滑按钮自定义
* author: dj
* date: 2017/3/10 10:01
*/
public class SlidingButtonView extends HorizontalScrollView {
private TextView mTextView_Delete;
private int mScrollWidth;
private Boolean isOpen = false;
private Boolean once = false;
public SlidingButtonView(Context context) {
this(context, null);
}
public SlidingButtonView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setOverScrollMode(OVER_SCROLL_NEVER);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!once) {
mTextView_Delete = (TextView) findViewById(R.id.tv_delete);
once = true;
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
this.scrollTo(0, 0);
//获取水平滚动条可以滑动的范围,即右侧按钮的宽度
mScrollWidth = mTextView_Delete.getWidth();
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mIonSlidingButtonListener.onDownOrMove(this);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
changeScrollx();
return true;
default:
break;
}
return super.onTouchEvent(ev);
}
/**
* 按滚动条被拖动距离判断关闭或打开菜单
*/
public void changeScrollx() {
if (getScrollX() >= (mScrollWidth / 2)) {
this.smoothScrollTo(mScrollWidth, 0);
isOpen = true;
mIonSlidingButtonListener.onMenuIsOpen(this);
} else {
this.smoothScrollTo(0, 0);
isOpen = false;
}
}
/**
* 打开菜单
*/
public void openMenu() {
if (isOpen) {
return;
}
this.smoothScrollTo(mScrollWidth, 0);
isOpen = true;
mIonSlidingButtonListener.onMenuIsOpen(this);
}
/**
* 关闭菜单
*/
public void closeMenu() {
if (!isOpen) {
return;
}
this.smoothScrollTo(0, 0);
isOpen = false;
}
private IonSlidingButtonListener mIonSlidingButtonListener;
public void setSlidingButtonListener(IonSlidingButtonListener listener) {
this.mIonSlidingButtonListener = listener;
}
public interface IonSlidingButtonListener {
void onMenuIsOpen(View view);
void onDownOrMove(SlidingButtonView slidingButtonView);
}
}
接下来我们看看activity的代码:
/**
* desc: Recycle的简单使用
* author: dj
* date: 2017/3/2 9:57
*/
public class RecycleActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
private RecyclerView mRecyclerView;
private RecycleAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private List list;
private SwipeRefreshLayout mSwipeRefreshLayout;
private static final int REFRESH_STATUS = 0;
private int j = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycle);
initDate();
initView();
}
private void initDate() {
list = new ArrayList();
for (int i = 0; i < 20; i++) {
list.add("西红柿炒鸡蛋");
}
}
private void initView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recycle_view);
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdapter = new RecycleAdapter(RecycleActivity.this, list);
mRecyclerView.setAdapter(mAdapter);
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright, android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light);
mSwipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
}
});
onRefresh();
mAdapter.setOnSlidingViewClickListener(new SwipeAdapter.IonSlidingViewClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(RecycleActivity.this, "单击" + position, Toast.LENGTH_SHORT).show();
}
@Override
public void onDeleteBtnClick(View view, int position) {
Toast.makeText(RecycleActivity.this, "删除" + position, Toast.LENGTH_SHORT).show();
mAdapter.removeItem(position);
}
});
}
@Override
public void onRefresh() {
refreshHandler.sendEmptyMessageDelayed(REFRESH_STATUS, 2000);
}
private Handler refreshHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case REFRESH_STATUS:
//下拉刷新执行的操作,刷新数据
mSwipeRefreshLayout.setRefreshing(false);
j++;
list.add("测试" + j);
mAdapter.updateData(list);
break;
}
}
};
}
其对应的activity_recycle.xml文件:
接下来就是最重要的adapter的写法了,代码如下:
/**
* desc: RecycleView的Adapter
* author: dj
* data: 2017/2/28 15:20
*/
public class RecycleAdapter extends RecyclerView.Adapter implements SlidingButtonView.IonSlidingButtonListener {
private Context mContext;
private List data;
private SlidingButtonView mMenu = null;
//构造器,接受数据集
public RecycleAdapter(Context context, List data) {
mContext = context;
this.data = data;
//mIDeleteBtnClickListener = (IonSlidingViewClickListener) context;
}
public void updateData(List data) {
this.data = data;
notifyDataSetChanged();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
//将数据填充到具体的view中
holder.icon.setImageResource(R.drawable.jian);
holder.title.setText(data.get(position));
holder.desc.setText(data.get(position));
//设置内容布局的宽为屏幕宽度
DisplayMetrics metrics = new DisplayMetrics();
((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(metrics);
int width = metrics.widthPixels;
holder.layout_content.getLayoutParams().width = width;
if (mIDeleteBtnClickListener != null){
holder.layout_content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//判断是否有删除菜单打开
if (menuIsOpen()) {
closeMenu();//关闭菜单
} else {
int n = holder.getLayoutPosition();
mIDeleteBtnClickListener.onItemClick(v, n);
}
}
});
holder.btn_Delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int n = holder.getLayoutPosition();
mIDeleteBtnClickListener.onDeleteBtnClick(v, n);
}
});
}
}
@Override
public int getItemCount() {
return data.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
public ImageView icon;
public TextView title;
public TextView desc;
public TextView btn_Delete;
public ViewGroup layout_content;
public ViewHolder(View itemView) {
super(itemView);
//由于itemView是item的布局文件,我们需要的是里面的textView,因此利用itemView.findViewById获
//取里面的textView实例,后面通过onBindViewHolder方法能直接填充数据到每一个textView了
icon = (ImageView) itemView.findViewById(R.id.item_image);
title = (TextView) itemView.findViewById(R.id.item_title);
desc = (TextView) itemView.findViewById(R.id.item_desc);
btn_Delete = (TextView) itemView.findViewById(R.id.tv_delete);
layout_content = (ViewGroup) itemView.findViewById(R.id.layout_content);
((SlidingButtonView) itemView).setSlidingButtonListener(RecycleAdapter.this);
}
}
/**
* 删除菜单打开信息接收
*/
@Override
public void onMenuIsOpen(View view) {
mMenu = (SlidingButtonView) view;
}
/**
* 滑动或者点击了Item监听
*
* @param slidingButtonView
*/
@Override
public void onDownOrMove(SlidingButtonView slidingButtonView) {
if (menuIsOpen()) {
if (mMenu != slidingButtonView) {
closeMenu();
}
}
}
/**
* 关闭菜单
*/
public void closeMenu() {
mMenu.closeMenu();
mMenu = null;
}
/**
* 判断是否有菜单打开
*/
public Boolean menuIsOpen() {
if (mMenu != null) {
return true;
}
return false;
}
private SwipeAdapter.IonSlidingViewClickListener mIDeleteBtnClickListener;
public void setOnSlidingViewClickListener(SwipeAdapter.IonSlidingViewClickListener listener) {
this.mIDeleteBtnClickListener = listener;
}
public interface IonSlidingViewClickListener {
void onItemClick(View view, int position);
void onDeleteBtnClick(View view, int position);
}
/**
* 向指定位置添加元素
*/
public void addItem(int position, String value) {
if (position > data.size()) {
position = data.size();
}
if (position < 0) {
position = 0;
}
/**
* 使用notifyItemInserted/notifyItemRemoved会有动画效果
* 而使用notifyDataSetChanged()则没有
*/
data.add(position, value);//在集合中添加这条数据
notifyItemInserted(position);//通知插入了数据
}
/**
* 移除指定位置元素
*/
public String removeItem(int position) {
if (position > data.size() - 1) {
return null;
}
String value = data.remove(position);//所以还需要手动在集合中删除一次
notifyItemRemoved(position);//通知删除了数据,但是没有删除list集合中的数据
return value;
}
}
其那个的item_list.xml文件:
虽然只是一些代码的堆叠,但是它带给我的思想是很好的,希望大家看博客的时候不只是看到他的功能适不适合你,其实学习的更多是它们的思想。
码代码不易,转载须注明出处,违者必究!
注意事项:对item监听的回调要注意,是对谁监听?否则监听无效,现在bug已改。
参考文档:http://blog.csdn.net/tyzlmjj/article/details/47060817