Android仿微信對(duì)話列表滑動(dòng)刪除效果
微信對(duì)話列表滑動(dòng)刪除效果很不錯(cuò)的,借鑒了github上SwipeListView(項(xiàng)目地址:https://github.com/likebamboo/SwipeListView),在其上進(jìn)行了一些重構(gòu),最終實(shí)現(xiàn)了微信對(duì)話列表滑動(dòng)刪除效果。
實(shí)現(xiàn)原理
1.通過ListView的pointToPosition(int x, int y)來獲取按下的position,然后通過android.view.ViewGroup.getChildAt(position)來得到滑動(dòng)對(duì)象swipeView
2.在onTouchEvent中計(jì)算要滑動(dòng)的距離,調(diào)用swipeView.scrollTo即可。
運(yùn)行效果如下
下面是最核心的部分SwipeListView代碼:
package com.fxsky.swipelist.widget; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.ListView; import com.fxsky.swipelist.R; public class SwipeListView extends ListView { private Boolean mIsHorizontal; private View mPreItemView; private View mCurrentItemView; private float mFirstX; private float mFirstY; private int mRightViewWidth; // private boolean mIsInAnimation = false; private final int mDuration = 100; private final int mDurationStep = 10; private boolean mIsShown; public SwipeListView(Context context) { this(context,null); } public SwipeListView(Context context, AttributeSet attrs) { this(context, attrs,0); } public SwipeListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.swipelistviewstyle); //獲取自定義屬性和默認(rèn)值 mRightViewWidth = (int) mTypedArray.getDimension(R.styleable.swipelistviewstyle_right_width, 200); mTypedArray.recycle(); } /** * return true, deliver to listView. return false, deliver to child. if * move, return true */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { float lastX = ev.getX(); float lastY = ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mIsHorizontal = null; System.out.println("onInterceptTouchEvent----->ACTION_DOWN"); mFirstX = lastX; mFirstY = lastY; int motionPosition = pointToPosition((int)mFirstX, (int)mFirstY); if (motionPosition >= 0) { View currentItemView = getChildAt(motionPosition - getFirstVisiblePosition()); mPreItemView = mCurrentItemView; mCurrentItemView = currentItemView; } break; case MotionEvent.ACTION_MOVE: float dx = lastX - mFirstX; float dy = lastY - mFirstY; if (Math.abs(dx) >= 5 && Math.abs(dy) >= 5) { return true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: System.out.println("onInterceptTouchEvent----->ACTION_UP"); if (mIsShown && (mPreItemView != mCurrentItemView || isHitCurItemLeft(lastX))) { System.out.println("1---> hiddenRight"); /** * 情況一: * <p> * 一個(gè)Item的右邊布局已經(jīng)顯示, * <p> * 這時(shí)候點(diǎn)擊任意一個(gè)item, 那么那個(gè)右邊布局顯示的item隱藏其右邊布局 */ hiddenRight(mPreItemView); } break; } return super.onInterceptTouchEvent(ev); } private boolean isHitCurItemLeft(float x) { return x < getWidth() - mRightViewWidth; } /** * @param dx * @param dy * @return judge if can judge scroll direction */ private boolean judgeScrollDirection(float dx, float dy) { boolean canJudge = true; if (Math.abs(dx) > 30 && Math.abs(dx) > 2 * Math.abs(dy)) { mIsHorizontal = true; System.out.println("mIsHorizontal---->" + mIsHorizontal); } else if (Math.abs(dy) > 30 && Math.abs(dy) > 2 * Math.abs(dx)) { mIsHorizontal = false; System.out.println("mIsHorizontal---->" + mIsHorizontal); } else { canJudge = false; } return canJudge; } /** * return false, can't move any direction. return true, cant't move * vertical, can move horizontal. return super.onTouchEvent(ev), can move * both. */ @Override public boolean onTouchEvent(MotionEvent ev) { float lastX = ev.getX(); float lastY = ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("---->ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: float dx = lastX - mFirstX; float dy = lastY - mFirstY; // confirm is scroll direction if (mIsHorizontal == null) { if (!judgeScrollDirection(dx, dy)) { break; } } if (mIsHorizontal) { if (mIsShown && mPreItemView != mCurrentItemView) { System.out.println("2---> hiddenRight"); /** * 情況二: * <p> * 一個(gè)Item的右邊布局已經(jīng)顯示, * <p> * 這時(shí)候左右滑動(dòng)另外一個(gè)item,那個(gè)右邊布局顯示的item隱藏其右邊布局 * <p> * 向左滑動(dòng)只觸發(fā)該情況,向右滑動(dòng)還會(huì)觸發(fā)情況五 */ hiddenRight(mPreItemView); } if (mIsShown && mPreItemView == mCurrentItemView) { dx = dx - mRightViewWidth; System.out.println("======dx " + dx); } // can't move beyond boundary if (dx < 0 && dx > -mRightViewWidth) { mCurrentItemView.scrollTo((int)(-dx), 0); } return true; } else { if (mIsShown) { System.out.println("3---> hiddenRight"); /** * 情況三: * <p> * 一個(gè)Item的右邊布局已經(jīng)顯示, * <p> * 這時(shí)候上下滾動(dòng)ListView,那么那個(gè)右邊布局顯示的item隱藏其右邊布局 */ hiddenRight(mPreItemView); } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: System.out.println("============ACTION_UP"); clearPressedState(); if (mIsShown) { System.out.println("4---> hiddenRight"); /** * 情況四: * <p> * 一個(gè)Item的右邊布局已經(jīng)顯示, * <p> * 這時(shí)候左右滑動(dòng)當(dāng)前一個(gè)item,那個(gè)右邊布局顯示的item隱藏其右邊布局 */ hiddenRight(mPreItemView); } if (mIsHorizontal != null && mIsHorizontal) { if (mFirstX - lastX > mRightViewWidth / 2) { showRight(mCurrentItemView); } else { System.out.println("5---> hiddenRight"); /** * 情況五: * <p> * 向右滑動(dòng)一個(gè)item,且滑動(dòng)的距離超過了右邊View的寬度的一半,隱藏之。 */ hiddenRight(mCurrentItemView); } return true; } break; } return super.onTouchEvent(ev); } private void clearPressedState() { // TODO current item is still has background, issue mCurrentItemView.setPressed(false); setPressed(false); refreshDrawableState(); // invalidate(); } private void showRight(View view) { System.out.println("=========showRight"); Message msg = new MoveHandler().obtainMessage(); msg.obj = view; msg.arg1 = view.getScrollX(); msg.arg2 = mRightViewWidth; msg.sendToTarget(); mIsShown = true; } private void hiddenRight(View view) { System.out.println("=========hiddenRight"); if (mCurrentItemView == null) { return; } Message msg = new MoveHandler().obtainMessage();// msg.obj = view; msg.arg1 = view.getScrollX(); msg.arg2 = 0; msg.sendToTarget(); mIsShown = false; } /** * show or hide right layout animation */ @SuppressLint("HandlerLeak") class MoveHandler extends Handler { int stepX = 0; int fromX; int toX; View view; private boolean mIsInAnimation = false; private void animatioOver() { mIsInAnimation = false; stepX = 0; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (stepX == 0) { if (mIsInAnimation) { return; } mIsInAnimation = true; view = (View)msg.obj; fromX = msg.arg1; toX = msg.arg2; stepX = (int)((toX - fromX) * mDurationStep * 1.0 / mDuration); if (stepX < 0 && stepX > -1) { stepX = -1; } else if (stepX > 0 && stepX < 1) { stepX = 1; } if (Math.abs(toX - fromX) < 10) { view.scrollTo(toX, 0); animatioOver(); return; } } fromX += stepX; boolean isLastStep = (stepX > 0 && fromX > toX) || (stepX < 0 && fromX < toX); if (isLastStep) { fromX = toX; } view.scrollTo(fromX, 0); invalidate(); if (!isLastStep) { this.sendEmptyMessageDelayed(0, mDurationStep); } else { animatioOver(); } } } public int getRightViewWidth() { return mRightViewWidth; } public void setRightViewWidth(int mRightViewWidth) { this.mRightViewWidth = mRightViewWidth; } }
Demo下載地址:http://xiazai.jb51.net/201608/yuanma/SwipeListView(jb51.net).rar
Demo中SwipeAdapter源碼中有一處由于粗心寫錯(cuò)了,會(huì)導(dǎo)致向下滑動(dòng)時(shí)出現(xiàn)數(shù)組越界異常,現(xiàn)更正如下:
@Override public int getCount() { // return 100; return data.size(); }
本文已被整理到了《Android微信開發(fā)教程匯總》,歡迎大家學(xué)習(xí)閱讀。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android實(shí)現(xiàn)單頁(yè)面浮層可拖動(dòng)view的示例代碼
本篇文章主要介紹了Android實(shí)現(xiàn)單頁(yè)面浮層可拖動(dòng)view的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10簡(jiǎn)單實(shí)現(xiàn)Android鬧鐘功能
這篇文章主要教大家如何簡(jiǎn)單實(shí)現(xiàn)Android鬧鐘功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Android camera2 判斷相機(jī)功能是否可控的實(shí)例
下面小編就為大家?guī)硪黄狝ndroid camera2 判斷相機(jī)功能是否可控的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03RecyclerView實(shí)現(xiàn)列表倒計(jì)時(shí)
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)列表倒計(jì)時(shí),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09Android自定義流式布局的實(shí)現(xiàn)示例
這篇文章主要介紹了Android自定義流式布局的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Android組件Glide實(shí)現(xiàn)圖片平滑滾動(dòng)效果
這篇文章主要介紹了Android組件Glide實(shí)現(xiàn)圖片平滑滾動(dòng)效果的相關(guān)資料,具有一定的參考價(jià)值,需要的朋友可以參考下2016-07-07