Android ListView侧滑item,仿QQ删除效果
生活随笔
收集整理的這篇文章主要介紹了
Android ListView侧滑item,仿QQ删除效果
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
尊重原創,轉載請注明(http://blog.csdn.net/aoshiwenrou/article/details/42971193)
最近的項目需求有一條是要實現仿QQ的側滑刪除效果,網上搜到了很多,但是與預想的都不太一樣,于是自己研究了一下,寫了一個Demo,記錄下來。
功能:
1.實現了仿QQ的Item側滑效果
2.可根據item的長度計算側滑范圍
3.實現item條目點擊監聽與刪除按鈕監聽
4.解決了刪除按鈕出現時ListView滑動錯位問題(偷懶:滑動時item復位)
主要代碼:
SlidePauseListView:
package com.jcking.slidepauselistview.view;import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.ListView; import android.widget.Scroller;public class SlidePauseListView extends ListView {/*** 當前滑動的ListView position*/private int slidePosition;/*** 手指按下X的坐標*/private int downY;/*** 手指按下Y的坐標*/private int downX;/*** 屏幕寬度*/private int screenWidth;/*** ListView的item*/private View itemView;/*** 滑動類*/private Scroller scroller;/*** 滑動速度邊界值*/private static final int SNAP_VELOCITY = 600;/*** 速度追蹤對象*/private VelocityTracker velocityTracker;/*** 是否響應滑動,默認為不響應*/private boolean isSlide = false;/*** 認為是用戶滑動的最小距離*/private int mTouchSlop;/*** 最多可滑動的范圍 */private int mTouchLimit;/*** 是否已經側滑*/private boolean hasSlided = false;public SlidePauseListView(Context context) {this(context, null);}public SlidePauseListView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SlidePauseListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);DisplayMetrics dm = new DisplayMetrics();((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(dm);screenWidth = dm.widthPixels;scroller = new Scroller(context);mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();mTouchLimit = screenWidth / 4;}/*** 設置可滑動的最大值* @param limit*/public void setTouchLimit(int limit){this.mTouchLimit = limit;}/*** 獲取可滑動的最大值* @return*/public int getTouchLimit(){return this.mTouchLimit;}/*** 退回原位*/public void slideBack(){if(hasSlided && itemView != null){scrollXBy(itemView, -mTouchLimit);hasSlided = false;}}/*** 分發事件,主要做的是判斷點擊的是那個item, 以及通過postDelayed來設置響應左右滑動事件*/@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: {addVelocityTracker(event);// 假如scroller滾動還沒有結束,我們直接返回if (!scroller.isFinished()) {return super.dispatchTouchEvent(event);}// 如果已經側滑了,我們直接返回if(hasSlided){return super.dispatchTouchEvent(event);}downX = (int) event.getX();downY = (int) event.getY();slidePosition = pointToPosition(downX, downY);// 無效的position, 不做任何處理if (slidePosition == AdapterView.INVALID_POSITION) {return super.dispatchTouchEvent(event);}// 獲取我們點擊的item viewitemView = getChildAt(slidePosition - getFirstVisiblePosition());if(itemView instanceof SlideListItem){SlideListItem item = (SlideListItem) itemView;int count = item.getChildCount();mTouchLimit = 0;for (int i = 0; i < count; i++) {if(i > 0){mTouchLimit += item.getChildAt(i).getMeasuredWidth();}}}break;}case MotionEvent.ACTION_MOVE: { // Log.d("test", "downX : " + downX + ", event.getX() : " + event.getX() + ", mTouchSlop : " + mTouchSlop);if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY|| (downX - event.getX() > mTouchSlop && Math.abs(event.getY() - downY) < mTouchSlop)) {isSlide = true;}break;}case MotionEvent.ACTION_UP:recycleVelocityTracker();break;}return super.dispatchTouchEvent(event);}/*** 處理我們拖動ListView item的邏輯*/@Overridepublic boolean onTouchEvent(MotionEvent ev) {Log.d("test", "hasSlided : " + hasSlided + ", isSlide : " + isSlide);// 如果已經側滑了,回歸原位if(hasSlided){scrollXBy(itemView, -mTouchLimit);hasSlided = false;// 攔截事件,不繼續處理return false;}if (isSlide && slidePosition != AdapterView.INVALID_POSITION) {requestDisallowInterceptTouchEvent(true);addVelocityTracker(ev);final int action = ev.getAction();int x = (int) ev.getX();switch (action) {case MotionEvent.ACTION_DOWN:break;case MotionEvent.ACTION_MOVE:MotionEvent cancelEvent = MotionEvent.obtain(ev);cancelEvent.setAction(MotionEvent.ACTION_CANCEL |(ev.getActionIndex()<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));onTouchEvent(cancelEvent);int deltaX = downX - x;downX = x;// 手指拖動itemView滾動, deltaX大于0向左滾動,小于0向右滾scrollXBy(itemView, deltaX);return true; //拖動的時候ListView不滾動case MotionEvent.ACTION_UP:int velocityX = getScrollVelocity();if (velocityX < -SNAP_VELOCITY) {scrollLeft();} else {scrollByDistanceX();}recycleVelocityTracker();// 手指離開的時候就不響應左右滾動isSlide = false;break;}}// boolean result = super.onTouchEvent(ev); // Log.d("test", "result : " + result);//否則直接交給ListView來處理onTouchEvent事件return super.onTouchEvent(ev);}/*** 讓指定的view滾動x位置,設置左右邊界,view最多滾動到邊界位置* @param view* @param x*/private void scrollXBy(View view, int x){// 如果已經滑動了最大值,并希望繼續向左滑,忽略if(view.getScrollX() >= mTouchLimit && x >= 0) return;// 如果已經回到原位,并希望繼續想右劃,忽略if(view.getScrollX() <= 0 && x <= 0) return;if(view.getScrollX() + x > mTouchLimit){x = mTouchLimit - view.getScrollX();}else if(view.getScrollX() + x < 0){x = -view.getScrollX();}view.scrollBy(x, 0);}@Overridepublic void computeScroll() {// 調用startScroll的時候scroller.computeScrollOffset()返回true,if (scroller.computeScrollOffset()) {// 讓ListView item根據當前的滾動偏移量進行滾動itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());postInvalidate();}}/*** 向左滑動,根據上面我們知道向左滑動為正值*/private void scrollLeft() {final int delta = (mTouchLimit - itemView.getScrollX());// 調用startScroll方法來設置一些滾動的參數,我們在computeScroll()方法中調用scrollTo來滾動itemscroller.startScroll(itemView.getScrollX(), 0, delta, 0,Math.abs(delta));postInvalidate(); // 刷新itemViewhasSlided = true;}/*** 根據手指滾動itemView的距離來判斷是滾動到開始位置還是向左或者向右滾動*/private void scrollByDistanceX() {// 如果向左滾動的距離大于屏幕的二分之一,就讓其刪除if (itemView.getScrollX() >= mTouchLimit / 2) {scrollLeft();} else {// 滾回到原始位置,為了偷下懶這里是直接調用scrollTo滾動itemView.scrollTo(0, 0);}}/*** 添加用戶的速度跟蹤器* * @param event*/private void addVelocityTracker(MotionEvent event) {if (velocityTracker == null) {velocityTracker = VelocityTracker.obtain();}velocityTracker.addMovement(event);}/*** 移除用戶速度跟蹤器*/private void recycleVelocityTracker() {if (velocityTracker != null) {velocityTracker.recycle();velocityTracker = null;}}/*** 獲取X方向的滑動速度,大于0向右滑動,反之向左* * @return*/private int getScrollVelocity() {velocityTracker.computeCurrentVelocity(1000);int velocity = (int) velocityTracker.getXVelocity();return velocity;}} 繼承ListView,實現主要功能, dispatchTouchEvent進行事件分發,由自身的 onTouchEvent方法處理側滑事件。其中
if(itemView instanceof SlideListItem){SlideListItem item = (SlideListItem) itemView;int count = item.getChildCount();mTouchLimit = 0;for (int i = 0; i < count; i++) {if(i > 0){mTouchLimit += item.getChildAt(i).getMeasuredWidth();}}}通過計算item的長度,設定側滑的最大偏移量。ScrollXBy方法,防止側滑越界。
/*** 讓指定的view滾動x位置,設置左右邊界,view最多滾動到邊界位置* @param view* @param x*/private void scrollXBy(View view, int x){// 如果已經滑動了最大值,并希望繼續向左滑,忽略if(view.getScrollX() >= mTouchLimit && x >= 0) return;// 如果已經回到原位,并希望繼續想右劃,忽略if(view.getScrollX() <= 0 && x <= 0) return;if(view.getScrollX() + x > mTouchLimit){x = mTouchLimit - view.getScrollX();}else if(view.getScrollX() + x < 0){x = -view.getScrollX();}view.scrollBy(x, 0);}
SlideListItem:繼承LinearLayout,主要通過重寫onLayout方法使其中的子View不壓縮變形。
package com.jcking.slidepauselistview.view;import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout;public class SlideListItem extends LinearLayout {public SlideListItem(Context context) {this(context, null);}public SlideListItem(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int count = getChildCount();for (int i = 0; i < count; i++) {measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);}}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {Log.d("test", "event : " + event);return super.dispatchTouchEvent(event);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int margeLeft = 0;int size = getChildCount();for (int i = 0; i < size; i++) {View view = getChildAt(i);if (view.getVisibility() != View.GONE) {int childWidth = view.getMeasuredWidth();// 將內部子孩子橫排排列view.layout(margeLeft, 0, margeLeft + childWidth,view.getMeasuredHeight());margeLeft += childWidth;}}} }
SlideAdapter:在Adapter中綁定Click事件,分別監聽。
package com.jcking.slidepauselistview;import java.util.List;import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.TextView;public class SlideAdapter extends BaseAdapter {private List<String> mData;private LayoutInflater mInflater;private ViewHolder mHolder;private OnSlideClickListener mListener;class ViewHolder{TextView tv;View front;Button btnClock;Button btnDelete;}public SlideAdapter(Context context, List<String> data, OnSlideClickListener listener){this.mData = data;this.mInflater = LayoutInflater.from(context);this.mListener = listener;}@Overridepublic int getCount() {return mData == null ? 0 : mData.size();}@Overridepublic String getItem(int position) {return mData == null ? null : mData.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {if(convertView == null){convertView = mInflater.inflate(R.layout.item_listview, null);mHolder = new ViewHolder();mHolder.tv = (TextView) convertView.findViewById(R.id.tv);mHolder.front = convertView.findViewById(R.id.front);mHolder.btnClock = (Button) convertView.findViewById(R.id.btnClock);mHolder.btnDelete = (Button) convertView.findViewById(R.id.btnDelete);convertView.setTag(mHolder);}else{mHolder = (ViewHolder) convertView.getTag();}mHolder.tv.setText(getItem(position));// 使隱藏的按鈕數量不一樣,測試可滑動范圍mHolder.btnClock.setVisibility(position%2==0 ? View.GONE : View.VISIBLE);if(mListener != null){final int pos = position;final View item = convertView;mHolder.btnClock.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mListener.onClockClick(pos, item);}});mHolder.btnDelete.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mListener.onDeleteClick(pos, item);}});mHolder.front.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mListener.onItemClick(pos, item);}});}return convertView;}public interface OnSlideClickListener{public void onItemClick(int position, View item);public void onClockClick(int position, View item);public void onDeleteClick(int position, View item);} }
MainActivity:簡單調用
package com.jcking.slidepauselistview;import java.util.ArrayList; import java.util.List;import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Toast;import com.jcking.slidepauselistview.SlideAdapter.OnSlideClickListener; import com.jcking.slidepauselistview.view.SlidePauseListView;public class MainActivity extends Activity implements OnSlideClickListener {private SlidePauseListView slideCutListView;private SlideAdapter adapter;private List<String> dataSourceList = new ArrayList<String>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();}private void init() {slideCutListView = (SlidePauseListView) findViewById(R.id.slideCutListView);for (int i = 0; i < 20; i++) {dataSourceList.add("滑動刪除" + i);}adapter = new SlideAdapter(this, dataSourceList, this);slideCutListView.setAdapter(adapter);}@Overridepublic void onItemClick(int position, View item) {// TODO Auto-generated method stubToast.makeText(this, "點擊條目 " + position, Toast.LENGTH_SHORT).show();}@Overridepublic void onClockClick(int position, View item) {// TODO Auto-generated method stubToast.makeText(this, "點擊鬧鐘 " + position, Toast.LENGTH_SHORT) .show();}@Overridepublic void onDeleteClick(int position, View item) {// TODO Auto-generated method stubToast.makeText(this, "點擊刪除 " + position, Toast.LENGTH_SHORT) .show();slideCutListView.slideBack();dataSourceList.remove(position);adapter.notifyDataSetChanged();}}
activity_main:主視圖布局
<?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="match_parent"android:orientation="vertical" ><com.jcking.slidepauselistview.view.SlidePauseListViewandroid:id="@+id/slideCutListView"android:layout_width="match_parent"android:layout_height="match_parent"android:cacheColorHint="@android:color/transparent"android:divider="#2b2b2b"android:dividerHeight="0.5dp"android:listSelector="@android:color/transparent" ></com.jcking.slidepauselistview.view.SlidePauseListView></LinearLayout>
item_listview:item布局
<?xml version="1.0" encoding="UTF-8"?> <com.jcking.slidepauselistview.view.SlideListItem xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#acacac"android:orientation="horizontal" ><!-- 這里一定要設置android:layout_width屬性為充滿屏幕 --><LinearLayoutandroid:id="@+id/front"android:layout_width="match_parent"android:layout_height="wrap_content"android:clickable="true"android:gravity="center_vertical"android:orientation="horizontal"android:padding="5dp" ><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="測試數據"android:textSize="18sp" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal" ><Buttonandroid:id="@+id/btnClock"android:layout_width="wrap_content"android:layout_height="match_parent"android:background="#ff0000"android:text="鬧鈴"android:textColor="#ffffff" /><Buttonandroid:id="@+id/btnDelete"android:layout_width="wrap_content"android:layout_height="match_parent"android:background="#00ffff"android:text="刪除"android:textColor="#ffffff" /></LinearLayout></com.jcking.slidepauselistview.view.SlideListItem>
提示:
1.item布局中,第一個一定要充滿屏幕
問題:
1.靈敏度不高,不能像QQ一樣完全精確識別手勢
參考:http://blog.csdn.net/xiaanming/article/details/17539199
? ? ? ? ??http://blog.csdn.net/jwzhangjie/article/details/39006007??
點擊此處,下載源碼
總結
以上是生活随笔為你收集整理的Android ListView侧滑item,仿QQ删除效果的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 使用Scroller实现
- 下一篇: android Animator详解