Starting Lineups on Football Pitch with Jetpack Compose.
At this article, I will make a simulation for the a real football game with line-ups and players events on the playground.
Let’s discover the process and break it down. I hope this can help.
As I have been working on a sports application for a while, I wanted to take my new application to another level so it could be more responsive and user-friendly. But, In this article we will talk just about UI with Compose.
Well, let’s take the shortest path.
Canvas is our main subject here. With canvas, we can draw different shapes based on the X-axis and Y-axes. Every shape move from top-left corner to draw. The movements are horizontally “move to the left for positive X values” and “move to the right for negative X values” and vertically “move down on the Y-axis for positive Y values” and “move up on the Y-axis for negative Y values.”.
If you want to know more about canvas visit this link or here.
Today we will make a football playground and add players to the pitch based on the provided formation.
Here is the football pitch drawn inside a Box. If you want to know how to draw this in detail, you will find it Here, as Nour gave a good explanation.
There are some little changes. For full implementation you can find it Here.
- So, first we have to draw this football pitch.
Box(
modifier = Modifier
.size(width = 400.dp, height = 800.dp)
.drawBehind {
//Draw Here
}
) {
//Here, we will put the players
}
After drawing these shapes like that, we have to put our players on the pitch. In the coming sections, we will break it down with each other.
We know that the whole shape is shattered into two slices, “top and bottom” by the central field line, and as we all know, the two battling teams own these parts as equal. So, the best choice here is to use a weight modifier to give each team a playing area.
2. To use the weight modifier inside the Box scope, we have to add this part of code to treat Box as a layout, or you can use a Column or Row with a fill max size, that’s OK too.
@Composable
fun BoxWithLayout(content: @Composable RowScope.()->Unit){
Row(
modifier = Modifier.background(Color.Transparent)
) {
content()
}
}
2. After that, we can call this function and cut this playground into two parts:
-> The parent column will fill the max size with an optional padding vertically.
-> So, the two child Columns will take the whole parent size equally, using the weight modifier.
BoxWithLayout {
Column(
modifier = Modifier
.fillMaxSize()
.weight(2f)
.padding(vertical = 25.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth
.weight(1f),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally
) {
//Team Home
}
Column(
modifier = Modifier
.fillMaxWidth
.weight(1f),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally
) {
//Team Away
}
}
}
Then, in football, every team is divided into 4 or 5 lines based on the provided formation, and rarely, in complex tactical games, the coach may rely on six lines with some slight changes on the ground. For this, you can avoid it easily with some steps by making responsive, composable components that can be modified easily.
3. So, we have to make a row for every line in the provided formation.
After that, we have to create a custom composable component for the player on the ground.
With a ConstraintLayout, Every composable is constrained around player image in conditions:
-> ex. If the player has playing minutes, then the rating will appear, and so on.
ConstraintLayout(
modifier = Modifier.background(DarkGreen)
) {
val (
image, nameNum, cap, goalIcon, goalsN, assistIcon,
assistsN, yellowCardIcon, redCardIcon, rating, subIcon
) = createRefs()
Card(
modifier = Modifier
.clip(CircleShape)
.constrainAs(image) {
top.linkTo(parent.top, margin = 2.dp)
start.linkTo(parent.start, margin = 10.dp)
end.linkTo(parent.end, margin = 10.dp)
},
colors = CardDefaults.cardColors(
containerColor = Color.White
),
elevation = CardDefaults.cardElevation(4.dp)
) {
Image(
modifier = Modifier
.size(width = 55.dp, height = 55.dp),
painter = painterResource(id = playerImage),
contentDescription = "image"
)
}
}
The rest is quite easy to implement.
Below each Column we have multiple Rows, Each one of these rows contains one or more of these components. All we have to do as we don’t have real data is like this:
//
Row(/**/) {/**/}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 1.dp)
.weight(1f),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
for (i in 1..4) {
PlayerOnPitchComponent(
playerImage = R.drawable.player_f,
name = "Def Home",
number = 3,
goals = 1,
assists = 2,
playerRating = "6.5",
yellowCards = 1,
redCards = 0,
hasSubbed = false,
hasInvolved = true,
isCaptain = false
)
}
}
Row(/**/) {/**/}
//
If you want to look at my simple tennis court hard surface, you will find it in the full code.
Conclusion
At the end of this, I hope this can help. If anyone has any questions, please don’t hesitate to ask. You will find the full code Here.
Thank You…