Skip to content

Grid Table Row

A grid table is a range of labeled columns and rows used to present numbers, text, or even images. Generally, grid tables are a grid layout of columns and rows. Grid tables are available only on regular screen sizes. On compact screen sizes, the data is displayed as object cells.

Example

A GridTableRow consists of a collection of columns, either image or text. When GridTableRow is used as the table header, only text should used as the columns. Here is an example of GridTableRows on a tablet:

Grid Table Row Example

When table content is scrolled, table header would better stay on the top and preferably raise the elevation:

Grid Table Row Scroll Example

Usage

GridTableRow can be used inside a RecyclerView, ListView or other ViewGroups. The example code below will assume the parent view is RecyclerView. GridTableRow is extended from ConstraintLayout and adds guidelines automatically when you add columns.

Construction

GridTableRow can be created either by the constructor in code or by declaring an GridTableRow element in XML like this:

<com.sap.cloud.mobile.fiori.object.GridTableRow
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="@dimen/grid_table_row_height_header"
    android:layout_width="match_parent"
    android:background="@color/sap_ui_list_header_background"
    app:layout_optimizationLevel="standard"
    app:textLines="1"
    app:isHeader="true">
</com.sap.cloud.mobile.fiori.object.GridTableRow>

Then the XML can be inflated in a RecyclerView.Adapter.

public ViewHolder onCreateViewHolder(
        ViewGroup parent, int viewType) {
    Context context = parent.getContext();
    LayoutInflater inflater = LayoutInflater.from(context);
    if (viewType == HEADER_TYPE){
        GridTableRow view = (GridTableRow) inflater.inflate(R.layout.grid_table_header, parent,
                false);
        return new GridTableRecyclerAdapter.HeaderViewHolder(view);
    }else{
        GridTableRow view = (GridTableRow) inflater.inflate(mObjectCellSpec.getLayoutId(), parent,
                false);
        return new GridTableRecyclerAdapter.RowViewHolder(view);
    }
}

The above XML declaration creates an GridTableRow with the following attributes:

  • app:textLines="1" 1 line of text is allowed, because this example is creating a table header. If it's a data row, at most 2 lines of text is allowed.
  • app:layout_optimizationLevel="standard" Standard ContraintLayout attribute, please check Android documentation for details.
  • app:isHeader="true" This is a table header. Font for header will be applied.
  • android:background="@color/sap_ui_list_header_background" Specify a background color so that it's opaque and it'll hide the content that scrolls underneath it.

Fields

Columns

Columns can be added either in XML or code.

<?xml version="1.0" encoding="utf-8"?>
<com.sap.cloud.mobile.fiori.object.GridTableRow
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="@dimen/grid_table_row_height_2lines"
    android:layout_width="match_parent"
    app:layout_optimizationLevel="standard"
    app:textLines="2">
    <ImageView
        android:id="@id/row_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_column="0"
        app:layout_columnWidth="88"
        android:contentDescription="@string/avatar"/>
    <TextView
        android:id="@id/row_headline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/sap_ui_base_text"
        app:layout_column="1"
        app:layout_columnWidth=".20"/>
    <TextView
        android:id="@id/row_sub_headline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/sap_ui_base_text"
        app:layout_column="2"
        app:layout_columnWidth="150"/>
    <TextView
        android:id="@id/row_description"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/sap_ui_base_text"
        app:layout_column="3"
        app:layout_columnWidth="-1"/>
    <TextView
        android:id="@id/row_price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/sap_ui_base_text"
        android:gravity="end"
        app:layout_column="4"
        app:layout_columnWidth="150"/>
    <ImageView
        android:id="@id/row_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_column="5"
        app:layout_columnWidth="68"
        app:layout_constraintVertical_bias="0"
        android:contentDescription="@string/warning"/>
</com.sap.cloud.mobile.fiori.object.GridTableRow>

For each child view, column index and column width can be specified.

  • app:layout_column="0" This is the Nth column.
  • app:layout_columnWidth="88" The column width is 88 dp. Since the recommended image size is 40 dp, and left/right padding for each column is 24 dp, the total width becomes 88 dp.
  • app:layout_columnWidth="68" This is for a small icon of 20 dp. 20 + 24 + 24 = 68.
  • app:layout_columnWidth=".20" This column will be 20% of the whole width.
  • app:layout_columnWidth="-1" This column will use all the remaining width. There can only be one such auto-size column.
  • android:gravity="end" This column should be end aligned. This should be used for numbers, dates, and currencies, etc.
  • app:layout_constraintVertical_bias="0" This column should be top aligned. This attribute comes from the parent class of GridTableRow: ConstraintLayout, whose powerful layout mechanism can be leveraged.
public ViewHolder onCreateViewHolder(
        ViewGroup parent, int viewType) {
    Context context = parent.getContext();
    LayoutInflater inflater = LayoutInflater.from(context);
    //both header and content are using GridTableRow
    GridTableRow view = (GridTableRow) inflater.inflate(mObjectCellSpec.getLayoutId(), parent,
            false);
    //image(""), headline, sub headline, description, price, status icon("")
    view.setColumnWidths(88, 0.2F, 150, -1, 150, 68);
    if (viewType == HEADER_TYPE){
        view.setId(R.id.grid_header_row);
        view.setHeader(true);
        view.setBackgroundColor(sHeaderBackgroundColor);
        view.setLines(1);
        TextView cImage = new TextView(context);
        cImage.setId(R.id.row_image);
        cImage.setText("");//no space to show header
        TextView cHeadline = new TextView(context);
        cHeadline.setId(R.id.row_headline);
        cHeadline.setText(R.string.grid_table_column_headline);
        cHeadline.setTextColor(sHeaderColor);
        TextView cSubHeadline = new TextView(context);
        cSubHeadline.setId(R.id.row_sub_headline);
        cSubHeadline.setText(R.string.grid_table_column_sub_headline);
        cSubHeadline.setTextColor(sHeaderColor);
        TextView cDescription = new TextView(context);
        cDescription.setId(R.id.row_description);
        cDescription.setText(R.string.grid_table_column_description);
        cDescription.setTextColor(sHeaderColor);
        TextView cPrice = new TextView(context);
        cPrice.setId(R.id.row_price);
        cPrice.setText(R.string.grid_table_column_price);
        cPrice.setGravity(Gravity.END);
        cPrice.setTextColor(sHeaderColor);
        TextView cStatus = new TextView(context);
        cStatus.setId(R.id.row_status);
        cStatus.setText("");
        view.setColumns(cImage, cHeadline, cSubHeadline, cDescription, cPrice, cStatus);
        return new HeaderViewHolder(view);
    }else{
        view.setLines(2);
        ImageView cImage = new ImageView(context);
        cImage.setId(R.id.row_image);
        TextView cHeadline = new TextView(context);
        cHeadline.setId(R.id.row_headline);
        cHeadline.setTextColor(sDataColumnColor);
        TextView cSubHeadline = new TextView(context);
        cSubHeadline.setId(R.id.row_sub_headline);
        cSubHeadline.setTextColor(sDataColumnColor);
        TextView cDescription = new TextView(context);
        cDescription.setId(R.id.row_description);
        cDescription.setTextColor(sDataColumnColor);
        TextView cPrice = new TextView(context);
        cPrice.setId(R.id.row_price);
        cPrice.setGravity(Gravity.END);
        cPrice.setTextColor(sDataColumnColor);
        ImageView cStatus = new ImageView(context);
        cStatus.setId(R.id.row_status);
        view.setColumns(cImage, cHeadline, cSubHeadline, cDescription, cPrice, cStatus);
        //layout params will be created after setColumns
        GridTableRow.LayoutParams layoutParams = (GridTableRow.LayoutParams)cStatus.getLayoutParams();
        //top align
        layoutParams.verticalBias = 0.0F;
        return new RowViewHolder(view);
    }
}

In java code, column width can be specified as:

view.setColumnWidths(88, 0.2F, 150, -1, 150, 68);

Which sets 6 columns with these widths respectively: 88 dp, 20%, 150 dp, auto-size, 150 dp, and 68 dp.

If you have some columns with fixed width, and you want to use percentage to arrange the rest of the columns, setPercentageOutOfRemainingWidth(true) can be used. E.g.:

view.setColumnWidths(0.5F, 0.5F, 68);

In this case, the last column uses 68 dp, while the first and second columns use half of the remaining space respectively.

After all the columns are created, set them to GridTableRow with setColumns.

Style

GridTableRow does not have predefined style for itself, but by default it does use specific font sizes for it's text columns depending on whether its a header or content. To use your own text appearance, call setUseDefaultTextSize(false) and set text appearance in TextView.

Tip

Although GridTableRow does not enforce the ImageView position, it's a good practice to put the primary image on the first column with 40 dp size.

GridTableRow can be used either as a header or data row. To make the header stay on the top during scroll, third party libraries can be used. One of the library is Sticky Header. In essence, there are only 2 things to be done in the app:

  • Let the RecyclerView.Adapter implement StickyHeaders and StickyHeaders.ViewSetup, and determines which position is header and configure the header view when necessary. It could be as simple as:
    @Override
    public boolean isStickyHeader(int position) {
        return position == 0;//use view holder type if there are multiple headers
    }

    @Override
    public void setupStickyHeaderView(View stickyHeader) {
        stickyHeader.setTranslationZ(stuckHeaderElevation);
    }
  • Use StickyHeadersLinearLayoutManager with your RecyclerView.
mRecyclerView.setLayoutManager(new StickyHeadersLinearLayoutManager<GridTableRecyclerAdapter>(this));

API Reference

GridTableRow


Last update: June 23, 2023