目錄介紹
- 01.規(guī)則瀑布流實(shí)現(xiàn)
- 02.不規(guī)則瀑布流實(shí)現(xiàn)
- 2.1 實(shí)現(xiàn)方式
- 2.2 遇到問題
- 03.瀑布流上拉加載
- 04.給瀑布流設(shè)置分割線
- 05.自定義Manager崩潰
- 06.如何避免刷新抖動
- 07.為何有時出現(xiàn)跳動
- 08.瀑布流圖片優(yōu)化
- 09.onBindViewHolder優(yōu)化
- 10.瀑布流item點(diǎn)擊事件優(yōu)化
- 11.Glide加載優(yōu)化
- 12.建議指定圖片的寬高
歡迎同行探討瀑布流極致優(yōu)化方案
- 如果同行看到這篇文章,有好的瀑布流優(yōu)化方案,歡迎給出建議百姓,或者給鏈接也可以框冀。
- 需求:
- 瀑布流大概有10來中不同type的item視圖劝评,然后視圖是根據(jù)動態(tài)設(shè)置寬高腋逆,服務(wù)器會返回比例
- 瀑布流中item需要切割圓角
- 目前使用glide加載圖片
- 產(chǎn)品說讓參考抖音快手類的app慕嚷,讓瀑布流滑動效果特別流暢……但目前遇到問題是滑動十幾頁沒什么問題偶宫,但是滑動三四十頁的時候會出現(xiàn)卡頓非迹。歡迎同行給出建議!
好消息
- 博客筆記大匯總【16年3月到至今】读宙,包括Java基礎(chǔ)及深入知識點(diǎn)彻秆,Android技術(shù)博客,Python學(xué)習(xí)筆記等等结闸,還包括平時開發(fā)中遇到的bug匯總唇兑,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護(hù)并且修正桦锄,持續(xù)完善……開源的文件是markdown格式的扎附!同時也開源了生活博客,從12年起结耀,積累共計(jì)N篇[近100萬字留夜,陸續(xù)搬到網(wǎng)上],轉(zhuǎn)載請注明出處图甜,謝謝碍粥!
- 鏈接地址:https://github.com/yangchong211/YCBlogs
- 如果覺得好,可以star一下黑毅,謝謝嚼摩!當(dāng)然也歡迎提出建議,萬事起于忽微,量變引起質(zhì)變枕面!
01.規(guī)則瀑布流實(shí)現(xiàn)
- 最簡單規(guī)則瀑布流實(shí)現(xiàn)愿卒,下面這種是設(shè)置3列數(shù)據(jù),然后組數(shù)據(jù)高度是相同的潮秘。是規(guī)則的瀑布流琼开。
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.不規(guī)則瀑布流實(shí)現(xiàn)
- 最簡單的不規(guī)則瀑布流實(shí)現(xiàn),下面這種是設(shè)置2列數(shù)據(jù)枕荞,然后數(shù)據(jù)的高度都不同柜候,圖片的高度隨機(jī)。
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);
- 這里是偽代碼买猖。假設(shè)設(shè)置不同高度改橘,代碼如下滋尉。簡單設(shè)置不同圖片高度不同玉控,這個是在onBindViewHolder中操作。
ViewGroup.LayoutParams params = imgPicture.getLayoutParams(); //假設(shè)有多種不同的類型 int type = getAdapterPosition()%5; //計(jì)算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 如何實(shí)現(xiàn)下拉加載更多狮惜;
- 2.StaggeredGridLayoutManager 顯示加載更多的時候高诺,加載更多作為最后一個item沒有單獨(dú)占滿屏幕寬度,只顯示為一個item的寬度碾篡;
- 3.StaggeredGridLayoutManager 如何隨機(jī)設(shè)置item的高度虱而;
- 4.StaggeredGridLayoutManager 上拉加載數(shù)據(jù)刷新UI時,由于高度隨機(jī)开泽,造成頁面item抖動問題牡拇;
- 5.RecyclerView莫名的Inconsistency detected崩潰;
- 卡頓和內(nèi)存釋放
- 瀑布流滑動幾頁感覺還行穆律,但是一旦滑動了三四十頁惠呼,就感覺頁面有些卡頓了。
03.瀑布流上拉加載
- 首先添加監(jiān)聽方法峦耘,添加了這個方法就可以上拉加載更多數(shù)據(jù)呢剔蹋。但是發(fā)現(xiàn)還有點(diǎn)問題,上拉加載更多的那個布局只是占1/spanCount列辅髓,這樣顯得特別難看泣崩。那么該如何處理呢,接著往下看……
- 首先要能夠監(jiān)聽recyclerView滑動事件洛口;
- 判斷recyclerView是否滑動到最后一個item矫付;
- recyclerView 加載更多RecyclerView.Adapter的設(shè)置處理:RecyclerView.Adapter的相關(guān)代碼,主要定義兩個ViewHolder類型,footType表示為底部的viewHolder第焰,normalType表示為正常的item的viewHolder买优,根據(jù)position的不同來顯示不同的viewholder;
// 實(shí)現(xiàn)上拉加載重要步驟,設(shè)置滑動監(jiān)聽器而叼,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 } } } });
- 因?yàn)槭瞧俨剂魍迅荩O(shè)置footerView占據(jù)一行娇钱,這個需要這樣設(shè)置,代碼如下所示
@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(); //如果是上拉加載更多類型绊困,則設(shè)置setFullSpan為true文搂,那么它就會占一行 if (getItemViewType(position) == footType) { params.setFullSpan(true); } } }
04.給瀑布流設(shè)置分割線
- 先來看看出現(xiàn)錯位,分割線出現(xiàn)問題的代碼秤朗。下面這種方式根據(jù)childCount來判斷奇數(shù)和偶數(shù)設(shè)置的不同間距煤蹭。
- 那么比如說當(dāng)為奇數(shù)時,設(shè)置該item到左為20取视,到右為5硝皂;當(dāng)為偶數(shù)時,該item到左為5作谭,到右為20稽物。
- 如果奇數(shù)的item都在左邊,偶數(shù)的item都在右邊折欠,那么間距就沒有問題贝或。
- 如果第一個item在左邊【高度很高】,第2個锐秦,第3個咪奖,第4個item都在右邊,第5個在左邊……那么思考一下农猬,這個時候第3個item在右邊赡艰,那么就會造成間距不規(guī)則。
- 顯然不能根據(jù)奇數(shù)或者偶數(shù)來設(shè)置item左右間距的大小的斤葱,會出現(xiàn)錯位慷垮。
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 會出現(xiàn)錯位的問題 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崩潰芹血;
- 出現(xiàn)這個異常原因:
- 使用RecyclerView加官方下拉刷新的時候,如果綁定的List對象在更新數(shù)據(jù)之前進(jìn)行了clear,而這時用戶緊接著迅速上滑RV幔烛,就會造成崩潰啃擦,而且異常不會報(bào)到你的代碼上,屬于RV內(nèi)部錯誤饿悬。
- 自定義一個CustomStaggeredGridLayoutManager 在onLayoutChildren對異常進(jìn)行捕獲:
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()); } } }
- 出現(xiàn)這個異常原因:
- 關(guān)于StaggeredGridLayoutManager異常說明
06.如何避免刷新抖動
- StaggeredGridLayoutManager 上拉加載數(shù)據(jù)刷新UI時令蛉,由于高度隨機(jī),造成頁面item抖動問題狡恬;
這里由于直接調(diào)用 notifyDataSetChanged();那么是全局刷新珠叔,而刷新的時候item的高度重新隨機(jī)分配,導(dǎo)致數(shù)據(jù)刷新的時候會造成抖動弟劲。建議采用notifyItemRangeChanged進(jìn)行局部刷新:public void setDatas(List mDatas ) { this.dataList = mDatas; notifyDataSetChanged(); } /** * 注意這個是未關(guān)注時候的瀑布流刷新數(shù)據(jù) * 上拉加載更多時祷安,建議刷新上拉加載那一部分?jǐn)?shù)據(jù)即可,不用刷新所有數(shù)據(jù) * @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.為何有時出現(xiàn)跳動
- 由于我們加載的圖片高度不確定(寬度確定因?yàn)榭梢愿鶕?jù)屏幕寬度和每行Item數(shù)目進(jìn)行等分)兔乞,而當(dāng)我們向RecyclerView下方滑動一段距離后汇鞭,由于ViewHolder的回收機(jī)制,item的尺寸并不確定报嵌,滑回到上方時Item需要重新自行繪制虱咧,于是這個又導(dǎo)致重繪熊榛,所以會有閃爍锚国、跳動、空白等問題玄坦。說到底血筑,只要我們在重繪前確定了Item的尺寸,那么就可以避免Item去重新計(jì)算自己的尺寸煎楣,就可以避免重繪導(dǎo)致的諸多問題豺总。
08.瀑布流圖片優(yōu)化
- 具體優(yōu)化方案
- 第一步:減少布局嵌套,并且在拿到服務(wù)器的尺寸后择懂,在onBindViewHolder中給圖片控件設(shè)置寬高時喻喳,避免創(chuàng)建大量臨時的LayoutParams對象
- 第二步:使用glide加載,綁定activity或者fragment的生命周期困曙,盡量不用用全局上下文或者靜態(tài)上下文表伦。注意with()方法中傳入的實(shí)例會決定Glide加載圖片的生命周期,如果傳入的是Activity或者Fragment的實(shí)例慷丽,那么當(dāng)這個Activity或Fragment被銷毀的時候蹦哼,圖片加載也會停止。如果傳入的是ApplicationContext要糊,那么只有當(dāng)應(yīng)用程序被殺掉的時候纲熏,圖片加載才會停止。
- 第三步:對于list條目,尤其是瀑布流局劲,不建議使用TransitionOptions來加載設(shè)置的動畫勺拣,尤其是不要使用自己自定義的動畫
- 避免使用圓角的ImageView
- 在實(shí)際項(xiàng)目內(nèi),經(jīng)常會用到一些帶圓角的圖片鱼填,或者直接就是圓形的圖片宣脉。圓形的圖片,多數(shù)用于一些用戶的頭像之類的顯示效果剔氏。
- 而在 Android 下塑猖,也有大量的類似 XxxImageView 的開源控件,用于操作 Bitmap 以達(dá)到一個圓角圖片的效果谈跛,例如 Github 上比較火的 RoundedImageView羊苟。
- 它們大部分的原理,是接收到你傳遞的 Bitmap 感憾,然后再輸出一個與原來 Bitmap 等大的新 Bitmap 蜡励,在此基礎(chǔ)之上,進(jìn)行圓角的一些處理阻桅,這就導(dǎo)致了凉倚,實(shí)際上會在內(nèi)存中,多持有一個 Bitmap 嫂沉,一下一張圖片占用的內(nèi)存就被加倍了稽寒。
- 所以既然已經(jīng)選擇使用Glide,推薦使用glide-transformations這個開源庫配合使用趟章,glide-transformations 利用 Glide 的 bitmapTransfrom() 接口杏糙,實(shí)現(xiàn)對加載的 Bitmap 的進(jìn)行一些變換操作。glide-transformations提供一系類對加載的圖片的變換操作蚓土,從形狀變換到色彩變換宏侍,全部支持,基本上滿足大部分開發(fā)需要蜀漆,并且它會復(fù)用 Glide 的 BitmapPool 谅河,來達(dá)到節(jié)約內(nèi)存的目的。
09.onBindViewHolder優(yōu)化
- 在這個方法中确丢,主要是做數(shù)據(jù)綁定到View視圖绷耍。由于瀑布流會有多種不同type類型的視圖,有些需要設(shè)置寬高蠕嫁,有的則要從服務(wù)器拿到寬高數(shù)據(jù)然后動態(tài)修改視圖屬性锨天。因此關(guān)于寬高的計(jì)算其實(shí)還可以做一些優(yōu)化。
- 先來看一下最初的代碼剃毒,這里只展示動態(tài)設(shè)置寬高的代碼病袄。如下所示
- 看了下面代碼搂赋,會發(fā)現(xiàn)這些問題。第一益缠,頻繁計(jì)算瀑布流中不同類型的寬度脑奠,該寬度是屏幕寬度減去間距,然后是除以2幅慌,瀑布流為兩行宋欺。第二,頻繁創(chuàng)建LayoutParams對象胰伍,然后設(shè)置寬高的屬性齿诞。
@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; //發(fā)布的短視頻的寬高也是限制為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; //活動頭圖,自適應(yīng)拿到圖片寬度骂租,然后寬高比是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; } }
- 然后看一下優(yōu)化后的代碼。
- 那么下面這種代碼渗饮,就可以極大減少頻繁的動態(tài)計(jì)算寬高等屬性但汞。通過imageView34.getLayoutParams()方式獲取layoutParams,就可以避免上面那種通過new創(chuàng)建大量的對象互站。要知道私蕾,平時像上面代碼那么用也沒問題,但是在recyclerView瀑布流中胡桃,也可以細(xì)微優(yōu)化一下踩叭。
@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; //發(fā)布的短視頻的是限制為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: //活動頭圖,自適應(yīng)拿到圖片寬度标捺,然后是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){ //這種方式會創(chuàng)建大量的對象 //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點(diǎn)擊事件優(yōu)化
- 關(guān)于rv設(shè)置item條目點(diǎn)擊事件有兩種方式: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()); } } }); } }
- 1.在onCreateViewHolder中寫
- onBindViewHolder() 中頻繁創(chuàng)建新的 onClickListener 實(shí)例沒有必要冤今,建議實(shí)際開發(fā)中應(yīng)該在 onCreateViewHolder() 中每次為新建的 View 設(shè)置一次就行闺兢。
11.Glide加載優(yōu)化
- glide優(yōu)化方案代碼,滑動時候禁止加載圖片戏罢,停止滑動后開始恢復(fù)加載圖片屋谭。
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"+ "恢復(fù)Glide加載圖片"); Glide.with(RecommendFragment.this).resumeRequests(); }else { LogUtil.i("yc---initRecyclerView"+"禁止Glide加載圖片"); Glide.with(RecommendFragment.this).pauseRequests(); } } });
- 綁定控件的生命周期
- 當(dāng)一個界面離開之后,我們更希望當(dāng)前的圖片取消加載龟糕,那么 Glide 是怎么做到的呢桐磁?當(dāng)在recyclerView列表中,item從可見滑動到不可見的時候讲岁,如何控制圖片請求的生命周期我擂,那么可以與控件生命周期相綁定衬以。
Glide.with(mImageView) .load(imgUrl) .apply(RequestOptions.bitmapTransform(multiTransformation) .placeholder(R.drawable.glide_load) .error(R.drawable.glide_error)) .into(mImageView);
- 低內(nèi)存的情況下主動清除緩存,看最新版本glide校摩,其實(shí)源碼中以及處理了下面相關(guān)的邏輯看峻。
/** * 低內(nèi)存的時候執(zhí)行 */ @Override public void onLowMemory() { Log.d("Application", "onLowMemory"); super.onLowMemory(); Glide.get(this).clearMemory(); } /** * HOME鍵退出應(yīng)用程序 * 程序在內(nèi)存清理的時候執(zhí)行 */ @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設(shè)置圖片控件wrap_content不建議支持的問題
- 官方說,不支持并且不建議imageview設(shè)置wrap_content衙吩。因?yàn)檫@樣glide不知道要加載多大的圖片給我們才好互妓,在他的接口(Sizes and dimensions)中也有體現(xiàn)。普通的imageview其實(shí)也還好坤塞,如果放在列表(RecyclerView)中, 由于我們并不知道目標(biāo)圖片大小是多大的冯勉,所以我們選擇了wrap_content,那么在上下來回滾動過程中摹芙,就會導(dǎo)致圖片一會大一會小的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/glide/dev/snapshots.html.
01.關(guān)于博客匯總鏈接
- 1.技術(shù)博客匯總
- 2.開源項(xiàng)目匯總
- 3.生活博客匯總
- 4.喜馬拉雅音頻匯總
- 5.其他匯總
02.關(guān)于我的博客
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 簡書:http://www.reibang.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜馬拉雅聽書:http://www.ximalaya.com/zhubo/71989305/
- 開源中國:https://my.oschina.net/zbj1618/blog
- 泡在網(wǎng)上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 郵箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault頭條:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e