之前,自己使用listview一直是长按删除,不过发现qq的消息和ios的都是侧滑删除,觉得效果很好,于是自己就想做一个侧滑删除。在网上找了些资料,有很多不是我理想的侧滑删除,最后还是找到了一个不错的,现在记录一下。
首先是自定义一个listview,这个里面需要重写onInterceptTouchEvent和onTouchEvent两个代码,一个是对滑动进行拦截,如果删除按钮已经显示,点击的不是删除的item,则把按钮隐藏。另一个是对item滑动的事件进行处理。滑动时item移动,显示删除按钮。
package com.zviewtech.videonetclient.mydeleteemo; import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.MotionEvent; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.LinearLayout; import android.widget.ListView; /** * Created by JunkChen on 2016/3/29 0029. */ public class SideslipListView extends ListView { private static final String TAG = "SideslipListView"; private int mScreenWidth;//屏幕的宽度 private boolean isDeleteShow;//删除组件是否显示 private ViewGroup mPointChild;//手指按下位置的item组件 private int mDeleteWidth;//删除组件的宽度 private LinearLayout.LayoutParams mItemLayoutParams;//手指按下时所在的item的布局参数 private int mDownX;//手指初次按下的X坐标 private int mDownY;//手指初次按下的Y坐标 private int mPointPosition;//手指按下位置所在的item位置 private boolean isAllowItemClick;//是否允许item点击 public SideslipListView(Context context) { super(context); init(context); } public SideslipListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public SideslipListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { // 获取屏幕宽度 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(dm); mScreenWidth = dm.widthPixels; Log.i(TAG, "***********mScreenWidth: " + mScreenWidth); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) {//事件拦截 switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { isAllowItemClick = true; //侧滑删除 mDownX = (int) ev.getX(); mDownY = (int) ev.getY(); mPointPosition = pointToPosition(mDownX, mDownY); Log.i(TAG, "*******pointToPosition(mDownX, mDownY): " + mPointPosition); if (mPointPosition != -1) { if (isDeleteShow) { ViewGroup tmpViewGroup = (ViewGroup) getChildAt(mPointPosition - getFirstVisiblePosition()); if (!mPointChild.equals(tmpViewGroup)) { turnNormal(); } } //获取当前的item mPointChild = (ViewGroup) getChildAt(mPointPosition - getFirstVisiblePosition()); mDeleteWidth = mPointChild.getChildAt(1).getLayoutParams().width; mItemLayoutParams = (LinearLayout.LayoutParams) mPointChild.getChildAt(0).getLayoutParams(); Log.i(TAG, "*********mItemLayoutParams.height: " + mItemLayoutParams.height + ", mDeleteWidth: " + mDeleteWidth); mItemLayoutParams.width = mScreenWidth; mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams); } break; } case MotionEvent.ACTION_MOVE: { int nowX = (int) ev.getX(); int nowY = (int) ev.getY(); int diffX = nowX - mDownX; Log.i(TAG, "******dp2px(4): " + dp2px(8) + ", dp2px(8): " + dp2px(8) + ", density: " + getContext().getResources().getDisplayMetrics().density); if (Math.abs(diffX) > dp2px(4) || Math.abs(nowY - mDownY) > dp2px(4)) { return true;//避免子布局中有点击的控件时滑动无效 } break; } } return super.onInterceptTouchEvent(ev); } public float dp2px(int dp) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics()); } @Override public boolean onTouchEvent(MotionEvent ev) {//事件响应 switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: performActionDown(ev); break; case MotionEvent.ACTION_MOVE: performActionMove(ev); break; case MotionEvent.ACTION_UP: performActionUp(ev); break; } return super.onTouchEvent(ev); } private void performActionDown(MotionEvent ev) { mDownX = (int) ev.getX(); mDownY = (int) ev.getY(); if (isDeleteShow) { ViewGroup tmpViewGroup = (ViewGroup) getChildAt(pointToPosition(mDownX, mDownY) - getFirstVisiblePosition()); Log.i(TAG, "*********mPointChild.equals(tmpViewGroup): " + mPointChild.equals(tmpViewGroup)); if (!mPointChild.equals(tmpViewGroup)) { turnNormal(); } } //获取当前的item mPointChild = (ViewGroup) getChildAt(pointToPosition(mDownX, mDownY) - getFirstVisiblePosition()); mDeleteWidth = mPointChild.getChildAt(1).getLayoutParams().width;//获取删除组件的宽度 Log.i(TAG, "**********pointToPosition(x,y): " + pointToPosition(mDownX, mDownY) + ", getFirstVisiblePosition() = " + getFirstVisiblePosition() + ", mDeleteWidth = " + mDeleteWidth); mItemLayoutParams = (LinearLayout.LayoutParams) mPointChild.getChildAt(0).getLayoutParams(); mItemLayoutParams.width = mScreenWidth; mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams); } private boolean performActionMove(MotionEvent ev) { int nowX = (int) ev.getX(); int nowY = (int) ev.getY(); int diffX = nowX - mDownX; if (Math.abs(diffX) > Math.abs(nowY - mDownY) && Math.abs(nowY - mDownY) < 20) { if (!isDeleteShow && nowX < mDownX) {//删除按钮未显示时向左滑 if (-diffX >= mDeleteWidth) {//如果滑动距离大于删除组件的宽度时进行偏移的最大处理 diffX = -mDeleteWidth; } mItemLayoutParams.leftMargin = diffX; mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams); isAllowItemClick = false; } else if (isDeleteShow && nowX > mDownX) {//删除按钮显示时向右滑 if (diffX >= mDeleteWidth) { diffX = mDeleteWidth; } mItemLayoutParams.leftMargin = diffX - mDeleteWidth; mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams); isAllowItemClick = false; } return true; } return super.onTouchEvent(ev); } private void performActionUp(MotionEvent ev) { //如果向左滑出超过隐藏的二分之一就全部显示 if (-mItemLayoutParams.leftMargin >= mDeleteWidth / 2) { mItemLayoutParams.leftMargin = -mDeleteWidth; isDeleteShow = true; mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams); } else { turnNormal(); } } /** * 转换为正常隐藏情况 */ public void turnNormal() { mItemLayoutParams.leftMargin = 0; mPointChild.getChildAt(0).setLayoutParams(mItemLayoutParams); isDeleteShow = false; } /** * 是否允许Item点击 * * @return */ public boolean isAllowItemClick() { return isAllowItemClick; } }
这个listview主要是设置leftMargin这个左偏移来保证按钮的显示和隐藏的。activity里调用的代码也很简单
package com.zviewtech.videonetclient.mydeleteemo; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private SideslipListView mSideslipListView; /** * 初始化数据 */ private ArrayList<String> mDataList = new ArrayList<String>() { { for (int i = 0; i < 50; i++) { add("ListView item " + i); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSideslipListView = (SideslipListView) findViewById(R.id.sideslipListView); mSideslipListView.setAdapter(new CustomAdapter());//设置适配器 //设置item点击事件 mSideslipListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (mSideslipListView.isAllowItemClick()) { Log.i(TAG, mDataList.get(position) + "被点击了"); Toast.makeText(MainActivity.this, mDataList.get(position) + "被点击了", Toast.LENGTH_SHORT).show(); } } }); //设置item长按事件 mSideslipListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { if (mSideslipListView.isAllowItemClick()) { Log.i(TAG, mDataList.get(position) + "被长按了"); Toast.makeText(MainActivity.this, mDataList.get(position) + "被长按了", Toast.LENGTH_SHORT).show(); return true;//返回true表示本次事件被消耗了,若返回 } return false; } }); } /** * 自定义ListView适配器 */ class CustomAdapter extends BaseAdapter { @Override public int getCount() { return mDataList.size(); } @Override public Object getItem(int position) { return mDataList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (null == convertView) { convertView = View.inflate(MainActivity.this, R.layout.item, null); viewHolder = new ViewHolder(); viewHolder.textView = (TextView) convertView.findViewById(R.id.textView); viewHolder.txtv_delete = (TextView) convertView.findViewById(R.id.txtv_delete); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.textView.setText(mDataList.get(position)); final int pos = position; viewHolder.txtv_delete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, mDataList.get(pos) + "被删除了", Toast.LENGTH_SHORT).show(); mDataList.remove(pos); notifyDataSetChanged(); mSideslipListView.turnNormal(); } }); return convertView; } } class ViewHolder { public TextView textView; public TextView txtv_delete; } }
其实和普通的listview是一致的,只是在点击时判断滑动变量,如果为true,则不响应点击事件。如果为false,则响应点击事件。
listview的item的布居我要显示出来,因为这和自定义的listview的代码相关,必须是这样的,如果修改,则mDeleteWidth = mPointChild.getChildAt(1).getLayoutParams().width;这里也必须做相应的修改,getChild()的参数和删除按钮的位置一致
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="8dp"> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="48dp" android:textSize="18sp" android:text="text"> </TextView> </LinearLayout> <!--必须指定宽度--> <TextView android:id="@+id/txtv_delete" android:layout_width="96dp" android:layout_height="match_parent" android:textSize="18sp" android:text="删除" android:textColor="#eeeeee" android:gravity="center" android:background="#e53935"> </TextView> </LinearLayout>
listview的滑动删除就完成了。
就这么简单。