-2

I'm new to android app development and i have recently made my first app containing a basic tablayout with two fragments in my MainActivity.

In my second tab, it consists of only one RecyclerView as a rootlayout. What i did was populating the RecyclerView with an arraylist of my own dataModel.

My RecyclerView has 13 items. The view of these items is a expansion layout from https://github.com/florent37/ExpansionPanel I followed exactly the way he did for RecyclerView and it worked perfectly.

HOWEVER, my app's startup time has INCREASED significantly to 1s++. I found that the onBindViewHolder method in my RecyclerViewAdapter is the culprit. I tried commenting out the content of onBindViewHolder method and the startup time has reduced by half to 600ms, which is okay for me.

So my questions is How can I solve this problem? Should I bind my data in a different way?

I dont want to have a slow startup time. I saw some posts about using databinding API, running on a different threads and asyncs, but i dont know how to do it.

Please help ~ Thanks in advance!

Here's my Adapter:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private Context context;
private ArrayList<DataModel> list;
private final ExpansionLayoutCollection expansionsCollection = new ExpansionLayoutCollection();

MyAdapter(ArrayList<DataModel> list, Context context) {
    this.list = list;
    this.context = context;
    expansionsCollection.openOnlyOne(true);
}


@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
   View itemView == LayoutInflater.from(parent.getContext()).inflate(R.layout.expansion_panel, parent, false);
   return new ViewHolder(itemView);
}


@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

    DataModel currentData = list.get(position);
    switch (currentData.getType()) {
        case DataModel.NO_FORMULA:
            holder.titleText.setText(currentData.getTitle());
            holder.contentText.setText(currentData.getContent());
            holder.formulaView.removeAllViews();
            expansionsCollection.add(holder.expansionLayout);
            break;

        case DataModel.WITH_FORMULA:
            View formulaLayout = ((Activity)context).getLayoutInflater().inflate(currentData.getFormulaLayoutId(),null);

            holder.titleText.setText(currentData.getTitle());
            holder.contentText.setText(currentData.getContent());
            holder.formulaView.removeAllViews();
            holder.formulaView.addView(formulaLayout);
            expansionsCollection.add(holder.expansionLayout);
            break;
    }
}


@Override
public int getItemCount() {
    return list.size();
}

private static class ViewHolder extends RecyclerView.ViewHolder {

    TextView titleText, contentText;
    ExpansionLayout expansionLayout;
    FrameLayout formulaView;

    ViewHolder(View itemView) {
        super(itemView);
        this.titleText = itemView.findViewById(R.id.title_text);
        this.contentText = itemView.findViewById(R.id.content_text);
        this.expansionLayout = itemView.findViewById(R.id.expansionLayout);
        this.formulaView  = itemView.findViewById(R.id.formula_view);
    }

}

This is my item layout:

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:ignore="ContentDescription,RtlHardcoded,HardcodedText">

    <com.github.florent37.expansionpanel.ExpansionHeader
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fff"
        app:expansion_headerIndicator="@id/headerIndicator"
        app:expansion_layout="@id/expansionLayout"
        app:expansion_toggleOnClick="true">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?android:attr/selectableItemBackground"
            android:minHeight="50dp"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:theme="@style/RecyclerViewTheme">

            <TextView
                android:id="@+id/title_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:layout_toLeftOf="@id/headerIndicator"
                android:textColor="#000" />

            <android.support.v7.widget.AppCompatImageView
                android:id="@+id/headerIndicator"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:layout_marginLeft="16dp"
                android:adjustViewBounds="true"
                app:srcCompat="@drawable/ic_expansion_header_indicator_grey_24dp" />

        </RelativeLayout>


    </com.github.florent37.expansionpanel.ExpansionHeader>

    <com.github.florent37.expansionpanel.ExpansionLayout
        android:id="@+id/expansionLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:expansion_expanded="false">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:background="#F5F5F5">

            <TextView
                android:id="@+id/content_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="16dp"
                android:textColor="#000" />

            <FrameLayout
                android:id="@+id/formula_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

        </LinearLayout>

    </com.github.florent37.expansionpanel.ExpansionLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.7dp"
        android:background="?android:attr/listDivider" />

</LinearLayout>

Here's my datamodel:

public class DataModel {

public static final int WITH_FORMULA = 0;
public static final int NO_FORMULA = 1;

private String title, content;
private int type, formulaLayoutId;

DataModel(int type, String title, String content) {
    this.type = type;
    this.title = title;
    this.content = content;
}

DataModel(int type, String title, String content, int formulaLayoutId) {
    this.type = type;
    this.title = title;
    this.content = content;
    this.formulaLayoutId = formulaLayoutId;
}

public String getTitle() {
    return title;
}

public String getContent() {
    return content;
}

public int getFormulaLayoutId() {
    return formulaLayoutId;
}

public int getType() {
    return type;
}

*I've created 13 DataModel objects with long titles and long paragraphs as contents and put them in an arraylist. When the user click on the expansion panel's header, the expansion layout will expand to show the contents of it.

Only 2 out of the 13 items in the recyclerview is the type of WITH_FORMULA that has to inflate the formulaLayout.

Philemon Khor
  • 367
  • 5
  • 12

1 Answers1

0

If you are sure that the root of your problem is onBindViewHolder then the only problem there is the inflation.

inflate is a very expensive operation because it parses XML and then creates the view hierarchy. Don't do that in onBindViewHolder. Inflate the view once in onCreateViewHolder instead. If you don't want to show it in NO_FORMULA case just set its visibility to GONE and to VISIBLE in WITH_FORMULA case. Changing the visibility is way more simple operation.

Gennadii Saprykin
  • 4,505
  • 8
  • 31
  • 41
  • Thanks for your explanation. I tried not to inflate any layouts at all, just setText for my titles and contents. The startup time has reduced to 850++ms (approx. 300ms longer compared to not binding any views). Is that the minimum time required to bind the viewholders? – Philemon Khor Jul 18 '18 at 09:41
  • I don't think "the minimum startup time" is documented anywhere because it depends on many things such as device capabilities and resources, Android OS version, other processes and services running, and, of course, your code. In most cases the last one is the real culprit but it can only be fixed through debugging and investigating what your app is doing. If you are sure that the adapter is the problem, and not some other code, then just make sure you don't `inflate` too much and that you don't call `findViewById` too often. In your case the adapter looks fine except that issue I mentioned. – Gennadii Saprykin Jul 18 '18 at 10:07