11

I am making a 9x9 Sudoku grid where each of the 81 cells is itself a 3x3 grid. A single cell looks something like this:

1 2 3

4 5 6

7 8 9

Each number represents pencil annotations for that cell. I have a file called cell_layout.xml representing this 3x3 arrangement.

I am already able to generate the grid, and the code works:

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.solver_principal);

        TableLayout sudokuGrid = (TableLayout) findViewById(R.id.sudokuGrid);
        sudokuGrid.setShrinkAllColumns(true);
        sudokuGrid.setStretchAllColumns(true);

        TableRow.LayoutParams paramsRow = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
        TableLayout.LayoutParams paramsLayout = new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT);

        for(int i = 0; i < 9; ++i)
        {
            TableRow tableRow = new TableRow(SolverActivity.this);
            tableRow.setDividerDrawable(getResources().getDrawable(R.drawable.column_divider));
            tableRow.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
            for(int j = 0; j < 9; ++j)
            {
                View cell = getLayoutInflater().inflate(R.layout.cell_layout, sudokuGrid, false);
                cell.setLayoutParams(paramsRow);
                tableRow.addView(cell);
            }

            tableRow.setLayoutParams(paramsLayout);
            sudokuGrid.addView(tableRow);
        }

    }

The code above just inflates 81 times the wanted layout into the TableLayout.

It works, so what is your problem?

The activity takes too long to create. Even if I test with just one row of the grid, it takes too long for the method to inflate many times the wanted layout.

I get:

Background concurrent copying GC freed 131244(9MB) AllocSpace objects, 0(0B) LOS objects, 24% free, 74MB/98MB, paused 127us total 444.411ms

Skipped 153 frames! The application may be doing too much work on its main thread.

Can anyone suggest a better approach for my situation? Is really too much work generating 81 times a 3x3 grid?

Thanks

Edit

So I now tried to manually write down the xml file. I thought that not having to inflate many times the xml would improve things, but it didn´t. I still have the same problem "Too much work in the main thread" just by loading the xml file.

<TableLayout
        android:id="@+id/sudokuGrid"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:divider="@drawable/row_divider"
        android:showDividers="middle"
        android:shrinkColumns="*"
        android:stretchColumns="*"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/solverTitle">

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:divider="@drawable/column_divider"
            android:showDividers="middle">
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:divider="@drawable/column_divider"
            android:showDividers="middle">
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:divider="@drawable/column_divider"
            android:showDividers="middle">
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:divider="@drawable/column_divider"
            android:showDividers="middle">
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:divider="@drawable/column_divider"
            android:showDividers="middle">
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:divider="@drawable/column_divider"
            android:showDividers="middle">
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:divider="@drawable/column_divider"
            android:showDividers="middle">
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:divider="@drawable/column_divider"
            android:showDividers="middle">
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:divider="@drawable/column_divider"
            android:showDividers="middle">
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
            <include
                layout="@layout/cell_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                />
        </TableRow>

    </TableLayout>

And here is my cell layout, in case someone wants to give it a try.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="match_parent">

    <TextView
        android:id="@+id/cellValue"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/pencilOne"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:text="1"
        android:textSize="10sp"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/pencilFour"
        app:layout_constraintEnd_toStartOf="@+id/pencilTwo"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/pencilTwo"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:text="2"
        android:textSize="10sp"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/pencilFive"
        app:layout_constraintEnd_toStartOf="@+id/pencilThree"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/pencilOne"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/pencilThree"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:text="3"
        android:textSize="10sp"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/pencilSix"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/pencilTwo"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/pencilFour"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:text="4"
        android:textSize="10sp"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/pencilSeven"
        app:layout_constraintEnd_toStartOf="@+id/pencilFive"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/pencilOne" />

    <TextView
        android:id="@+id/pencilSix"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:text="6"
        android:textSize="10sp"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/pencilNine"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/pencilFive"
        app:layout_constraintTop_toBottomOf="@+id/pencilThree" />

    <TextView
        android:id="@+id/pencilSeven"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:text="7"
        android:textSize="10sp"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/pencilEight"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/pencilFour" />

    <TextView
        android:id="@+id/pencilEight"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:text="8"
        android:textSize="10sp"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/pencilNine"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/pencilSeven"
        app:layout_constraintTop_toBottomOf="@+id/pencilFive" />

    <TextView
        android:id="@+id/pencilNine"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:text="9"
        android:textSize="10sp"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/pencilEight"
        app:layout_constraintTop_toBottomOf="@+id/pencilSix" />

    <TextView
        android:id="@+id/pencilFive"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="1:1"
        android:text="5"
        android:textSize="10sp"
        android:gravity="center"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/pencilEight"
        app:layout_constraintEnd_toStartOf="@+id/pencilSix"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/pencilFour"
        app:layout_constraintTop_toBottomOf="@+id/pencilTwo" />
</android.support.constraint.ConstraintLayout>
PhysicsPDF
  • 361
  • 1
  • 9
  • 3
    What about using a GridLayout, instead? – Phantômaxx Sep 29 '19 at 16:39
  • 2
    @Fantômas Could you expand on why is it a more suitable approach than the described above? Isn't Grid layout deprecated? – PhysicsPDF Sep 29 '19 at 17:25
  • 1
    As far as I know, it is not. https://developer.android.com/reference/android/widget/GridLayout – Phantômaxx Sep 29 '19 at 18:30
  • 1
    You might want to use `GridLayoutManager` and a `RecyclerView`. Though if you need a scrollable one in both directions... that requires a custom layout manager. – EpicPandaForce Oct 02 '19 at 18:17
  • 1
    Don't inflate in a for loop on the main thread, inflate on worker threads. *Reading from storage is slow*. possibly with [AsyncLayoutInflater.](https://developer.android.com/reference/android/support/v4/view/AsyncLayoutInflater.html) – Jon Goodwin Oct 03 '19 at 02:02
  • 2
    Are all the the views visible at once (i.e. no scroll) ? if so a RecyclerView will not help – bwt Oct 03 '19 at 11:20
  • 1
    You should make a custom view. An example is given here: https://github.com/ogarcia/opensudoku/blob/master/app/src/main/java/org/moire/opensudoku/gui/SudokuBoardView.java – Benjamin Oct 08 '19 at 21:29

5 Answers5

6

For more information about Application doing too much work in main thread Please refer this post

https://stackoverflow.com/a/21126690/7666442

You should use RecyclerView with GridLayoutManager

Follow this steps

First add below dependencies in your Build.Gradle file to use RecyclerView

implementation 'com.google.android.material:material:1.0.0-beta01'

Note : no need to add extra dependencies of RecyclerView if you have already added dependencies of com.google.android.material:material

Now add RecyclerView in your activity file 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="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".JavaActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:id="@+id/myRecyclerView"
        android:layout_height="wrap_content" />

</LinearLayout>

Now you need to use GridLayoutManager to display your list item as grid

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

public class JavaActivity extends AppCompatActivity {

    RecyclerView myRecyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_java);

        myRecyclerView = findViewById(R.id.myRecyclerView);

        GridLayoutManager gridLayoutManager = new GridLayoutManager(JavaActivity.this, 3);
        myRecyclerView.setLayoutManager(gridLayoutManager);
        myRecyclerView.setAdapter(new MyAdapter(this));
    }

}

Create a MyAdapter class like this

import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

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

    private Context context;

    public MyAdapter(Context context) {
        this.context = context;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.row_list_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.tvText.setText(String.valueOf(position + 1));
        if (position % 2 == 0) {
            holder.imgBanner.setBackgroundColor(Color.RED);
        } else {
            holder.imgBanner.setBackgroundColor(Color.GREEN);
        }
    }

    @Override
    public int getItemCount() {
        return 81;
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        ImageView imgBanner;
        TextView tvText;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            imgBanner = itemView.findViewById(R.id.imgBanner);
            tvText = itemView.findViewById(R.id.tvText);
        }
    }
}

row_list_item layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/imgBanner"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:contentDescription="@string/app_name"
        android:src="@drawable/ic_launcher_background" />

    <TextView
        android:id="@+id/tvText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textColor="@android:color/white"
        android:textSize="15sp"
        android:textStyle="bold" />
</RelativeLayout>

OUTPUT

enter image description here

Community
  • 1
  • 1
AskNilesh
  • 67,701
  • 16
  • 123
  • 163
  • 1
    Could you explain why this would work opposed to the approach taken by the OP? You would still have to load the 719 text views at once. Additional, how do you simulate the "stretch all, and shrink all" columns to occupy all space as OP is doing with the table layout? – Daniel Duque Oct 03 '19 at 11:34
  • @DanielDuque *Could you explain why this would work opposed to the approach taken by the OP?* read this [RecyclerView overview](https://developer.android.com/guide/topics/ui/layout/recyclerview#structure) – AskNilesh Oct 03 '19 at 11:43
  • @DanielDuque *how do you simulate the "stretch all, and shrink all" columns to occupy all space as OP is doing with the table layout?* for that purpose you can modify `row_list_item layout` as per your requirement – AskNilesh Oct 03 '19 at 11:45
  • 6
    All vies are visible at once. I understand how recycler view would work when scrolling is enabled, but I don´t think it helps in he case described by the OP, where are views are visible and loaded at once. – Daniel Duque Oct 03 '19 at 19:39
  • @DanielDuque can you please share your expected output as image for whole screen? – AskNilesh Oct 04 '19 at 04:50
  • Basically see @Cheticamp's answer. ALL items visible at once. RecyclerView would do ZERO recycling. If you see OP's XML Layout, it has no scrolling at all. – Vitor Hugo Schwaab Oct 09 '19 at 08:23
3

Too much work in the main thread

What this means is that your code is taking long to process and frames are being skipped because of it, It maybe because of some heavy processing that you are doing at the heart of your application or DB access or any other thing which causes the thread to stop for a while. Read Android UI : Fixing skipped frames

To ensure your app performs well across a wide variety of devices, ensure your code is efficient at all levels and aggressively optimize your performance.

Use enhanced for loop syntax

The enhanced for loop (also sometimes known as "for-each" loop) can be used for collections that implement the Iterable interface and for arrays.

Example

static class Foo {
    int splat;
}

Foo[] array = ...

public void zero() {
    int sum = 0;
    for (int i = 0; i < array.length; ++i) {
        sum += array[i].splat;
    }
}

public void one() {
    int sum = 0;
    Foo[] localArray = array;
    int len = localArray.length;

    for (int i = 0; i < len; ++i) {
        sum += localArray[i].splat;
    }
}

public void two() {
    int sum = 0;
    for (Foo a : array) {
        sum += a.splat;
    }
}

zero() is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the loop.

one() is faster. It pulls everything out into local variables, avoiding the lookups. Only the array length offers a performance benefit.

two() is fastest for devices without a JIT, and indistinguishable from one() for devices with a JIT. It uses the enhanced for loop syntax introduced in version 1.5 of the Java programming language.

FYI

To ensure that a user's interaction with your app is smooth, your app should render frames in under 16ms to achieve 60 frames per second (why 60fps?). If your app suffers from slow UI rendering, then the system is forced to skip frames and the user will perceive stuttering in your app. We call this jank. Read Slow rendering.

If Systrace shows that the Layout segment of Choreographer#doFrame is doing too much work, or doing work too often, that means you're hitting layout performance issues. The layout performance of your app depends on what portion of the View hierarchy has changing layout parameters or inputs.

The Android Profiler tools provide real-time data to help you to understand how your app uses CPU, memory, network, and battery resources. See Measure app performance with Android Profiler.

IntelliJ Amiya
  • 74,896
  • 15
  • 165
  • 198
  • 3
    I don't think this really answers the problem, except the last sentence. Could you expand on why a Grid Layout manager with recycler view would solve the problem? – Daniel Duque Oct 03 '19 at 11:36
2

Create your views on another thread:

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int 
    viewType) {

       new Thread(new Runnable() {
          public void run(){
               View view = 
               LayoutInflater.from(context).inflate(R.layout.row_list_item, 
               parent, false);
               return new MyViewHolder(view);
           }
       }).start();
    }
Ashish Patel
  • 163
  • 6
1

You are contending with more than just slow inflation. Your 729+ views must also be measured and laid out and that is a lot of work given all the cross dependencies of the views in your layout. (See "How Android Draws".)

Here is one way to approach the problem that will produce the grid I think you want. You may need to make modifications to meet your requirements.

The following approach takes a FrameLayout and populates it directly with TextViews that are created on a worker thread. The TextViews are then added to the FrameLayout on the UI thread. Placement of each TextView is made using the translationX and translationY properties. The benefit of this approach is that layout is greatly simplified since the position of each view is specified precisely as well as the size of each view.

I show much of the work being done on a worker thread, but I believe that this may be overkill. I think that you would not have a problem just doing everything on the UI thread, but you will need to determine if you want to do that or not.

Here is the output of the sample app:

enter image description here

SolverActivity.java

public class SolverActivity extends AppCompatActivity {
    int textViewWidth;
    int textViewHeight;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.solver_principal);
        final FrameLayout sudokuGrid = findViewById(R.id.sudokuGrid);

        // post to get the size of the sudoku grid post-layout.
        sudokuGrid.post(() ->
                new Thread(() -> {
                    textViewWidth = sudokuGrid.getWidth() / 27;
                    textViewHeight = sudokuGrid.getHeight() / 27;
                    createGrid(sudokuGrid);
                }).start());
    }

    @WorkerThread
    private void createGrid(FrameLayout sudokuGrid) {
        int blockWidth = textViewWidth * 3;
        int blockHeight = textViewHeight * 3;

        // row, col refers to the row and column of the 3x3 blocks of TextViews.
        for (int row = 0; row < 9; row++) {
            for (int col = 0; col < 9; col++) {
                int blockX = col * blockWidth;
                int blockY = row * blockHeight;
                // The block view covers the whole 3x3 TextView block.
                final TextView blockView = createTextView(blockX, blockY, null);
                runOnUiThread(() -> {
                    blockView.setBackgroundResource(R.drawable.outline);
                    sudokuGrid.addView(blockView, new FrameLayout.LayoutParams(blockWidth, blockHeight));
                });
                // Fill in the 3x3 block.
                for (int tvCell = 0; tvCell < 9; tvCell++) {
                    int transX = blockX + (tvCell % 3) * textViewWidth;
                    int transY = blockY + (tvCell / 3) * textViewHeight;
                    String text = String.valueOf(tvCell + 1);
                    final TextView tv = createTextView(transX, transY, text);
                    runOnUiThread(() ->
                            sudokuGrid.addView(tv, new FrameLayout.LayoutParams(textViewWidth, textViewHeight)));
                }
            }
        }
    }

    private TextView createTextView(int transX, int transY, @Nullable String text) {
        final TextView tv = new TextView(SolverActivity.this);

        if (text != null) {
            tv.setText(text);
        }
        tv.setTranslationX(transX);
        tv.setTranslationY(transY);
        tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
//                            tv.setVisibility(View.INVISIBLE);
        tv.setGravity(Gravity.CENTER);
        return tv;
    }
}

solver_principal.xml

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

outlined.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <stroke android:width="1dp" android:color="#FFCCCCCC"/>
    <padding android:left="1dp" android:top="1dp"
        android:right="1dp" android:bottom="1dp" />
</shape>

Another solution would be just to draw on the canvas of the FrameLayout. You would have to handle touches directly, but this would probably be the fastest implementation.

Cheticamp
  • 61,413
  • 10
  • 78
  • 131
0

Intro

Lets have a more general approach (helpful for more users), regardless of:

main thread

What does the main thread do (it does a lot, don't overload it!)?

  • The default, primary thread created anytime an Android application is launched.

  • Also known as a UI thread .

  • It is in charge of handling all user interface and activities, unless otherwise specified.

  • Runnable is an interface meant to handle sharing code between threads.

  • It contains only one method: run() .

from "Google Search Results", "Featured snippet from the web"

Inflation

Inflating a big complex TableLayout on the main thread is insane!

(1) use worker (background) threads processes-and-threads

There are two simple rules to Android's thread model:

  • Do not block the UI thread
  • Do not access the Android UI toolkit from outside the UI thread (with the exception of things like AsyncLayoutInflater.)

(2) Try AsyncLayoutInflater.

SolverActivity.java:

public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    final AsyncLayoutInflater.OnInflateFinishedListener callback = new AsyncLayoutInflater.OnInflateFinishedListener()
    {
        @Override
        public void onInflateFinished(View view, int resid, ViewGroup parent)
        {
           TableLayout sudokuGrid = (TableLayout) findViewById(R.id.sudokuGrid);
           sudokuGrid.setShrinkAllColumns(true);
           sudokuGrid.setStretchAllColumns(true);

           TableRow.LayoutParams paramsRow = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
           TableLayout.LayoutParams paramsLayout = new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT);

           for(int i = 0; i < 9; ++i)
           {
               TableRow tableRow = new TableRow(SolverActivity.this);
               tableRow.setDividerDrawable(getResources().getDrawable(R.drawable.column_divider));
               tableRow.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
               for(int j = 0; j < 9; ++j)
               {
                   View cell = getLayoutInflater().inflate(R.layout.cell_layout, sudokuGrid, false);
                   cell.setLayoutParams(paramsRow);
                   tableRow.addView(cell);
               }

               tableRow.setLayoutParams(paramsLayout);
               sudokuGrid.addView(tableRow);
        }
    };
    if (savedInstanceState == null) {
        AsyncLayoutInflater inflater = new AsyncLayoutInflater(this);
        inflater.inflate(R.layout.solver_principal, null, callback);
    } else {
        View view = getLayoutInflater().inflate(R.layout.solver_principal, null);
        Callback.onInflateFinished(view, R.layout.solver_principal, null)
    }
}

(3) Inflation is intensive (read from storage, analyse XML, check for errors,allocate resources, memory...)

You do not have to inflate at all, do it programatically.

How not to inflate

(a) SO Should I inflate a layout or programmatically create it?

(b) Here's the approach I prefer (small project 4 source .java files, based on ImageView with OnCellTouchListener) which you could modify: android-calendar-view

enter image description here

Jon Goodwin
  • 9,053
  • 5
  • 35
  • 54