1. 程式人生 > >Android一步一步帶你實現RecyclerView的拖拽和側滑刪除功能

Android一步一步帶你實現RecyclerView的拖拽和側滑刪除功能

先上效果圖:
這裡寫圖片描述

本篇文章我們來學習一個開源專案Android-ItemTouchHelper-Demo
這個專案使用了RecyclerView的ItemTouchHelper類實現了Item的拖動和刪除功能,ItemTouchHelper是v7包下的一個類,我們看一下他的介紹

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.

這是一個工具類,專門用來配合RecyclerView實現滑動刪除和拖拽功能的類

先搭起一個小框架

我們從頭開始,一點一點實現最終的功能,首先我們先搭起一個小框架,我們的首頁顯示兩個Item,一個點選進入ListView形式的RecyclerView;一個點選進入GridView形式的RecyclerView。
這裡寫圖片描述

我們先在values/strings.xml中定義一個數組

 <array name="main_items">
        <item>List - Basic Drag and Swipe</item>
        <item>Grid - Basic Drag</item>
 </array>

再建立一個MainFragment繼承自ListFragment

public class MainFragment extends ListFragment {
    private onListItemClickListener mListItemClickListener;
    //定義一個回撥介面,用來將點選事件傳回他的宿主Activity去做,Fragment中不做具體的邏輯操作
public interface onListItemClickListener{ void onListItemClick(int position); } public MainFragment(){ } @Override public void onAttach(Context context) { super.onAttach(context); //他的宿主Activity將實現onListItemClickListener介面 //使用getActivity()獲得的宿主Activity,將他強轉成onListItemClickListener介面
mListItemClickListener = (onListItemClickListener)getActivity(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); //獲得我們在strings.xml中定義個數組 final String[] items = getResources().getStringArray(R.array.main_items); //建立介面卡 final ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, items); //設定介面卡 setListAdapter(adapter); } @Override public void onListItemClick(ListView l, View v, int position, long id) { if (mListItemClickListener!=null){ //由於宿主Activity實現了onListItemClickListener介面 //因此呼叫的是宿主Activity的onListItemClick方法 //並且將點選的item的position傳給Activity mListItemClickListener.onListItemClick(position); } } }

我們再建立一個RecyclerListFragment,我們先不做具體的實現,只是先把架子搭起來

public class RecyclerListFragment extends Fragment {
    public RecyclerListFragment(){}

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return new RecyclerView(container.getContext());
    }
}

再來一個RecyclerGridFragment

public class RecyclerGridFragment extends Fragment {
    public RecyclerGridFragment(){}

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return new RecyclerView(container.getContext());
    }
}

好了,Fragment我們已經準備好了,就差一個宿主Activity了,現在我們就來建立MainActivity,並且實現MainFragment.OnListItemClickListener介面,重寫onListItemClick方法

public class MainActivity extends AppCompatActivity implements MainFragment.onListItemClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //當savedInstanceState為null時才new一個MainFragment出來
        //否則每次旋轉螢幕都會new出來一個
        if (savedInstanceState == null){
            MainFragment fragment = new MainFragment();
            //用add將MainFragment新增到framelayout上
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.content,fragment)
                    .commit();
        }
    }


    @Override
    public void onListItemClick(int position) {
        //當MainFragment的Item被點選後,就會回撥此方法
        //在此方法中寫真正的邏輯,這樣Activity和Fragment
        //之間就是鬆耦合關係,MainFragment可以複用
        Fragment fragment = null;
        switch (position){
            case 0:
                //當點選第一個item時候,new一個RecyclerListFragment
                fragment = new RecyclerListFragment();
                break;
            case 1:
                //當點選第二個item時候,new一個RecyclerGridFragment
                fragment = new RecyclerGridFragment();
                break;
        }
        //這次用replace,替換framelayout的佈局,也就是MainFragment
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.content,fragment)
                .addToBackStack(null)
                .commit();
    }
}

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <FrameLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

好了,現在我們可以執行一下,執行的結果就是一開始那個截圖的效果,我們點選item會進入相應的Fragment中,但是現在是空白的,因為我們還沒寫完呢。

為RecyclerView寫Adapter

我們之前使用ListView的時候,資料是靠Adapter適配到ListView上的吧,RecyclerView也是靠Adapter,所以我們先來寫個Adapter吧

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ItemViewHolder> {

    /**在這裡反射出我們的item的佈局*/
    @Override
    public RecyclerViewAdapter.ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }
    /**在這裡為佈局中的控制元件設定資料*/
    @Override
    public void onBindViewHolder(ItemViewHolder holder, int position) {

    }
    /**返回資料個數*/
    @Override
    public int getItemCount() {
        return 0;
    }
    /**相當於ListView中的ViewHolder*/
    public static class ItemViewHolder extends RecyclerView.ViewHolder{

        public ItemViewHolder(View itemView) {
            super(itemView);
        }
    }
}

這就是一個標準的Adapter的結構,接下來我們要逐一完善其中的方法,首先我們先在values/strings.xml中增加我們item的陣列

<array name="dummy_items">
        <item>One</item>
        <item>Two</item>
        <item>Three</item>
        <item>Four</item>
        <item>Five</item>
        <item>Six</item>
        <item>Seven</item>
        <item>Eight</item>
        <item>Nine</item>
        <item>Ten</item>
    </array>

接著在構造方法中將資料新增到ArrayList中

 public RecyclerViewAdapter(Context context){
        //初始化資料
        mItems.addAll(Arrays.asList(context.getResources().getStringArray(R.array.dummy_items)));
    }

然後我們再寫我們item的佈局檔案

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:text="one"
        android:padding="20dp"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <ImageView
        android:id="@+id/handle"
        android:layout_width="?listPreferredItemHeight"
        android:layout_height="?listPreferredItemHeight"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:scaleType="center"
        android:src="@drawable/ic_reorder_grey_500_24dp"
        />
</RelativeLayout>

接下來是在ItemViewHolder中進行findViewById操作

 /**相當於ListView中的ViewHolder*/
    public static class ItemViewHolder extends RecyclerView.ViewHolder{
        private TextView text;
        private ImageView handle;
        public ItemViewHolder(View itemView) {
            super(itemView);
            text = (TextView) itemView.findViewById(R.id.text);
            handle = (ImageView) itemView.findViewById(R.id.handle);
        }
    }

然後在onCreateViewHolder中加載出佈局,並且完成控制元件的初始化

 /**在這裡反射出我們的item的佈局*/
    @Override
    public RecyclerViewAdapter.ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //利用反射將item的佈局加載出來
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view,null);
        //new一個我們的ViewHolder,findViewById操作都在ItemViewHolder的構造方法中進行了
        return new ItemViewHolder(view);
    }

然後在onBindViewHolder中給控制元件繫結資料

 /**在這裡為佈局中的控制元件設定資料*/
    @Override
    public void onBindViewHolder(ItemViewHolder holder, int position) {
        holder.text.setText(mItems.get(position));
        //handle是我們拖動item時候要用的,目前先空著
        holder.handle.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return false;
            }
        });
    }

還有這個方法別忘了

 /**返回資料個數*/
    @Override
    public int getItemCount() {
        return mItems.size();
    }

好了我們一個Adapter已經寫完了,然後我們來到RecyclerListFragment中給我們的RecyclerView進行配置

public class RecyclerListFragment extends Fragment {
    public RecyclerListFragment(){}

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return new RecyclerView(container.getContext());
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        RecyclerViewAdapter adapter = new RecyclerViewAdapter(getActivity());
        //引數view即為我們在onCreateView中return的view
        RecyclerView recyclerView = (RecyclerView)view;
        //固定recyclerview大小
        recyclerView.setHasFixedSize(true);
        //設定adapter
        recyclerView.setAdapter(adapter);
        //設定佈局型別為LinearLayoutManager,相當於ListView的樣式
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

    }
}

同樣的,我們再來配置RecyclerGridFragment

public class RecyclerGridFragment extends Fragment {
    public RecyclerGridFragment(){}

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return new RecyclerView(container.getContext());
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        RecyclerViewAdapter adapter = new RecyclerViewAdapter(getActivity());
        RecyclerView recyclerView = (RecyclerView)view;
        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(adapter);
        //只有這裡和RecyclerListFragment不一樣,這裡我們指定佈局為GridView樣式,2列
        recyclerView.setLayoutManager(new GridLayoutManager(getActivity(),2));

    }
}

好了,現在我們可以運行了,這就是recyclerView的使用方法,接下來我們就要為recyclerView新增拖拽和側滑刪除的功能了

實現拖拽和側滑刪除功能

拖拽和側滑刪除的功能我們要藉助ItemTouchHelper這個類,我們只需要創建出一個ItemTouchHelper物件,然後呼叫mItemTouchHelper.attachToRecyclerView(recyclerView);就可以了。
我們看一下ItemTouchHelper的構造方法,他需要一個Callback

    public ItemTouchHelper(Callback callback) {
        mCallback = callback;
    }

這個Callback是ItemTouchHelper的內部類,所以我們需要寫一個類繼承自ItemTouchHelper.Callback ,然後重寫裡面的方法

public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
    /**這個方法是用來設定我們拖動的方向以及側滑的方向的*/
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
       return 0;
    }
    /**當我們拖動item時會回撥此方法*/
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {

        return false;
    }
    /**當我們側滑item時會回撥此方法*/
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

    }
}

首先先來完成getMovementFlags方法

 /**這個方法是用來設定我們拖動的方向以及側滑的方向的*/
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

        //如果是ListView樣式的RecyclerView
        if (recyclerView.getLayoutManager() instanceof LinearLayoutManager){
            //設定拖拽方向為上下
            final int dragFlags = ItemTouchHelper.UP|ItemTouchHelper.DOWN;
            //設定側滑方向為從左到右和從右到左都可以
            final int swipeFlags = ItemTouchHelper.START|ItemTouchHelper.END;
            //將方向引數設定進去
            return makeMovementFlags(dragFlags,swipeFlags);
        }else{//如果是GridView樣式的RecyclerView
            //設定拖拽方向為上下左右
            final int dragFlags = ItemTouchHelper.UP|ItemTouchHelper.DOWN|
                    ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
            //不支援側滑
            final int swipeFlags = 0;
            return makeMovementFlags(dragFlags,swipeFlags);
        }
    }

當item被拖拽或者側滑的時候會回撥onMove和onSwiped方法,所以我們需要同時Adapter做出相應的改變,對mItems資料做出交換或者刪除的操作,因此我們需要一個回撥介面來繼續回撥Adapter中的方法

public interface onMoveAndSwipedListener {
    boolean onItemMove(int fromPosition , int toPosition);
    void onItemDismiss(int position);
}

我們讓RecyclerViewAdapter實現此介面,並且重寫裡面的兩個方法

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ItemViewHolder>
                implements onMoveAndSwipedListener

重寫兩個方法

 @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        //交換mItems資料的位置
        Collections.swap(mItems,fromPosition,toPosition);
        //交換RecyclerView列表中item的位置
        notifyItemMoved(fromPosition,toPosition);
        return true;
    }

    @Override
    public void onItemDismiss(int position) {
        //刪除mItems資料
        mItems.remove(position);
        //刪除RecyclerView列表對應item
        notifyItemRemoved(position);
    }

好了,現在我們再回到我們的SimpleItemTouchHelperCallback,在構造方法中將實現了onMoveAndSwipedListener介面的RecyclerViewAdapter 傳進來

private onMoveAndSwipedListener mAdapter;

    public SimpleItemTouchHelperCallback(onMoveAndSwipedListener listener){
        mAdapter = listener;
    }

現在我們在onMove和onSwipe方法中呼叫mAdapter的onItemMove和onItemDismiss方法,就相當於通知adapter去做相應的改變了

 /**當我們拖動item時會回撥此方法*/
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        //如果兩個item不是一個型別的,我們讓他不可以拖拽
        if (viewHolder.getItemViewType() != target.getItemViewType()){
            return false;
        }
        //回撥adapter中的onItemMove方法
        mAdapter.onItemMove(viewHolder.getAdapterPosition(),target.getAdapterPosition());
        return true;
    }
    /**當我們側滑item時會回撥此方法*/
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        //回撥adapter中的onItemDismiss方法
        mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
    }

好了,現在我們回到RecyclerListFragment中,在onViewCreated方法中新增如下幾行程式碼,將ItemTouchHelper和recyclerView關聯起來

 @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        RecyclerViewAdapter adapter = new RecyclerViewAdapter(getActivity());
        //引數view即為我們在onCreateView中return的view
        RecyclerView recyclerView = (RecyclerView)view;
        //固定recyclerview大小
        recyclerView.setHasFixedSize(true);
        //設定adapter
        recyclerView.setAdapter(adapter);
        //設定佈局型別為LinearLayoutManager,相當於ListView的樣式
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        //關聯ItemTouchHelper和RecyclerView
        ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
        mItemTouchHelper = new ItemTouchHelper(callback);
        mItemTouchHelper.attachToRecyclerView(recyclerView);
    }

現在執行一下程式,我們已經可以實現拖拽和側滑刪除的功能了
這裡寫圖片描述

現在我們為RecyclerGridFragment同樣新增一下關聯程式碼

 @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        RecyclerViewAdapter adapter = new RecyclerViewAdapter(getActivity());
        RecyclerView recyclerView = (RecyclerView)view;
        recyclerView.setHasFixedSize(true);
        recyclerView.setAdapter(adapter);
        //只有這裡和RecyclerListFragment不一樣,這裡我們指定佈局為GridView樣式,2列
        recyclerView.setLayoutManager(new GridLayoutManager(getActivity(),2));

        ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
        mItemTouchHelper = new ItemTouchHelper(callback);
        mItemTouchHelper.attachToRecyclerView(recyclerView);

    }

看一下效果
這裡寫圖片描述

處理細節

1.拖動圖示即可拖拽整個item

OK,目前我們的功能已經實現了,但是還有一些細節我們需要處理,我們還記得當時我們的item中有一個ImageView對吧,我們想通過點選ImageView就可以拖拽item,而目前只能通過長按才能夠拖動。
我們回到RecyclerListFragment中,找到剛才我們還空著的ImageView的onTouch方法

 holder.handle.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return false;
            }
        });

在onTouch方法中,我們應該回調RecyclerListFragment類中的mItemTouchHelper,呼叫mItemTouchHelper的onStartDrag方法,因此我們又需要一個回撥介面

public interface onStartDragListener {
    void startDrag(RecyclerView.Adapter adapter);
}

我們讓RecyclerListFragment實現此介面並且重寫startDrag方法

 @Override
    public void startDrag(RecyclerView.ViewHolder viewHolder) {
        mItemTouchHelper.startDrag(viewHolder);
    }

我們應該將實現了onStartDragListener介面的RecyclerListFragment物件傳給RecyclerViewAdapter,那麼我們就要在RecyclerViewAdapter的構造方法中新增一個引數

 public RecyclerViewAdapter(Context context , onStartDragListener startDragListener){
        //初始化資料
        mItems.addAll(Arrays.asList(context.getResources().getStringArray(R.array.dummy_items)));
        mStartDragListener = startDragListener;
    }

接著在ImageView的onTouch方法中做如下操作

  holder.handle.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //如果按下
                if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN){
                    //回撥RecyclerListFragment中的startDrag方法
                    //讓mItemTouchHelper執行拖拽操作
                    mStartDragListener.startDrag(holder);
                }
                return false;
            }
        });

好了,現在我們可以通過拖動item右側的ImageView來拖拽整個item了
這裡寫圖片描述
這裡寫圖片描述

2.拖拽item時改變item的背景顏色

我們來到SimpleItemTouchHelperCallback中,重寫onSelectedChanged這個回撥方法

/**當狀態改變時回撥此方法*/
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        //當前狀態不是idel(空閒)狀態時,說明當前正在拖拽或者側滑
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE){
            //TODO 改變item的背景顏色
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

改變item的背景顏色我們仍然需要在adapter中去做實際的修改,因此我們還需要一個回撥介面,我們已經寫了3個回撥介面了

public interface onStateChangedListener {
    void onItemSelected();
}

我們應該讓誰來實現這個介面並且重寫onItemSelected方法呢?我們看到onSelectedChanged方法中第一個引數是RecyclerView.ViewHolder。 其實在RecyclerView.ViewHolder中有個成員引數itemView,他就是我們item的佈局,我們修改item的背景顏色直接修改itemView的背景顏色就可以了,所以我們讓我們的ViewHolder實現這個介面

 public static class ItemViewHolder extends RecyclerView.ViewHolder 
                implements onStateChangedListener{
        private TextView text;
        private ImageView handle;
        public ItemViewHolder(View itemView) {
            super(itemView);
            text = (TextView) itemView.findViewById(R.id.text);
            handle = (ImageView) itemView.findViewById(R.id.handle);
        }

        @Override
        public void onItemSelected() {
            //設定item的背景顏色為淺灰色
            itemView.setBackgroundColor(Color.LTGRAY);
        }
    }

我們來完善onSelectedChanged方法

/**當狀態改變時回撥此方法*/
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        //當前狀態不是idel(空閒)狀態時,說明當前正在拖拽或者側滑
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE){
            //看看這個viewHolder是否實現了onStateChangedListener介面
            if (viewHolder instanceof onStateChangedListener){
                onStateChangedListener listener = (onStateChangedListener)viewHolder;
                //回撥ItemViewHolder中的onItemSelected方法來改變item的背景顏色
                listener.onItemSelected();
            }
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

執行一下看看效果
這裡寫圖片描述
有點問題,我們發現每個item的背景顏色不會自動變回原來的顏色,所以我們還得再手動改回他的背景顏色,所以我們再在onStateChangedListener介面中新增一個方法,用於當拖拽結束後回撥修改item背景顏色

public interface onStateChangedListener {
    void onItemSelected();
    void onItemClear();
}

然後在ItemViewHolder中重寫onItemClear方法

  @Override
        public void onItemClear() {
            //恢復item的背景顏色
            itemView.setBackgroundColor(0);
        }

同時,我們還得在SimpleItemTouchHelperCallback中再重寫一個clearView方法

 /**當用戶拖拽完或者側滑完一個item時回撥此方法,用來清除施加在item上的一些狀態*/
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        if (viewHolder instanceof onStateChangedListener){
            onStateChangedListener listener = (onStateChangedListener)viewHolder;
            listener.onItemClear();
        }
    }

我們再來看一下效果
這裡寫圖片描述

3.側滑刪除時item的顏色逐漸變淺

我們希望在側滑刪除一個item的時候有一種顏色逐漸變淺的效果,這個效果我們要藉助SimpleItemTouchHelperCallback的onChildDraw方法

/**這個方法可以判斷當前是拖拽還是側滑*/
    @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){
            //根據側滑的位移來修改item的透明度
            final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(alpha);
            viewHolder.itemView.setTranslationX(dX);
        }
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }

我們來看一下效果
這裡寫圖片描述

結束語

這個專案我們學習完了,通過這個專案我們真的可以學到很多東西,比如Fragment的使用,RecyclerView的使用,ItemTouchHelper的使用,回撥介面的使用等等。一個好的專案值得我們去仔細推敲。