Skip to content

Object Cell

Object Cell is a list item view that can represent a business object.

Anatomy

An Object Cell consists of an unread object indicator, avatar,icon stack, labels (headline, subheadline,footnote), tags, description, statuses, attribute icons, avatar stack and action icon.

Object Cell Anatomy
Object Cell Anatomy

Example

Object Cell Example
Object Cell on a Tablet

Development

Object Cell are implemented entirely using Jetpack Compose, including a Composable FioriHorizonTheme. To understand the fundamentals of Jetpack Compose, refer to the Android Developer's documentation.

The Object Cell provides two UI components for application developers to use

  • FioriObjectCell – a UI component to display a single Fiori Object Cell.
  • FioriObjectCellList – a UI component to display a series of Object Cell in a horizontally scrollable list.

Usage in an Application

This section describes how to use the Object Cell components in an application.

Sample Application on a Phone in Light Mode
Object Cell on a Phone

How to Create an Object Cell

FioriObjectCell is a Composable function that takes below parameters:

  • modifier – a Modifier that is used to define the height and width of the cell.
  • cellData – a FioriObjectCellData that specifies the data to be displayed in the cell.
  • cellType – a CellType that specifies the cell type. Users can choose OBJECT or CONTACT.
  • onClick – a lambda that specifies the lamda function that can be called when click on cell
  • onLongPress – a lambda that specifies the lamda function that can be called when long press on cell
  • onSwipeToStart – a lambda that specifies the lamda function that can be called when swipe to Start, this function will not work when cellSwipeable in FioriObjectCellData not set to true
  • onSwipeToStartIcon – a Painter icon define for the icon displayed when swipe to Start
  • onSwipeToEnd – a lambda that specifies the lamda function that can be called when swipe to End, this function will not work when cellSwipeable in FioriObjectCellData not set true
  • onSwipeToEndIcon – a Painter icon define for the icon displayed when swipe to End
  • progress – a lambda for the progress bar. This will displayed when displayProgress set to true in FioriObjectCellUiState
  • colors – a FioriObjectCellColors define the color use in cell
  • textStyles – a FioriObjectCellTextStyles define the textStyles in cell
  • styles – a FioriObjectCellStyles define the styles such like padding in cell
@Composable
fun FioriObjectCell(
    modifier: Modifier = Modifier,
    cellData: FioriObjectCellData,
    onClick: (() -> Unit)? = null,
    onLongPress: (() -> Unit)? = null,
    onSwipeToStart: (() -> Unit)? = null,
    onSwipeToStartIcon: Painter = PainterBuilder.build(
        icon = FioriIcon(resId = R.drawable.ic_sap_icon_delete_filled)
    ),
    onSwipeToEnd: (() -> Unit)? = null,
    onSwipeToEndIcon: Painter = PainterBuilder.build(
        icon = FioriIcon(resId = R.drawable.ic_sap_icon_add)
    ),
    progress: (@Composable () -> Unit)? = {
        CircularProgressIndicator(
            modifier = Modifier.size(FioriObjectCellDefaults.styles().actionButtonSize()),
            color = FioriObjectCellDefaults.colors().progressIndicatorColor()
        )
    },
    colors: FioriObjectCellColors = FioriObjectCellDefaults.colors(),
    textStyles: FioriObjectCellTextStyles = FioriObjectCellDefaults.textStyles(),
    styles: FioriObjectCellStyles = FioriObjectCellDefaults.styles(),
)

FioriObjectCellData is like below

data class FioriObjectCellData(
    override val headline: String = "",
    val annotatedHeadline: @RawValue MutableState<AnnotatedString>? = null,
    override val avatar: FioriAvatarConstruct? = null,
    var iconStack: List<IconStackElement>? = null,
    override val subheadline: String? = null,
    val annotatedSubheadline: @RawValue MutableState<AnnotatedString>? = null,
    override val footnote: String? = null,
    val annotatedFootnote: @RawValue MutableState<AnnotatedString>? = null,
    val description: String? = null,
    val tags: List<FioriTagData> = listOf(),
    val avatarStack: FioriAvatarConstruct? = null,
    var status: FioriObjectCellStatusData? = null,
    var subStatus: FioriObjectCellStatusData? = null,
    val attributeIcons: AttributeIcons? = null,
    val action: Action? = null,
    override val cellClickable: Boolean = true,
    override val cellSwipeable: Boolean = false,
    var displayReadIndicator: MutableState<Boolean> = mutableStateOf(true),
    var displayProcess: MutableState<Boolean> = mutableStateOf(false),
    override var displaySelectBox: MutableState<Boolean> = mutableStateOf(false),
    override var contentDescription: String = "",
    override val enabled: Boolean = true,
    override var isSelected: MutableState<Boolean> = mutableStateOf(false),
)

The FioriObjectCellData object is now created using a Builder class which provides a fluent interface for object construction. This Builder class encapsulates the construction logic of FioriObjectCellData and allows for cleaner and more readable code.

A typical usage of the builder would look like this:

val fioriObjectCellData = FioriObjectCellData.Builder()
    .setHeadline("Headline")
    .setAvatar(someAvatarConstruct)
    .setIconStack(someIconStackList)
    .setCellClickable(true)
    .build()

Each setter method in the Builder class corresponds to a property of FioriObjectCellData.

The build() method is called at the end to finalize the creation of FioriObjectCellData. The build() method ensures all required properties are set and then returns an instance of FioriObjectCellData.

Note: Some properties, such as headline and annotatedHeadline, cannot be set simultaneously. An IllegalArgumentException will be thrown if an attempt is made to do so.

Fields

Headline

The headline is the main area for text content. The headline is the only mandatory content for Object Cell. The title can be specified by:

headline = ""

in FioriObjectCellData

Avatar

The avatar provides a visual representation of the object , can be specified by :

avatar = FioriAvatarConstruct(
    hasBadge = hasBadge,
    hasBorder = hasBorder,
    avatarList = listOf(
        FioriAvatarData(
            image = FioriImage(url = ""),
        ), FioriAvatarData(
            image = FioriImage(url = ""),
        )
    ),
    size = 18.dp,
    avatarBadge = FioriIcon(
        resId = R.drawable.ic_circle_unread
    ),
    type = FioriAvatarType.DOUBLE
)

FioriAvatarConstruct is used to construct the Avatar area.

@Parcelize
data class FioriAvatarConstruct(
    val type: FioriAvatarType = FioriAvatarType.SINGLE,
    var avatarList: List<FioriAvatarData>,
    val hasBadge: Boolean = false,
    val avatarBadge: FioriIcon? = null,
    val groupDisplayNumber: Int = 0,
    val size: Dp = 40.dp,
    val hasBorder: Boolean = false,
    val borderColor: Color? = null,
    val backgroundColor: Color? = null,
    val shape: FioriAvatarShape = FioriAvatarShape.ROUNDEDCORNER
) : Parcelable
  • type – a FioriAvatar have three type. SINGLE,DOUBLE,GROUP. SINGLE and DOUBLE is used to display in the Avatar area. Group is used to display in Avatar stack area.
  • avatarList – a List contains FioriAvatarData objects
  • hasBadge – a Boolean is used to display the badge or not
  • avatarBadge – a FioriIcon is used to define the badge
  • groupDisplayNumber – a Int is used to display the number when use the group type.
  • size – a Dp is used to define the Avatar area size when AvatarType is Single and Double.
  • hasBorder – a Boolean is used to display the border of Avatar.
  • borderColor – a Color is used to define the color of border.
  • backgroundColor – a Color is used to display the background of Avatar.
  • Shape – a FioriAvatarShape is used to support two type.ROUNDEDCORNER and CIRCLE.

Sample Avatar

Icon Stack

The iconStack is the area for icon and text display. The text will always displayed at the top of iconStack. Two iconStack elements can be displayed in the stack. The iconStack can be specified as follows:

iconStack = listOf(
    IconStackElement(text = "Display"),
    IconStackElement(
        icon = FioriIcon(
            resId = R.drawable.ic_directions_24px,
            contentDescription = "checked"
        )
      )
    ),

Subheadline

The subheadline is under the headline and provides additional information.The subheadline can be specified by:

subheadline = ""

Footnote

The footnote is under the subheadline and provides further information.The footnote can be specified by:

footnote = ""

Description

The description is under the headline, subheadline, footnote, tags, and status, substatus, attribute icons, and provides further information. The description can be specified as follows:

description = ""

Tags

Tags may be used to indicate categories, types, or statuses. Tags are displayed next to the footnote if space is available. The tags can be specified by :

tags= listOf(
    FioriTagData(text = "gray"),
    FioriTagData(text = "blue"),
    FioriTagData(text = "red"),
)

Avatar Stack

The avatar stack is a FioriAvatarConstruct that displays a row of FioriAvatar at the bottom of the cell. FioriAvatarType should set to the Group. The Avatar stack can be specified by :

avatarStack = FioriAvatarConstruct(
    hasBadge = false,
    type = FioriAvatarType.GROUP,
    avatarList = listOf(avatar2, avatar3, avatar4, avatar5),
    size = 16.dp,
    groupDisplayNumber = 4,
    hasBorder = false
)

Status

Up to two statuses(status and substatus) can be displayed, stacked vertically, to show attributes of the object. Status could be customized as either text or an image.

statusData = FioriObjectCellStatusData(
    label = "Success",
    icon = FioriIcon(
        resId = R.drawable.ic_baseline_tag_faces_24,
        contentDescription = "Positive status icon",
    ),
    type = FioriObjectCellStatusType.Positive,
    isIconAtStart = true
)

Attribute Icons

A set of icons can be displayed horizontally under the statuses. These icons can represent both persistent and variable information about the object. It is recommended to have at most four attribute icons.

attribute = AttributeIcons(
    iconsList = listOf(
        FioriIcon(
            resId = R.drawable.ic_chip_checked,
            contentDescription = "checked"
        ),
        FioriIcon(
            resId = R.drawable.ic_chip_checked,
            contentDescription = "checked"
        )
    )

Action

Action can be displayed on the right of cell. It is suggest to only have one action in object cell.

iconAction =
    Action(
        icon = FioriIcon(
            resId = R.drawable.ic_email_black_24dp,
            contentDescription = "email",
        )
    )

Progress

User can define the progress by using lambda. Below is the default Progress bar. Progress display or not decide by displayProcess in FioriObjectCellUiState

{
    CircularProgressIndicator(
        modifier = Modifier.size(FioriObjectCellDefaults.styles().actionButtonSize()),
        color = FioriObjectCellDefaults.colors().progressIndicatorColor(),
    )
},

User Action

Simple Tap/Click to Enable/Disable Read Mode

Upon clicking or tapping an Object Cell, the entire cell will display a ripple effect and then the details about the clicked Object Cell will open up. Returning to the list of Object Cells, the clicked Object Cell will be marked as Read, i.e.the unread object indicator will disappear. User can customize the onClick function. Below is the default implementation.

Long Press to Enable/Disable Selection Mode

An Object Cell can enter and exit Selection mode using displaySelectBox in FioriObjectCellData. The Object Cell gains a checkbox in Selection mode. Users can also customize the onLongPress. The following sample illustrates how to enable Selection mode in FioriObjectCellList

var enableSelectBox by remember { mutableStateOf(false) }
FioriObjectCellList(
        cellList = cellList,
        onClick = { p1, _ ->
            cellList[p1].displayReadIndicator.value = false
        },
        onLongPress = { _, _ ->
            enableSelectBox = !enableSelectBox
            cellViewModel.updateObjectCellDisplaySelectBoxInList(enableSelectBox)
        },
        cellType = CellType.OBJECT,
    )

Swipe Delete

Enabling Actions on Swipe

cellSwipeable can set to true to enable the swipe action of Object Cell. onSwipeToStart,onSwipeToStartIcon,onSwipeToEnd,onSwipeToEndIcon are used to define the swipe lambda and swipe icon. App developers can define their own actions in response to the swipe gesture in two directions. In the demo app provided by Fiori UI, a delete action is defined for swiping from right to left, and a mock add action is defined for swiping from left to right. Swipe can defined either in FioriObjectCell or FioriObjectCellList. Below is the example in FioriObjectCellList. Swipe can have two behavior when Release the swipe. Release.EXTEND and Release.Reset . These two behavior can defined in styles.

FioriObjectCellList(
    objectCellList = objectList,
    enableSelectBox = enableSelectBox,
    onClick = {
        if (it.isRead == false) {
            it.isRead = true
        }
        currentNews.newsData = it.stateData
        navController.navigate(Screen.ObjectCellDetail.route)
    },
    onSwipeToEnd = { p1, p2 ->
        p2.isRead = !p2.isRead
    },
    onSwipeToStart = { p1, p2 ->
        objectList.remove(p2)
        Toast.makeText(
            context,
            " The index ${p1!!} item is deleted from list ",
            Toast.LENGTH_SHORT
        )
            .show()
    },
    styles = FioriObjectCellDefaults.styles(
        headlineDisplayLineMaxNumber = headlineNumber,
        subheadlineDisplayLineMaxNumber = subheadlineNumber,
        footnoteDisplayLineMaxNumber = footnoteNumber,
        swipeToEnd = SwipeRelease.RESET,
        swipeToStart = SwipeRelease.EXTEND
    ),
)

Swipe Delete

Swipe Add

Contact Cell

Object Cells can defined as a special type of Contact Cell. Contact Cells can have multiple actions. Users can define the Contact Cell using FioriContactCell or FioriContactCellList:

 FioriContactCell(
        cellData = FioriContactCellData(
            headline = "Headline",
            subheadline = "Developer",
            footnote = "Earth",
            avatar = FioriAvatarConstruct(
                avatarList = listOf(
                    FioriAvatarData(
                        text = "A",
                        shape = FioriAvatarShape.ROUNDEDCORNER
                    )
                ),
            ),
            contactActions = listOf(
                Action(
                    icon = FioriIcon(
                        resId = R.drawable.ic_sap_icon_email,
                        contentDescription = "email",
                    )
                ),
                Action(
                    icon = FioriIcon(
                        resId = R.drawable.ic_sap_icon_call,
                        contentDescription = "email",
                    ),
                )
            ),
        ),
        styles = FioriObjectCellDefaults.styles(
            divider = true,
        ),
        cellType = CellType.CONTACT
        )

Contact Cell

Contact Cell can have avatar,headline,subheadline and footnote in the left. Contact Cell only can have contact actions in the right accessories.

Fiori Object Cell List

FioriObjectCellList allows you to display the Object Cell in a list. It accepts a list of FioriObjectCellData as a parameter.

@Composable
fun FioriObjectCellList(
    modifier: Modifier = Modifier,
    cellList: List<FioriObjectCellData>,
    loadMore: (() -> Unit)? = null,
    onSwipeToStart: ((Int, FioriObjectCellData) -> Unit)? = null,
    onSwipeToStartIcon: Painter = PainterBuilder.build(
        icon = FioriIcon(resId = R.drawable.ic_sap_icon_delete_filled)
    ),
    onSwipeToEnd: ((Int, FioriObjectCellData) -> Unit)? = null,
    onSwipeToEndIcon: Painter = PainterBuilder.build(
        icon = FioriIcon(resId = R.drawable.ic_sap_icon_add)
    ),
    onClick: ((Int, FioriObjectCellData) -> Unit)? = { _, cellCommonData ->
        cellCommonData.setDisplayReadIndicator(false)
    },
    onLongPress: ((Int, FioriObjectCellData) -> Unit)? = null,
    progress: (@Composable () -> Unit)? = {
        CircularProgressIndicator(
            modifier = Modifier.size(FioriObjectCellDefaults.styles().actionButtonSize()),
            color = FioriObjectCellDefaults.colors().progressIndicatorColor()
        )
    },
    colors: FioriObjectCellColors = FioriObjectCellDefaults.colors(),
    textStyles: FioriObjectCellTextStyles = FioriObjectCellDefaults.textStyles(),
    styles: FioriObjectCellStyles = FioriObjectCellDefaults.styles(),
)

Load More (Lazy Loading)

The loadMore function is used to implement lazy loading in the FioriObjectCellList. This function is invoked when the user has scrolled to a certain point near the end of the list, defined by the startToLoad style. The loadMore function should then load more items and add them to the cellList.

Here's an example of how you might use the loadMore function:

val currentPage = 0
val pageSize = 10
fun loadMoreItems() {
    viewModelScope.launch(Dispatchers.IO) {
        val newItems = retriveData(currentPage * pageSize, pageSize)
        _objectCellStateList.value =
            ((_objectCellStateList.value!! + newItems) as MutableList<FioriObjectCellData>)
        currentPage++
    }
}

Styles

The style of the component can be customized. The recommended approach is to override FioriObjectCellDefaults.styles and only customize the attributes you desire. This means that other attributes can be retained and all the components in the application will have a consistent look and feel. For example:

FioriObjectCell(
    cellData = myCellData,
    modifier: Modifier = Modifier,
    progress = { MyProgressButton() },
    styles = FioriObjectCellDefaults.styles(avatarSize = 40.dp)
)

Last update: April 5, 2024