RecyclerView 全面使用及分析 - 基礎(chǔ)篇(一)

一族展、RecyclerView 介紹

RecyclerView—Recycling

在 RecyclerView 出來之前,大家都在使用 ListView楣富、GridView访忿,當(dāng)然 RecyclerView 出來之后瞧栗,基本上都轉(zhuǎn)向了 RecyclerView,從名字上可以看出海铆,它能夠?qū)崿F(xiàn)view 的復(fù)用迹恐,同樣 ListView 在使用時我們自己也可以通過 converView 來實現(xiàn)復(fù)用,但是 RecyclerView 已經(jīng)幫我們做好了卧斟,我們只需要給出需要裝載 view 的 ViewHolder 就行殴边,同時允許我們添加 ItemDecoration,ItemAnimator珍语,設(shè)置多樣的LayoutManager锤岸。

有這樣一段英文:

the RecyclerView itself doesn't care about visuals at all. It doesn't care about placing the elements at the right place, it doesn't care about separating any items and not about the look of each individual item either. To exaggerate a bit: All RecyclerView does, is recycle stuff. Hence the name.

Anything that has to do with layout, drawing and so on, that is anything that has to do with how your data set is presented, is delegated to pluggable classes. That makes the new RecyclerView API extremely flexible.

大概意思是:RecyclerView 不關(guān)心視圖,不關(guān)心元素位置板乙,只關(guān)心復(fù)用是偷,也就是說 RecyclerView 幫我們做好了復(fù)用的實現(xiàn), 其他方面我們自己來配置,這樣更加靈活晓猛,達到所謂的“插拔式”效果饿幅,即可添加 分割線,可添加動畫效果戒职,可設(shè)置布局效果等等。

其實復(fù)用透乾,就是多創(chuàng)建一個或者幾個視圖洪燥,當(dāng)滑動到底部時,加載更多的視圖時乳乌,復(fù)用已經(jīng)不可見的視圖捧韵。至于多創(chuàng)建視圖是幾個,每一屏上的視圖是幾個汉操,暫時我也不知道再来,后面進行源碼分析時,再來揭曉磷瘤,先知道它大概的原理芒篷。

RecyclerView—Recycling

二、基本使用

RecyclerView 使用很頻繁采缚,其實主要場景就是要展示的視圖數(shù)量很多针炉,無法在一個屏幕之下展示出來,當(dāng)然你想到 ScrollView扳抽,ScrollView 展示的視圖也是有限的篡帕,也不會很多,否則會造成卡頓贸呢,甚至 OOM镰烧。RecyclerView 一般的用法就是做分頁加載,上拉加載楞陷,下拉刷新怔鳖,然后將數(shù)據(jù)鋪到界面上,復(fù)雜一些時猜谚,界面上有幾種不同的視圖败砂,通過不同的 ViewHolder 來裝載。

下面就先開看看最基本的用法:

Steps

首先在 gradle 中引入 RecyclerView 魏铅,這里同時引入 cardview昌犹,不是必須的,只是為了在添加每個Item 視圖時使用 cardview 览芳,更加美觀一些斜姥。

    implementation 'com.android.support:cardview-v7:27.1.1'
    implementation 'com.android.support:recyclerview-v7:27.1.1'

1. XML 布局設(shè)置

引用 recyclerview

activity_main.xml

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

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--使用 toolbar 設(shè)置菜單-->
        <include layout="@layout/view_toolbar" />

        <!--用于刷新-->
        <com.ralf.www.recyclerviewtest.widget.MultiSwipeRefreshLayout
            android:id="@+id/swipe_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <!--重點部分,使用RecyclerView,高度設(shè)置铸敏,-->
            <!--如果是垂直布局缚忧,使用match_parent-->
            <!--如果是水平布局,可使用wrap_content -->
            <android.support.v7.widget.RecyclerView
                android:id="@+id/rv_meizhi"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </com.ralf.www.recyclerviewtest.widget.MultiSwipeRefreshLayout>

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/main_fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right|bottom"
            android:layout_marginBottom="24dp"
            android:layout_marginRight="16dp"
            android:clickable="true"
            android:onClick="onFab"
            android:src="@mipmap/ic_refresh_white_24dp"
            app:borderWidth="0dp"
            app:elevation="4dp"
            app:layout_anchor="@id/swipe_refresh_layout"
            app:layout_anchorGravity="right|bottom"
            app:layout_behavior="com.ralf.www.recyclerviewtest.widget.FABAutoHideBehavior" />

    </android.support.design.widget.CoordinatorLayout>

</FrameLayout>

子項的 XML 布局

item_main_activity_rv.xml

子項布局在重寫 Adapter 時使用,外面包了一層 CardView杈笔,更加美觀一些

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    android:id="@+id/meizhi_card"
    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="wrap_content"
    android:layout_margin="5dp"
    android:clickable="true"
    android:foreground="?attr/selectableItemBackground"
    app:cardCornerRadius="2dp"
    app:cardElevation="4dp"
    tools:minWidth="160dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/iv_meizhi"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:scaleType="fitXY"
            tools:src="@mipmap/ic_launcher"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:paddingBottom="10dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:paddingTop="10dp">

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceSmall"
                tools:text="Title"/>

        </LinearLayout>

    </LinearLayout>

</android.support.v7.widget.CardView>

2. LayoutManager 設(shè)置

RecyclerView 設(shè)置 LayoutManager闪水,系統(tǒng)提供了3種 LayoutManager:LinearLayoutManager(線性布局)、GridLayoutManager(網(wǎng)格布局)蒙具、StaggeredGridLayoutManager(瀑布布局)球榆,根據(jù)場景需要自己設(shè)定,也可以自己實現(xiàn) LayoutManager禁筏。
這里先給出簡單的使用持钉,對于詳細的分析以及自己實現(xiàn) 后面的文章(加粗下,提醒自己篱昔。每强。)再給出分析。


//        final StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(
//                2, StaggeredGridLayoutManager.VERTICAL
//        );
        final GridLayoutManager layoutManager = new GridLayoutManager(this, 2
                , GridLayoutManager.VERTICAL, false);

//        final LinearLayoutManager layoutManager = new LinearLayoutManager(this,
//                LinearLayoutManager.VERTICAL, false);
                
       // 設(shè)置布局管理器        
       mRecyclerView.setLayoutManager(layoutManager);

3. 設(shè)置數(shù)據(jù)源

數(shù)據(jù)源一般就用 List 傳遞給 Adapter州刽,作為其構(gòu)造函數(shù)中的一個參數(shù)給出空执。

private List<GanHuo> mPicList = new ArrayList<>();

直接在 成員變量聲明時給出 List 對象,一般數(shù)據(jù)源 都是通過網(wǎng)絡(luò)求來的怀伦,在 demo 中我集成了 Retrofit + Rxjava 進行網(wǎng)絡(luò)請求脆烟,加載圖片,對這部分沒接觸的童鞋可以看看 Retrofit + Rxjava房待,或者自己替換成本地的圖片也行邢羔。

有一點,需要注意下:對于初始化時桑孩,設(shè)置布局管理器拜鹤,設(shè)置 Adapter,設(shè)置數(shù)據(jù)源流椒,沒有嚴格的先后順序敏簿。開始沒有在 List 中加入數(shù)據(jù),初始化時候宣虾,加載網(wǎng)絡(luò)數(shù)據(jù)或者本地數(shù)據(jù)惯裕,然后通過刷新列表即可。

4. 實現(xiàn) Adapter 并設(shè)置

PictureAdapter 需要集成 RecyclerView.Adapter绣硝,并需要聲明泛型類型 ViewHolder蜻势,否則是 Object 類型,并實現(xiàn) 三個方法:

生成用于持有每個 View 的 ViewHolder鹉胖,實現(xiàn)復(fù)用
onCreateViewHolder

將ViewHolder綁定握玛,即將數(shù)據(jù)綁定到視圖上
onBindViewHolder

獲取子 View 的數(shù)量够傍,即傳過來的 List 的大小
getItemCount


public class PictureAdapter extends RecyclerView.Adapter<PictureAdapter.PictureViewHolder> {

    private List<GanHuo> picList;
    private ItemClickListener mClickListener;
    private Context mContext;

    public PictureAdapter(Context context, List<GanHuo> picList) {
        this.picList = picList;
        mContext = context;
    }

    @NonNull
    @Override
    public PictureViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // 加載布局 item_main_activity_rv.xml
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_main_activity_rv,
                parent, false);
        return new PictureViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull final PictureViewHolder holder, final int position) {
        if (picList != null) {
            GanHuo ganHuo = picList.get(position);
            String url = ganHuo.getUrl();
            String who = ganHuo.getWho();
            
            // Glide圖片加載
            RequestOptions requestOptions = RequestOptions.placeholderOf(R.mipmap.ic_launcher)
                    .error(R.mipmap.ic_refresh_white_24dp)
                    .useAnimationPool(true);
            Glide.with(mContext)
                    .load(url)
                    .apply(requestOptions)
                    .into(holder.imageView);
            holder.textView.setText(who);
            // 點擊事件
            if (mClickListener != null) {
                holder.textView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mClickListener.onItemClick(PictureAdapter.this, v, position);
                    }
                });
                holder.textView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        mClickListener.onItemLongClick(PictureAdapter.this, v, position);
                        return true;
                    }
                });

                holder.imageView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mClickListener.onItemClick(PictureAdapter.this, v, position);
                    }
                });
                holder.imageView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        mClickListener.onItemLongClick(PictureAdapter.this, v, position);
                        return true;
                    }
                });
            }
        } else {
            // default url
            RequestOptions requestOptions = RequestOptions.placeholderOf(R.mipmap.ic_launcher)
                    .error(R.mipmap.ic_refresh_white_24dp);
            Glide.with(mContext)
                    .load(R.mipmap.ic_launcher)
                    .apply(requestOptions)
                    .into(holder.imageView);
            holder.textView.setText("看不到的妹紙");
        }
    }

    @Override
    public int getItemCount() {
        if (picList == null || picList.size() < 1) {
            return 0;
        }
        return picList.size();
    }

    // ViewHolder 用于存放 View,這個View也就是 前面設(shè)置的 .xml挠铲,RecyclerView 通過 ViewHolder進行復(fù)用
    static class PictureViewHolder extends RecyclerView.ViewHolder {

        private View mView;
        private ImageView imageView;
        private TextView textView;

        public PictureViewHolder(View itemView) {
            super(itemView);
            mView = itemView;
            initView();
        }

        private void initView() {
            imageView = mView.findViewById(R.id.iv_meizhi);
            textView = mView.findViewById(R.id.tv_title);
        }
    }

    public ItemClickListener getClickListener() {
        return mClickListener;
    }

    public void setClickListener(ItemClickListener clickListener) {
        mClickListener = clickListener;
    }

    // 點擊事件接口
    public interface ItemClickListener {

        void onItemClick(RecyclerView.Adapter adapter,
                         View view, int position);

        void onItemLongClick(RecyclerView.Adapter adapter,
                             View view, int position);
    }
}

實現(xiàn) Adapter 之后冕屯,創(chuàng)建實例,然后用 RecyclerView setAdapter拂苹。


 // mPicList 就是用于存放數(shù)據(jù)的
 mAdapter = new PictureAdapter(this, mPicList);
 mRecyclerView.setAdapter(mAdapter);

請求數(shù)據(jù)安聘,通過網(wǎng)絡(luò)請求數(shù)據(jù) Retrofit + Rxjava

private void requestData(int index) {
        // 開始刷新
        setRefreshing(true);
        RetrofitClient.getRetrofitClientInstance()
                .requestNetForData(NetApi.BASE_URL, RequestDataService.class)
                .getGanHuoData("福利", 10, index)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(new DefaultObserver<BaseEntity<GanHuo>>() {
                    @Override
                    public void onNext(BaseEntity<GanHuo> ganHuo) {
                        if (ganHuo != null && !ganHuo.isError()) {
                            // 刷新列表
                            refreshRecyclerView(ganHuo.getResults());
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        setRefreshing(false);
                    }

                    @Override
                    public void onComplete() {
                        // 刷新結(jié)束,如果是下拉刷新瓢棒,滾動到最頂部
                        setRefreshing(false);
                        if (mIndex == 1) {
                            mRecyclerView.smoothScrollToPosition(0);
                        }
                    }
                });
    }

刷新 RecyclerView搞挣,有上拉加載和下拉刷新,對于不同的情況做了簡單的處理音羞,防止更新數(shù)據(jù)時,有閃爍現(xiàn)象仓犬。

private void refreshRecyclerView(List<GanHuo> ganHuoList) {
        if (ganHuoList == null || ganHuoList.size() < 1) {
            return;
        }
        if (mIndex == 1) {
            mPicList.clear();
            mPicList.addAll(ganHuoList);
            mAdapter.notifyDataSetChanged();
        } else {
            mPicList.addAll(ganHuoList);
            mAdapter.notifyItemRangeInserted(mAdapter.getItemCount()
                    , ganHuoList.size());
        }
    }

此時嗅绰,就可以運行,能夠看到加載的圖片列表了搀继。

load_refresh

這里面有 RecyclerView的滑動事件監(jiān)聽窘面,用來滑動到底部時,做上拉加載更多數(shù)據(jù)叽躯。


mRecyclerView.addOnScrollListener(
                new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView rv, int dx, int dy) {
                    // 判斷是否滑動到最底部
                        if (!mSwipeRefreshLayout.isRefreshing() &&
                                layoutManager.findLastCompletelyVisibleItemPosition() >= mAdapter.getItemCount() - 1) {

                            mIndex += 1;
                            requestData(mIndex);
                        }
                    }
                }
        );

5. 設(shè)置分割線

在圖中看出财边,已經(jīng)設(shè)置了分割線。設(shè)置分割線相對簡單点骑,主要有兩種方法:

  • 在 xml 布局中添加 0.5 dp的View
  • 使用 ItemDecoration

第一種就不介紹了酣难,自己嘗試弄一下。第二種實現(xiàn)ItemDecoration黑滴,系統(tǒng)默認只給出來了一種實現(xiàn) DividerItemDecoration憨募,
一般情況下,也夠用了袁辈,除了改一下顏色之類的菜谣。


// 添加分割線
// 第二個參數(shù)設(shè)置方向,垂直布局設(shè)置垂直分割線晚缩,水平布局設(shè)置水平分割線
mRecyclerView.addItemDecoration(new DividerItemDecoration(
        this, DividerItemDecoration.VERTICAL));

分割線的顏色怎么修改的尾膊?
DividerItemDecoration 的顏色采用的應(yīng)是主題的顏色,所以要改變顏色荞彼,需要自己定義一個顏色冈敛,然后加到應(yīng)用的主題中。

在 drawable 中定義一個 shape

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    // 漸變顏色
    <gradient
        android:centerColor="#ff00ff00"
        android:endColor="#ff0000ff"
        android:startColor="#ffff0000"
        android:type="linear" />
        
        // 分割線寬度
    <size android:height="4dp"/>

</shape>

加到應(yīng)用主題中

<!-- Application theme. -->
    <style name="AppTheme1" parent="Theme.AppCompat.NoActionBar">
        <item name="colorPrimary">@color/theme_primary</item>
        <item name="colorPrimaryDark">@color/theme_primary_dark</item>
        <item name="colorAccent">@color/md_red_400</item>
        // 分割線主題顏色設(shè)置
        <item name="android:listDivider">@drawable/divider_shape</item>
        <!-- 加入toolbar溢出【彈出】菜單的風(fēng)格 -->
        <item name="actionOverflowMenuStyle">@style/OverflowMenuStyle</item>

   </style>

這樣就能改變分割線顏色卿泽,同時寬度也可以 shape 中修改

DividerItemDecoration

6. 設(shè)置動畫

設(shè)置動畫相對比較簡單莺债,調(diào)用 setItemAnimator 即可滋觉,動畫可以使用提供的默認的動畫,或者自己實現(xiàn) RecyclerView.ItemAnimator齐邦,達到自己想要的結(jié)果椎侠。


    // 添加動畫
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    

動畫用于數(shù)據(jù)改變時顯示的動畫效果,這里在 Adapter 中添加兩個方法措拇,測試一下


/**
     * 添加測試我纪,用于展現(xiàn)動畫
     * @param position
     */
    public void indertTest(int position){
        picList.add(1,new GanHuo());
        // 這里更新數(shù)據(jù)集不是用adapter.notifyDataSetChanged()而是
        // notifyItemInserted(position)與notifyItemRemoved(position)
        // 否則沒有動畫效果。
        notifyItemInserted(position);
    }

    /**
     * 刪除測試丐吓,用于展現(xiàn)動畫
     * @param position
     */
    public void removeTest(int position){
        picList.remove(picList.get(position));
        notifyItemRemoved(position);
    }
animatirs

想看看到更多的動畫效果浅悉,可以參考 github 上的開源項目 RecyclerViewItemAnimators

7. 設(shè)置點擊事件

對于 RecyclerView 的點擊事件,系統(tǒng)沒有提供接口 ClickListener和 LongClickListener券犁,需要自己實現(xiàn)术健。常用的方式一般有兩種:

  • mRecyclerView.addOnItemTouchListener(listener);根據(jù)手勢動作判斷
  • 第二種是自己在 Adapter 中設(shè)置接口,然后將實現(xiàn)傳遞進去

這里給出第二種的方式粘衬,看一下效果:

在 onBindViewHolder 方法中代碼有點多荞估,主要看setOnClickListener 部分,實際上還是給普通的控件設(shè)置點擊事件稚新,在 onClick 中回調(diào)我們設(shè)置的接口勘伺,這樣執(zhí)行的方法就是我們想要的動作了。

public class PictureAdapter extends RecyclerView.Adapter<PictureAdapter.PictureViewHolder> {

    ...
    @Override
    public void onBindViewHolder(@NonNull final PictureViewHolder holder, final int position) {
        if (picList != null) {
            GanHuo ganHuo = picList.get(position);
            String url = ganHuo.getUrl();
            String who = ganHuo.getWho();
            RequestOptions requestOptions = RequestOptions.placeholderOf(R.mipmap.ic_launcher)
                    .error(R.mipmap.ic_refresh_white_24dp)
                    .useAnimationPool(true);
            Glide.with(mContext)
                    .load(url)
                    .apply(requestOptions)
                    .into(holder.imageView);
            holder.textView.setText(who);
            // 點擊事件
            if (mClickListener != null) {
                holder.textView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mClickListener.onItemClick(PictureAdapter.this, v, position);
                    }
                });
                holder.textView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        mClickListener.onItemLongClick(PictureAdapter.this, v, position);
                        return true;
                    }
                });

                holder.imageView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mClickListener.onItemClick(PictureAdapter.this, v, position);
                    }
                });
                holder.imageView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        mClickListener.onItemLongClick(PictureAdapter.this, v, position);
                        return true;
                    }
                });
            }
        } else {
            // default url
            RequestOptions requestOptions = RequestOptions.placeholderOf(R.mipmap.ic_launcher)
                    .error(R.mipmap.ic_refresh_white_24dp);
            Glide.with(mContext)
                    .load(R.mipmap.ic_launcher)
                    .apply(requestOptions)
                    .into(holder.imageView);
            holder.textView.setText("看不到的妹紙");
        }
    }
    
    // ClickListener和 LongClickListener 回調(diào)接口
    public interface ItemClickListener {
    
            void onItemClick(RecyclerView.Adapter adapter,
                             View view, int position);
    
            void onItemLongClick(RecyclerView.Adapter adapter,
                                 View view, int position);
        }

在 MainActivity 中添加接口的具體實現(xiàn)


// 設(shè)置點擊事件
        mAdapter.setClickListener(new PictureAdapter.ItemClickListener() {
            @Override
            public void onItemClick(RecyclerView.Adapter adapter, View view, int position) {
                GanHuo ganHuo = mPicList.get(position);
                String who = ganHuo.getWho();
                String desc = ganHuo.getDesc();
                if (view.getId() == R.id.tv_title) {
                    Toast.makeText(MainActivity.this, "你點擊了" + who, Toast.LENGTH_SHORT).show();
                } else if (view.getId() == R.id.iv_meizhi) {
                    Toast.makeText(MainActivity.this, "你點擊了" + desc, Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onItemLongClick(RecyclerView.Adapter adapter, View view, int position) {
                GanHuo ganHuo = mPicList.get(position);
                String who = ganHuo.getWho();
                String desc = ganHuo.getDesc();
                if (view.getId() == R.id.tv_title) {
                    Toast.makeText(MainActivity.this, "你長按了" + who, Toast.LENGTH_SHORT).show();
                } else if (view.getId() == R.id.iv_meizhi) {
                    Toast.makeText(MainActivity.this, "你長按了" + desc, Toast.LENGTH_SHORT).show();
                }
            }

        });

這樣執(zhí)行的方法就是我們想要的效果了褂删。

click

總結(jié)

主要介紹了 RecyclerView 的主要特點飞醉,與ListView 相比的優(yōu)勢,在于 view 的復(fù)用上屯阀;同時詳細介紹了基本的使用方法缅帘。使用 RecyclerView 可以很方便的使用 動畫,分割線蹲盘,以及布局管理器的樣式改變股毫。例子中的也給出了上拉加載和下拉刷新,但是僅供參看召衔,使用起來還是存在一定問題的铃诬,后面會做修正,在項目中使用的話苍凛,可以使用開源框架 Android智能下拉刷新框架-SmartRefreshLayout

好了趣席,就到這里,對 RecyclerView 不熟悉的趕緊試試吧醇蝴,后面會再寫一篇宣肚,介紹使用過程中的一些小細節(jié),避免采坑悠栓!

Demo地址

參考

Android RecyclerView 使用完全解析 體驗藝術(shù)般的控件

動畫開源庫

A First Glance at Android’s RecyclerView

Android智能下拉刷新框架-SmartRefreshLayout

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霉涨,一起剝皮案震驚了整個濱河市按价,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笙瑟,老刑警劉巖楼镐,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異往枷,居然都是意外死亡框产,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門错洁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秉宿,“玉大人,你說我怎么就攤上這事屯碴∶枘溃” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵导而,是天一觀的道長酌摇。 經(jīng)常有香客問我,道長嗡载,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任仍稀,我火速辦了婚禮洼滚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘技潘。我一直安慰自己遥巴,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布享幽。 她就那樣靜靜地躺著铲掐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪值桩。 梳的紋絲不亂的頭發(fā)上摆霉,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天,我揣著相機與錄音奔坟,去河邊找鬼携栋。 笑死,一個胖子當(dāng)著我的面吹牛咳秉,可吹牛的內(nèi)容都是我干的婉支。 我是一名探鬼主播,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼澜建,長吁一口氣:“原來是場噩夢啊……” “哼向挖!你這毒婦竟也來了蝌以?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤何之,失蹤者是張志新(化名)和其女友劉穎跟畅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帝美,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡碍彭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了悼潭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庇忌。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖舰褪,靈堂內(nèi)的尸體忽然破棺而出皆疹,到底是詐尸還是另有隱情,我是刑警寧澤占拍,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布略就,位于F島的核電站,受9級特大地震影響晃酒,放射性物質(zhì)發(fā)生泄漏表牢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一贝次、第九天 我趴在偏房一處隱蔽的房頂上張望崔兴。 院中可真熱鬧,春花似錦蛔翅、人聲如沸敲茄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽堰燎。三九已至,卻和暖如春笋轨,著一層夾襖步出監(jiān)牢的瞬間秆剪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工爵政, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鸟款,地道東北人。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓茂卦,卻偏偏與公主長得像何什,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子等龙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,332評論 25 707
  • 用兩張圖告訴你处渣,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料伶贰? 從這篇文章中你...
    hw1212閱讀 12,754評論 2 59
  • 內(nèi)容 抽屜菜單 ListView WebView SwitchButton 按鈕 點贊按鈕 進度條 TabLayo...
    小狼W閱讀 1,614評論 0 10
  • 【Android 控件 RecyclerView】 概述 RecyclerView是什么 從Android 5.0...
    Rtia閱讀 307,566評論 27 439
  • 車 內(nèi) 情 侶 之后從癱軟中回過神來,環(huán)顧了四周發(fā)現(xiàn)了我左前方正在對啃的熱戀情侶罐栈,雖然這種事情隨著發(fā)展已經(jīng)很常見了...
    零x一閱讀 330評論 1 3