RecyclerView瀑布流優化方案探討

目錄介紹

01.規則瀑布流實現
02.不規則瀑布流實現
2.1 實現方式
2.2 遇到問題
03.瀑布流上拉加載
04.給瀑布流設置分割線
05.自定義Manager崩潰
06.如何避免刷新抖動
07.爲何有時出現跳動
08.瀑布流圖片優化
09.onBindViewHolder優化
10.瀑布流item點擊事件優化
11.Glide加載優化
12.建議指定圖片的寬高
歡迎同行探討瀑布流極致優化方案

如果同行看到這篇文章,有好的瀑布流優化方案,歡迎給出建議,或者給鏈接也可以。
需求:
瀑布流大概有10來中不同type的item視圖,然後視圖是根據動態設置寬高,服務器會返回比例
瀑布流中item需要切割圓角
目前使用glide加載圖片
產品說讓參考抖音快手類的app,讓瀑布流滑動效果特別流暢……但目前遇到問題是滑動十幾頁沒什麼問題,但是滑動三四十頁的時候會出現卡頓。歡迎同行給出建議!
好消息

博客筆記大彙總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,當然也在工作之餘收集了大量的面試題,長期更新維護並且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計N篇[近100萬字,陸續搬到網上],轉載請註明出處,謝謝!
鏈接地址:https://github.com/yangchong2...
如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起於忽微,量變引起質變!
01.規則瀑布流實現

最簡單規則瀑布流實現,下面這種是設置3列數據,然後組數據高度是相同的。是規則的瀑布流。
adapter = new ImageAdapter(this);
recyclerView.setAdapter(adapter);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,3);
gridLayoutManager.setSpanSizeLookup(adapter.obtainGridSpanSizeLookUp(3));
recyclerView.setLayoutManager(gridLayoutManager);
SpaceViewItemLine itemDecoration = new SpaceViewItemLine(20);
recyclerView.addItemDecoration(itemDecoration);
02.不規則瀑布流實現

最簡單的不規則瀑布流實現,下面這種是設置2列數據,然後數據的高度都不同,圖片的高度隨機。
adapter = new ImageStageredAdapter(this);
recyclerView.setAdapter(adapter);
StaggeredGridLayoutManager staggeredGridLayoutManager =

    new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);

recyclerView.setLayoutManager(staggeredGridLayoutManager);
SpaceViewItemLine itemDecoration = new SpaceViewItemLine(20);
recyclerView.addItemDecoration(itemDecoration);
這裏是僞代碼。假設設置不同高度,代碼如下。簡單設置不同圖片高度不同,這個是在onBindViewHolder中操作。
ViewGroup.LayoutParams params = imgPicture.getLayoutParams();
//假設有多種不同的類型
int type = getAdapterPosition()%5;
//計算View的高度
int height = 300;
switch (type){

case 0:
    height = 500;
    break;
case 1:
    height = 750;
    break;
case 2:
    height = 880;
    break;
case 3:
    height = 360;
    break;
case 4:
    height = 660;
    break;
default:
    break;

}
params.height = height;
imgPicture.setLayoutParams(params);
2.2 遇到問題

遇到問題
1.RecyclerView 如何實現下拉加載更多;
2.StaggeredGridLayoutManager 顯示加載更多的時候,加載更多作爲最後一個item沒有單獨佔滿屏幕寬度,只顯示爲一個item的寬度;
3.StaggeredGridLayoutManager 如何隨機設置item的高度;
4.StaggeredGridLayoutManager 上拉加載數據刷新UI時,由於高度隨機,造成頁面item抖動問題;
5.RecyclerView莫名的Inconsistency detected崩潰;
卡頓和內存釋放
瀑布流滑動幾頁感覺還行,但是一旦滑動了三四十頁,就感覺頁面有些卡頓了。
03.瀑布流上拉加載

首先添加監聽方法,添加了這個方法就可以上拉加載更多數據呢。但是發現還有點問題,上拉加載更多的那個佈局只是佔1/spanCount列,這樣顯得特別難看。那麼該如何處理呢,接着往下看……
首先要能夠監聽recyclerView滑動事件;
判斷recyclerView是否滑動到最後一個item;
recyclerView 加載更多RecyclerView.Adapter的設置處理:RecyclerView.Adapter的相關代碼,主要定義兩個ViewHolder類型,footType表示爲底部的viewHolder,normalType表示爲正常的item的viewHolder,根據position的不同來顯示不同的viewholder;
// 實現上拉加載重要步驟,設置滑動監聽器,RecyclerView自帶的ScrollListener
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
    super.onScrollStateChanged(recyclerView, newState);
    // 在newState爲滑到底部時
    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
        // 如果沒有隱藏footView,那麼最後一個條目的位置就比我們的getItemCount少1,自己可以算一下
        if (!adapter.isFadeTips() && lastVisibleItem + 1 == adapter.getItemCount()) {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    updateRecyclerView(adapter.getRealLastPosition(),
                            adapter.getRealLastPosition() + PAGE_COUNT);
                }
            }, 2500);
        }

        // 如果隱藏了提示條,我們又上拉加載時,那麼最後一個條目就要比getItemCount要少2
        if (adapter.isFadeTips() && lastVisibleItem + 2 == adapter.getItemCount()) {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    updateRecyclerView(adapter.getRealLastPosition(),
                            adapter.getRealLastPosition() + PAGE_COUNT);
                }
            }, 2500);
        }
    }
}

@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
    super.onScrolled(recyclerView, dx, dy);
    // 在滑動完成後,拿到最後一個可見的item的位置
    int positions[] = staggeredGridLayoutManager.findLastVisibleItemPositions(null);
    for(int pos : positions){
        if(pos > lastVisibleItem){
            lastVisibleItem = pos;//得到最後一個可見的item的position
        }
    }
}

});
因爲是瀑布流,要設置footerView佔據一行,這個需要這樣設置,代碼如下所示
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {

super.onViewAttachedToWindow(holder);
ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
if (layoutParams != null && layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
    StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) layoutParams;
    int position = holder.getLayoutPosition();
    //如果是上拉加載更多類型,則設置setFullSpan爲true,那麼它就會佔一行
    if (getItemViewType(position) == footType) {
        params.setFullSpan(true);
    }
}

}
04.給瀑布流設置分割線

先來看看出現錯位,分割線出現問題的代碼。下面這種方式根據childCount來判斷奇數和偶數設置的不同間距。
那麼比如說當爲奇數時,設置該item到左爲20,到右爲5;當爲偶數時,該item到左爲5,到右爲20。
如果奇數的item都在左邊,偶數的item都在右邊,那麼間距就沒有問題。
如果第一個item在左邊【高度很高】,第2個,第3個,第4個item都在右邊,第5個在左邊……那麼思考一下,這個時候第3個item在右邊,那麼就會造成間距不規則。
顯然不能根據奇數或者偶數來設置item左右間距的大小的,會出現錯位。
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {

@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
                           @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
    int position = parent.getChildAdapterPosition(view);
    int spanCount = 0;
    int spanIndex = 0;
    RecyclerView.Adapter adapter = parent.getAdapter();
    RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
    if (adapter==null || layoutManager==null){
        return;
    }
    if (layoutManager instanceof StaggeredGridLayoutManager){
        spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
        spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
    }
    //普通Item的尺寸
    //TODO 會出現錯位的問題
    int itemCount = adapter.getItemCount();
    int childCount = layoutManager.getChildCount();
    RefreshLogUtils.d("SpaceViewItemLine--count--"+itemCount + "-----"+childCount+"---索引--"+position+"---"+spanIndex);
    if (position<itemCount && spanCount==2) {
        if (childCount % 2 == 0){
            //這個是右邊item
            outRect.left = 5;
            outRect.right = 20;
        } else {
            //這個是左邊item
            outRect.left = 20;
            outRect.right = 5;
        }
        if (childCount==1 || childCount==2){
            outRect.top = 0;
        } else {
            outRect.top = 20;
        }
        RefreshLogUtils.d("SpaceViewItemLine--間距--"+childCount+"----"+outRect.left+"-----"+outRect.right);
    }
}

});
解決辦法,可以通過StaggeredGridLayoutManager.LayoutParams裏的getSpanIndex()來判斷,這個方法不管你高度怎樣,他都是左右左右開始排列的。
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {

@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
                           @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
    int position = parent.getChildAdapterPosition(view);
    int spanCount = 0;
    int spanIndex = 0;
    RecyclerView.Adapter adapter = parent.getAdapter();
    RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
    if (adapter==null || layoutManager==null){
        return;
    }
    if (layoutManager instanceof StaggeredGridLayoutManager){
        spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
        spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
    }
    //普通Item的尺寸
    int itemCount = adapter.getItemCount();
    int childCount = layoutManager.getChildCount();
    RefreshLogUtils.d("SpaceViewItemLine--count--"+itemCount + "-----"+childCount+"---索引--"+position+"---"+spanIndex);
    if (position<itemCount && spanCount==2) {
        if (spanIndex != GridLayoutManager.LayoutParams.INVALID_SPAN_ID) {
            //getSpanIndex方法不管控件高度如何,始終都是左右左右返回index
            if (spanIndex % 2 == 0) {
                //這個是左邊item
                outRect.left = 20;
                outRect.right = 5;
            } else {
                //這個是右邊item
                outRect.left = 5;
                outRect.right = 20;
            }
            if (childCount==1 || childCount==2){
                outRect.top = 0;
            } else {
                outRect.top = 20;
            }
        }
        //outRect.top = space;
        RefreshLogUtils.d("SpaceViewItemLine--間距--"+spanIndex+"----"+outRect.left+"-----"+outRect.right);
    }
}

});
05.自定義Manager崩潰

RecyclerView莫名的Inconsistency detected崩潰;
出現這個異常原因:
使用RecyclerView加官方下拉刷新的時候,如果綁定的List對象在更新數據之前進行了clear,而這時用戶緊接着迅速上滑RV,就會造成崩潰,而且異常不會報到你的代碼上,屬於RV內部錯誤。
自定義一個CustomStaggeredGridLayoutManager 在onLayoutChildren對異常進行捕獲:
public class CustomStaggeredGridLayoutManager extends StaggeredGridLayoutManager {

private static final String TAG = "LOG_CustomStaggered";
public CustomStaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}

public CustomStaggeredGridLayoutManager(int spanCount, int orientation) {
    super(spanCount, orientation);
}

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    try {
        super.onLayoutChildren(recycler, state);
    }catch (Exception e){
        Log.i(TAG, "onLayoutChildren: e " + e.getMessage());
    }
}

}
關於StaggeredGridLayoutManager異常說明
06.如何避免刷新抖動

StaggeredGridLayoutManager 上拉加載數據刷新UI時,由於高度隨機,造成頁面item抖動問題; 這裏由於直接調用 notifyDataSetChanged();那麼是全局刷新,而刷新的時候item的高度重新隨機分配,導致數據刷新的時候會造成抖動。建議採用notifyItemRangeChanged進行局部刷新:
public void setDatas(List mDatas ) {

this.dataList = mDatas;
notifyDataSetChanged();

}

/**

  • 注意這個是未關注時候的瀑布流刷新數據
  • 上拉加載更多時,建議刷新上拉加載那一部分數據即可,不用刷新所有數據
  • @param puBuList 瀑布流集合

*/
public void setMoreData(List<BasePubuBean> puBuList) {

int start = mayContentList.size();
if (puBuList!=null && puBuList.size()!=0){
    mayContentList.addAll(puBuList);
    int end = mayContentList.size();
    mRecommendPuBuAdapter.notifyItemRangeInserted(start,end);
}

}
07.爲何有時出現跳動

由於我們加載的圖片高度不確定(寬度確定因爲可以根據屏幕寬度和每行Item數目進行等分),而當我們向RecyclerView下方滑動一段距離後,由於ViewHolder的回收機制,item的尺寸並不確定,滑回到上方時Item需要重新自行繪製,於是這個又導致重繪,所以會有閃爍、跳動、空白等問題。說到底,只要我們在重繪前確定了Item的尺寸,那麼就可以避免Item去重新計算自己的尺寸,就可以避免重繪導致的諸多問題。
08.瀑布流圖片優化

具體優化方案
第一步:減少佈局嵌套,並且在拿到服務器的尺寸後,在onBindViewHolder中給圖片控件設置寬高時,避免創建大量臨時的LayoutParams對象
第二步:使用glide加載,綁定activity或者fragment的生命週期,儘量不用用全局上下文或者靜態上下文。注意with()方法中傳入的實例會決定Glide加載圖片的生命週期,如果傳入的是Activity或者Fragment的實例,那麼當這個Activity或Fragment被銷燬的時候,圖片加載也會停止。如果傳入的是ApplicationContext,那麼只有當應用程序被殺掉的時候,圖片加載纔會停止。
第三步:對於list條目,尤其是瀑布流,不建議使用TransitionOptions來加載設置的動畫,尤其是不要使用自己自定義的動畫
避免使用圓角的ImageView
在實際項目內,經常會用到一些帶圓角的圖片,或者直接就是圓形的圖片。圓形的圖片,多數用於一些用戶的頭像之類的顯示效果。
而在 Android 下,也有大量的類似 XxxImageView 的開源控件,用於操作 Bitmap 以達到一個圓角圖片的效果,例如 Github 上比較火的 RoundedImageView。
它們大部分的原理,是接收到你傳遞的 Bitmap ,然後再輸出一個與原來 Bitmap 等大的新 Bitmap ,在此基礎之上,進行圓角的一些處理,這就導致了,實際上會在內存中,多持有一個 Bitmap ,一下一張圖片佔用的內存就被加倍了。
所以既然已經選擇使用Glide,推薦使用glide-transformations這個開源庫配合使用,glide-transformations 利用 Glide 的 bitmapTransfrom() 接口,實現對加載的 Bitmap 的進行一些變換操作。glide-transformations提供一系類對加載的圖片的變換操作,從形狀變換到色彩變換,全部支持,基本上滿足大部分開發需要,並且它會複用 Glide 的 BitmapPool ,來達到節約內存的目的。
09.onBindViewHolder優化

在這個方法中,主要是做數據綁定到View視圖。由於瀑布流會有多種不同type類型的視圖,有些需要設置寬高,有的則要從服務器拿到寬高數據然後動態修改視圖屬性。因此關於寬高的計算其實還可以做一些優化。
先來看一下最初的代碼,這裏只展示動態設置寬高的代碼。如下所示
看了下面代碼,會發現這些問題。第一,頻繁計算瀑布流中不同類型的寬度,該寬度是屏幕寬度減去間距,然後是除以2,瀑布流爲兩行。第二,頻繁創建LayoutParams對象,然後設置寬高的屬性。
@Override
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {

super.onBindViewHolder(holder, position);
int width = 0;
int height = 0;
switch (holder.getItemViewType()) {
    case BasePubuBean.content_type9:
        break;
    //發佈的短視頻的寬高也是限制爲3:4
    case BasePubuBean.content_type2:
        int videoWidth = (SysUtils.getScreenWidth((Activity) mContext)-SysUtils.Dp2Px(mContext,43)) / 2;
        int videoHeight = (int) (videoWidth * (4/3.0f));
        RelativeLayout.LayoutParams puBuParams2 = ViewUtils.getPuBuParams(mContext, videoWidth, videoHeight);
        holder.getView(R.id.mImageView).setLayoutParams(puBuParams2);
        break;
    //活動頭圖,自適應拿到圖片寬度,然後寬高比是4:3
    case BasePubuBean.content_type4:
        int imageWidth = (SysUtils.getScreenWidth((Activity) mContext)-SysUtils.Dp2Px(mContext,43)) / 2;
        int imageHeight = (int) (imageWidth * (3/4.0f));
        RelativeLayout.LayoutParams puBuParams4 = ViewUtils.getPuBuParams(mContext,imageWidth, imageHeight);
        holder.getView(R.id.mImageView).setLayoutParams(puBuParams4);
        break;
    //推薦車系,寬高比是4:3
    case BasePubuBean.content_type10:
        int imageWidth10 = (SysUtils.getScreenWidth((Activity) mContext)-SysUtils.Dp2Px(mContext,43)) / 2;
        int imageHeight10 = (int) (imageWidth10 * (3/4.0f));
        ImageView imageView = holder.getView(R.id.mImageView);
        ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
        layoutParams.height = imageHeight10;
        layoutParams.width = imageWidth10;
        imageView.setLayoutParams(layoutParams);
        break;
    case BasePubuBean.content_type1:
    case BasePubuBean.content_type3:
    case BasePubuBean.content_type5:
    case BasePubuBean.content_type6:
    case BasePubuBean.content_type7:
    case BasePubuBean.content_type8:
        int imageWidth = (SysUtils.getScreenWidth((Activity) mContext)-SysUtils.Dp2Px(mContext,43)) / 2;
        width = mData.get(position).getWidth();
        height =  mData.get(position).getHeight();
        if( width!=0 && height!=0){
             RelativeLayout.LayoutParams   puBuParams1 = ViewUtils.getPuBuParams(mContext,imageWidth, height);
            holder.getView(R.id.mImageView).setLayoutParams(puBuParams1);
        }else {
            int video_width = mData.get(position).getVideo_width();
            int video_height =  mData.get(position).getVideo_height();
            if(video_width!=0 && video_height!=0){
                holder.getView(R.id.mImageView).setLayoutParams(ViewUtils.getPuBuParams(
                        mContext,imageWidth, video_height));
            }else {
                holder.getView(R.id.mImageView).setLayoutParams(ViewUtils.getPuBuParams(
                        mContext,imageWidth , 125));
            }
        }
        break;
    default:
        break;
}

}
然後看一下優化後的代碼。
那麼下面這種代碼,就可以極大減少頻繁的動態計算寬高等屬性。通過imageView34.getLayoutParams()方式獲取layoutParams,就可以避免上面那種通過new創建大量的對象。要知道,平時像上面代碼那麼用也沒問題,但是在recyclerView瀑布流中,也可以細微優化一下。
@Override
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {

super.onBindViewHolder(holder, position);
//獲取屏幕寬度
if (screenWidth==0){
    screenWidth = SysUtils.getScreenWidth((Activity) mContext);
}
//16+11+16
if (padding==0){
    padding = SysUtils.Dp2Px(mContext,43);
}
if (imageWidth==0){
    imageWidth = (screenWidth-padding) / 2;
}
//寬高爲3:4的圖片高度
if (imageHeight34 ==0){
    imageHeight34 = (int) (imageWidth * (4/3.0f));
}
//寬高4:3的圖片高度
if (imageHeight43 ==0){
    imageHeight43 = (int) (imageWidth * (3/4.0f));
}
//長寬爲16:9的圖片高度
if (imageHeight169 ==0){
    imageHeight169 = (int) (imageWidth * (16/9.0f));
}
switch (holder.getItemViewType()) {
    case BasePubuBean.content_type9:
        break;
    //發佈的短視頻的是限制爲3:4
    case BasePubuBean.content_type2:
        int width34 = imageWidth;
        int height34 = imageHeight34;
        ImageView imageView34 = holder.getView(R.id.mImageView);
        ViewGroup.LayoutParams layoutParams34 = imageView34.getLayoutParams();
        layoutParams34.height = height34;
        layoutParams34.width = width34;
        imageView34.setLayoutParams(layoutParams34);
        break;
    //行情文章也是4:3
    case BasePubuBean.content_type8:
    //活動頭圖,自適應拿到圖片寬度,然後是4:3
    case BasePubuBean.content_type4:
    //文章詳情也是4:3
    case BasePubuBean.content_type5:
    //推薦車系是4:3
    case BasePubuBean.content_type10:
        int width43 = imageWidth;
        int height43 = imageHeight43;
        ImageView imageView43 = holder.getView(R.id.mImageView);
        ViewGroup.LayoutParams layoutParams43 = imageView43.getLayoutParams();
        layoutParams43.height = height43;
        layoutParams43.width = width43;
        imageView43.setLayoutParams(layoutParams43);
        break;
    //長視頻16:9
    case BasePubuBean.content_type3:
        int width169 = imageWidth;
        int height169 = imageHeight169;
        ImageView imageView169= holder.getView(R.id.mImageView);
        ViewGroup.LayoutParams layoutParams169 = imageView169.getLayoutParams();
        layoutParams169.height = height169;
        layoutParams169.width = width169;
        imageView169.setLayoutParams(layoutParams169);
        break;
    case BasePubuBean.content_type1:
    case BasePubuBean.content_type6:
    case BasePubuBean.content_type7:
        ImageView imageView= holder.getView(R.id.mImageView);
        int width = mData.get(position).getWidth();
        int height =  mData.get(position).getHeight();
        if( width!=0 && height!=0){
            //這種方式會創建大量的對象
            //RelativeLayout.LayoutParams puBuParams1 = ViewUtils.getPuBuParams(mContext,width, height);
            //holder.getView(R.id.mImageView).setLayoutParams(puBuParams1);
            ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
            layoutParams.height = height;
            layoutParams.width = imageWidth;
            imageView.setLayoutParams(layoutParams);
        }else {
            int videoWidth = mData.get(position).getVideo_width();
            int videoHeight =  mData.get(position).getVideo_height();
            if(videoWidth!=0 && videoHeight!=0){
                ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
                layoutParams.height = videoHeight;
                layoutParams.width = imageWidth;
                imageView.setLayoutParams(layoutParams);
                //holder.getView(R.id.mImageView).setLayoutParams(ViewUtils.getPuBuParams(
                //        mContext,imageWidth, video_height));
            }else {
                ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
                layoutParams.height = 125;
                layoutParams.width = imageWidth;
                imageView.setLayoutParams(layoutParams);
                //holder.getView(R.id.mImageView).setLayoutParams(ViewUtils.getPuBuParams(
                //        mContext,imageWidth,125));
            }
        }
        break;
    default:
        break;
}

}
10.瀑布流item點擊事件優化

關於rv設置item條目點擊事件有兩種方式:1.在onCreateViewHolder中寫;2.在onBindViewHolder中寫;3.在ViewHolder中寫。那麼究竟是哪一種好呢?
1.在onCreateViewHolder中寫
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

final View view = LayoutInflater.from(mContext).inflate(R.layout.item_me_gv_grid, parent, false);
final MyViewHolder holder = new MyViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (listener != null) {
            listener.onItemClick(view, holder.getLayoutPosition());
        }
    }
});
return holder;

}
2.在onBindViewHolder中寫
@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, int position) {

holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (listener != null) {
            listener.onItemClick(holder.itemView, holder.getAdapterPosition());
        }
    }
});

}
3.在ViewHolder中寫
class MyViewHolder extends RecyclerView.ViewHolder {

MyViewHolder(final View itemView) {
    super(itemView);
    itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (listener != null) {
                listener.onItemClick(itemView, getAdapterPosition());
            }
        }
    });
}

}
onBindViewHolder() 中頻繁創建新的 onClickListener 實例沒有必要,建議實際開發中應該在 onCreateViewHolder() 中每次爲新建的 View 設置一次就行。
11.Glide加載優化

glide優化方案代碼,滑動時候禁止加載圖片,停止滑動後開始恢復加載圖片。
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
    super.onScrollStateChanged(recyclerView, newState);
    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
        LogUtil.i("yc---initRecyclerView"+ "恢復Glide加載圖片");
        Glide.with(RecommendFragment.this).resumeRequests();
    }else {
        LogUtil.i("yc---initRecyclerView"+"禁止Glide加載圖片");
        Glide.with(RecommendFragment.this).pauseRequests();
    }
}

});
綁定控件的生命週期
當一個界面離開之後,我們更希望當前的圖片取消加載,那麼 Glide 是怎麼做到的呢?當在recyclerView列表中,item從可見滑動到不可見的時候,如何控制圖片請求的生命週期,那麼可以與控件生命週期相綁定。
Glide.with(mImageView)

    .load(imgUrl)
    .apply(RequestOptions.bitmapTransform(multiTransformation)
            .placeholder(R.drawable.glide_load)
            .error(R.drawable.glide_error))
    .into(mImageView);

低內存的情況下主動清除緩存,看最新版本glide,其實源碼中以及處理了下面相關的邏輯。
/**

  • 低內存的時候執行

*/
@Override
public void onLowMemory() {

Log.d("Application", "onLowMemory");
super.onLowMemory();
Glide.get(this).clearMemory();

}

/**

  • HOME鍵退出應用程序
  • 程序在內存清理的時候執行

*/
@Override
public void onTrimMemory(int level) {

Log.d("Application", "onTrimMemory");
super.onTrimMemory(level);
if (level == TRIM_MEMORY_UI_HIDDEN){
    Glide.get(this).clearMemory();
}
Glide.get(this).trimMemory(level);

}
12.建議指定圖片的寬高

Glide設置圖片控件wrap_content不建議支持的問題
官方說,不支持並且不建議imageview設置wrap_content。因爲這樣glide不知道要加載多大的圖片給我們纔好,在他的接口(Sizes and dimensions)中也有體現。普通的imageview其實也還好,如果放在列表(RecyclerView)中, 由於我們並不知道目標圖片大小是多大的,所以我們選擇了wrap_content,那麼在上下來回滾動過程中,就會導致圖片一會大一會小的bug.
官方 issue 作者回答如下:
所以,如果可以,那麼還是指定圖片控件的寬高吧!
Don't use wrap_content.

Even if you don't use Glide, wrap_content necessarily means that the size of your views in RecyclerView are going to change from item to item. That's going to cause all sorts of UI weirdness.

One option is to try to obtain the image dimensions in whatever metadata you're using to populate the RecyclerView. Then you can set a fixed View size in onBindViewHolder so the view size at least doesn't change when the image is loaded. You're still likely to see weird scroll bar behavior though.

If nothing else, you can always pick a uniform size that's large enough for all items and use the same consistent size for every item.

For the image file size, you can downscale or upscale by setting the ImageView size manually to 150dp x 150dp. Ultimately either you need uniform view sizes or predetermined view sizes. There's nothing else that will prevent content from expanding or shrinking in your RecyclerView.

For the placeholder bit, I think that will be fixed by 648c58e, you can check by trying the 4.2.0-SNAPSHOT version: http://bumptech.github.io/gli...
01.關於博客彙總鏈接

1.技術博客彙總
2.開源項目彙總
3.生活博客彙總
4.喜馬拉雅音頻彙總
5.其他彙總
02.關於我的博客

github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡書:http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽書:http://www.ximalaya.com/zhubo...
開源中國:https://my.oschina.net/zbj161...
泡在網上的日子:http://www.jcodecraeer.com/me...
郵箱:[email protected]
阿里雲博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV
segmentfault頭條:https://segmentfault.com/u/xi...
掘金:https://juejin.im/user/593943...
業餘demo鏈接:https://github.com/yangchong2...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章