Examples of code covered in this article can be found in the link below:

Github.com/CodeTillDoo…

Problem analysis

During this period, business needs used RecyclerView waterfall flow to load and display large numbers of pictures. However, at the beginning, RecyclerView was used to load pictures directly, which caused problems such as flashing when sliding to the top, Item automatic switching position (data after switching is not consistent with the displayed picture), and blank at the top. The experience was terrible, so I went on a journey of optimization. Now record the optimization process and method for reference.

The solution

① When looking up information on the Internet, some netizens offer a solution

layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE)  
Copy the code

This does solve the problem of Item switching left and right when sliding to the top, but it’s not enough. When loading waterfall streams, there are still issues such as jumpy columns, flickering columns, and white space at the top, which need further optimization.

② Why does this kind of column jump, item flicker and blank appear? After analysis, the reason should be that the height of the picture we loaded is uncertain (the width is certain because it can be divided equally according to the screen width and the number of items in each row), and the size of Item is uncertain due to the recycling mechanism of ViewHolder after we slide a certain distance to RecyclerView. When sliding back to the top, Item needs to be redrawn by itself, which leads to redrawing, so there will be flickering, jumping, blank and other problems. In the final analysis, as long as we determine the size of Item before redrawing, we can avoid Item recalculating its own size, thus avoiding many problems caused by redrawing.

At this time there will be students to say, then I don’t let RecyclerView recycling is not over, need you to make these crooked doorways? For these students I can only say: OOM to learn.




You are out of your mind

Now that we have a plan, the next step is to start.

So once we get the image from the background, we download it, we use an IntentService, we get the Bitmap from the Url. Background execution do not understand, use up that discard do not understand).

After successfully pulling the image from the background, start IntentService to process the image.

ImageService.startService(MainActivity.this, data, mSubtype);  
Copy the code

How to do it: Use IntentService to fetch the Bitmap from the URL, process the image in the child thread, and then use EventBus to tell the main thread: “Hey, I’m done. You can show it.”

public class ImageService extends IntentService {  

public DataService() {  
    super("");  
 }  



public static void startService(Context context, List datas, String subtype) {  

  Intent intent =new Intent(context, ImageService.class);  

  intent.putParcelableArrayListExtra("data", (ArrayList) datas);  

  intent.putExtra("subtype", subtype);  

  context.startService(intent);  
  }  



@Override  

protected void onHandleIntent(Intent intent) {  
    if (intent == null) {  
        return;  
     }  
    List datas = intent.getParcelableArrayListExtra("data");  

    String subtype = intent.getStringExtra("subtype");  

    handleGirlItemData(datas, subtype);  

    }  



private void handleGirlItemData(List datas, String subtype) {  

        if (datas.size() == 0) {  

        EventBus.getDefault().post("finish");  

        return;  

      }  

        for (GirlItemData data : datas) {  

        Bitmap bitmap = ImageLoader.load(this, data.getUrl());  

        if (bitmap != null) {  

                data.setWidth(bitmap.getWidth());  

                data.setHeight(bitmap.getHeight());  

            }  

            data.setSubtype(subtype);  

        }  

        EventBus.getDefault().post(datas);  

    }  

}  
Copy the code

After processing, load into Adapter:

public class GirlAdapter extends BaseQuickAdapter { public GirlAdapter(){ super(R.layout.item_girl_layout); } @Override protected void convert(BaseViewHolder helper, GirlItemData item) { ScaleImageView imageView = helper.getView(R.id.girl_item_iv); imageView.setInitSize(item.getWidth(), item.getHeight()); ImageLoader.load(BaseApplication.getContext(), item.getUrl(), imageView); } public void deleteItem(int position){ remove(position); notifyDataSetChanged(); }}Copy the code

At this point we can see that the waterfall stream does not flicker, nor does it suddenly switch columns, and the whitespace phenomenon seems to disappear.

But there is still something wrong: the waterfall stream loads much slower… This problem may be serious. When the user opens 5s, he sees nothing, so he goes back to the desktop and uninstalls our APP…

Why is this a problem? Before optimization, we got Json file (including picture ID, URL, owner, etc.) from the background, waterfall stream started to load without a word, Glide downloaded the picture according to the URL, downloaded a picture in the waterfall stream to show a placeholder before downloading.

And after optimization? For example, when we pull the JSON data of 10 photos at one time, we need to download the 10 pictures completely and process the length and width information before displaying them, which will take a long time.

Therefore, at this time, we can only put forward requirements for the background students: the Json data should contain the length and width information of the picture, so that we do not need to process in the client.


Therefore, the above code, suitable for the background students do not add requirements.

③ Finally, we found in the test that after deleting an Item in the waterfall stream, there was still a small probability that there was a blank space at the top of the page when we slid back to the home page. For this problem, only need to set up RecyclerView listening, if delete Item and slide back to the home page, refresh Adapter again.

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (isItemDeleted){ StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) recyclerView.getLayoutManager(); int[] firstVisibleItem = null; firstVisibleItem = layoutManager.findFirstVisibleItemPositions(firstVisibleItem); if (firstVisibleItem ! = null && firstVisibleItem[0] == 0) { if (mAdapter! =null) { isItemDeleted =false; mAdapter.notifyDataSetChanged(); }}}}});Copy the code

Basically, the above three solutions can handle most cases of Item confusion in waterfall flow.

After the optimization of the waterfall flow is still very stable, see the little sister is very strong.

If you want to see more details of the code, you can go to the link below to download the source of this article. If it helps you, please give it to Star😋

Github.com/CodeTillDoo…