国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

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

Android 中通過ViewDragHelper實現(xiàn)ListView的Item的側(cè)拉劃出效果

2019-10-22 18:30:57
字體:
供稿:網(wǎng)友

先來看看,今天要實現(xiàn)的自定義控件效果圖:

viewdraghelper,listview,item,Android

關(guān)于ViewDragHelper的使用,大家可以先看這篇文章ViewDragHelper的使用介紹

實現(xiàn)該自定義控件的大體步驟如下:

1.ViewDragHelper使用的3部曲,初始化ViewDragHelper,傳遞觸摸事件,實現(xiàn)ViewDragHelper.Callback抽象類.

2.需要創(chuàng)建2個直接的子View,分別是前景View和背景View,代表ListView每一項Item的布局的組成,如下所示:

未劃出時顯示的FrontView:

viewdraghelper,listview,item,Android

劃出后的右邊顯示BackView:

viewdraghelper,listview,item,Android

以上2部分就是該自定義控件要包含的2個直接子View.

3.需要獲取FrontView的寬高,寬度其實就是屏幕的寬度,高度就是ListView每一項Item的高度;還需獲取BackView的寬度,因為這個寬度就是側(cè)滑的最大范圍.

4.需要確定FrontView和BackView的初始位置,在onLayout方法中確定,即默認情況下是只顯示FrontView的.這個實現(xiàn)起來也很簡單,FrontView的left=0,BackView的left=FrontView的right即可.

5.需要同步FrontView和BackView的滑動,即滑動FrontView的時候BackView也需要跟著劃出,同樣滑動BackView的時候也需要FrontView跟著滑動.

6.需要解決側(cè)拉劃出的效果是否有動畫效果.平滑滑動的動畫可以通過ViewDragHelper輕松實現(xiàn).

好了,直接上自定義的SwipeLayout源碼:

/**  * Created by mChenys on 2015/12/26.  */ public class SwipeLayout extends FrameLayout {   private ViewDragHelper.Callback mCallback;   private ViewDragHelper mDragHelper;   private View mBackView; //item的側(cè)邊布局   private View mFrontView;//當(dāng)前顯示的item布局   private int mWidth; //屏幕的寬度,mFrontView的寬度   private int mHeight; //mFrontView的高度   private int mRange;//mFrontView側(cè)拉時向左移動的最大距離,即mBackView的寬度   public SwipeLayout(Context context) {     this(context, null);   }   public SwipeLayout(Context context, AttributeSet attrs) {     this(context, attrs, 0);   }   public SwipeLayout(Context context, AttributeSet attrs, int defStyleAttr) {     super(context, attrs, defStyleAttr);     init();   }   //1.初始ViewDragHelper   private void init() {     mCallback = new ViewDragHelper.Callback() {       //3.在回調(diào)方法中處理觸摸事件       @Override       public boolean tryCaptureView(View child, int pointerId) {         return true; //允許所有子控件的滑動       }       //設(shè)定滑動的邊界值       @Override       public int clampViewPositionHorizontal(View child, int left, int dx) {         if (child == mFrontView) {           //前景View的滑動范圍是(0~ -mRange)           if (left > 0) {             left = 0;           } else if (left < -mRange) {             left = -mRange;           }         }         if (child == mBackView) {           //背景View的滑動范圍是(mWidth - mRange ~ mWidth)           if (left > mWidth) {             left = mWidth;           } else if (left < (mWidth - mRange)) {             left = mWidth - mRange;           }         }         //返回修正過的建議值         return left;       }       //監(jiān)聽View的滑動位置的改變,同步前景View和背景View的滑動事件       @Override       public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {         if (changedView == mFrontView) {           //當(dāng)滑動前景View時,也需要滑動背景View           mBackView.offsetLeftAndRight(dx);         } else if (changedView == mBackView) {           //當(dāng)滑動背景View時,也需要滑動前景View           mFrontView.offsetLeftAndRight(dx);         }         // 兼容老版本         invalidate();       }       //處理釋放后的開啟和關(guān)閉動作       @Override       public void onViewReleased(View releasedChild, float xvel, float yvel) {         if (xvel < 0) {           //有向左滑動的速度,則打開           open();         } else if (xvel == 0 && mFrontView.getLeft() < -mRange / 2.0f) {           //前景View向左滑動的left小于背景View寬度一半的負值時,打開           open();         } else {           //其他情況為關(guān)閉           close();         }       }     };     mDragHelper = ViewDragHelper.create(this, mCallback);   }   //2.傳遞觸摸事件   @Override   public boolean onInterceptTouchEvent(MotionEvent ev) {     return mDragHelper.shouldInterceptTouchEvent(ev);   }   @Override   public boolean onTouchEvent(MotionEvent event) {     try {       mDragHelper.processTouchEvent(event);     } catch (Exception e) {       e.printStackTrace();     }     return true;   }   //獲取子控件的引用   @Override   protected void onFinishInflate() {     super.onFinishInflate();     mBackView = getChildAt(0); //獲取背景View,即展示數(shù)據(jù)的Item的右邊隱藏的側(cè)滑布局     mFrontView = getChildAt(1);//獲取前景View,即展示數(shù)據(jù)的Item   }   //獲取子控件的相關(guān)寬高信息   @Override   protected void onSizeChanged(int w, int h, int oldw, int oldh) {     super.onSizeChanged(w, h, oldw, oldh);     mWidth = mFrontView.getMeasuredWidth();     mHeight = mFrontView.getMeasuredHeight();     mRange = mBackView.getMeasuredWidth();   }   //確定子控件的初始位置   @Override   protected void onLayout(boolean changed, int left, int top, int right, int bottom) {     super.onLayout(changed, left, top, right, bottom);     layoutChildView(false);   }   /**    * 放置子控件的位置    *    * @param isOpen 是否是打開前景View,true打開,false關(guān)閉    */   private void layoutChildView(boolean isOpen) {     //計算前景View的位置,將坐標(biāo)信息封裝到矩形中     Rect fontRect = computerFontViewRect(isOpen);     //擺放前景View     mFrontView.layout(fontRect.left, fontRect.top, fontRect.right, fontRect.bottom);     //擺放背景View,left坐標(biāo)是前景View的right坐標(biāo)     int left = fontRect.right;     mBackView.layout(left, 0, left + mRange, mHeight);     //由于上面是后擺放背景View,所以會覆蓋前景View,因此需要通過下面的方式將前景View顯示在前面     bringChildToFront(mFrontView);   }   /**    * 計算前景View的坐標(biāo)    *    * @param isOpen 是否是打開前景View    * @return    */   private Rect computerFontViewRect(boolean isOpen) {     int left = isOpen ? -mRange : 0;     return new Rect(left, 0, left + mWidth, mHeight);   }   /**    * 打開側(cè)邊欄mBackView,默認平滑打開    */   public void open() {     open(true);   }   /**    * 打開側(cè)邊欄mBackView    *    * @param isSmooth 是否平滑打開    */   public void open(boolean isSmooth) {     if (isSmooth) {       if (mDragHelper.smoothSlideViewTo(mFrontView, -mRange, 0)) {         //動畫在繼續(xù)         ViewCompat.postInvalidateOnAnimation(this);       }     } else {       layoutChildView(true);     }   }   /**    * 關(guān)閉側(cè)邊欄mBackView,默認平滑關(guān)閉    */   public void close() {     close(true);   }   /**    * 關(guān)閉側(cè)邊欄mBackView    *    * @param isSmooth 是否平滑關(guān)閉    */   public void close(boolean isSmooth) {     if (isSmooth) {       if (mDragHelper.smoothSlideViewTo(mBackView, mWidth, 0)) {         //動畫在繼續(xù)         ViewCompat.postInvalidateOnAnimation(this);       }     } else {       layoutChildView(false);     }   }   @Override   public void computeScroll() {     super.computeScroll();     if (mDragHelper.continueSettling(true)) {       //動畫還在繼續(xù)       ViewCompat.postInvalidateOnAnimation(this);     }   } } 

如何使用呢?

使用該控件,必須要讓其有2個直接的子控件,如下布局所示:

<?xml version="1.0" encoding="utf-8"?> <mchenys.net.csdn.blog.myswipelayout.view.SwipeLayout    xmlns:android="http://schemas.android.com/apk/res/android"   android:id="@+id/sl"   android:layout_width="match_parent"   android:layout_height="60dp"   android:minHeight="60dp"   android:background="#44000000" >   <!--后置布局-->   <LinearLayout     android:layout_width="wrap_content"     android:layout_height="match_parent"     android:orientation="horizontal" >     <TextView       android:id="@+id/tv_call"       android:layout_width="60dp"       android:layout_height="match_parent"       android:background="#666666"       android:gravity="center"       android:text="Edit"       android:textColor="#ffffff" />     <TextView       android:id="@+id/tv_del"       android:layout_width="60dp"       android:layout_height="match_parent"       android:background="#ff0000"       android:gravity="center"       android:text="Delete"       android:textColor="#ffffff" />   </LinearLayout>   <!--前景布局-->   <LinearLayout     android:layout_width="match_parent"     android:layout_height="match_parent"     android:background="#44ffffff"     android:gravity="center_vertical"     android:orientation="horizontal" >     <ImageView       android:id="@+id/iv_image"       android:layout_width="40dp"       android:layout_height="40dp"       android:layout_marginLeft="15dp"       android:src="@drawable/head_1" />     <TextView       android:id="@+id/tv_name"       android:layout_width="wrap_content"       android:layout_height="wrap_content"       android:layout_marginLeft="15dp"       android:text="Name" />   </LinearLayout> </mchenys.net.csdn.blog.myswipelayout.view.SwipeLayout> 

就是這么簡單,跑起來就可以用了.不過這個只是定義出了SwipeLayout控件,如果要集成到ListView中,還需要做進一步的處理.
例如實現(xiàn)如下效果:

viewdraghelper,listview,item,Android

需要考慮2點:

1.在自定義SwipeLayout控件內(nèi)需要處理3種狀態(tài),打開,關(guān)閉,拖拽.

2.需要添加一個側(cè)滑監(jiān)聽接口,用于對外暴露當(dāng)前SwipeLayout的打開,關(guān)閉,拖拽,將要打開,將要關(guān)閉這5種情況.接口定義如下所示:

/**  * 側(cè)拉SwipeLayout的監(jiān)聽  * Created by mChenys on 2015/12/26.  */ public interface SwipeViewListener {   //關(guān)閉   void onClose(SwipeLayout mSwipeLayout);   //打開   void onOpen(SwipeLayout mSwipeLayout);   //正在側(cè)拉   void onDraging(SwipeLayout mSwipeLayout);   //開始要去關(guān)閉   void onStartClose(SwipeLayout mSwipeLayout);   //開始要去開啟   void onStartOpen(SwipeLayout mSwipeLayout); } 

SwipeLayout的3種狀態(tài),用enum表示即定義接收獲取SwipeViewListener監(jiān)聽器的方法1

//以下是定義SwipeLayout的打開,關(guān)閉,滑動的3種狀態(tài)   public enum Status {     CLOSE, OPEN, DRAGING;   }   //默認關(guān)閉   private Status mStatus = Status.CLOSE;   //滑動的監(jiān)聽器   private SwipeViewListener mSwipeViewListener;   //設(shè)置監(jiān)聽器   public void setSwipeViewListener(SwipeViewListener swipeViewListener) {     mSwipeViewListener = swipeViewListener;   } 

在onViewPositionChanged方法內(nèi)添加多一個方法,用于處理拖拽的監(jiān)聽.

/**    * 處理滑動,打開,關(guān)閉的3種情況    * 在onViewPositionChanged 調(diào)用    */   private void dispatchSwipeEvent() {     if (mSwipeViewListener != null) {       mSwipeViewListener.onDraging(this);     }     //記錄上一次的狀態(tài)     Status preStatus = mStatus;     //獲取當(dāng)前的狀態(tài)     mStatus = getCurrStatus();     if (preStatus != mStatus && null != mSwipeViewListener) {       //說明有狀態(tài)發(fā)生變化       if (mStatus == Status.CLOSE) {         //關(guān)閉         mSwipeViewListener.onClose(this);       } else if (mStatus == Status.OPEN) {         //打開         mSwipeViewListener.onOpen(this);       } else if (mStatus == Status.DRAGING) {         //這里有2中情況,要么要打開,要么要關(guān)閉         if (preStatus == Status.CLOSE) {           //如果之前是關(guān)閉的,那么就是要打開           mSwipeViewListener.onStartOpen(this);         } else if (preStatus == Status.OPEN) {           //如果之前是打開,那么就是要關(guān)閉           mSwipeViewListener.onStartClose(this);         }       }     }   }   /**    * 獲取當(dāng)前的狀態(tài)    *    * @return    */   private Status getCurrStatus() {     int left = mFrontView.getLeft();     if (left == 0) {       return Status.CLOSE;     } else if (left == -mRange) {       return Status.OPEN;     }     return Status.DRAGING;   } 

最后來看看MainActivity的測試:

public class MainActivity extends AppCompatActivity {   private List<String> mData = new ArrayList<>();//數(shù)據(jù)集合   @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     //獲取數(shù)據(jù),注意:Arrays.asList返回的并不是一個java.util.ArrayList,而是一個Arrays類的內(nèi)部類,該List實現(xiàn)是不能進行增刪操作的     //因此必須再包裝一下     mData = new ArrayList<>(Arrays.asList(Constant.NAME));     ListView listView = new ListView(this);     listView.setAdapter(mAdapter);     setContentView(listView);   }   //自定義適配器   private BaseAdapter mAdapter = new BaseAdapter() {     //標(biāo)記當(dāng)前打開的SwipeLayout的集合     private List<SwipeLayout> mOpenItem = new ArrayList<>();     @Override     public int getCount() {       return mData.size();     }     @Override     public String getItem(int position) {       return mData.get(position);     }     @Override     public long getItemId(int position) {       return position;     }     @Override     public View getView(final int position, View convertView, ViewGroup parent) {       ViewHolder holder = null;       if (null == convertView) {         holder = new ViewHolder();         convertView = View.inflate(MainActivity.this, R.layout.item_list, null);         holder.mSwipeLayout = (SwipeLayout) convertView;         holder.tvName = (TextView) convertView.findViewById(R.id.tv_name);         holder.tvDel = (TextView) convertView.findViewById(R.id.tv_del);         holder.tvEdit = (TextView) convertView.findViewById(R.id.tv_edit);         convertView.setTag(holder);       } else {         holder = (ViewHolder) convertView.getTag();       }       //設(shè)置側(cè)拉監(jiān)聽       holder.mSwipeLayout.setSwipeViewListener(getSwipeViewListener());       holder.tvName.setText(getItem(position));       holder.tvDel.setOnClickListener(new View.OnClickListener() {         @Override         public void onClick(View v) {           //刪除           mData.remove(position);           mAdapter.notifyDataSetChanged();         }       });       holder.tvEdit.setOnClickListener(new View.OnClickListener() {         @Override         public void onClick(View v) {           ToastUtils.showToast(MainActivity.this,"編輯");         }       });       return convertView;     }     class ViewHolder {       TextView tvName, tvDel, tvEdit;       SwipeLayout mSwipeLayout;     }     //獲取滑動監(jiān)聽器     private SwipeViewListener getSwipeViewListener() {       return new SwipeViewListener() {         @Override         public void onClose(SwipeLayout mSwipeLayout) {           //關(guān)閉是移除           mOpenItem.remove(mSwipeLayout);           ToastUtils.showToast(MainActivity.this, "關(guān)閉");         }         @Override         public void onOpen(SwipeLayout mSwipeLayout) {           //打開時添加           mOpenItem.add(mSwipeLayout);           ToastUtils.showToast(MainActivity.this, "打開");         }         @Override         public void onDraging(SwipeLayout mSwipeLayout) {         }         @Override         public void onStartClose(SwipeLayout mSwipeLayout) {           ToastUtils.showToast(MainActivity.this, "開始關(guān)閉");         }         @Override         public void onStartOpen(SwipeLayout mSwipeLayout) {           //將要打開時,需要將集合中的之前打開的SwipeLayout統(tǒng)統(tǒng)關(guān)閉           for (SwipeLayout swipeLayout : mOpenItem) {             swipeLayout.close();           }           mOpenItem.clear();//清空集合           ToastUtils.showToast(MainActivity.this, "開始打開");         }       };     }   }; } 

總結(jié)

以上所述是小編給大家介紹的 Android 中通過ViewDragHelper實現(xiàn)ListView的Item的側(cè)拉劃出效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對VEVB武林網(wǎng)網(wǎng)站的支持!


注:相關(guān)教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 渝中区| 上饶市| 泰和县| 甘德县| 呼玛县| 石家庄市| 新乡县| 太谷县| 铅山县| 荣昌县| 江达县| 彰化市| 凤凰县| 贺兰县| 奈曼旗| 且末县| 礼泉县| 金川县| 年辖:市辖区| 轮台县| 宝清县| 孟连| 华安县| 富宁县| 东乡| 东城区| 和林格尔县| 额尔古纳市| 郑州市| 滦南县| 台北市| 通化县| 峨眉山市| 华蓥市| 海原县| 临西县| 贵州省| 精河县| 大埔区| 正宁县| 张家口市|