如何根据接口响应动态实现巴士座位的RecyclerView网格布局?
Got it, let's fix this bus seat layout issue. The problem with RecyclerView + GridLayoutManager is that it's great for column spans but doesn't handle row spans cleanly—so using a GridLayout directly is the way to go here since it natively supports both row and column spans. Here's a step-by-step implementation:
1. First, define your Seat model
Create a data class to map the JSON response:
data class Seat( val column: Int, val length: Int, // Vertical span (number of rows the seat occupies) val name: String, val row: Int, val width: Int // Horizontal span (number of columns the seat occupies) )
2. Parse the JSON response
Use Gson (or Moshi) to convert the JSON string into a list of Seat objects:
import com.google.gson.Gson // Replace with your actual JSON string val jsonResponse = "[...]" val seatList = Gson().fromJson(jsonResponse, Array<Seat>::class.java).toList()
3. Set up your layout XML
Add a GridLayout to your activity/fragment layout—we'll populate it programmatically:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <GridLayout android:id="@+id/seatGrid" android:layout_width="match_parent" android:layout_height="wrap_content" android:alignmentMode="alignMargins" android:useDefaultMargins="true"/> </LinearLayout>
4. Calculate grid dimensions
First, figure out how many columns and rows we need to fit all seats (including their spans):
// Calculate total columns needed (max column + seat width) val totalColumns = seatList.maxOf { it.column + it.width } // Calculate total rows needed (max row + seat length) val totalRows = seatList.maxOf { it.row + it.length } // Set grid dimensions seatGrid.columnCount = totalColumns seatGrid.rowCount = totalRows
5. Dynamically add seats to the GridLayout
Create a view for each seat, set its span parameters, and add it to the grid. We'll use a Button for simplicity, but you can use a custom CardView if you want more styling:
// Define dimensions (adjust these values to fit your design) val seatMargin = resources.getDimensionPixelSize(R.dimen.seat_margin) // e.g., 4dp val seatTextSize = 12f seatList.forEach { seat -> val seatButton = Button(this).apply { text = seat.name textSize = seatTextSize gravity = Gravity.CENTER // Set custom background (create a selector drawable for different states) background = ContextCompat.getDrawable(this@MainActivity, R.drawable.seat_background) // Handle seat clicks setOnClickListener { it.isSelected = !it.isSelected // Add your selection logic here } } // Configure GridLayout parameters with spans val layoutParams = GridLayout.LayoutParams().apply { // Set column span: start at seat.column, span seat.width columns columnSpec = GridLayout.spec(seat.column, seat.width, 1f) // Set row span: start at seat.row, span seat.length rows rowSpec = GridLayout.spec(seat.row, seat.length, 1f) // Make seats responsive (fill available space) width = 0 height = 0 // Add margins between seats marginStart = seatMargin marginEnd = seatMargin marginTop = seatMargin marginBottom = seatMargin } seatButton.layoutParams = layoutParams seatGrid.addView(seatButton) }
6. Add a custom seat background (optional)
Create a selector drawable to style seats for available, selected, and occupied states. Save this as res/drawable/seat_background.xml:
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true"> <shape android:shape="rectangle"> <solid android:color="@color/teal_200"/> <corners android:radius="8dp"/> <stroke android:width="2dp" android:color="@color/black"/> </shape> </item> <item android:state_enabled="false"> <shape android:shape="rectangle"> <solid android:color="@color/grey"/> <corners android:radius="8dp"/> <stroke android:width="2dp" android:color="@color/black"/> </shape> </item> <item> <shape android:shape="rectangle"> <solid android:color="@color/white"/> <corners android:radius="8dp"/> <stroke android:width="2dp" android:color="@color/black"/> </shape> </item> </selector>
Why this works better than RecyclerView
GridLayout natively supports both row and column spans, which is exactly what you need for those longer seats in your layout. RecyclerView's GridLayoutManager is designed for uniform row heights, so it struggles with items that span multiple rows.
This implementation will dynamically render your seats exactly as per the JSON data, including all the spans, and it's responsive to different screen sizes.
内容的提问来源于stack exchange,提问作者Sandip Patel

