Search code examples
androidandroid-layoutandroid-recyclerviewandroid-custom-view

Optimise a complex layout for an item displayed inside a RecyclerView


I have to display posts from a json feed inside a RecyclerView and I have a layout for how a single row looks inside the RecyclerView as follows enter image description here

I have not yet implemented the bottom part in my actual layout which contains the red and green boxes from the figure above and my layout looks like this enter image description here

I am also implementing Swipe to delete with undo which as you know requires a FrameLayout as the root , and 2 nodes under it, one showing the normal area and one showing the layout which is revealed on swipe. In my case, when I swipe the item, this is what you will see.

enter image description here

Now the problem is, there are 13 views per row and I don't like the odds of that, I will be displaying a maximum of 100 items in the RecyclerView at a given time and as you see it would lead to a large number of Views. I have certain approaches in mind to make a custom View to reduce the number of Views in the row. What would be the best way according to you to optimise this View or should I say, reduce the number of Views per row in the RecyclerView. I get all the data from JSON and the central text area needs to be expandable in nature with a Read More or Read Less depending on its state.

Approach 1

Slight simplification

In this approach, I will combine the person's profile picture at the top left, the TextView with the name and updated time into a single Custom View, in other words, it will extend from View, have its own canvas for drawing the Bitmap, the Strings but I'll have to manually code RTL and LTR and other items using a StaticLayout in Android. I need the central text area to be expandable in nature so I will stick with one of the Expandable Views everyone keeps mentioning on stackoverflow.

Approach 2

Highly modular Custom UI component.

The entire diagram composed of the user's image, text with name, time, central text and image can be made into a single CustomView, I am not sure how I can make this expandable yet because a StaticLayout once initialised in Android cannot be modified. What do you think? Is this the correct approach to go? I will end up having only 4 children per row if I make the entire thing a single View. Is that a valid use case for custom Views?


Solution

  • Well, I found the answer myself. There are two types of post items I am dealing with, ones that contain an ImageView to display a 16:9 post image and the ones that don't. My RecyclerView was lagging heavily in the scroll operation till now since I had a single layout file which had an ImageView with its width set to match_parent and height set to wrap_content. When I was scrolling, the posts with Images were using Glide to load images and were calling requestLayout() since the size of those items were changing. This was resulting in too many requestLayout() calls causing the heavy lag while scrolling.

    How did I fix this?

    I made an Adapter which was sectioned having 2 types of rows.

    @Override
    public int getItemViewType(int position) {
        if (mResults != null) {
            return mResults.get(position).getPicture() != null ? IMAGE : NO_IMAGE;
        }
        return NO_IMAGE;
    }
    

    Based on whether the result at the current position contains an Image in the post or not, I had my onCreateViewHolder method modified to set a predefined size for the ImageView incase my item had a post image.

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        if (viewType == IMAGE) {
            view = mLayoutInflater.inflate(R.layout.row_post_image, parent, false);
            RowImageHolder holder = new RowImageHolder(view);
    
            //To set the width and height of the ImageView of our image in the post, we get its LayoutParams and adjust its width and height to maintain 16:9 ratio
            ViewGroup.LayoutParams params = holder.mPostPicture.getLayoutParams();
            params.width = mPostImageWidth;
            params.height = mPostImageHeight;
            holder.mPostPicture.setLayoutParams(params);
            return holder;
        } else {
            view = mLayoutInflater.inflate(R.layout.row_post_image, parent, false);
            RowNoImageHolder holder = new RowNoImageHolder(view);
            return holder;
        }
    }
    

    And thats all which needs to be done. The onBindViewHolder sets an image if the type of the item supports an image. The benefit is that space is reserved in advance for those items that have images and the performance of the RecyclerView on scroll improves tremendously. I achieved this without using any custom views so far :)