- Published on
- ยท10 min read
Creating Square Brackets ([ ]) Shaped Like Lottery Ticket Numbers (Android)
I apologize in advance for any awkward expressions in English.
English is not my native language, and I have relied on ChatGPT's assistance to proceed with the translation.
Quick Access to the Service
I created a lottery number generator service with a UI similar to a lottery ticket for both web and Android services. For the CSS styling of the lottery ticket numbers on the web service, please refer to the following post:
Creating Square Brackets ([ ]) Shaped Like Lottery Ticket Numbers(css)
Content to Explain
In this post, I'll explain how to implement the square bracket ([ ]) shape for lottery ticket numbers in an Android app.
You can see how the sections from 1 to 45, marked with square brackets, were implemented in the UI in the image below.
Implementation
1. Create Drawables
- Unselected number drawable:
app/src/main/res/drawable/border_lotto_number.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A rectangle with a 2dp border in the @color/lotto color -->
<item>
<shape android:shape="rectangle">
<stroke android:width="2dp" android:color="@color/lotto"/>
<solid android:color="@color/lotto_background"/>
</shape>
</item>
<!-- It covers a rectangle with @color/lotto_background background color located 10dp above and below -->
<item android:top="10dp" android:bottom="10dp">
<shape android:shape="rectangle">
<!-- I noticed that the stroke part can be removed while writing this post, and it still works the same. -->
<stroke android:width="2dp" android:color="@color/lotto_background"/>
<solid android:color="@color/lotto_background"/>
</shape>
</item>
</layer-list>
- Selected number drawable:
app/src/main/res/drawable/border_lotto_number_selected.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A rectangle with a 2dp border in the @color/lotto color -->
<item>
<shape android:shape="rectangle">
<stroke android:width="2dp" android:color="@color/lotto"/>
<solid android:color="@color/lotto_background"/>
</shape>
</item>
<!-- It covers a rectangle with @color/lotto_background background color located 10dp above and below -->
<item android:top="10dp" android:bottom="10dp">
<shape android:shape="rectangle">
<stroke android:width="2dp" android:color="@color/lotto_background"/>
<solid android:color="@color/lotto_background"/>
</shape>
</item>
<!-- It covers rectangles with @color/black color located 2dp inside on the top, bottom, left, and right sides. -->
<item android:top="2dp" android:bottom="2dp" android:left="2dp" android:right="2dp">
<shape android:shape="rectangle">
<solid android:color="@color/black"/>
</shape>
</item>
</layer-list>
When you preview the above code in Android Studio using the Design or Split view, it should look as follows.
2. Use Drawable Resources as the Background of TextView
Use the created drawable
resources as the background
for the TextView.
app/src/main/res/layout/number_item.xml > android:background="@drawable/border_lotto_number"
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/txt_lotto_number"
android:layout_width="29dp"
android:layout_height="45dp"
android:gravity="center"
tools:text="1"
android:background="@drawable/border_lotto_number"
android:textColor="@color/lotto"
android:textStyle="bold"
android:layout_marginBottom="10dp"
/>
When you preview the above code in Android Studio using the Design or Split view, it should look like this:
number_item
in RecyclerView
3. Use Use number_item.xml
within a RecyclerView
with a GridLayoutManager
.
The connection with number_item
isn't visible in the layout code below, but it's linked through the source code in the Adapter.
app/src/main/res/layout/game_item.xml > <androidx.recyclerview.widget.RecyclerView .../>
<?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="457dp"
android:background="@drawable/border_lotto"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
>
<!-- Abbreviation of the game round and selected number display section-->
<LinearLayout
android:id="@+id/game_summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- Abbreviation of the game round display section -->
<TextView
android:id="@+id/txt_game_number"
tools:text="1"/>
</LinearLayout>
<!-- This is where the lottery numbers are displayed. Refer to the LottoNumberAdapter code. -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/view_number_recycler"
android:layout_width="0dp"
android:layout_height="0dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/game_summary"
app:layout_constraintBottom_toBottomOf="parent"
app:spanCount="7"
android:padding="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
When you preview the code in Android Studio using the Design or Split view, it should look like this (the part that says "item0 item1 item2..." should display the number_item
created earlier):
The part in the source code that connects the RecyclerView
and number_item
is in:
app/src/main/java/kr/sosone/lotto/adapter/LottoNumberAdapter.kt
package kr.sosone.lotto.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import kr.sosone.lotto.R
import kr.sosone.lotto.model.LottoNumber
class LottoNumberAdapter (private val context: Context, private val dataset: List<LottoNumber>)
: RecyclerView.Adapter<LottoNumberAdapter.ItemViewHolder>()
{
class ItemViewHolder(private val view: View) : RecyclerView.ViewHolder(view){
// TextView defined in number_item.xml
val txtLottoNumber: TextView = view.findViewById(R.id.txt_lotto_number)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val adapterLayout = LayoutInflater.from(parent.context)
.inflate(R.layout.number_item, parent, false)
return ItemViewHolder(adapterLayout);
}
override fun onBindViewHolder(holder: LottoNumberAdapter.ItemViewHolder, position: Int) {
val item = dataset[position]
if(item.isPick){
// If it's a selected item, change the background to border_lotto_number_selected and the text color
holder.txtLottoNumber.setBackgroundResource(R.drawable.border_lotto_number_selected)
holder.txtLottoNumber.setTextColor(ContextCompat.getColor(context, R.color.white))
}
holder.txtLottoNumber.text = item.number.toString()
}
override fun getItemCount() = dataset.size
}
app/src/main/java/kr/sosone/lotto/model/LottoNumber.kt
package kr.sosone.lotto.model
data class LottoNumber(val number: Int, val isPick: Boolean)
The LottoNumberAdapter
is assigned in the parent RecyclerView Adapter.
(Note: The code related to this is beyond the scope of what we are explaining here, so it is abbreviated.)
app/src/main/java/kr/sosone/lotto/model/LottoGame.kt
class LottoGameAdapter(private val context: Context, private val dataset: List<LottoGame>)
: RecyclerView.Adapter<LottoGameAdapter.ItemViewHolder>()
{
class ItemViewHolder(private val view: View) : RecyclerView.ViewHolder(view){
// Handling nested RecyclerView
val viewNumberRecycler: RecyclerView = view.findViewById(R.id.view_number_recycler)
}
override fun onBindViewHolder(holder: LottoGameAdapter.ItemViewHolder, position: Int) {
// Handling nested RecyclerView
holder.viewNumberRecycler.adapter = LottoNumberAdapter(context, item.fullNumbers)
holder.viewNumberRecycler.setHasFixedSize(true)
}
}