Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 安卓日記——仿QQ列表

安卓日記——仿QQ列表

編輯:關於Android編程

最近學習了如何做一個像QQ的左滑RecyclerView的item顯示選項的,主要是用到Scroller

我們首先新建一個自己的RecyclerView

定義好一些要用的的變量
重寫構造方法,把前兩個構造方法改為如下,使無論如何構造都要執行第三個構造方法
在第三個構造方法裡初始化Scroller

public class LeftSwipeMenuRecyclerView extends RecyclerView {

    //置頂按鈕
    private TextView tvTop;
    //刪除按鈕
    private TextView tvDelete;
    //item相應的布局
    private LinearLayout mItemLayout;
    //菜單的最大寬度
    private int mMaxLength;

    //上一次觸摸行為的x坐標
    private int mLastX;
    //上一次觸摸行為的y坐標
    private int mLastY;

    //當前觸摸的item的位置
    private int mPosition;

    //是否在垂直滑動列表
    private boolean isDragging;
    //item是在否跟隨手指移動
    private boolean isItemMoving;
    //item是否開始自動滑動
    private boolean isStartScroll;

    //左滑菜單狀態   0:關閉 1:將要關閉 2:將要打開 3:打開
    private int mMenuState;
    private static int MENU_CLOSED = 0;
    private static int MENU_WILL_CLOSED = 1;
    private static int MENU_OPEN = 2;
    private static int MENU_WILL_OPEN = 3;

    //實現彈性滑動,恢復
    private Scroller mScroller;

    //item的事件監聽
    private OnItemActionListener mListener;

    public LeftSwipeMenuRecyclerView(Context context) {
        this(context, null);
    }

    public LeftSwipeMenuRecyclerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LeftSwipeMenuRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mScroller = new Scroller(context, new LinearInterpolator());
    }

重寫onTouchEvent方法

event主要有以下幾個Action

ACTION_DOWN 手指接觸到屏幕 ACTION_MOVE 手指在屏幕滑動 ACTION_UP 手指離開屏幕

一開始肯定要獲取x和y的相對坐標

int x= (int) event.getX();
int y= (int) event.getY();

然後接下來分別處理3個不同的行為

1.ACTION_DOWN

case MotionEvent.ACTION_DOWN:
                if (mMenuState == MENU_CLOSED) {
                    //根據坐標獲得view
                    View view = findChildViewUnder(x, y);
                    if (view == null) {
                        return false;
                    }
                    //獲得這個view的ViewHolder
                    RVAdapter.Holder holder = (RVAdapter.Holder) getChildViewHolder(view);
                    //獲得這個view的position
                    mPosition = holder.getAdapterPosition();
                    //獲得這個view的整個布局
                    mItemLayout = holder.llLayout;
                    //獲得這個view的刪除按鈕
                    tvDelete = holder.tvDelete;
                    //這個view的整個置頂按鈕
                    tvTop = holder.tvTop;
                    //兩個按鈕的寬度
                    mMaxLength = tvDelete.getWidth() + tvTop.getWidth();

                    //設置刪除按鈕點擊監聽
                    tvDelete.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            mItemLayout.scrollTo(0, 0);
                            mMenuState =MENU_CLOSED;
                            mListener.OnItemDelete(mPosition);
                        }
                    });
                    //設置置頂按鈕點擊監聽
                    tvTop.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            mItemLayout.scrollTo(0, 0);
                            mMenuState =MENU_CLOSED;
                            mListener.OnItemTop(mPosition);
                        }
                    });
                    //如果是打開狀態,點擊其他就把當前menu關閉掉
                } else if (mMenuState == MENU_OPEN) {
                    mScroller.startScroll(mItemLayout.getScrollX(), 0, -mMaxLength, 0, 200);
                    invalidate();
                    mMenuState = MENU_CLOSED;
                    //該點擊無效
                    return false;
                } else {
                    return false;
                }
                break;

2.ACTION_MOVE

            case MotionEvent.ACTION_MOVE:
                //計算偏移量
                int dx = mLastX - x;
                int dy = mLastY - y;
                //當前滑動的x
                int scrollx = mItemLayout.getScrollX();

                if (Math.abs(dx) > Math.abs(dy)) {

                    isItemMoving = true;
                    //超出左邊界則始終保持不動
                    if (scrollx + dx <= 0) {
                        mItemLayout.scrollTo(0, 0);
                        //滑動無效
                        return false;
                    //超出右邊界則始終保持不動
                    } else if (scrollx + dx >= mMaxLength) {
                        mItemLayout.scrollTo(mMaxLength, 0);
                        //滑動無效
                        return false;
                    }
                    //菜單隨著手指移動
                    mItemLayout.scrollBy(dx, 0);
                //如果水平移動距離大於30像素的話,recyclerView不會上下滑動
                }else  if (Math.abs(dx) > 30){
                    return true;
                }
                //如果菜單正在打開就不能上下滑動
                if (isItemMoving){
                    mLastX = x;
                    mLastY = y;
                    return true;
                }
                break;

3.ACTION_UP

case MotionEvent.ACTION_UP:
                //手指抬起時判斷是否點擊,靜止且有Listener才能點擊
                if (!isItemMoving && !isDragging && mListener != null) {
                    mListener.OnItemClick(mPosition);
                }
                isItemMoving = false;

                //等一下要移動的距離
                int deltaX = 0;
                int upScrollx = mItemLayout.getScrollX();
                //滑動距離大於1/2menu長度就自動展開,否則就關掉
                if (upScrollx >= mMaxLength / 2) {
                    deltaX = mMaxLength - upScrollx;
                    mMenuState = MENU_WILL_OPEN;
                } else if (upScrollx < mMaxLength / 2) {
                    deltaX = -upScrollx;
                    mMenuState = MENU_WILL_CLOSED;
                }
                //知道我們為什麼不直接把mMenuState賦值為MENU_OPEN或者MENU_CLOSED嗎?因為滑動時有時間的,我們可以在滑動完成時才把狀態改為已經完成
                mScroller.startScroll(upScrollx, 0, deltaX, 0, 200);
                isStartScroll = true;
                //刷新界面
                invalidate();
                break;

之後還要在onTouchEvent方法裡刷新坐標

        //只有更新mLastX,mLastY數據才會准確
        mLastX = x;
        mLastY = y;
        return super.onTouchEvent(e);

因為我們用到了startScroll所以要重寫computeScroll方法

    public void computeScroll() {
        //判斷scroller是否完成滑動
        if (mScroller.computeScrollOffset()) {
            mItemLayout.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            //這個很重要
            invalidate();
        //如果已經完成就改變狀態
        } else if (isStartScroll) {
            isStartScroll = false;
            if (mMenuState == MENU_WILL_CLOSED) {
                mMenuState = MENU_CLOSED;
            }
            if (mMenuState == MENU_WILL_OPEN) {
                mMenuState = MENU_OPEN;
            }
        }
    }

**還要監聽RecyclerView是否在上下滑動

 @Override
    public void onScrollStateChanged(int state) {
        super.onScrollStateChanged(state);
        //是否在上下滑動
        isDragging = state == SCROLL_STATE_DRAGGING;
    }

還要設置Listener

//設置Listener
    public void setOnItemActionListener(OnItemActionListener onItemActionListener) {
        this.mListener = onItemActionListener;
    }

這個Listener是要自己新建的

public interface OnItemActionListener {
    void OnItemClick(int position);
    void OnItemTop(int position);
    void OnItemDelete(int position);
}

最後是點擊,置頂,刪除在Activity裡的回調

這裡只展示回調實現部分,我這裡用的List是LinkedList,可以在第一位添加數據

rv.setOnItemActionListener(new OnItemActionListener() {
            //點擊
            @Override
            public void OnItemClick(int position) {
                Toast.makeText(MainActivity.this,"Click"+position,Toast.LENGTH_SHORT).show();
            }
            //置頂
            @Override
            public void OnItemTop(int position) {
                //獲得當前位置的內容
                String temp =list.get(position);
                //移除這個item
                list.remove(position);
                //把它添加到第一個
                list.addFirst(temp);
                //更新數據源
                adapter.notifyDataSetChanged();
            }
            //刪除
            @Override
            public void OnItemDelete(int position) {
                list.remove(position);
                //更新數據源
                adapter.notifyDataSetChanged();
            }
        });

Adapter和布局的代碼太簡單我就不放出來了,大家可以到源碼裡看看有什麼

效果圖

效果圖

源碼地址

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