RecyclerView -- 給你一個不卡的滑動列表

https://www.jianshu.com/p/519bb23987caandroid

 

看完此文章 你就會學到什麼...git

  • RecyclerView添加頭部,尾部,或list列表中某位置添加view
  • RecyclerView+SwipeRefreshLayout 實現上下拉刷新效果
  • 分頁加載數據
  • 列表優化方案
  • 利用Glide加載圖片,滑動的時候按back鍵,App會crash蹦掉處理。

添加依賴github

//recyclerview
    compile 'com.android.support:recyclerview-v7:25.1.1'
    //glide
    compile 'com.github.bumptech.glide:glide:3.7.0'

    //butterknife 注意須要在三個第三添加代碼
    //1 module gradle裏面底部添加
    compile 'com.jakewharton:butterknife:8.5.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
    //2 module gradle 裏面頂部添加
    apply plugin: 'com.jakewharton.butterknife'
    //3 在project gradle裏面添加
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        classpath 'com.jakewharton:butterknife-gradle-plugin:8.5.1'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

RecyclerView 任意位置添加view

RecyclerView中有如下三個函數:編程

  1. getItemCount --獲得列表中Item的總個數緩存

  2. getItemViewType(int position)--決定佈局使用哪種類型,返回一個int型標誌position,傳遞給onCreateViewHolder的第二個參數。
    通俗說:一個列表有不少個Item,每一個Item均可以是本身的一個自定義的佈局且每一個都會有一個惟一的位置,能夠經過Item中的位置position標誌,規定這個position將會返回的Item的佈局類型。例如:網絡

@Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return TYPE_HEAD;
        } else if (position + 1 == getItemCount()) {
            return TYPE_FOOTER;
        } else {
            return TYPE_ITEM;
        }
    }

3.onCreateViewHolder(ViewGroup parent, int viewType) 依據getItemViewType返回的每一個position位置所返回的佈局類型,去渲染具體的ViewHolder 。例如:app

@Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_HEAD) {   
            View view = LayoutInflater.from(context).inflate(R.layout.recycler_view_discover_head, parent, false);
            return new HeadViewHolder(view);
        } else if (viewType == TYPE_ITEM) {
            View view = LayoutInflater.from(context).inflate(R.layout.recycler_view_discover_base, parent, false);
            return new ItemViewHolder(view);
        } else if (viewType == TYPE_FOOTER) {
            View view = LayoutInflater.from(context).inflate(R.layout.recycler_view_discover_foot, parent, false);
            return new FootViewHolder(view);
        }
        return null;
    }

RecyclerView+SwipeRefreshLayout 實現上下拉刷新效果

xml以下佈局框架

<android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeRefreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/discover_recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />
    </android.support.v4.widget.SwipeRefreshLayout>

用法ide

//進入界面時候顯示動畫
 swipeRefreshLayout.post(new Runnable() {
            @Override
            public void run() {
                swipeRefreshLayout.setRefreshing(false); 
            }
        });

//每次下拉刷新時候加載刷新數據
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                if (!isLoading) {
                    isLoading = true;
                    data.clear();
                    getData();
                }
            }
        });

利用了SwipeRefreshLayout實現了下拉數據刷新功能,而上拉加載數據是利用RecyclerView的函數

public abstract static class OnScrollListener {
       //newState 參數有:
       //  SCROLL_STATE_TOUCH_SCROLL(1) 正在滾動      
       // SCROLL_STATE_FLING(2) 手指作了拋的動做(手指離開屏幕前,用力滑了一下)      
       // SCROLL_STATE_IDLE(0) 中止滾動     
        public void onScrollStateChanged(RecyclerView recyclerView, int newState){}

       //滾動時一直回調,直到中止滾動時才中止回調。單擊時回調一次
        public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
    }

分頁加載數據&&列表優化方案(思想編程)

分頁加載:
實際就是每次上拉,而後將數據加進list集合裏面,而獲取特定數據的控制,就是從頁碼來,每次上拉都將page頁碼加一放到請求參數裏面,而每次下拉,將list集合的數據清除,將page還原成原來的1,請求到的數據放進list裏面。

列表優化方案:
1.佈局方面,儘可能少點組件嵌套,儘可能多用LinearLayout,少用RelativeLayout之類的,由於RelativeLayout渲染時候須要須要資源較多。
2.圖片優化,建議使用現有的圖片框架,例如 Glide,Picasso,Fresco,ImageLoader 等的開源框架,由於裏面很好的封裝了圖片緩存機制,以及沒用時候,或者用頻率少的時候,那圖片緩存會優先被回收。
3.圖片加載. 當列表滑動時候,將圖片用佔位圖片顯示,或則圖片徹底不顯示來處理,等滑動中止後,再加載。本demo用Glide,結合RecyclerView.OnScrollListener 來處理圖片加載的。具體看demo源碼。
4.每次分頁加載數據,數據添加進List的時間(也能夠說是網絡請求數據的時間最好在何時),通過本人研究了bilibili安卓App和其它App列表滑動流暢度的對比,bilibili是滑動最不卡最流暢的,爲何這樣呢?其實是由於分頁時網絡請求數據的開始時間決定的。在RecyclerView.OnScrollListener 的onScrolled滑動事件中處理。
下面代碼中有個 5 ,它就是代碼的滑動流暢的關鍵,也能夠不必定是5,按實際須要寫。

@Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
                if (lastVisibleItemPosition + 5 >= adapter.getItemCount()) {
                    boolean isRefreshing = swipeRefreshLayout.isRefreshing();
                    if (isRefreshing) {
                        adapter.notifyItemRemoved(adapter.getItemCount());
                        return;
                    }
                    if (!isLoading) {        
                        isLoading = true;
                        getData();
                    }
                }
            }

這樣的優化優勢:滑動特別流暢  缺點:可能由於屏幕的大小不一而加載開始時間不一樣,還有的就是你體驗不到上拉加載數據的動畫感(上拉加載更多.. 這樣的字你就難看到了,不過網絡慢的時候,仍是能夠看到的)~。

利用Glide加載圖片,滑動的時候按back鍵,App會crash蹦掉處理。

剛所說的列表滑動優化處理,而我本身就隨便用了個Glide去加載圖片,能夠看到,在一瞬間滑動,而後我按back鍵退出的時候,App會crash蹦掉,why?why?why?why? due to 「You cannot start a load for a destroyed activity」。

 

緣由的出處是由於我在滑動那裏作的圖片滑動時中止加載,中止時加載圖片致使的,「You cannot start a load for a destroyed activity」,說白了就是activity在你按back鍵時候已經銷燬了,而那個滾動事件的Glide圖片處理事件還在執行。

mOnScrollListener = new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                switch (newState) {
                    case SCROLL_STATE_IDLE:
                        Glide.with(MainActivity.this).resumeRequests();
                        break;
                    case SCROLL_STATE_DRAGGING:
                    case SCROLL_STATE_SETTLING:
                        Glide.with(MainActivity.this).pauseRequests();
                        break;
                }
            }

解決問題:

  1. 嘗試在每一個Glide使用前判斷,並且在activity的onDestory方法裏寫上以下,解決問題失敗! 博客連接:http://blog.csdn.net/a940659387/article/details/50555327
if(Util.isOnMainThread()) {
            Glide.with(this).pauseRequest();
        }
  1. 嘗試在每一個Glide使用時候,this改爲getApplicationContext(),依然crash! 博客連接:http://www.jianshu.com/p/4a3177b57949
Glide.with(this).resumeRequests();
  1. 我使用的推薦成功能夠完美oknice的方法,不在Glide那裏下手,在滾動那裏下手。 Get something: 思惟轉變,別一直糾纏Glide那裏。
@Override
    protected void onPause() {
        recyclerView.removeOnScrollListener(mOnScrollListener);
        super.onPause();
    }

僅供學習使用 提供思路,具體實現仍是得按照本身業務邏輯處理

源碼地址
https://github.com/androidHRTZ/SuperRecyclerView.git

轉載請在開頭註明做者詳細信息和本文出處 謝謝

做者:rivc 連接:https://www.jianshu.com/p/519bb23987ca 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

相關文章
相關標籤/搜索