自定义SwipeLayout控件实现ListView条目侧滑出现删除按钮,点击实现删除ListView条目

来源:互联网 发布:mac什么视频播放器好 编辑:程序博客网 时间:2024/05/18 19:23

今天,我们来实现类似于QQ最近联系人的聊天记录向右滑动出现删除按钮的功能。效果图如下:



布局文件如下:

这里这需要自定义一个SwipeLayout实现滑动出现删除按钮的效果,SwipeLayout.java代码如下:

package com.example.myoperation;import android.content.Context;import android.graphics.Rect;import android.support.v4.view.ViewCompat;import android.support.v4.widget.ViewDragHelper;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.widget.FrameLayout;/** * 侧拉删除控件 */public class SwipeLayout extends FrameLayout {   private Status status = Status.Close;   private OnSwipeLayoutListener swipeLayoutListener;      public Status getStatus() {      return status;   }   public void setStatus(Status status) {      this.status = status;   }   public OnSwipeLayoutListener getSwipeLayoutListener() {      return swipeLayoutListener;   }   public void setSwipeLayoutListener(OnSwipeLayoutListener swipeLayoutListener) {      this.swipeLayoutListener = swipeLayoutListener;   }   /**    * 三种状态,关闭开启和正在拖拽    */   public static enum Status{      Close, Open, Draging   }   public static interface OnSwipeLayoutListener {            void onClose(SwipeLayout mSwipeLayout);      void onOpen(SwipeLayout mSwipeLayout);      void onDraging(SwipeLayout mSwipeLayout);      // 要去关闭      void onStartClose(SwipeLayout mSwipeLayout);      // 要去开启      void onStartOpen(SwipeLayout mSwipeLayout);   }   /**    * 第一步实现父类方法,初始化ViewDragHelper    */   public SwipeLayout(Context context) {      this(context, null);   }   public SwipeLayout(Context context, AttributeSet attrs) {      this(context, attrs, 0);   }   public SwipeLayout(Context context, AttributeSet attrs, int defStyle) {      super(context, attrs, defStyle);      mDragHelper = ViewDragHelper.create(this, 1.0f, mCallback);   }   /**    * 第三步重写触摸事件方法    */   ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {      /**       * 根据返回结果决定当前child是否可以被拖拽       */      @Override      public boolean tryCaptureView(View view, int id) {         return true;//表示子View都可以被拖拽      }      /**       * 根据建议值修正将要移动到的(横向)位置       * @param child:当前拖拽的view       * @param left:建议值       * return: 真正值       */      public int clampViewPositionHorizontal(View child, int left, int dx) {         if(child == mFrontView){            if(left > 0){               return 0;            }else if(left < -mRange){               return -mRange;            }         }else if (child == mBackView) {            if(left > mWidth){               return mWidth;            }else if (left < mWidth - mRange) {               return mWidth - mRange;            }         }         return left;      }      /**       * 当View位置改变的时候。处理要做的事(更新状态,伴随动画,重绘界面)       * @param changedView:改变位置的View       * @param left:新的左边值       * @param dx:水平方向变化量       */      public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {         // 传递事件,是两个布局同时移动         if(changedView == mFrontView){            mBackView.offsetLeftAndRight(dx);         }else if (changedView == mBackView) {            mFrontView.offsetLeftAndRight(dx);         }         dispatchSwipeEvent();         // 兼容老版本         invalidate();      }      /**       * 当View被释放的时候,处理的事情(执行动画)       * @param releasedChild:被释放的子View       * @param xvel:释放时水平方向的速度       * @param yvel:释放时竖直方向的速度       */      public void onViewReleased(View releasedChild, float xvel, float yvel) {         if (xvel == 0 && mFrontView.getLeft() < -mRange / 2.0f) {            //当水平方向无速度并且向左滑动的距离大于范围的一半则打开            open();         }else if (xvel < 0) {            //当有水平方向向左的速度则打开            open();         }else {            //其余情况关闭            close();         }      }   };   /**    * 第二步将触摸事件传递给ViewDragHelper,计算子View宽高和移动范围    */   private ViewDragHelper mDragHelper;   private View mBackView;   private View mFrontView;   private int mHeight;   private int mWidth;   private int mRange;   @Override   public boolean onInterceptTouchEvent(MotionEvent ev) {      return mDragHelper.shouldInterceptTouchEvent(ev);   }      protected void dispatchSwipeEvent() {            if(swipeLayoutListener != null){         swipeLayoutListener.onDraging(this);      }      // 记录上一次的状态      Status preStatus = status;      // 更新当前状态      status = updateStatus();      if (preStatus != status && swipeLayoutListener != null) {         if (status == Status.Close) {            swipeLayoutListener.onClose(this);         } else if (status == Status.Open) {            swipeLayoutListener.onOpen(this);         } else if (status == Status.Draging) {            if(preStatus == Status.Close){               swipeLayoutListener.onStartOpen(this);            }else if (preStatus == Status.Open) {               swipeLayoutListener.onStartClose(this);            }         }      }   }   /**    * 更新状态    */   private Status updateStatus() {            int left = mFrontView.getLeft();      if(left == 0){         return Status.Close;      }else if (left == -mRange) {         return Status.Open;      }      return Status.Draging;   }   /**    * 关闭条目    */   public void close() {      close(true);   }   public void close(boolean isSmooth){      int finalLeft = 0;      if(isSmooth){         //开始动画         if(mDragHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)){            ViewCompat.postInvalidateOnAnimation(this);         }      }else {         layoutContent(false);      }   }   public void open() {      open(true);   }   public void open(boolean isSmooth){      int finalLeft = -mRange;      if(isSmooth){         //开始动画         if(mDragHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)){            ViewCompat.postInvalidateOnAnimation(this);         }      }else {         layoutContent(true);      }   }      @Override   public void computeScroll() {      super.computeScroll();      //持续平滑动画 (高频率调用)      if(mDragHelper.continueSettling(true)){         //如果返回true, 动画还需要继续执行         ViewCompat.postInvalidateOnAnimation(this);      }   }   @Override   public boolean onTouchEvent(MotionEvent event) {      try {         mDragHelper.processTouchEvent(event);      } catch (Exception e) {         e.printStackTrace();      }      return true;   }   /**    * 获取前后两个布局    */   @Override   protected void onFinishInflate() {      super.onFinishInflate();      // 当xml被填充完毕时调用      mBackView = getChildAt(0);      mFrontView = getChildAt(1);   }   /**    * 计算前后两个布局的宽高和移动的范围    */   @Override   protected void onSizeChanged(int w, int h, int oldw, int oldh) {      super.onSizeChanged(w, h, oldw, oldh);      mHeight = mFrontView.getMeasuredHeight();      mWidth = mFrontView.getMeasuredWidth();      mRange = mBackView.getMeasuredWidth();   }   /**    * 对前后两个布局进行摆放    */   @Override   protected void onLayout(boolean changed, int left, int top, int right,         int bottom) {      super.onLayout(changed, left, top, right, bottom);      // 摆放位置      layoutContent(false);   }   private void layoutContent(boolean isOpen) {      // 摆放前View      Rect frontRect = computeFrontViewRect(isOpen);      mFrontView.layout(frontRect.left, frontRect.top, frontRect.right, frontRect.bottom);      // 摆放后View      Rect backRect = computeBackViewViaFront(frontRect);      mBackView.layout(backRect.left, backRect.top, backRect.right, backRect.bottom);      // 调整顺序, 把mFrontView前置      bringChildToFront(mFrontView);   }   /**    * 计算前布局摆放的位置    */   private Rect computeFrontViewRect(boolean isOpen) {      int left = 0;      if(isOpen){         left = -mRange;      }      return new Rect(left, 0, left + mWidth, 0 + mHeight);   }   /**    * 根据前布局计算后布局的位置    */   private Rect computeBackViewViaFront(Rect frontRect) {      int left = frontRect.right;      return new Rect(left, 0, left + mRange, 0 + mHeight);   }}
activity_main.xml布局文件代码如下:

<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"    tools:context=".MainActivity" >    <ListView         android:layout_width="match_parent"        android:layout_height="match_parent"        android:id="@+id/lv"        >    </ListView></RelativeLayout>
listview的条目item_list布局文件如下,这里注意要使用自己定义的SwipeLayout控件作为根布局:

<?xml version="1.0" encoding="utf-8"?><com.example.myoperation.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="Call"            android:textColor="#fff" />        <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></com.example.myoperation.SwipeLayout>
MainActivity.java布局文件如下:

package com.example.myoperation;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.BaseAdapter;import android.widget.ListView;import android.widget.TextView;import com.example.myoperation.SwipeLayout.OnSwipeLayoutListener;import java.util.ArrayList;public class MainActivity extends Activity {   MyAdapter myAdapter;   private ArrayList<SwipeLayout> opendItems = new ArrayList<>();   private ArrayList<String> NAMES = new ArrayList<>();   private ListView mList;   @Override   protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      setContentView(R.layout.activity_main);      mList = (ListView) findViewById(R.id.lv);      initData();      myAdapter = new MyAdapter();      mList.setAdapter(myAdapter);   }   private void initData() {      for (int i = 0; i < 20; i++){         NAMES.add("name"+i);      }   }   public class MyAdapter extends BaseAdapter {      @Override      public int getCount() {         return NAMES.size();      }      @Override      public String getItem(int position) {         return NAMES.get(position);      }      @Override      public long getItemId(int position) {         // TODO Auto-generated method stub         return position;      }      @Override      public View getView(final int position, View convertView, ViewGroup parent) {         ViewHolder holder;         if(convertView == null){            convertView = View.inflate(MainActivity.this, R.layout.item_list, null);            holder = new ViewHolder();            holder.tv_name = (TextView)convertView.findViewById(R.id.tv_name);            holder.tv_del = (TextView)convertView.findViewById(R.id.tv_del);            convertView.setTag(holder);         }else {            holder = (ViewHolder)convertView.getTag();         }         holder.tv_name.setText(getItem(position));         holder.tv_del.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {               NAMES.remove(position);               myAdapter.notifyDataSetChanged();            }         });         /**          * 设置listview滑动监听,当listview滑动时关闭已经开启的的Item          */         mList.setOnScrollListener(new AbsListView.OnScrollListener() {            @Override            public void onScrollStateChanged(AbsListView view, int scrollState) {            }            @Override            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {               for (SwipeLayout layout : opendItems) {                  layout.close();               }               opendItems.clear();            }         });         /**          * 设置SwipeLayout滑动监听,当新的Item开启时,关闭之前已经开启的Item          */         SwipeLayout sl = (SwipeLayout)convertView;         sl.setSwipeLayoutListener(new OnSwipeLayoutListener() {            @Override            public void onStartOpen(SwipeLayout mSwipeLayout) {               for (SwipeLayout layout : opendItems) {                  layout.close();               }               opendItems.clear();            }            @Override            public void onStartClose(SwipeLayout mSwipeLayout) {            }            @Override            public void onOpen(SwipeLayout mSwipeLayout) {               opendItems.add(mSwipeLayout);            }            @Override            public void onDraging(SwipeLayout mSwipeLayout) {            }            @Override            public void onClose(SwipeLayout mSwipeLayout) {               opendItems.remove(mSwipeLayout);            }         });         return convertView;      }   }   static class ViewHolder {      TextView tv_name;      TextView tv_del;   }}
将上述布局文件和java代码布局好,编译运行就可以实现listview向右滑动点击删除按钮达到删除条目的效果了。代码中的一些注意点我都注释了,如果有不明白的地方可以留言哦,我尽量帮大家解决。

0 0
原创粉丝点击