RecyclerView瀑布流優化方案探討

目錄介紹php

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

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

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

最簡單規則瀑布流實現,下面這種是設置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 =segmentfault

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.瀑布流上拉加載markdown

首先添加監聽方法,添加了這個方法就能夠上拉加載更多數據呢。可是發現還有點問題,上拉加載更多的那個佈局只是佔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() {app

@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...
郵箱:yangchong211@163.com
阿里雲博客: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...

相關文章
相關標籤/搜索