首页 > 系统 > Android > 正文

Android开发中模仿qq列表信息滑动删除功能

2019-12-12 04:05:05
字体:
来源:转载
供稿:网友

这个效果的完成主要分为两个部分

自定义view作为listview的列表项 一个view里面包括 显示头像,名字,消息内容等的contentView和滑动才能显示出来的删除,置顶的右边菜单menuView 在手指移动的时候同时改变这两个视图的位置

重写listview 判断item向左还是向右滑动 正常的滚动还是左右滑动等等 重写onTouchEvent 进行事件分发

大致思路:

listview进行事件分发,判断需要滑动还是滚动等状态,如果需要滑动将事件传递给item进行滑动处理. 在item中控制contentView和menuView进行位置的变化完成滚动效果

重写listview代码

public class SlideListView extends ListView{  private SlideItem mTouchView=null;//记录当前点击的item View  private float mDownX;//x轴坐标  private float mDownY;//y轴坐标  private int mTouchState;//记录点击状态  private int mTouchPosition;//记录点击位置  private static final int TOUCH_STATE_NONE=0; //按下状态  private static final int TOUCH_STATE_X=1;//横滑状态  private static final int TOUCH_STATE_Y=2;//竖滑状态  //判断横竖滑动的最小值  private static final int MAX_Y=5;  private static final int MAX_X=3;  public SlideListView(Context context) {    super(context);  }  public SlideListView(Context context, AttributeSet attrs) {    super(context, attrs);  }  public SlideListView(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);  }  @Override  public boolean onTouchEvent(MotionEvent ev) {    if (ev.getAction() != MotionEvent.ACTION_DOWN && mTouchView == null)      return super.onTouchEvent(ev);    switch (ev.getAction()) {      case MotionEvent.ACTION_DOWN:        //按住的item的position        int oldPosition = mTouchPosition;        //记录位置        mDownX = ev.getX();        mDownY = ev.getY();        mTouchState = TOUCH_STATE_NONE;        //根据当前横纵坐标点获取点击的item的position        mTouchPosition = this.pointToPosition((int) ev.getX(), (int) ev.getY());        //判断当前点击的是否和上次点击的item是同一个,如果是同一个,并且状态是打开了的就记录状态和坐标        //记录坐标通过Item中的downX属性        if (mTouchPosition == oldPosition && mTouchView != null && mTouchView.isOpen()) {          mTouchState = TOUCH_STATE_X;          mTouchView.onSwipe(ev);          return true;        }        //获取当前的item的View        View currentView = getChildAt(mTouchPosition - getFirstVisiblePosition());        //如果不是同一个item 那么点击的话就关闭掉之前打开的item        if (mTouchView != null && mTouchView.isOpen()) {          mTouchView.smoothCloseMenu();          mTouchView = null;          return super.onTouchEvent(ev);        }        //判断该view的类型        if (currentView instanceof SlideItem) {          mTouchView = (SlideItem) currentView;        }        if (mTouchView != null) {          mTouchView.onSwipe(ev);        }        break;      case MotionEvent.ACTION_MOVE:        float dy = Math.abs((ev.getY() - mDownY));        float dx = Math.abs((ev.getX() - mDownX));        if (mTouchState == TOUCH_STATE_X) {          if (mTouchView != null) {            //执行滑动            mTouchView.onSwipe(ev);          }          return true;        } else if (mTouchState == TOUCH_STATE_NONE) {          //判断滑动方向,x方向执行滑动,Y方向执行滚动          if (Math.abs(dy) > MAX_Y) {            mTouchState = TOUCH_STATE_Y;          } else if (dx > MAX_X) {            mTouchState = TOUCH_STATE_X;          }        }        break;      case MotionEvent.ACTION_UP:        //判断状态        if (mTouchState == TOUCH_STATE_X) {          if (mTouchView != null) {            mTouchView.onSwipe(ev);            //如过最后状态是打开 那么就重新初始化            if (!mTouchView.isOpen()) {              mTouchPosition = -1;              mTouchView = null;            }          }          ev.setAction(MotionEvent.ACTION_CANCEL);          super.onTouchEvent(ev);          return true;        }        break;    }    return super.onTouchEvent(ev);  }}

重写item项

view的滑动效果都是在里完成的 使用了Scroller类

关于Scroller的使用文章最后已经粘出了大神的帖子 不懂的同学可以先把Scroller的使用理解了在看这个滑动效果就很好懂了 我在这里简单讲讲

这个类的并没有实际的完成滚动效果 它是一个计算控件移动轨迹的辅助类,
比如说:在1秒内从位置0移动到位置100 这个类会计算出移动的数值,它并没有完成滑动的效果,但是告诉了我们这个滑动的过程 实际的上的view移动操作在computeScroll()完成 这个方法是view的自带方法 需要我们重写

computeScroll方法又是怎么情况呢 看源码 本身是个空的 就等着我们实现 我们实际改变view位置的代码就是在此方法内调用的

额。。。英语一般

大致意思 我们要通过Scroller实现一个滚动效果的时候 父布局就会调用此方法来完成子视图的位置更新

官方的描述是:当我们执行ontouch或invalidate()或postInvalidate()都会导致这个方法的执行

在此方法中不断的获取到移动的距离 通过view自带的layout()方法更新view所在位置

 /**   * Called by a parent to request that a child update its values for mScrollX   * and mScrollY if necessary. This will typically be done if the child is   * animating a scroll using a {@link android.widget.Scroller Scroller}   * object.   */  public void computeScroll() {  }public class SlideItem extends LinearLayout {  private View contentView = null; //不滑动显示的view  private View menuView = null; //左滑显示的view  //计算滑动 动画效果  private Scroller mOpenScroller;  private Scroller mCloseScroller;  private int downX; //开始按下的位置  //记录状态  private int state = STATE_CLOSE;  private static final int STATE_CLOSE = 0;  private static final int STATE_OPEN = 1;  private int mBaseX;//在关闭滑动的时候计算与父布局的剩余距离  public SlideItem(Context context) {    super(context);  }  public SlideItem(Context context, AttributeSet attrs) {    super(context, attrs);  }  public SlideItem(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);  }  public void setContentView(View contentView, View rightView){    this.contentView = contentView;    this.menuView = rightView;    //初始化mColoseScroller和mOpenScroller    mCloseScroller=new Scroller(getContext());    mOpenScroller = new Scroller(getContext());    initView();  }  //child view的布局参数设定好后 添加到parent view里面  private void initView() {    //这是设置宽和高    LayoutParams contentParams = new LayoutParams        (LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);    LayoutParams rightParams=new LayoutParams        (LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);    contentView.setLayoutParams(contentParams);    contentView.setPadding(10,10,10,10);    menuView.setLayoutParams(rightParams);    this.addView(contentView);    this.addView(menuView);  }  // 判断是否滑出的状态  public boolean isOpen() {    return state == STATE_OPEN;  }  /**   * 供listView调用 进行视图的移动  listView判断状态 什么情况下左滑   * @param event   * @return   */  public boolean onSwipe(MotionEvent event) {    switch (event.getAction()) {      case MotionEvent.ACTION_DOWN:        downX = (int) event.getX();        break;      case MotionEvent.ACTION_MOVE:        //按下位置减去移动位置 获取移动的距离        int dis = (int) (downX - event.getX());        if (state == STATE_OPEN) {          dis += menuView.getWidth();        }        //移动        move(dis);        break;      case MotionEvent.ACTION_UP:        //当滑到右边视图一半的距离 自动滑进滑出        if ((downX - event.getX()) > (menuView.getWidth() / 2)) {          smoothOpenMenu();        } else {          smoothCloseMenu();          return false;        }        break;    }    //消费掉事件    return true;  }  /**   * 视图重新绘制时调用   */  @Override  public void computeScroll() {    if (state == STATE_OPEN) {      //computeScrollOffset滑动是否结束      if (mOpenScroller.computeScrollOffset()) {        move(mOpenScroller.getCurrX());        postInvalidate();      }    } else {      if (mCloseScroller.computeScrollOffset()) {        move(mBaseX - mCloseScroller.getCurrX());        postInvalidate();      }    }  }  /**   * 移动视图   * @param dis   */  private void move(int dis) {    //这两个判断是为了保证 不要把视图移动过多 导致视图偏移    if (dis > menuView.getWidth()) {      dis = menuView.getWidth();    }    if (dis < 0) {      dis = 0;    }    //view.layout()控制view相对于其父布局的位置  在触发移动的时候调用不断改变位置 完成实际的滑动效果    contentView.layout(-dis, contentView.getTop(), contentView.getWidth() - dis, getMeasuredHeight());    menuView.layout(contentView.getWidth() - dis, menuView.getTop(), contentView.getWidth() + menuView.getWidth() - dis, menuView.getBottom());  }  /**   * 滑动关闭   * contentView.getLeft() 与其父视图的相对位置   */  public void smoothCloseMenu() {    state = STATE_CLOSE;    mBaseX = -contentView.getLeft();    mCloseScroller.startScroll(0, 0, mBaseX, 0, 350);    postInvalidate();  }  /**   * 滑动打开   */  public void smoothOpenMenu() {    state = STATE_OPEN;    mOpenScroller.startScroll(-contentView.getLeft(), 0, menuView.getWidth(), 0, 350);    postInvalidate();  }  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    if(menuView != null)      menuView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),          MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));  }  @Override  protected void onLayout(boolean changed, int l, int t, int r, int b) {    //确保centerView menuView的显示位置    if(contentView != null)      contentView.layout(0, 0, getMeasuredWidth(), contentView.getMeasuredHeight());    if(menuView != null)      menuView.layout(getMeasuredWidth(), 0, getMeasuredWidth() + menuView.getMeasuredWidth(), contentView.getMeasuredHeight());  }}

适配器

public class SlideAdapter extends BaseAdapter implements View.OnClickListener{  private List<String> dataList;  private Context context;  private LayoutInflater inflater;  public SlideAdapter(Context context, List<String> dataList) {    this.context = context;    this.dataList = dataList;    this.inflater=LayoutInflater.from(context);  }  @Override  public int getCount() {    return 5;  }  @Override  public Object getItem(int position) {    return null;  }  @Override  public long getItemId(int position) {    return 0;  }  @Override  public View getView(int position, View convertView, ViewGroup parent) {    ViewHolder holder=null;    if (convertView==null){      View content=inflater.inflate(R.layout.adapter_item_content,null);      View menu=inflater.inflate(R.layout.adapter_item_menu,null);      holder=new ViewHolder(content,menu);      SlideItem slideItem=new SlideItem(context);      slideItem.setContentView(content,menu);      convertView=slideItem;      convertView.setTag(holder);    }else {      holder= (ViewHolder) convertView.getTag();    }    holder.itemTvDelete.setOnClickListener(this);    holder.itemTvNoRead.setOnClickListener(this);    holder.itemTvToTop.setOnClickListener(this);    return convertView;  }  class ViewHolder{    TextView itemTvToTop;    TextView itemTvNoRead;    TextView itemTvDelete;    public ViewHolder(View center,View menu) {      this.itemTvToTop = (TextView) menu.findViewById(R.id.item_to_top);      this.itemTvNoRead = (TextView) menu.findViewById(R.id.item_no_read);      this.itemTvDelete = (TextView) menu.findViewById(R.id.item_delete);    }  }  @Override  public void onClick(View v) {    switch (v.getId()){      case R.id.item_no_read:        Toast.makeText(context,"标为未读",Toast.LENGTH_SHORT).show();        break;      case R.id.item_to_top:        Toast.makeText(context,"置顶了熬",Toast.LENGTH_SHORT).show();        break;      case R.id.item_delete:        Toast.makeText(context,"删除啦",Toast.LENGTH_SHORT).show();        break;    }  }}

参考文档:

SwipeMenuListView github上的实现此效果的开源项目

以上所述是小编给大家介绍的Android开发中模仿qq列表信息滑动删除功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对武林网网站的支持!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表