Search code examples
androidandroid-recyclerviewandroid-tablelayout

TableRow in TableLayout and TableRow from RecyclerView aren’t align


I believe TableRow from activity_main and TableRow from recycler_item_header_row have the same values of fields. But they are not aligned! They are shown like this:

enter image description here enter image description here

Why the positions of textViews in rows from the activity_main and from the RecyclerView aren’t aligned?

activity_main structure:

<TableLayout>
<TableRow ><\TableRow>
<RecyclerView> <\RecyclerView>
<\Table Layout>

activity_main:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".MainActivity">

    <TableRow xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:weightSum="1">

        <TextView
            android:layout_column="0"
            android:layout_gravity="center"
            android:layout_weight="0.3"
            android:width="0dp"
            android:gravity="center"
            android:text="ID"
            android:textSize="30sp"
            android:textStyle="bold" />

        <TextView
            android:layout_column="1"
            android:layout_gravity="center"
            android:layout_weight="0.4"
            android:width="0dp"
            android:gravity="center"
            android:text="Name"
            android:textSize="16dp"
            android:textStyle="bold" />


        <TextView
            android:layout_column="2"
            android:layout_gravity="center"
            android:layout_weight="0.3"
            android:width="0dp"
            android:gravity="center"
            android:text="Payment"
            android:textSize="16dp"
            android:textStyle="bold" />

    </TableRow>

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

    </androidx.recyclerview.widget.RecyclerView>


</TableLayout>

Two types of item for Recycler View:

recycler_item_header_row:

<?xml version="1.0" encoding="utf-8"?>


<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:weightSum="1">

<TextView
    android:layout_column="0"
    android:layout_gravity="center"
    android:layout_weight="0.3"
    android:width="0dp"
    android:gravity="center"
    android:text="ID"
    android:textSize="30sp"
    android:textStyle="bold" />

<TextView
    android:layout_column="1"
    android:layout_gravity="center"
    android:layout_weight="0.4"
    android:width="0dp"
    android:gravity="center"
    android:text="Name"
    android:textSize="16dp"
    android:textStyle="bold" />


<TextView
    android:layout_column="2"
    android:layout_gravity="center"
    android:layout_weight="0.3"
    android:width="0dp"
    android:gravity="center"
    android:text="Payment"
    android:textSize="16dp"
    android:textStyle="bold" />

</TableRow>

recycler_item_regular_row:

<?xml version="1.0" encoding="utf-8"?>


<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:weightSum="1"
    >

    <TextView
        android:id="@+id/recycler_item_regular_cell_ID"
        android:layout_column="0"
        android:layout_weight="0.3"
        android:width="0dp"
        android:gravity="center"
        android:text="ID"
        android:textSize="16dp" />

    <TextView
        android:id="@+id/cell_name"
        android:layout_column="1"
        android:layout_weight="0.4"
        android:width="0dp"
        android:gravity="center"
        android:text="Name"
        android:textSize="16dp" />

    <TextView
        android:id="@+id/cell_payment"
        android:layout_column="2"
        android:layout_weight="0.3"
        android:width="0dp"
        android:gravity="center"
        android:text="Payment"
        android:textSize="16dp" />

</TableRow>

MainActivity

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private ArrayList<PaymentModel> paymentData;

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

        paymentData = new ArrayList<>();
        add10TestItems(paymentData);

        recyclerView = findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);

        RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, paymentData);
        recyclerView.setAdapter(adapter);


    }

    private void add10TestItems(ArrayList<PaymentModel> paymentData) {

        for (int i = 0; i < 10; i++) {
            paymentData.add(new PaymentModel("A" + i, "Name",
                    String.valueOf(5 * i)));
        }
        paymentData.add(new PaymentModel("IDDDDDDDDDDDDDDDDDD", "dddddddddddddddd", "dsfdfdf"));
        paymentData.add(new PaymentModel("ID", "Name", "Payment"));
    }
}

Adapter:

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

    public static final int HEADER_ROW_TYPE = 0;
    public static final int REGULAR_ROW_TYPE = 1;

    private Context context;
    private List<PaymentModel> paymentModelList;

    public RecyclerViewAdapter(Context context, List<PaymentModel> paymentModelList) {
        this.context = context;
        this.paymentModelList = paymentModelList;
    }

    @Override
    public int getItemViewType(int position) {
        if (0 == position) {
            return HEADER_ROW_TYPE;
        } else {
            return REGULAR_ROW_TYPE;
        }

    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;
        RecyclerView.ViewHolder viewHolder;
        if (viewType == HEADER_ROW_TYPE) {
            view = LayoutInflater.from(context).inflate(R.layout.recycler_item_header_row,
                    parent, false);
            viewHolder = new ViewHolderHeaderRow(view);
        } else {
            view = LayoutInflater.from(context).inflate(R.layout.recycler_item_regular_row,
                    parent, false);
            viewHolder = new ViewHolderRegularRow(view);
        }

        return viewHolder;
    }

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

        if (position == 0) {
            ViewHolderHeaderRow headerAdapter = (ViewHolderHeaderRow) holder;
        } else {
            ViewHolderRegularRow regularAdapter = (ViewHolderRegularRow) holder;
            regularAdapter.setData(paymentModelList.get(position - 1));
        }


    }

    @Override
    public int getItemCount() {
        return (paymentModelList.size() + 1);
    }

    public class ViewHolderRegularRow extends RecyclerView.ViewHolder {

        private TextView cellID;
        private TextView cellName;
        private TextView cellPayment;

        public ViewHolderRegularRow(@NonNull View itemView) {
            super(itemView);
            cellID = itemView.findViewById(R.id.recycler_item_regular_cell_ID);
            cellName = itemView.findViewById(R.id.cell_name);
            cellPayment = itemView.findViewById(R.id.cell_payment);
        }

        public void setData(PaymentModel paymentModel) {
            cellID.setText(paymentModel.getId());
            cellName.setText(paymentModel.getName());
            cellPayment.setText(paymentModel.getPayment());
        }
    }


    public class ViewHolderHeaderRow extends RecyclerView.ViewHolder {

        public ViewHolderHeaderRow(@NonNull View itemView) {
            super(itemView);
        }

    }


}

Model:

public class PaymentModel {

    private String id;
    private String name;
    private String payment;

    public PaymentModel(String id, String name, String payment) {
        this.id = id;
        this.name = name;
        this.payment = payment;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getPayment() {
        return payment;
    }
}

Solution

  • I would not use TableLayout and TableRow as TableLayout extra logic to resize any child that is a TableRow BUT as you only have one TableRow as a child of the TableLayout that logic is redundant and is less efficient.

    The TableRow's that are inside the RecyclerView are the children of the RecyclerView not children of the TableLayout and as the Docs says

    If a TableRow's parent is not a TableLayout, the TableRow will behave as an horizontal LinearLayout

    And thus won't do any of their normal resizing of the table columns

    So in practice you have actually got a structure like:-

    <TableLayout>
    <TableRow ><\TableRow>
    <\TableLayout>
    <RecyclerView>
    <\LinearLayout>
    <\LinearLayout>
    .....
    <\RecyclerView>
    
    

    So as the layout weights are based on content and how much each is allowed to grow if need to, the long content RecyclerView rows grow the size of the long content cells differently to the items in the unrelated TableLayout Rows which don't need to grow.

    I've designed a similar type layout but used a ConstraintLayout with a layout_constraintWidth_percent value to achieve wider Name column you are trying to achieve with the weights. This works as this is based on the percentage of the fixed width of the parent NOT the variable width of the content and thus the items in the RecyclerView behave the same are the Header line as they both have the same parent size.

    Below are the new layout files need

    activity_main

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.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"
        tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/HeaderID"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="ID"
            android:textSize="30sp"
            android:textStyle="bold"
            app:layout_constraintEnd_toStartOf="@+id/HeaderName"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintWidth_percent="0.3"/>
    
        <TextView
            android:id="@+id/HeaderName"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Name"
            android:textSize="16dp"
            android:textStyle="bold"
            app:layout_constraintStart_toEndOf="@+id/HeaderID"
            app:layout_constraintEnd_toStartOf="@+id/HeaderPayment"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="@+id/HeaderID"
            app:layout_constraintWidth_percent="0.4"/>
    
    
        <TextView
            android:id="@+id/HeaderPayment"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Payment"
            android:textSize="16dp"
            android:textStyle="bold"
            app:layout_constraintStart_toEndOf="@+id/HeaderName"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="@+id/HeaderID"
            app:layout_constraintWidth_percent="0.3"/>
    
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/HeaderID"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    

    recycler_item_header_row

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.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="wrap_content">
    
        <TextView
            android:id="@+id/HeaderRowID"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="ID"
            android:textSize="30sp"
            android:textStyle="bold"
            app:layout_constraintEnd_toStartOf="@+id/HeaderRowName"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintWidth_percent="0.3"/>
    
        <TextView
            android:id="@+id/HeaderRowName"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Name"
            android:textSize="16dp"
            android:textStyle="bold"
            app:layout_constraintStart_toEndOf="@+id/HeaderRowID"
            app:layout_constraintEnd_toStartOf="@+id/HeaderRowPayment"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="@+id/HeaderRowID"
            app:layout_constraintWidth_percent="0.4"/>
    
    
        <TextView
            android:id="@+id/HeaderRowPayment"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Payment"
            android:textSize="16dp"
            android:textStyle="bold"
            app:layout_constraintStart_toEndOf="@+id/HeaderRowName"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="@+id/HeaderRowID"
            app:layout_constraintWidth_percent="0.3"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    

    recycler_item_regular_row

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.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="wrap_content">
    
        <TextView
            android:id="@+id/recycler_item_regular_cell_ID"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textSize="16dp"
            app:layout_constraintEnd_toStartOf="@+id/cell_name"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintWidth_percent="0.3"
            tools:text="ID" />
    
        <TextView
            android:id="@+id/cell_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textSize="16dp"
            app:layout_constraintStart_toEndOf="@+id/recycler_item_regular_cell_ID"
            app:layout_constraintEnd_toStartOf="@+id/cell_payment"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="@+id/recycler_item_regular_cell_ID"
            app:layout_constraintWidth_percent="0.4"
            tools:text="Name" />
    
    
        <TextView
            android:id="@+id/cell_payment"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textSize="16dp"
            app:layout_constraintBottom_toBottomOf="@+id/recycler_item_regular_cell_ID"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/cell_name"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintWidth_percent="0.3"
            tools:text="Payment" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    

    This produces the following result with all the columns lined up.

    Layout Result