都知道QQ有一個(gè)比較牛逼的效果就是測(cè)拉刪除效果,目前這個(gè)功能,網(wǎng)上自定義控件也有很多實(shí)現(xiàn)方式了,本篇也自己實(shí)現(xiàn)一個(gè)測(cè)拉刪除效果的自定義控件。雖然功能一樣,實(shí)現(xiàn)方式不同罷了,也希望提供一些思路,對(duì)自己和讀者有些幫助~
由于QQ測(cè)拉功能強(qiáng)大,手寫(xiě)文字耗費(fèi)時(shí)間,就做個(gè)低配置版的測(cè)拉效果。廢話不多講,還是乖乖搞事情吧~
1、實(shí)現(xiàn)測(cè)拉刪除的真整體布局:
對(duì)于自定義View的布局:
<?xml version="1.0" encoding="utf-8"?><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" tools:context="com.itydl.a07sweepview.MainActivity"> <com.itydl.a07sweepview.SweepView android:id="@+id/sv" android:layout_width="match_parent" android:layout_height="65dp" > <!--左側(cè)內(nèi)容區(qū)域--> <include layout="@layout/content"/> <!--右側(cè)刪除區(qū)域--> <include layout="@layout/delete"/> </com.itydl.a07sweepview.SweepView></RelativeLayout>通過(guò)include的方式引入布局。這兩個(gè)布局分別表示內(nèi)容區(qū)域,和左側(cè)刪除區(qū)域。代碼如下:content.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="80dp"> <TextView android:gravity="center" android:textColor="#ffffff" android:background="#88000000" android:textSize="25sp" android:layout_width="match_parent" android:layout_height="match_parent" android:text="測(cè)試數(shù)據(jù)"/></LinearLayout>delete.xml:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="150dp" android:layout_height="65dp"> <TextView android:gravity="center" android:textColor="#ffffff" android:background="#ff0000" android:textSize="25sp" android:layout_width="match_parent" android:layout_height="match_parent" android:text="刪除"/></LinearLayout>自定義View的代碼:
public class SweepView extends ViewGroup { PRivate View mContentView; private View mDeleteView; private int mDeleteWidth; public SweepView(Context context) { this(context, null); } public SweepView(Context context, AttributeSet attrs) { super(context, attrs); } //xml文件加載完成 @Override protected void onFinishInflate() { //一般用于拿到孩子對(duì)象 mContentView = getChildAt(0); mDeleteView = getChildAt(1); //拿到DeleteView的params對(duì)象 LayoutParams params = mDeleteView.getLayoutParams(); mDeleteWidth = params.width; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //測(cè)量孩子 mContentView.measure(widthMeasureSpec, heightMeasureSpec); int measureSpecWidth = MeasureSpec.makeMeasureSpec(mDeleteWidth, MeasureSpec.EXACTLY); mDeleteView.measure(measureSpecWidth, heightMeasureSpec); int widthMeasureSpecSelf = MeasureSpec.getSize(widthMeasureSpec); int heightMeasureSpecSelf = MeasureSpec.getSize(heightMeasureSpec); //設(shè)置自定義View的大小 setMeasuredDimension(widthMeasureSpecSelf, heightMeasureSpecSelf); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //給孩子布局 int contentWidth = mContentView.getMeasuredWidth(); int contentHeight = mContentView.getMeasuredHeight(); mContentView.layout(0, 0, contentWidth, contentHeight); int deleteWidth = mDeleteView.getMeasuredWidth(); int deleteHeight = mDeleteView.getMeasuredWidth(); mDeleteView.layout(contentWidth, 0, contentWidth + deleteWidth, deleteHeight); }}上面進(jìn)行l(wèi)ayout和measue相信已經(jīng)簡(jiǎn)單到跟寫(xiě)button代碼一樣easy了,沒(méi)啥好說(shuō)的。運(yùn)行程序:
知識(shí)簡(jiǎn)單的布局,點(diǎn)擊并沒(méi)辦法滑動(dòng)。接下來(lái)實(shí)現(xiàn)滑動(dòng)效果:
這里滑動(dòng)采用v4包里面的工具類:ViewDragHelper
2、ViewDragHelper在本項(xiàng)目中的使用:
1)、創(chuàng)建實(shí)例
public SweepView(Context context, AttributeSet attrs) { super(context, attrs); mDragHelper = ViewDragHelper.create(this,new MyCallBack());}2、touch事件委托給ViewDragHelper離開(kāi)監(jiān)聽(tīng)處理,在它內(nèi)部把觸摸事件封裝的很好了。@Overridepublic boolean onTouchEvent(MotionEvent event) { mDragHelper.processTouchEvent(event); return true;}3、實(shí)現(xiàn)自己的callback:我們?cè)趯?shí)例化ViewDragHelper的時(shí)候, 這里參數(shù)1就代表自定義的View,傳入this即可。著重說(shuō)一下callBack,我們通過(guò)創(chuàng)建內(nèi)部類方式,創(chuàng)建MyCallBack類,并重寫(xiě)里面的回調(diào)方法:
class MyCallBack extends ViewDragHelper.Callback{ //是否分析(返回true才會(huì)有效)view的touch;參數(shù)1:觸摸的view;2:touch的id。 @Override public boolean tryCaptureView(View child, int pointerId) { // 去分析child。表示我分析ContentView和DeleteView的topuch事件 System.out.println(child == mContentView); return child == mContentView || child == mDeleteView; } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { System.out.println(left); return left; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { }}這里一共重寫(xiě)了四個(gè)回調(diào)方法。我們一一解釋都代表什么意思,以及每個(gè)方法功能和參數(shù)意義。1)、tryCaptureView(View child, int pointerId)在發(fā)生touch事件的down事件的時(shí)候回調(diào)
代表我是否分析(返回true才會(huì)有效)view的touch事件;參數(shù)1:當(dāng)前觸摸的view;2:touch的id。如果這個(gè)方法返回值為false,表示已不對(duì)觸摸的view進(jìn)行分析。則表示我ViewDragHelper不支持滑動(dòng)效果了。后期的方法也都無(wú)效。
在里面打印了一行l(wèi)og,我們手滑內(nèi)容區(qū)域,發(fā)現(xiàn)此時(shí)已經(jīng)能夠滑動(dòng)了:
首先,可以實(shí)現(xiàn)滑動(dòng)效果,再此時(shí)看log日志:
02-05 08:36:02.445 4596-4596/com.itydl.a07sweepview I/System.out: true發(fā)現(xiàn)返回值為true、因此后續(xù)的操作才得以實(shí)現(xiàn)。
2)clampViewPositionHorizontal(View child, int left, int dx):水平移動(dòng)的回調(diào),發(fā)生touch時(shí)間move時(shí)回調(diào)。
當(dāng)touch移動(dòng)后的回調(diào) 參數(shù)1:分析的是哪個(gè)孩子組件移動(dòng)了;參數(shù)2:左上角的坐標(biāo),child的左側(cè)的邊距,控件移動(dòng)到左邊什么位置,值會(huì)根據(jù)移動(dòng)變化;參數(shù)3:增量的x(記錄相對(duì)上一次的變化量dx>0右滑,dx<0左滑)。這里的值是預(yù)期的值,可以在這里做邊距的監(jiān)測(cè),做越界處理。該方法的返回值表示:// 確定要移動(dòng)多少,移動(dòng)到什么位置去 return left;。【這個(gè)方法里面經(jīng)常換主角,touch到哪個(gè)view這里的child就是哪個(gè)view】
這么多理論知識(shí),估計(jì)一時(shí)明白也夠嗆,別著急,相信往下繼續(xù)學(xué)習(xí)會(huì)非常清楚的。
在這里我也做了一行打印:
02-05 08:46:59.090 4596-4596/com.itydl.a07sweepview I/System.out: 34-----3402-05 08:46:59.107 4596-4596/com.itydl.a07sweepview I/System.out: 66-----3202-05 08:46:59.125 4596-4596/com.itydl.a07sweepview I/System.out: 99-----3302-05 08:46:59.142 4596-4596/com.itydl.a07sweepview I/System.out: 146-----4702-05 08:46:59.159 4596-4596/com.itydl.a07sweepview I/System.out: 177-----3102-05 08:46:59.175 4596-4596/com.itydl.a07sweepview I/System.out: 203-----2602-05 08:46:59.200 4596-4596/com.itydl.a07sweepview I/System.out: 227-----24
通過(guò)log可以更加清晰的了解參數(shù)的具體意義,那個(gè)dx值是一個(gè)變化量。必須我最初left=0,下一次=10,第三次=60.那么dx分別為:10,50
除了clampViewPositionHorizontal當(dāng)然還有clampViewPositionHorizontal,會(huì)一種相信另一種也是信手拈來(lái)。
3)、onViewPositionChanged(View changedView, int left, int top, int dx, int dy)當(dāng)【控件位置】移動(dòng)時(shí)的回調(diào)
參數(shù)意義:// @changedView: 哪個(gè)view移動(dòng)了 // @left,top:view移動(dòng)后的左上角的坐標(biāo) // @dx,dy: 移動(dòng)的增量
這個(gè)方法跟上邊clampViewPositionHorizontal差不多,都是移動(dòng)回調(diào),如果說(shuō)clampViewPositionHorizontal用于處理越界以及確定位置的話,那么onViewPositionChanged一般用于移動(dòng)view的布局重置。后續(xù)代碼可以看到兩者的各自責(zé)任,以及實(shí)現(xiàn)什么功能。
4)、onViewReleased(View releasedChild, float xvel, float yvel)松開(kāi)收時(shí)候的回調(diào)up事件的回調(diào)。
@releasedChild:松開(kāi)了哪個(gè)view; @xvel,yvel:速率。該方法一般用于“松手回彈”效果的操作,即松開(kāi)手,自定義控件往哪個(gè)位置回彈。上一篇自定義ViewPage可以看到回彈效果,那里是使用Scrollor實(shí)現(xiàn)的,而在ViewDragHelper有它的手段,稍后會(huì)用到。
3、滑動(dòng)刪除滑動(dòng)的實(shí)現(xiàn)。
了解了上述幾個(gè)方法,我們就要在這幾個(gè)方法里面做一些操作了,比如先實(shí)現(xiàn)滑動(dòng)效果。不僅僅點(diǎn)擊contentview區(qū)域可滑動(dòng),點(diǎn)擊deleteview區(qū)域也可以實(shí)現(xiàn)控件的整體滑動(dòng)效果。
代碼如下:
@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { int contentWidth = mContentView.getMeasuredWidth(); int contentHeight = mContentView.getMeasuredHeight(); int deleteWidth = mDeleteView.getMeasuredWidth(); int deleteHeight = mDeleteView.getMeasuredWidth(); if (changedView == mContentView) { mDeleteView.layout(contentWidth + left, 0, contentWidth + deleteWidth + left, deleteHeight); } else if (changedView == mDeleteView) { mContentView.layout(left-contentWidth,0,left,contentHeight); }}就像前面介紹所說(shuō)的在onViewPositionChanged方法中根據(jù)move事件,對(duì)xml可以做重新布局操作。上面代碼的值都是一些很簡(jiǎn)單的小算法,相信看起來(lái)還是蠻簡(jiǎn)單的。當(dāng)我們滑動(dòng)灰色內(nèi)容區(qū)域,此時(shí)的changeView就是灰色內(nèi)容區(qū)域,當(dāng)滑動(dòng)刪除位置,此時(shí)的changeView就代表了紅色區(qū)域。
4、滑動(dòng)刪除邊界的處理,解決越界問(wèn)題。
@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) { Log.e("YDL", dx + ""); if (child == mContentView) { if (left < 0 && left < -mDeleteView.getMeasuredWidth()) {//左滑 return -mDeleteView.getMeasuredWidth(); } else if (left > 0) {//右滑 return 0; } } else if (child == mDeleteView) { if (left > mContentView.getMeasuredWidth()) { return mContentView.getMeasuredWidth(); }else if(left < mContentView.getMeasuredWidth() - mDeleteView.getMeasuredWidth()){ return mContentView.getMeasuredWidth() - mDeleteView.getMeasuredWidth(); } } return left;}就像前面介紹所說(shuō)的在clampViewPositionHorizontal方法中根據(jù)move事件,根據(jù)滑動(dòng)不同的子View,來(lái)確定邊界值不越界。運(yùn)行程序:
此時(shí),實(shí)現(xiàn)了滑動(dòng)效果,并解決了越界問(wèn)題。
5、實(shí)現(xiàn)滑動(dòng)“回彈”效果
此時(shí)核心的邏輯已經(jīng)實(shí)現(xiàn)了,接下來(lái)就是處理一些細(xì)節(jié)
@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) { // up時(shí)的回調(diào) // @releasedChild:松開(kāi)了哪個(gè)view // @xvel,yvel:速率 int left = mContentView.getLeft(); int contentWidth = mContentView.getMeasuredWidth(); int contentHeight = mContentView.getMeasuredHeight(); int deleteWidth = mDeleteView.getMeasuredWidth(); int deleteHeight = mDeleteView.getMeasuredWidth(); if(-left < mDeleteView.getMeasuredWidth()/2){ mContentView.layout(0, 0, contentWidth, contentHeight); mDeleteView.layout(contentWidth, 0, contentWidth + deleteWidth, deleteHeight); }else{ mContentView.layout(-deleteWidth, 0, contentWidth - deleteWidth, contentHeight); mDeleteView.layout(contentWidth - deleteWidth, 0, contentWidth, deleteHeight); }}UP事件后,通過(guò)判斷ContentView左側(cè)坐標(biāo)位置,重新確定了兩個(gè)孩子組件的位置。運(yùn)行科自行調(diào)試,恢復(fù)布局很生硬,因此加入緩慢恢復(fù)功能。修改上述代碼:
@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) { // up時(shí)的回調(diào) // @releasedChild:松開(kāi)了哪個(gè)view // @xvel,yvel:速率 int left = mContentView.getLeft(); int contentWidth = mContentView.getMeasuredWidth(); int deleteWidth = mDeleteView.getMeasuredWidth(); if(-left < mDeleteView.getMeasuredWidth()/2){ mDragHelper.smoothSlideViewTo(mContentView,0,0); mDragHelper.smoothSlideViewTo(mDeleteView,contentWidth,0); }else{ mDragHelper.smoothSlideViewTo(mContentView,-deleteWidth,0); mDragHelper.smoothSlideViewTo(mDeleteView,contentWidth - deleteWidth,0); } //效果等同于invalidate()---->會(huì)調(diào)用computeScroll ViewCompat.postInvalidateOnAnimation(SweepView.this);}其中smoothSlideViewTo已經(jīng)把平滑恢復(fù)封裝的很好了。只需要傳入View、該最終左側(cè)坐標(biāo)、最終top坐標(biāo)即可。這里必須進(jìn)行invalidate();刷新,使用了ViewCompat.postInvalidateOnAnimation(SweepView.this);代替,這個(gè)api可以兼容更低的版本。然而這里只是委托作用,真正的平滑移動(dòng)效果在移動(dòng)回調(diào)方法computeScroll()中。重寫(xiě)之:
@Overridepublic void computeScroll() { if(mDragHelper.continueSettling(true)){ //直接刷新即可 ViewCompat.postInvalidateOnAnimation(SweepView.this); }}在這里面,只需要簡(jiǎn)單刷新界面調(diào)用ViewCompat.postInvalidateOnAnimation(SweepView.this);即可。當(dāng)我們調(diào)用ViewCompat.postInvalidateOnAnimation(SweepView.this);后,內(nèi)部會(huì)調(diào)用computeScroll方法,在這里面隔一段距離并借助scrollTo()完成平滑移動(dòng)。運(yùn)行:
6、滑動(dòng)刪除的實(shí)現(xiàn):
在MainActivity中使用ListView,相信都會(huì)熟練使用。在定義Adapter適配器的時(shí)候,我們通過(guò)下面方式實(shí)現(xiàn)ListView加載數(shù)據(jù),而且每個(gè)Item都加入是用自定義控件。
@Overridepublic View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = View.inflate(MainActivity.this, R.layout.item_list, null); holder.mTextView = (TextView) convertView.findViewById(R.id.tv_content); holder.mSweepView = (SweepView) convertView.findViewById(R.id.sv); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } String itemStr = (String) getItem(position); holder.mTextView.setText(itemStr); return convertView;}getView的item的布局:<?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.itydl.a07sweepview.SweepView android:id="@+id/sv" android:layout_width="match_parent" android:layout_height="65dp" > <!--左側(cè)內(nèi)容區(qū)域--> <include layout="@layout/content"/> <!--右側(cè)刪除區(qū)域--> <include layout="@layout/delete"/> </com.itydl.a07sweepview.SweepView></LinearLayout>運(yùn)行程序:
7、實(shí)現(xiàn)真正的側(cè)欄刪除,以及一些細(xì)節(jié)的處理。
首先添加可刪除事件,直接在getView方法里面設(shè)置item上的孩子組件的點(diǎn)擊事件即可。
//給listview的Item上添加點(diǎn)擊事件,點(diǎn)擊刪除該item條目holder.mTextDelet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mList.remove(position); notifyDataSetChanged(); }});運(yùn)行程序:
此時(shí)已經(jīng)可以完成刪除了,只不過(guò)我們發(fā)現(xiàn)刪除之后接著看下一條item不對(duì)勁,我沒(méi)有滑動(dòng)下一條item,怎么這么顯示呢?還有我們往下滑動(dòng)ListView,由于復(fù)用的原因,也會(huì)出現(xiàn)類似情況。
那么緊跟著解決細(xì)節(jié)問(wèn)題:
要解決問(wèn)題其實(shí)也挺簡(jiǎn)單,只需要再點(diǎn)擊item的時(shí)候,關(guān)閉掉所有打開(kāi)的item即可。那么,如何才能控制打開(kāi)與關(guān)閉呢?其實(shí)在前面的恢復(fù)布局就已經(jīng)隱含了這個(gè)功能。只需要把原來(lái)位置抽取一個(gè)方法就好了。
@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) { // up時(shí)的回調(diào) // @releasedChild:松開(kāi)了哪個(gè)view // @xvel,yvel:速率 int left = mContentView.getLeft(); int contentWidth = mContentView.getMeasuredWidth(); int deleteWidth = mDeleteView.getMeasuredWidth(); if(-left < mDeleteView.getMeasuredWidth()/2){ mDragHelper.smoothSlideViewTo(mContentView,0,0); mDragHelper.smoothSlideViewTo(mDeleteView,contentWidth,0); }else{ mDragHelper.smoothSlideViewTo(mContentView,-deleteWidth,0); mDragHelper.smoothSlideViewTo(mDeleteView,contentWidth - deleteWidth,0); } //效果等同于invalidate()---->會(huì)調(diào)用computeScroll ViewCompat.postInvalidateOnAnimation(SweepView.this);}修改為:@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) { // up時(shí)的回調(diào) // @releasedChild:松開(kāi)了哪個(gè)view // @xvel,yvel:速率 int left = mContentView.getLeft(); if(-left < mDeleteView.getMeasuredWidth()/2){ //還原item(關(guān)閉) close(); }else{ //(打開(kāi)) open(); }}打開(kāi)和關(guān)閉方法就要暴露方法出去:/** * 關(guān)閉item */public void close() { int contentWidth = mContentView.getMeasuredWidth(); if(mSweepChangeListener != null){ mSweepChangeListener.sweepChanged(SweepView.this,false); } mDragHelper.smoothSlideViewTo(mContentView,0,0); mDragHelper.smoothSlideViewTo(mDeleteView,contentWidth,0); //效果等同于invalidate()---->會(huì)調(diào)用computeScroll.這里必須有刷新才可以 ViewCompat.postInvalidateOnAnimation(SweepView.this);}/** * 打開(kāi)item */public void open() { int contentWidth = mContentView.getMeasuredWidth(); int deleteWidth = mDeleteView.getMeasuredWidth(); if(mSweepChangeListener != null){ mSweepChangeListener.sweepChanged(SweepView.this,true); } mDragHelper.smoothSlideViewTo(mContentView,-deleteWidth,0); mDragHelper.smoothSlideViewTo(mDeleteView,contentWidth - deleteWidth,0); //效果等同于invalidate()---->會(huì)調(diào)用computeScroll.這里必須有刷新才可以 ViewCompat.postInvalidateOnAnimation(SweepView.this);}為了判斷item的view是打開(kāi)還是關(guān)閉,因此在自定義View中暴露接口出去,并在每一次getView的時(shí)候通過(guò)接口會(huì)掉的方式告知當(dāng)前的item是打開(kāi)還是關(guān)閉的:public void setOnSweepChangeListener(OnSweepChangeListener listener){ this.mSweepChangeListener = listener;}public interface OnSweepChangeListener{ /** * item是自定義SweepView對(duì)象。每個(gè)ListView的item都是SweepView對(duì)象,且不同 * @param sweepView * @param isOpend */ void sweepChanged(SweepView sweepView,boolean isOpend);}對(duì)于接口對(duì)象調(diào)用接口方法,上邊的打開(kāi)和關(guān)閉方法中已經(jīng)很給出了調(diào)用。最后是在getView方法中作如下修改:
holder.mSweepView.setOnSweepChangeListener(new SweepView.OnSweepChangeListener() { @Override public void sweepChanged(SweepView sweepView, boolean isOpend) { if(isOpend){ //如果打開(kāi),將該item對(duì)應(yīng)的SweepView對(duì)象則保存至集合中 if(!mSweepViews.contains(sweepView)){ mSweepViews.add(sweepView); } }else{ mSweepViews.remove(sweepView); } }});當(dāng)加載每一個(gè) item的時(shí)候,每條item都監(jiān)聽(tīng)當(dāng)前Item的SweepView子布局是打開(kāi)還是關(guān)閉,如果打開(kāi),將該item對(duì)應(yīng)的SweepView對(duì)象則保存至集合中。 而當(dāng)點(diǎn)擊了刪除按鈕的時(shí)候,我們需要如下操作://給listview的Item上添加點(diǎn)擊事件,點(diǎn)擊刪除該item條目holder.mTextDelet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mList.remove(itemStr); closeAll(); notifyDataSetChanged(); }});private void closeAll() { //使用迭代器 /*ListIterator<SweepView> iterator = mSweepViews.listIterator(); while (iterator.hasNext()) { SweepView view = iterator.next(); view.close(); }*/ for (SweepView sweepView : mSweepViews) { sweepView.close(); }}此時(shí)實(shí)現(xiàn)了刪除,運(yùn)行程序:
可能你覺(jué)得已經(jīng)完成了,其實(shí),還存在一個(gè)嚴(yán)重的bug,當(dāng)我們往下滑動(dòng)的時(shí)候就可以看到了,以及我們滑動(dòng)多個(gè)item都能打開(kāi),顯然不符合QQ滑動(dòng)刪除效果。那么最后就解決這個(gè)bug:
解決思路:當(dāng)我們按下的時(shí)候,記錄按下滑動(dòng)打開(kāi)時(shí)候的SweepView的實(shí)例,這個(gè)實(shí)例用臨時(shí)變量保存起來(lái)。然后通過(guò)接口回調(diào)的方式,傳遞用戶下一次按下的item的SweepView實(shí)例。這次新添加的接口回調(diào)方法,在View的onTouchEvent的Action_Down的時(shí)候調(diào)用。代碼如下:
1)添加接口方法
/** * 按下時(shí)候的回調(diào)。按下,如果按下時(shí)當(dāng)前的item與打開(kāi)的item不一致,按下關(guān)閉掉item * @param sweepView */void sweepDown(SweepView sweepView);2)在View的onTouchEvent的Action_Down的時(shí)候調(diào)用。@Overridepublic boolean onTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN){ //按下的時(shí)候 mSweepChangeListener.sweepDown(this); } mDragHelper.processTouchEvent(event); return true;}3)、記錄按下滑動(dòng)打開(kāi)時(shí)候的SweepView的實(shí)例,以及實(shí)現(xiàn)只允許一條item展示。private SweepView mSweepView;holder.mSweepView.setOnSweepChangeListener(new SweepView.OnSweepChangeListener() { @Override public void sweepChanged(SweepView sweepView, boolean isOpend) { if (isOpend) { mSweepView = sweepView; //如果打開(kāi),將該item對(duì)應(yīng)的SweepView對(duì)象則保存至集合中 if (!mSweepViews.contains(sweepView)) { mSweepViews.add(sweepView); } } else { mSweepViews.remove(sweepView); } } @Override public void sweepDown(SweepView sweepView) { //如果不滑動(dòng),mSweepView為null,因此要過(guò)濾為null情況 if(mSweepView != null && mSweepView != sweepView){ mSweepView.close(); } }});最后運(yùn)行看看地方QQ側(cè)拉刪除的效果吧:
效果還不錯(cuò),加個(gè)關(guān)注唄~
打開(kāi)微信掃描下方二維碼查看更多安卓文章:
打開(kāi)微信搜索公眾號(hào) Android程序員開(kāi)發(fā)指南 或者手機(jī)掃描下方二維碼 在公眾號(hào)閱讀更多Android文章。
微信公眾號(hào)圖片:
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注