Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發中模仿qq列表信息滑動刪除功能

Android開發中模仿qq列表信息滑動刪除功能

編輯:關於Android編程

這個效果的完成主要分為兩個部分

自定義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列表信息滑動刪除功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對本站網站的支持!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved