玩轉RecyclerView

鋸齒流沙發表於2018-01-08

第一次接觸RecyclerView你一定被它強大而神奇的功能所吸引,它可以實現ListView、GridView、瀑布流方式和水平列表方式,其自帶刪除和新增item的動畫,甚至簡簡單單就可以實現item拖拽和滑動刪除,其強大的功能往往讓我們歎為觀止。 今天我們一起玩RecyclerView。 本來錄製好視訊了,由於我用了Markdown版本來編寫,Markdown不能插入視訊,所以只能給大家看一張效果圖了

S70810-233812.jpg
新建一個名為RecyclerActivity的Activity 主頁面佈局(activity_recycler.xml)很簡單

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.lwj.uiproject.RecyclerActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:orientation="horizontal">
        <Button
            android:id="@+id/layout_vertical"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:text="vertical"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/layout_horizontal"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:text="horizontal"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/layout_grid"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:text="grid"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/layout_Staggered"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:text="Staggered"
            android:layout_height="wrap_content" />

    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:orientation="horizontal">
        <Button
            android:id="@+id/layout_add"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:text="add"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/layout_remove"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:text="remove"
            android:layout_height="wrap_content" />

    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="3dp"
        android:background="#ffededed"/>
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview_for2"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
複製程式碼

接下來看主頁面檔案內容,即RecyclerActivity

public class RecyclerActivity extends BaseActivity {

    private RecyclerView mRecyclerview;
    private RecyclerView2Aadapter mAdapter;
    private List<String> mDatas = new ArrayList<>();
    private Button vertical,horizontal,grid,staggered,add,remove;
    private ItemTouchHelper mItemTouchHelper;//實現item滑動

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler);
        getDatas();
        initView();
        initListener();
    }

    private void initView() {
        grid = (Button)this.findViewById(R.id.layout_grid);
        horizontal = (Button)this.findViewById(R.id.layout_horizontal);
        vertical = (Button)this.findViewById(R.id.layout_vertical);
        staggered = (Button)this.findViewById(R.id.layout_Staggered);
        add = (Button)this.findViewById(R.id.layout_add);
        remove = (Button)this.findViewById(R.id.layout_remove);
        mRecyclerview = (RecyclerView)this.findViewById(R.id.recyclerview_for2);
        //設定點選事件
        grid.setOnClickListener(mOnclickLitener);
        horizontal.setOnClickListener(mOnclickLitener);
        vertical.setOnClickListener(mOnclickLitener);
        staggered.setOnClickListener(mOnclickLitener);
        add.setOnClickListener(mOnclickLitener);
        remove.setOnClickListener(mOnclickLitener);

        //設定layout顯示樣式
        mRecyclerview.setLayoutManager(new GridLayoutManager(this,3));
        mAdapter = new RecyclerView2Aadapter(RecyclerActivity.this,mDatas);
        mRecyclerview.setAdapter(mAdapter);

        ItemTouchHelperCallBack back = new ItemTouchHelperCallBack(mAdapter);
        mItemTouchHelper = new ItemTouchHelper(back);
        mItemTouchHelper.attachToRecyclerView(mRecyclerview);//關聯Recyclerview
    }

    /**
     * 點選事件監聽器
     */
    private View.OnClickListener mOnclickLitener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mAdapter.setStagger(false);
            switch (view.getId()){
                case R.id.layout_grid:
                    mRecyclerview.setLayoutManager(new GridLayoutManager(RecyclerActivity.this,3));
                    break;
                case R.id.layout_horizontal:
                    mRecyclerview.setLayoutManager(new LinearLayoutManager(RecyclerActivity.this,LinearLayoutManager.HORIZONTAL,true));
                    break;
                case R.id.layout_Staggered:
                    mAdapter.setStagger(true);
                    mRecyclerview.setLayoutManager(new StaggeredGridLayoutManager(4,LinearLayoutManager.VERTICAL));
                    break;
                case R.id.layout_vertical:
                    mRecyclerview.setLayoutManager(new LinearLayoutManager(RecyclerActivity.this,LinearLayoutManager.VERTICAL,true));
                    break;
                case R.id.layout_add:
                    mAdapter.addData(20);
                    break;
                case R.id.layout_remove:
                    mAdapter.removeData(21);
                    break;
                default:
                    break;
            }
        }
    };

    //獲取資料
    public void getDatas() {
        for (int i = 0; i < 100; i++) {
            String data = "Itme "+i;
            mDatas.add(data);
        }
    }
    private void initListener() {
        //通過回撥來設定item的點選事件
        mAdapter.setOnItemOnclickListener(new RecyclerView2Aadapter.OnItemOnclickListener() {
            @Override
            public void onclick(String str) {
                Toast.makeText(RecyclerActivity.this,str,Toast.LENGTH_SHORT).show();
            }
        });
        //設定拖拽或者滑動的開始
        mAdapter.setOnStartDragListener(new ItemTouchHelperCallBack.OnStartDragListener() {
            @Override
            public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
                mItemTouchHelper.startDrag(viewHolder);
            }
        });
    }
}
複製程式碼

由於RecyclerView列表的item不會像ListView一樣具有item點選事件監聽器,所以我們需要自己設定一個點選事件的監聽器回撥,這樣子就可以在activity中處理item的點選事件了。這裡還通過button的點選給使用者展示不一樣的列表,包括像ListView、GridView、水平列表和瀑布流。RecyclerView自帶拖拽和滑動刪除的功能,這裡會通過ItemTouchHelper設定它的CallBack,然後關聯上RecyclerView即可實現這功能。

下面我們看adapter檔案RecyclerView2Aadapter.java的內容

public class RecyclerView2Aadapter extends RecyclerView.Adapter implements ItemTouchHelperCallBack.ItemTouchHelperAdapterListener {

    private List<String> mDatas;
    private Context mContext;
    private OnItemOnclickListener mOnItemOnclickListener;
    private List<Integer> heights;
    private boolean isStagger = false;
    private ItemTouchHelperCallBack.OnStartDragListener mDragStartListener;

    public RecyclerView2Aadapter(Context mContext, List<String> mDatas) {
        this.mDatas = mDatas;
        this.mContext = mContext;
        heights = new ArrayList<Integer>();
        if (mDatas != null && mDatas.size() > 0) {
            for (int i = 0; i < mDatas.size(); i++) {
                heights.add((int) Math.max(200, Math.random() * 550));
            }
        }
    }

    public void setOnStartDragListener(ItemTouchHelperCallBack.OnStartDragListener l){
        this.mDragStartListener = l;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LinearLayout view = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.recycler_view_item, parent, false);
        return new RecyclerViewItemLayout(view);
    }

    //繫結資料
    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        RecyclerViewItemLayout item = (RecyclerViewItemLayout) holder;
        int height = heights.get(position);
        String str = mDatas.get(position);
        setData(item, str, height);
//        item.mBtn.setOnTouchListener(new OnTouchListener() {
//            @Override
//            public boolean onTouch(View v, MotionEvent event) {
//                if (event.getAction() == MotionEvent.ACTION_DOWN) {
//                    mDragStartListener.onStartDrag(holder);
//                }
//                return false;
//            }
//        });
    }

    @Override
    public int getItemCount() {
        if (mDatas != null && mDatas.size() > 0) {
            return mDatas.size();
        }
        return 0;
    }

    /**
     * 設定資料
     *
     * @param item
     * @param str
     * @param height
     */
    public void setData(RecyclerViewItemLayout item, String str, int height) {
        if (TextUtils.isEmpty(str) || str == item.mTagStr) {
            return;
        } else {
            item.mTagStr = str;
            item.mBtn.setText(item.mTagStr);
            if (isStagger) {
                LinearLayout.LayoutParams p = (LinearLayout.LayoutParams) item.mBtn.getLayoutParams();
                p.height = height;
                item.mBtn.setLayoutParams(p);
            } else {
                LinearLayout.LayoutParams p = (LinearLayout.LayoutParams) item.mBtn.getLayoutParams();
                p.height = LinearLayout.LayoutParams.WRAP_CONTENT;
                item.mBtn.setLayoutParams(p);
            }
        }
    }

    private class RecyclerViewItemLayout extends RecyclerView.ViewHolder implements ItemTouchHelperCallBack.ItemTouchHelperViewHolder{
        private String mTagStr;
        private Button mBtn;

        public RecyclerViewItemLayout(LinearLayout view) {
            super(view);
            mBtn = (Button) view.findViewById(R.id.item_btn);
            mBtn.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (mOnItemOnclickListener != null) {
                        mOnItemOnclickListener.onclick(mTagStr);
                    }
                }
            });
        }


        @Override
        public void onItemSelected() {
            itemView.setBackgroundColor(0xffadadad);
            Animation animation=new ScaleAnimation(0,1.05f,0,1.05f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);//放大5%,即1.05倍,中點為縮放中心
            animation.setFillAfter(true);
            itemView.startAnimation(animation);
            itemView.bringToFront();
            itemView.setAlpha(1);//不透明
        }

        @Override
        public void onItemClear() {
            itemView.setBackgroundColor(0xffededed);
            itemView.clearAnimation();//清除動畫
        }
    }

    /**
     * 拖拽實現
     * @param fromPosition
     * @param toPosition
     * @return
     */
    @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        Collections.swap(mDatas, fromPosition, toPosition);
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

    /**
     * 滑動刪除
     * @param position
     */
    @Override
    public void onItemDismiss(int position) {
        mDatas.remove(position);
        notifyItemRemoved(position);
    }


    public interface OnItemOnclickListener {
        void onclick(String str);
    }

    /**
     * 設定item點選事件的回撥
     * @param listener
     */
    public void setOnItemOnclickListener(OnItemOnclickListener listener) {
        this.mOnItemOnclickListener = listener;
    }

    public void setStagger(boolean stagger) {
        isStagger = stagger;
    }

    /**
     * 往列表新增一個item
     * @param position
     */
    public void addData(int position) {
        mDatas.add(position, "additem" + position);
        notifyItemInserted(position);
    }

    /**
     * 刪除列表的一個item
     * @param position
     */
    public void removeData(int position) {
        mDatas.remove(position);
        notifyItemRemoved(position);
    }
}
複製程式碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <Button
        android:id="@+id/item_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="2dp"
        android:text="hello" />

</LinearLayout>
複製程式碼

RecyclerView的adapter需要實現RecyclerView.Adapter,預設需要重寫裡面的getItemCount、onBindViewHolder、onCreateViewHolder這三個方法,這裡需要ItemTouchHelperAdapterListener介面,實現item拖拽和滑動的回撥。RecyclerView具有的adapter好處就是具有解綁特性,為我們區分了item介面建立和資料繫結的方法,而不像ListView一樣都集中在getView方法中處理。RecyclerView.ViewHolder本身已經具有holder的快取。所以我們不用做特殊處理,大大提高了開發效率。 這裡建立的holder(RecyclerViewItemLayout)除了要繼承ViewHolder之外還要實現ItemTouchHelperViewHolder這個介面,這裡介面主要通過回撥來實現item拖拽和滑動,當使用者按住(選擇)item時和完成操作時item所做的一些效果處理。

接下來我們看看實現拖拽和滑動的ItemTouchHelper.Callback

public class ItemTouchHelperCallBack extends ItemTouchHelper.Callback{
    private static final float ALPHA = 1.0f;
    private ItemTouchHelperAdapterListener mItemTouchHelperAdapterListener;

    public ItemTouchHelperCallBack(ItemTouchHelperAdapterListener l){
        this.mItemTouchHelperAdapterListener = l;
    }

    /**
     * 是否支援長按
     * @return
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    /**
     * 是否支援滑動
     * @return
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return false;
    }


    /**
     * 滑動方向
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        if(recyclerView.getLayoutManager() instanceof GridLayoutManager || recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager)
        {
            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            final int swipeFlags = 0;
            return makeMovementFlags(dragFlags, swipeFlags);
        }
        else
        {
            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
            return makeMovementFlags(dragFlags, swipeFlags);
        }
    }

    /**
     * 拖拽完成
     * @param recyclerView
     * @param viewHolder
     * @param target
     * @return
     * Called when ItemTouchHelper wants to move the dragged item from its old position to the new position.
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        if(viewHolder.getItemViewType() != target.getItemViewType())
        {
            return false;
        }
        if (mItemTouchHelperAdapterListener != null){
            mItemTouchHelperAdapterListener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        }
        return true;
    }

    /**
     * 左右滑動刪除
     * Called when a ViewHolder is swiped by the user.
     * @param viewHolder
     * @param direction
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        if (mItemTouchHelperAdapterListener != null){
            mItemTouchHelperAdapterListener.onItemDismiss(viewHolder.getAdapterPosition());
        }
    }

    /**
     * 拖拽的時候給item新增一個背景色
     *
     * @param viewHolder
     * @param actionState
     * Called when the ViewHolder swiped or dragged by the ItemTouchHelper is changed.
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if(actionState != ItemTouchHelper.ACTION_STATE_IDLE)
        {
            if(viewHolder instanceof ItemTouchHelperViewHolder)
            {
                ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder)viewHolder;
                itemViewHolder.onItemSelected();
            }
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    /**
     * 在這個方法實現互動規則
     * @param c
     * @param recyclerView
     * @param viewHolder
     * @param dX
     * @param dY
     * @param actionState
     * @param isCurrentlyActive
     */
    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {

        if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE)
        {
            final float alpha = ALPHA - Math.abs(dX) / (float)viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(alpha);
            viewHolder.itemView.setTranslationX(dX);
        }
        else
        {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }

    }

    /**
     * Called by the ItemTouchHelper
     * when the user interaction with an element is over
     * 操作完成並且其動畫也結束後會呼叫該方法
     * 一般在該方法內恢復ItemView的初始狀態
     * and it also completed its animation.
     * @param recyclerView
     * @param viewHolder
     *
     */
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);

        viewHolder.itemView.setAlpha(ALPHA);
        if(viewHolder instanceof ItemTouchHelperViewHolder)
        {
            ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder)viewHolder;
            itemViewHolder.onItemClear();
        }
    }

    public interface ItemTouchHelperViewHolder
    {
        void onItemSelected();

        void onItemClear();
    }


    public interface ItemTouchHelperAdapterListener
    {

        boolean onItemMove(int fromPosition, int toPosition);

        void onItemDismiss(int position);
    }

    //當檢視請求開始挪動是呼叫
    public interface OnStartDragListener
    {
        void onStartDrag(RecyclerView.ViewHolder viewHolder);
    }

}
複製程式碼

這裡主要是實現item拖拽和滑動的功能,以及一些設定。使用者需要實現列表的item的滑動和拖拽功能就必須要重寫Callback的抽象方法才能完成。

參考文章: 吳小龍同學.《Android ItemTouchHelper 實踐》

有發現問題的可以留言,喜歡的點個贊。你的喜歡就是我分享的動力!!!