{"id":29627817,"url":"https://github.com/touchlane/gridpad-android","last_synced_at":"2025-07-21T08:06:02.716Z","repository":{"id":64371345,"uuid":"567230233","full_name":"touchlane/gridpad-android","owner":"touchlane","description":"GridPad is a Jetpack Compose library that allows you to place UI elements in a predefined grid, manage spans in two dimensions, have flexible controls to manage row and column sizes.","archived":false,"fork":false,"pushed_at":"2023-12-12T17:51:21.000Z","size":348,"stargazers_count":52,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2023-12-12T19:12:40.629Z","etag":null,"topics":["android","android-library","android-ui","dsl","grid","gridpad","kotlin"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/touchlane.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-11-17T10:56:33.000Z","updated_at":"2023-11-29T06:02:41.000Z","dependencies_parsed_at":"2023-12-12T19:03:49.561Z","dependency_job_id":null,"html_url":"https://github.com/touchlane/gridpad-android","commit_stats":null,"previous_names":[],"tags_count":8,"template":null,"template_full_name":null,"purl":"pkg:github/touchlane/gridpad-android","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchlane%2Fgridpad-android","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchlane%2Fgridpad-android/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchlane%2Fgridpad-android/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchlane%2Fgridpad-android/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/touchlane","download_url":"https://codeload.github.com/touchlane/gridpad-android/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchlane%2Fgridpad-android/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266263057,"owners_count":23901356,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-library","android-ui","dsl","grid","gridpad","kotlin"],"created_at":"2025-07-21T08:06:02.175Z","updated_at":"2025-07-21T08:06:02.707Z","avatar_url":"https://github.com/touchlane.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GridPad Jetpack Compose layout\n\n![github_title](https://user-images.githubusercontent.com/2251498/207064470-d719699d-319a-4267-9636-c4f058d5a7aa.png)\n\n**GridPad** is a Jetpack Compose library that allows you to place UI elements in a predefined grid, \nmanage spans in two dimensions, have flexible controls to manage row and column sizes.\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.touchlane/gridpad?label=MavenCentral\u0026logo=apache-maven)](https://search.maven.org/artifact/com.touchlane/gridpad)\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![API](https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=21)\n[![Build Status](https://github.com/touchlane/gridpad-android/workflows/Test/badge.svg)](https://github.com/touchlane/gridpad-android/actions)\n\n# Usage\n\nGridPad is inspired by LazyRow/LazyColumn and LazyVerticalGrid/LazyHorizontalGrid APIs, which makes\nits use intuitive.\n\nKey features and limitations:\n\n* Follows [slot API](https://developer.android.com/jetpack/compose/layouts/basics#slot-based-layouts)\nconcept.\n* Not lazy. All content will be measured and placed instantly.\n* GridPad must have limited bounds.\n* It's possible to specify the exact place on the grid for each element.\n* A cell can contain more than one item. The draw order will be the same as the place order.\n* Each item in a cell can have different spans.\n* Each item can have horizontal and vertical spans simultaneously.\n* Each row and column can have a specific size: fixed or weight-based.\n\n![grid_examples](https://user-images.githubusercontent.com/2251498/204779586-c1ed214a-e30c-42a9-915e-37dda13d15ec.png)\n\n## Install\n\nAdd the dependency below to your **module's** `build.gradle` file:\n\n```groovy\ndependencies {\n    implementation \"com.touchlane:gridpad:1.1.0\"\n}\n```\n\n## Define the grid\n\nSpecifying the exact grid size is required, but specifying each row and column size is optional. The\nfollowing code initializes a 3x4 grid with rows and columns weights equal to 1:\n\n```kotlin\nGridPad(cells = GridPadCells(rowCount = 3, columnCount = 4)) {\n    // content        \n}\n```\n\n![simgle_define_grid_dark](https://user-images.githubusercontent.com/2251498/204765082-51283636-c85e-46a0-beb1-1b966193a215.png)\n\nBy default rows and columns have **weight** size equal to 1, but it's possible specify different\nsize to specific row or column.\nThe library support 2 types of sizes:\n\n* **GridPadCellSize.Fixed** - fixed size in Dp, not change when the bounds of GridPad change.\n* **GridPadCellSize.Weight** - a relative, depends on other weights, remaining space after placing\n  fixed sizes, and the GridPad bounds.\n\nTo define a specific size for a row or column you need to use `GridPadCells.Builder` API:\n\n```kotlin\nGridPad(\n    cells = GridPadCells.Builder(rowCount = 3, columnCount = 4)\n        .rowSize(index = 0, size = GridPadCellSize.Weight(2f))\n        .columnSize(index = 3, size = GridPadCellSize.Fixed(90.dp))\n        .build()\n) {\n    // content\n}\n```\n\n![custom_define_grid_dark](https://user-images.githubusercontent.com/2251498/204765232-8f0a7f53-536c-4f39-82f9-5b867d352ec0.png)\n\nThe algorithm for allocating available space between cells:\n\n1. All fixed (**GridPadCellSize.Fixed**) values are subtracted from the available space.\n2. The remaining space is allocated between the remaining cells according to their weight value.\n\n## Place the items\n\nA cell content should be wrapped in a `item` element:\n\n```kotlin\nGridPad(\n    cells = GridPadCells(rowCount = 3, columnCount = 4)\n) {\n    item {\n        // cell content\n    }\n}\n```\n\nItems in a GridPad can be placed **explicitly** and **implicitly**. In the example above items are\nplaced implicitly. Implicit placing placed the item **next to the last placed item** (including span\nsize). Placement direction and first placement position depend\non [`placementPolicy`](#placement-policy). By default, the first placing will be at position \\[0;0] \nwith a horizontal direction from start to end.\n\n```kotlin\nGridPad(\n    cells = GridPadCells(rowCount = 4, columnCount = 3)\n) {\n    item {\n        // 1-st item, row = 0, column = 0\n    }\n    item {\n        // 2-nd item, row = 0, column = 1\n    }\n    item {\n        // 3-rd item, row = 0, column = 2\n    }\n    item {\n        // 4-th item, row = 1, column = 0\n    }\n}\n```\n\n\u003e :warning: When the placement reaches the last cell, the following items will be ignored.\n\u003e Placing items outside the grid is not allowed.\n\nTo place an item explicitly needs to specify both properties `row` and `column` in the item.\nWhen defines `row` and `column` property it's also possible to place all items in a different order\nwithout regard to the actual location.\n\n```kotlin\nGridPad(\n    cells = GridPadCells(rowCount = 3, columnCount = 4)\n) {\n    item(row = 1, column = 2) {\n        // cell content\n    }\n    item(row = 0, column = 1) {\n        // cell content\n    }\n}\n```\n\n![place_items_specific_dark](https://user-images.githubusercontent.com/2251498/204765324-211e2044-593b-41aa-893e-ad62565c9ded.png)\n\n\u003e :warning: A cell can contain more than one item. The draw order will be the same as the place\n\u003e order. GridPad does not limit the item's size when the child has an explicit size. That means that\n\u003e the item can go outside the cell bounds.\n\n## Placement policy\n\nTo define the direction of placement items in an implicit method used the `placementPolicy` \nproperty.\n\n```kotlin\nGridPad(\n    cells = GridPadCells(rowCount = 3, columnCount = 4),\n    placementPolicy = GridPadPlacementPolicy(\n        mainAxis = GridPadPlacementPolicy.MainAxis.HORIZONTAL,\n        horizontalDirection = GridPadPlacementPolicy.HorizontalDirection.START_END,\n        verticalDirection = GridPadPlacementPolicy.VerticalDirection.TOP_BOTTOM\n    )\n) {\n    // content\n}\n```\n\nThe `GridPadPlacementPolicy` class has three properties that allow controlling different aspects \nof placement items.\n\n* `mainAxis` sets the axis along which the item will be placed. When the axis is filled to the end,\n  the next item will be placed on the next axis. If `mainAxis` is `HORIZONTAL` then items will be\n  placed sequentially one by one by horizontal line. If `mainAxis` is `VERTICAL` then items will be\n  placed sequentially one by one by vertical line.\n* `horizontalDirection` sets the direction of placement horizontally. When `mainAxis` is\n  `HORIZONTAL` this property describes the direction of placement of the next item. When `mainAxis`\n  is `VERTICAL` this property describes the direction of moving to the next axis. The `START_END`\n  means that the direction of placement items or moving main axis will begin from the start layout\n  direction and move to the end layout direction (depending on LTR or RTL). The `END_START` means\n  the same but in the opposite order.\n* `verticalDirection` sets the direction of placement vertically. When `mainAxis` is\n  `VERTICAL` this property describes the direction of placement of the next item. When `mainAxis`\n  is `HORIZONTAL` this property describes the direction of moving to the next axis. The `TOP_BOTTOM`\n  means that the direction of placement items or moving main axis will begin from the top and\n  move to the bottom. The `BOTTOM_TOP` means the same but in the opposite order.\n\n![placement_policy](https://user-images.githubusercontent.com/2251498/209689448-3a89070b-7640-4a2d-8654-9873f960e747.png)\n\n## Spans\n\nBy default, each item has a span of 1x1. To change it, specify one or both of the `rowSpan`\nand `columnSpan` properties of the item.\n\n```kotlin\nGridPad(\n    cells = GridPadCells(rowCount = 3, columnCount = 4)\n) {\n    item(rowSpan = 3, columnSpan = 2) {\n        // row = 0, column = 0, rowSpan = 3, columnSpan = 2\n    }\n    item(rowSpan = 2, columnSpan = 2) {\n        // row = 0, column = 2, rowSpan = 2, columnSpan = 1\n    }\n}\n```\n\n![spanned_dark](https://user-images.githubusercontent.com/2251498/204765367-86177508-5551-4076-b6a1-b48b8f183f9d.png)\n\nWhen an item has a span that goes outside the grid, the item is skipped and doesn't draw at all.\nYou can handle skipping cases by [diagnostic logger](#diagnostic).\n\n```kotlin\nGridPad(\n    cells = GridPadCells(rowCount = 3, columnCount = 4)\n) {\n    item(row = 1, column = 3, rowSpan = 1, columnSpan = 3) {\n        // will be skipped in a drawing process because the item is placed in the column range [3;5] \n        // but the maximum allowable is 3\n    }\n}\n```\n\n\u003e :warning: When you have a complex structure it's highly recommended to use an **explicit** method\n\u003e of placing all items to avoid unpredictable behavior and mistakes during the placement of the\n\u003e items.\n\n\n## Anchor\n\nWhen `rowSpan` or `columnSpan` is more than 1 then the content is placed relative to the implicit \nparameter - **anchor**. The anchor is the point in the corner from which the span expands. \nThe value depends on `horizontalDirection` and `verticalDirection` values in the `placementPolicy` \nproperty.\n\n![anchor](https://user-images.githubusercontent.com/2251498/209725897-ab8d840d-0744-4ff6-89a7-f786e1522203.png)\n\n## Layout Direction\n\nThe library handles the parent's layout direction value. That means that placement in **RTL**\ndirection with `horizontalDirection = START_END` will have the same behavior as **LTR** direction\nwith `horizontalDirection = END_START`.\n\n## Diagnostic\n\nThe library doesn't throw any exceptions when an item is tried to place outside of the defined grid. \nInstead, the library just sends a signal through the special class `GridPadDiagnosticLogger`, \nskipping this item and moving to the next one. This silent behavior might be not suitable during \nthe development process, so there is a way to have more control - define a custom listener. \nAs a dev solution, you can just redirect the message to the console log or throw an exception to \nfix it immediately.\n\n```kotlin\nGridPadDiagnosticLogger.skippingItemListener = { message -\u003e \n    Log.w(\"GridPad\", message)\n}\n```\n\n# Performance\n\nGridPad respect Jetpack Compose recommendations and avoid not necessary recompositions. You can\nalways check restartability and skippability by running the following command in the sample project:\n\n```shell\n./gradlew assembleRelease -P gridpad.enableComposeCompilerReports=true\n```\n\nResults will be placed under the directory: `build/compose_metrics`\n\n```text\nrestartable skippable scheme(\"[androidx.compose.ui.UiComposable]\") fun GridPad(\n  stable cells: GridPadCells\n  stable modifier: Modifier? = @static Companion\n  stable content: @[ExtensionFunctionType] Function1\u003cGridPadScope, Unit\u003e\n)\n```\n\n# Enjoy using this library?\n\nJoin [:dizzy:Stargazers](https://github.com/touchlane/gridpad-android/stargazers) to support future development.\n\n# License\n\n```\nMIT License\n\nCopyright (c) 2022 Touchlane LLC tech@touchlane.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftouchlane%2Fgridpad-android","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftouchlane%2Fgridpad-android","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftouchlane%2Fgridpad-android/lists"}