a亚洲精品_精品国产91乱码一区二区三区_亚洲精品在线免费观看视频_欧美日韩亚洲国产综合_久久久久久久久久久成人_在线区

首頁 > 系統(tǒng) > Android > 正文

詳解Android中實現(xiàn)ListView左右滑動刪除條目的方法

2019-12-12 06:41:22
字體:
來源:轉載
供稿:網(wǎng)友

使用Scroller實現(xiàn)絢麗的ListView左右滑動刪除Item效果
這里來給大家?guī)硎褂肧croller的小例子,同時也能用來幫助初步解除的讀者更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我們就能實現(xiàn)很多滑動的效果。例如側滑菜單,launcher,ListView的下拉刷新等等效果,我今天實現(xiàn)的是ListView的item的左右滑動刪除item的效果,現(xiàn)在很多朋友看到這個效果應該是在Android的通知欄下拉中看到這個滑動刪除的效果吧,我看到這個效果是在我之前的三星手機上左右滑動打電話發(fā)短信的效果,感覺很棒,不過現(xiàn)在很多手機聯(lián)系人滑動都不是我之前那臺手機的效果啦,網(wǎng)上很多朋友也寫了關于滑動刪除ListView的item的例子,有些是滑動手指離開之后然后給item加向左或者向右的移動動畫,我覺得這樣子的用戶體驗不是很好,所以今天自己也寫了一個關于ListView左右滑動刪除Item的小例子,ListView的item會隨著手指在屏幕上的滑動而滑動,手指離開屏幕的時候item會根據(jù)判斷向左或者向右劃出屏幕,就是跟通知欄的效果差不多,接下來就帶大家來實現(xiàn)這個效果。
先說下實現(xiàn)該效果的主要思路
先根據(jù)手指觸摸的點來獲取點擊的是ListView的哪一個item
手指在屏幕中滑動我們利用scrollBy()來使該item跟隨手指一起滑動
手指放開的時候,我們判斷手指拖動的距離來判斷item到底是滑出屏幕還是回到開始位置
主要思路就是上面這三步,接下來我們就用代碼來實現(xiàn)吧,首先我們新建一個項目,叫SlideCutListView
根據(jù)需求我們需要自己自定義一個ListView來實現(xiàn)該功能,接下來先貼出代碼再講解具體的實現(xiàn)

package com.example.slidecutlistview;  import android.content.Context; import android.util.AttributeSet; 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 SlideCutListView 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;   /**    * 移除item后的回調(diào)接口    */   private RemoveListener mRemoveListener;   /**    * 用來指示item滑出屏幕的方向,向左或者向右,用一個枚舉值來標記    */   private RemoveDirection removeDirection;    // 滑動刪除方向的枚舉值   public enum RemoveDirection {     RIGHT, LEFT;   }     public SlideCutListView(Context context) {     this(context, null);   }    public SlideCutListView(Context context, AttributeSet attrs) {     this(context, attrs, 0);   }    public SlideCutListView(Context context, AttributeSet attrs, int defStyle) {     super(context, attrs, defStyle);     screenWidth = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();     scroller = new Scroller(context);     mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();   }      /**    * 設置滑動刪除的回調(diào)接口    * @param removeListener    */   public void setRemoveListener(RemoveListener removeListener) {     this.mRemoveListener = removeListener;   }    /**    * 分發(fā)事件,主要做的是判斷點擊的是那個item, 以及通過postDelayed來設置響應左右滑動事件    */   @Override   public boolean dispatchTouchEvent(MotionEvent event) {     switch (event.getAction()) {     case MotionEvent.ACTION_DOWN: {       addVelocityTracker(event);        // 假如scroller滾動還沒有結束,我們直接返回       if (!scroller.isFinished()) {         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 view       itemView = getChildAt(slidePosition - getFirstVisiblePosition());       break;     }     case MotionEvent.ACTION_MOVE: {       if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY           || (Math.abs(event.getX() - downX) > mTouchSlop && Math               .abs(event.getY() - downY) < mTouchSlop)) {         isSlide = true;                }       break;     }     case MotionEvent.ACTION_UP:       recycleVelocityTracker();       break;     }      return super.dispatchTouchEvent(event);   }    /**    * 往右滑動,getScrollX()返回的是左邊緣的距離,就是以View左邊緣為原點到開始滑動的距離,所以向右邊滑動為負值    */   private void scrollRight() {     removeDirection = RemoveDirection.RIGHT;     final int delta = (screenWidth + itemView.getScrollX());     // 調(diào)用startScroll方法來設置一些滾動的參數(shù),我們在computeScroll()方法中調(diào)用scrollTo來滾動item     scroller.startScroll(itemView.getScrollX(), 0, -delta, 0,         Math.abs(delta));     postInvalidate(); // 刷新itemView   }    /**    * 向左滑動,根據(jù)上面我們知道向左滑動為正值    */   private void scrollLeft() {     removeDirection = RemoveDirection.LEFT;     final int delta = (screenWidth - itemView.getScrollX());     // 調(diào)用startScroll方法來設置一些滾動的參數(shù),我們在computeScroll()方法中調(diào)用scrollTo來滾動item     scroller.startScroll(itemView.getScrollX(), 0, delta, 0,         Math.abs(delta));     postInvalidate(); // 刷新itemView   }    /**    * 根據(jù)手指滾動itemView的距離來判斷是滾動到開始位置還是向左或者向右滾動    */   private void scrollByDistanceX() {     // 如果向左滾動的距離大于屏幕的二分之一,就讓其刪除     if (itemView.getScrollX() >= screenWidth / 2) {       scrollLeft();     } else if (itemView.getScrollX() <= -screenWidth / 2) {       scrollRight();     } else {       // 滾回到原始位置,為了偷下懶這里是直接調(diào)用scrollTo滾動       itemView.scrollTo(0, 0);     }    }    /**    * 處理我們拖動ListView item的邏輯    */   @Override   public boolean onTouchEvent(MotionEvent ev) {     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向右滾         itemView.scrollBy(deltaX, 0);                  return true; //拖動的時候ListView不滾動       case MotionEvent.ACTION_UP:         int velocityX = getScrollVelocity();         if (velocityX > SNAP_VELOCITY) {           scrollRight();         } else if (velocityX < -SNAP_VELOCITY) {           scrollLeft();         } else {           scrollByDistanceX();         }                  recycleVelocityTracker();         // 手指離開的時候就不響應左右滾動         isSlide = false;         break;       }     }      //否則直接交給ListView來處理onTouchEvent事件     return super.onTouchEvent(ev);   }    @Override   public void computeScroll() {     // 調(diào)用startScroll的時候scroller.computeScrollOffset()返回true,     if (scroller.computeScrollOffset()) {       // 讓ListView item根據(jù)當前的滾動偏移量進行滾動       itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());              postInvalidate();        // 滾動動畫結束的時候調(diào)用回調(diào)接口       if (scroller.isFinished()) {         if (mRemoveListener == null) {           throw new NullPointerException("RemoveListener is null, we should called setRemoveListener()");         }                  itemView.scrollTo(0, 0);         mRemoveListener.removeItem(removeDirection, slidePosition);       }     }   }    /**    * 添加用戶的速度跟蹤器    *    * @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 item滑出屏幕,回調(diào)這個接口    * 我們需要在回調(diào)方法removeItem()中移除該Item,然后刷新ListView    *    * @author xiaanming    *    */   public interface RemoveListener {     public void removeItem(RemoveDirection direction, int position);   }  } 

首先我們重寫dispatchTouchEvent()方法,該方法是事件的分發(fā)方法,我們在里面只做了一些簡單的步驟,我們按下屏幕的時候,如果某個item正在進行滾動,我們直接交給SlideCutListView的父類處理分發(fā)事件,否則根據(jù)點擊的X,Y坐標利用pointToPosition(int x, int y)來獲取點擊的是ListView的哪一個position,從而獲取到我們需要滑動的item的View,我們還在該方法加入了滑動速度的檢測,并且在ACTION_MOVE的時候來判斷是否響應item的左右移動,用isSlide來記錄是否響應左右滑動
然后就是重寫onTouchEvent()方法,我們先判斷isSlide為true,并且我們點擊的是ListView上面的有效的position,否則直接交給SlideCutListView的父類也就是ListView來處理,在ACTION_MOVE中調(diào)用scrollBy()來移動item,scrollBy()是相對item的上一個位置進行移動的,所以我們每次都要用現(xiàn)在移動的距離減去上一個位置的距離然后賦給scrollBy()方法,這樣子我們就實現(xiàn)了item的平滑移動,當我們將手指抬起的時候,我們先根據(jù)手指滑動的速度來確定是item是滑出屏幕還是滑動至原始位置,如果向右的速度大于我們設置的SNAP_VELOCITY,item就調(diào)用scrollRight()方法滾出屏幕,如果向左的速度小于-SNAP_VELOCITY,則調(diào)用scrollLeft()向左滾出屏幕,如果我們是緩慢的移動item,則調(diào)用scrollByDistanceX()方法來判斷是滾到那個位置
在scrollRight()和scrollLeft()方法中我們使用Scroller類的startScroll()方法先設置滾動的參數(shù),然后調(diào)用postInvalidate()來刷新界面,界面刷新就會調(diào)用computeScroll()方法,我們在里面處理滾動邏輯就行了,值得一提的是computeScroll()里面的這段代碼

itemView.scrollTo(0, 0); 

我們需要將該item滾動在(0, 0 )這個點,因為我們只是將ListView的Item滾動出屏幕而已,并沒有將該item移除,而且我們不能手動調(diào)用removeView()來從ListView中移除該item,我們只能通過改變ListView的數(shù)據(jù),然后通過notifyDataSetChanged()來刷新ListView,所以我們需要將其滾動至(0, 0),這里比較關鍵。
定義好了我們左右滑動的ListView,接下來就來使用它,布局很簡單,一個RelativeLayout包裹我們自定義的ListView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"   xmlns:tools="http://schemas.android.com/tools"   android:layout_width="match_parent"   android:layout_height="match_parent"   android:background="@android:color/darker_gray">    <com.example.slidecutlistview.SlideCutListView     android:id="@+id/slideCutListView"     android:layout_width="match_parent"     android:layout_height="match_parent"      android:listSelector="@android:color/transparent"     android:divider="@drawable/reader_item_divider"     android:cacheColorHint="@android:color/transparent">   </com.example.slidecutlistview.SlideCutListView>  </RelativeLayout> 

接下來我們來看看ListView的item的布局

<?xml version="1.0" encoding="UTF-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="fill_parent"   android:layout_height="wrap_content" >    <LinearLayout     android:layout_width="fill_parent"     android:layout_height="wrap_content"     android:background="@drawable/friendactivity_comment_detail_list2" >      <TextView       android:id="@+id/list_item"       android:layout_width="match_parent"       android:layout_height="wrap_content"       android:layout_margin="15dip" />   </LinearLayout>  </LinearLayout> 

還記得我在上一篇文章中提到過調(diào)用scrollTo()方法是對里面的子View進行滾動的,而不是對整個布局進行滾動的,所以我們用LinearLayout來套住我們的item的布局,這點需要注意一下,不然滾動的只是TextView。
主頁面MainActivity里面的代碼比較簡單,里面使用的也是ArrayAdapter,相信大家都能看懂

package com.example.slidecutlistview;  import java.util.ArrayList; import java.util.List;  import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Toast;  import com.example.slidecutlistview.SlideCutListView.RemoveDirection; import com.example.slidecutlistview.SlideCutListView.RemoveListener;  public class MainActivity extends Activity implements RemoveListener{   private SlideCutListView slideCutListView ;   private ArrayAdapter<String> adapter;   private List<String> dataSourceList = new ArrayList<String>();    @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     init();   }    private void init() {     slideCutListView = (SlideCutListView) findViewById(R.id.slideCutListView);     slideCutListView.setRemoveListener(this);          for(int i=0; i<20; i++){       dataSourceList.add("滑動刪除" + i);      }          adapter = new ArrayAdapter<String>(this, R.layout.listview_item, R.id.list_item, dataSourceList);     slideCutListView.setAdapter(adapter);          slideCutListView.setOnItemClickListener(new OnItemClickListener() {        @Override       public void onItemClick(AdapterView<?> parent, View view,           int position, long id) {         Toast.makeText(MainActivity.this, dataSourceList.get(position), Toast.LENGTH_SHORT).show();       }     });   }       //滑動刪除之后的回調(diào)方法   @Override   public void removeItem(RemoveDirection direction, int position) {     adapter.remove(adapter.getItem(position));     switch (direction) {     case RIGHT:       Toast.makeText(this, "向右刪除 "+ position, Toast.LENGTH_SHORT).show();       break;     case LEFT:       Toast.makeText(this, "向左刪除 "+ position, Toast.LENGTH_SHORT).show();       break;      default:       break;     }        }     } 

這里面需要對SlideCutListView設置RemoveListener,然后我們在回調(diào)方法removeItem(RemoveDirection direction, int position)中刪除該position的數(shù)據(jù),在調(diào)用notifyDataSetChanged()刷新ListView,我這里用的是ArrayAdatper,直接調(diào)用remove()就可以了。
所有的代碼就編寫完了,我們來運行下程序看看效果吧

使用NineOldAndroids實現(xiàn)絢麗的ListView左右滑動刪除Item效果
再給大家來一個ListView左右滑動刪除Item效果的例子,上面使用的是滑動類Scroller來實現(xiàn)的,但是看了下通知欄的左右滑動刪除效果,確實很棒,當我們滑動Item超過一半的時候,item的透明度就變成了0,我們就知道抬起手指的時候item就被刪除了,當item的透明度不為0的時候,我們抬起手指Item會回到起始位置,這樣我們就知道拖動到什么位置item會刪除,什么位置Item不刪除,用戶體驗更好了,還有一個效果,就是我們滑動刪除了item的時候,ListView的其他item會出現(xiàn)向上或者向下滾動的效果,感覺效果很棒,所以在GitHub上面搜索了下,發(fā)現(xiàn)很多開源庫都有這個效果,比如ListViewAnimations, android-swipelistview等等,我看了下實現(xiàn)原理,使用的是Jake Wharton的動畫開源庫NineOldAndroids,這個庫究竟是干嘛的呢?在API3.0(Honeycomb), SDK新增了一個android.animation包,里面的類是實現(xiàn)動畫效果相關的類,通過Honeycomb API,能夠實現(xiàn)非常復雜的動畫效果,但是如果開發(fā)者想在3.0以下使用這一套API, 則需要使用開源框架Nine Old Androids,在這個庫中會根據(jù)我們運行的機器判斷其SDK版本,如果是API3.0以上則使用Android自帶的動畫類,否則就使用Nine Old Androids庫中,這是一個兼容庫,接下來我們就來看看這個效果的具體實現(xiàn)吧
實現(xiàn)該效果的主要思路
先根據(jù)手指觸摸的點來獲取點擊的是ListView的哪一個Item
當手指在屏幕上面滑動的時候,我們要使得Item跟隨手指的滑動而滑動
當我們抬起手指的時候,我們根據(jù)滑動的距離或者手指在屏幕上面的速度來判斷Item是滑出屏幕還是滑動至其實位置
Item滑出屏幕時,使ListView的其他item產(chǎn)生向上擠壓或者向下擠壓的效果
大致的思路這是這四步,其中的一些細節(jié)接下來我會一一為大家解答的,接下來我們就用代碼來實現(xiàn)這種效果吧
首先我們新建一個工程,叫Swipedismisslistview,我們需要將Nine Old Androids這個庫引入到工程,大家可以去https://github.com/JakeWharton/NineOldAndroids下載,可以使用Jar包,也可以使用工程庫的形式引入到我們自己的工程,我們還需要自定義一個ListView,我們先看代碼然后給大家講解下具體的功能實現(xiàn)

package com.example.swipedismisslistview;  import static com.nineoldandroids.view.ViewHelper.setAlpha; import static com.nineoldandroids.view.ViewHelper.setTranslationX; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView;  import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.AnimatorListenerAdapter; import com.nineoldandroids.animation.ValueAnimator; import com.nineoldandroids.view.ViewHelper; import com.nineoldandroids.view.ViewPropertyAnimator; public class SwipeDismissListView extends ListView {   /**    * 認為是用戶滑動的最小距離    */   private int mSlop;   /**    * 滑動的最小速度    */   private int mMinFlingVelocity;   /**    * 滑動的最大速度    */   private int mMaxFlingVelocity;   /**    * 執(zhí)行動畫的時間    */   protected long mAnimationTime = 150;   /**    * 用來標記用戶是否正在滑動中    */   private boolean mSwiping;   /**    * 滑動速度檢測類    */   private VelocityTracker mVelocityTracker;   /**    * 手指按下的position    */   private int mDownPosition;   /**    * 按下的item對應的View    */   private View mDownView;   private float mDownX;   private float mDownY;   /**    * item的寬度    */   private int mViewWidth;   /**    * 當ListView的Item滑出界面回調(diào)的接口    */   private OnDismissCallback onDismissCallback;    /**    * 設置動畫時間    *    * @param mAnimationTime    */   public void setmAnimationTime(long mAnimationTime) {     this.mAnimationTime = mAnimationTime;   }    /**    * 設置刪除回調(diào)接口    *    * @param onDismissCallback    */   public void setOnDismissCallback(OnDismissCallback onDismissCallback) {     this.onDismissCallback = onDismissCallback;   }    public SwipeDismissListView(Context context) {     this(context, null);   }    public SwipeDismissListView(Context context, AttributeSet attrs) {     this(context, attrs, 0);   }    public SwipeDismissListView(Context context, AttributeSet attrs,       int defStyle) {     super(context, attrs, defStyle);      ViewConfiguration vc = ViewConfiguration.get(context);     mSlop = vc.getScaledTouchSlop();     mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 8; //獲取滑動的最小速度     mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); //獲取滑動的最大速度   }       @Override   public boolean onTouchEvent(MotionEvent ev) {     switch (ev.getAction()) {     case MotionEvent.ACTION_DOWN:       handleActionDown(ev);       break;     case MotionEvent.ACTION_MOVE:       return handleActionMove(ev);     case MotionEvent.ACTION_UP:       handleActionUp(ev);       break;     }     return super.onTouchEvent(ev);   }    /**    * 按下事件處理    *    * @param ev    * @return    */   private void handleActionDown(MotionEvent ev) {     mDownX = ev.getX();     mDownY = ev.getY();          mDownPosition = pointToPosition((int) mDownX, (int) mDownY);      if (mDownPosition == AdapterView.INVALID_POSITION) {       return;     }      mDownView = getChildAt(mDownPosition - getFirstVisiblePosition());      if (mDownView != null) {       mViewWidth = mDownView.getWidth();     }      //加入速度檢測     mVelocityTracker = VelocityTracker.obtain();     mVelocityTracker.addMovement(ev);   }       /**    * 處理手指滑動的方法    *    * @param ev    * @return    */   private boolean handleActionMove(MotionEvent ev) {     if (mVelocityTracker == null || mDownView == null) {       return super.onTouchEvent(ev);     }      // 獲取X方向滑動的距離     float deltaX = ev.getX() - mDownX;     float deltaY = ev.getY() - mDownY;      // X方向滑動的距離大于mSlop并且Y方向滑動的距離小于mSlop,表示可以滑動     if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < mSlop) {       mSwiping = true;              //當手指滑動item,取消item的點擊事件,不然我們滑動Item也伴隨著item點擊事件的發(fā)生       MotionEvent cancelEvent = MotionEvent.obtain(ev);       cancelEvent.setAction(MotionEvent.ACTION_CANCEL |             (ev.getActionIndex()<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));       onTouchEvent(cancelEvent);     }      if (mSwiping) {       // 跟誰手指移動item       ViewHelper.setTranslationX(mDownView, deltaX);       // 透明度漸變       ViewHelper.setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX)/ mViewWidth)));        // 手指滑動的時候,返回true,表示SwipeDismissListView自己處理onTouchEvent,其他的就交給父類來處理       return true;     }      return super.onTouchEvent(ev);    }    /**    * 手指抬起的事件處理    * @param ev    */   private void handleActionUp(MotionEvent ev) {     if (mVelocityTracker == null || mDownView == null|| !mSwiping) {       return;     }      float deltaX = ev.getX() - mDownX;          //通過滑動的距離計算出X,Y方向的速度     mVelocityTracker.computeCurrentVelocity(1000);     float velocityX = Math.abs(mVelocityTracker.getXVelocity());     float velocityY = Math.abs(mVelocityTracker.getYVelocity());          boolean dismiss = false; //item是否要滑出屏幕     boolean dismissRight = false;//是否往右邊刪除          //當拖動item的距離大于item的一半,item滑出屏幕     if (Math.abs(deltaX) > mViewWidth / 2) {       dismiss = true;       dismissRight = deltaX > 0;              //手指在屏幕滑動的速度在某個范圍內(nèi),也使得item滑出屏幕     } else if (mMinFlingVelocity <= velocityX         && velocityX <= mMaxFlingVelocity && velocityY < velocityX) {       dismiss = true;       dismissRight = mVelocityTracker.getXVelocity() > 0;     }      if (dismiss) {       ViewPropertyAnimator.animate(mDownView)           .translationX(dismissRight ? mViewWidth : -mViewWidth)//X軸方向的移動距離           .alpha(0)           .setDuration(mAnimationTime)           .setListener(new AnimatorListenerAdapter() {             @Override             public void onAnimationEnd(Animator animation) {               //Item滑出界面之后執(zhí)行刪除               performDismiss(mDownView, mDownPosition);             }           });     } else {       //將item滑動至開始位置       ViewPropertyAnimator.animate(mDownView)       .translationX(0)       .alpha(1)         .setDuration(mAnimationTime).setListener(null);     }          //移除速度檢測     if(mVelocityTracker != null){       mVelocityTracker.recycle();       mVelocityTracker = null;     }          mSwiping = false;   }          /**    * 在此方法中執(zhí)行item刪除之后,其他的item向上或者向下滾動的動畫,并且將position回調(diào)到方法onDismiss()中    * @param dismissView    * @param dismissPosition    */   private void performDismiss(final View dismissView, final int dismissPosition) {     final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();//獲取item的布局參數(shù)     final int originalHeight = dismissView.getHeight();//item的高度      ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 0).setDuration(mAnimationTime);     animator.start();      animator.addListener(new AnimatorListenerAdapter() {       @Override       public void onAnimationEnd(Animator animation) {         if (onDismissCallback != null) {           onDismissCallback.onDismiss(dismissPosition);         }          //這段代碼很重要,因為我們并沒有將item從ListView中移除,而是將item的高度設置為0         //所以我們在動畫執(zhí)行完畢之后將item設置回來         ViewHelper.setAlpha(dismissView, 1f);         ViewHelper.setTranslationX(dismissView, 0);         ViewGroup.LayoutParams lp = dismissView.getLayoutParams();         lp.height = originalHeight;         dismissView.setLayoutParams(lp);        }     });      animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {       @Override       public void onAnimationUpdate(ValueAnimator valueAnimator) {         //這段代碼的效果是ListView刪除某item之后,其他的item向上滑動的效果         lp.height = (Integer) valueAnimator.getAnimatedValue();         dismissView.setLayoutParams(lp);       }     });    }    /**    * 刪除的回調(diào)接口    *    * @author xiaanming    *    */   public interface OnDismissCallback {     public void onDismiss(int dismissPosition);   }  } 

看過Android 使用Scroller實現(xiàn)絢麗的ListView左右滑動刪除Item效果你會發(fā)現(xiàn),這個自定義的SwipeDismissListView只重寫了onTouchEvent()方法,其實我們重寫這一個方法就能實現(xiàn)我們需要的效果
1. 我們先看手指按下屏幕的處理方法handleActionDown();該方法里面根據(jù)我們手指按下的點根據(jù)pointToPosition()方法來獲取我們點擊的position,然后利用getChildAt()來獲取我們按下的item的View對象,并且加入手指在屏幕滑動的速度檢查,這一步相對來說還是比較簡單

2. 接下來就是手指在屏幕上面滑動的處理方法handleActionMove(),這個方法就稍微的復雜些,我們需要根據(jù)手指在X軸的滑動距離和Y軸的滑動距離來判斷是ListView item的水平滑動還是ListView的上下滑動,當滿足Math.abs(deltaX) > mSlop && Math.abs(deltaY) < mSlop這個條件時候,我們用一個布爾值mSwiping來標記Item現(xiàn)在處于水平滑動的狀態(tài),這時候我們需要處理Item跟隨手指的滑動而滑動的邏輯,我們使用ViewHelper來處理Item的滑動邏輯,這個類會根據(jù)機器的SDK版本來判斷使用Android系統(tǒng)的API還是NineOldandroids中自己實現(xiàn)的API使得View滑動的效果,NineOldandroids中主要使用Camera(可以實現(xiàn)各種復雜動畫效果的類),我們直接使用ViewHelper的setTranslationX()和setAlpha()就實現(xiàn)了item滑動和透明度漸變的效果,為了使得我們在滑動item的時候,ListView不上下滾動,我們必須返回true來屏蔽ListView的上下滾動,這里需要我們要非常熟悉Android的事件分發(fā)機制,這里我就不說明了,大家不了解的去網(wǎng)上找找相關的文章看看
還有一個問題,就是當我們滑動ListView的item的時候,會伴隨著item的點擊事件,這不是我們想要的效果,所以當Item滑動的時候我們需要取消ListView Item的點擊事件

3. 在看手指抬起的時候的處理方法handleActionUp(),這里面需要根據(jù)手指的滑動速度或者Item移動的距離來判斷Item是滑出屏幕還是滑動至起始位置,并且要判斷item向左還是向右滑出屏幕等等邏輯,具體的邏輯可以看代碼,相信大家都看得懂.
我這里要說說ViewPropertyAnimator類,這個類能更好的實現(xiàn)一個View同時進行多個動畫的功能,當然我們也可以使用ObjectAnimator利用AnimatorSet來實現(xiàn)一個View上的多個同時進行的動畫效果,例如我們可以將

ViewPropertyAnimator.animate(mDownView)     .translationX(dismissRight ? mViewWidth : -mViewWidth)//X軸方向的移動距離     .alpha(0)     .setDuration(mAnimationTime)     .setListener(new AnimatorListenerAdapter() {       @Override       public void onAnimationEnd(Animator animation) {         //Item滑出界面之后執(zhí)行刪除         performDismiss(mDownView, mDownPosition);       }     }); 

替換成

AnimatorSet set = new AnimatorSet();       set.playTogether(ObjectAnimator.ofFloat(mDownView, "translationX", dismissRight ? mViewWidth : -mViewWidth),                ObjectAnimator.ofFloat(mDownView, "alpha", 0));       set.setDuration(mAnimationTime).start();       set.addListener(new AnimatorListenerAdapter() {             @Override             public void onAnimationEnd(Animator animation) {               //Item滑出界面之后執(zhí)行刪除               performDismiss(mDownView, mDownPosition);             }           }); 

在效果上面是一樣的,但是ViewPropertyAnimator在性能上要比使用ObjectAnimator來實現(xiàn)多個同時進行的動畫要高的多,舉個例子,假如要對View使用移動和透明度的動畫,使用ViewPropertyAnimator的話,某個時間點上我們只需要調(diào)用一次invalidate()方法刷新界面就行了,而使用ObjectAnimator的話,移動的動畫需要調(diào)用invalidate(),透明度的動畫也需要調(diào)用invalidate()方法,在性能上使用AnimationSet比ViewPropertyAnimator要低,但是有的時候我們還是需要使用ObjectAnimator,比如,在某個時間內(nèi),我們需要將View先變大在變小在變大等復雜情況,這時候ObjectAnimator就派上用場了,例如

ObjectAnimator.ofInt(mDownView, "scaleX", 0 ,100 ,0, 100).setDuration(100).start() 
通過上面的幾步我們就實現(xiàn)了ListView的左右滑動刪除item的效果啦,但是還有一個效果,item刪除之后,ListView的其他item向上或者向下緩緩滑動的效果,實現(xiàn)這個也很容易,就是動態(tài)設置item的高度,item高度逐漸變小,這樣其他的item就會出現(xiàn)向上或者向下擠壓的效果啦!

4. 這里我們使用的是ValueAnimator這個類,這個類并不是針對View作用的動畫,而是對某個值作用的動畫,他默認使用的Interpolator(插補器)是AccelerateDecelerateInterpolator(開始和結束的時候慢,中間快) , 舉個很簡單的例子,我們在10秒內(nèi)使用ValueAnimator將某個值從0變化到100,如果使用LinearInterpolator(線性插補器,勻速變化)在第2秒的時候,這個值變成了20,而是用AccelerateDecelerateInterpolator,可能在第二秒的時候這個值為15或者13,所以我們在ValueAnimator變化的時候設置值動畫變化的監(jiān)聽器AnimatorUpdateListener就知道某個時間這個值變成了多少,從而對View的某個屬性進行設置(例如大小),所以ValueAnimator是間接的對View設置動畫的
了解了ValueAnimator的使用原理,我們就可以現(xiàn)實上面的動畫效果了,我們使用ValueAnimator將item的高度變成0,設置ValueAnimator變化的監(jiān)聽,我們在回調(diào)函數(shù)onAnimationUpdate()中動態(tài)的設置item的高度, 然后添加AnimatorListener監(jiān)聽動畫的狀態(tài)(例如動畫開始,結束,重復等)監(jiān)聽,在動畫結束的回調(diào)函數(shù)onAnimationEnd()中刪除該item的數(shù)據(jù),調(diào)用notifyDataSetChanged刷新ListView,看看下面這段代碼

ViewHelper.setAlpha(dismissView, 1f);         ViewHelper.setTranslationX(dismissView, 0);         ViewGroup.LayoutParams lp = dismissView.getLayoutParams();         lp.height = originalHeight;         dismissView.setLayoutParams(lp); 

我們使用動畫只是將item移動出了屏幕,并且將item的高度設置為了0,并沒有將item的View從ListView中Remove掉,況且ListView也不能直接Remove掉Item的,只能將數(shù)據(jù)源刪除,在調(diào)用notifyDataSetChanged()刷新,所以我們需要將剛剛滑出屏幕高度設置為0的Item恢復回來

自定義控件的代碼我們已經(jīng)編寫完了,接下來我們就要使用它了,先看界面的布局代碼

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent">      <com.example.swipedismisslistview.SwipeDismissListView     android:id="@+id/swipeDismissListView"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:listSelector="@android:color/transparent"     android:cacheColorHint="@android:color/transparent">    </com.example.swipedismisslistview.SwipeDismissListView>   </RelativeLayout>  

很簡單,一個RelativeLayout包裹我們自定義的ListView控件,接下來就是主界面的代碼編寫,跟平常的ListView使用一樣,但是我們需要設置OnDismissCallback()監(jiān)聽,在
onDismiss()中刪除該位置對于的數(shù)據(jù),刷新ListView

package com.example.swipedismisslistview;  import java.util.ArrayList; import java.util.List;  import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Toast;  import com.example.swipedismisslistview.SwipeDismissListView.OnDismissCallback;  public class SwipeActivity extends Activity {   private SwipeDismissListView swipeDismissListView;   private ArrayAdapter<String> adapter;   private List<String> dataSourceList = new ArrayList<String>();    @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_swipe);     init();   }    private void init() {     swipeDismissListView = (SwipeDismissListView) findViewById(R.id.swipeDismissListView);     for (int i = 0; i < 20; i++) {       dataSourceList.add("滑動刪除" + i);     }      adapter = new ArrayAdapter<String>(this,         android.R.layout.simple_list_item_1,         android.R.id.text1, dataSourceList);          swipeDismissListView.setAdapter(adapter);          swipeDismissListView.setOnDismissCallback(new OnDismissCallback() {              @Override       public void onDismiss(int dismissPosition) {          adapter.remove(adapter.getItem(dismissPosition));        }     });               swipeDismissListView.setOnItemClickListener(new OnItemClickListener() {        @Override       public void onItemClick(AdapterView<?> parent, View view,           int position, long id) {         Toast.makeText(SwipeActivity.this, adapter.getItem(position), Toast.LENGTH_SHORT).show();       }     });    }  } 

所有的代碼都已經(jīng)編寫完畢了,接下來就是運行工程,看看具體的效果是不是我們想要的

201645144001733.gif (411×698)

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 日韩欧美一级精品久久 | 另类二区 | 中文字幕一区二区三区四区五区 | 黄色精品一区二区 | 国产日韩欧美精品一区 | 亚洲第一se情网站 | 黄色精品视频 | 日韩网站在线 | 成人不卡 | 国产精品一区二区三区在线播放 | 欧美a级成人淫片免费看 | 免费看片国产 | 国产成人精品999在线观看 | 美女超碰 | 免费看的黄色网 | 久久亚洲一区二区三 | 99久久免费观看 | 欧美一级淫片免费视频黄 | www.av7788.com| 免费人成在线观看网站 | 国产成人在线免费观看视频 | 亚洲乱码国产乱码精品精98午夜 | 免费国产一区二区 | 欧洲一区二区视频 | 最新中文字幕在线 | 日韩中文字幕视频在线观看 | 成人在线 | 日韩在线色 | 国产视频精品一区二区三区 | 亚洲一区二区精品 | 九九色综合 | 中文字国产精久久无 | 亚洲经典视频在线观看 | 五月婷婷久久久 | 欧美激情视频一区二区三区在线播放 | 欧美小电影| 91精品国产欧美一区二区 | 欧美日韩亚 | 中文字幕一区二区三区四区五区 | 国产精品久久久久久久娇妻 | 91久色|