Skip to content
Snippets Groups Projects
Commit a39f7f98 authored by Baryshnikov Oleg's avatar Baryshnikov Oleg
Browse files

Add the navigation compose dependency.

Organize functions into separate packages.
Add screens description (Screen.kt) and the navigation logic.
parent 40e723ad
No related branches found
No related tags found
No related merge requests found
......@@ -73,4 +73,8 @@ dependencies {
//splash screen
implementation "androidx.core:core-splashscreen:1.0.1"
// Navigation component
def nav_version = "2.6.0"
implementation "androidx.navigation:navigation-compose:$nav_version"
}
\ No newline at end of file
......@@ -3,47 +3,25 @@ package cz.fel.barysole.ackeetesttask
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import cz.fel.barysole.ackeetesttask.ui.characterlist.CharacterList
import cz.fel.barysole.ackeetesttask.ui.characterlist.model.CharacterInfo
import cz.fel.barysole.ackeetesttask.ui.mainuielements.MyNavigationBar
import cz.fel.barysole.ackeetesttask.ui.mainuielements.MyTopAppBar
import cz.fel.barysole.ackeetesttask.ui.navigation.Screen
import cz.fel.barysole.ackeetesttask.ui.navigation.appScreenList
import cz.fel.barysole.ackeetesttask.ui.theme.AckeeTestTaskTheme
class MainActivity : ComponentActivity() {
......@@ -52,145 +30,55 @@ class MainActivity : ComponentActivity() {
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
setContent {
MainScreen(listOf(Character("Rick", "Sanchez", true), Character("Morty", "", false)))
MainScreen()
}
}
}
@Composable
fun MainScreen(characterList: List<Character> = emptyList()) {
fun MainScreen() {
// Navigation elements
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
// We can set selectedScreen by using callbacks from navigation composable, but it not guarantee any performance improvements in this small app
val selectedScreen =
appScreenList.find { item -> currentDestination?.hierarchy?.first()?.route == item.route }
// Main content
AckeeTestTaskTheme {
Scaffold(modifier = Modifier.fillMaxSize(1f),
topBar = {
MyTopAppBar()
MyTopAppBar(selectedScreen)
},
content = { innerPadding ->
CharacterList(characterList, innerPadding)
NavHost(
navController,
startDestination = Screen.Characters.route,
Modifier.padding(innerPadding)
) {
composable(Screen.Characters.route) {
CharacterList(
listOf(
CharacterInfo(
"Rick",
"Sanchez",
true
), CharacterInfo("Morty", "", false)
)
)
}
composable(Screen.Favorite.route) { }
composable(Screen.CharacterDetail.route) { }
}
},
bottomBar = {
MyNavigationBar()
MyNavigationBar(navController, selectedScreen)
})
}
}
//Main screen contains the TopAppBar, which is experimental and is likely to change or to be removed in the future.
//But in the small test app it looks nice, i guess.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyTopAppBar() {
TopAppBar(
modifier = Modifier.shadow(16.dp),
title = {
Text(
"Characters",
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
actions = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
imageVector = Icons.Default.Search,
contentDescription = "Search button"
)
}
},
colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.background)
)
}
@Composable
fun MyNavigationBar() {
var selectedItem by remember { mutableStateOf(0) }
val items = listOf<Pair<Painter, String>>(
Pair(
painterResource(id = R.drawable.ic_rick_face),
"Favorites"
), Pair(painterResource(id = R.drawable.baseline_star_24), "Favorites")
)
NavigationBar(
modifier = Modifier.shadow(8.dp),
containerColor = MaterialTheme.colorScheme.surface,
tonalElevation = 0.dp
) {
items.forEachIndexed { index, item ->
NavigationBarItem(
icon = {
Icon(
item.first,
contentDescription = item.second,
modifier = Modifier.size(24.dp)
)
},
label = { Text(item.second) },
selected = selectedItem == index,
onClick = { selectedItem = index }
)
}
}
}
data class Character(val firstName: String, val secondName: String, val isAlive: Boolean)
@Composable
fun CharacterList(characterList: List<Character>, innerPadding: PaddingValues) {
Surface(
modifier = Modifier.padding(innerPadding),
color = MaterialTheme.colorScheme.background
) {
LazyColumn(modifier = Modifier.padding(vertical = 4.dp)) {
items(characterList) { character ->
CharacterItem(character)
}
}
}
}
@Composable
fun CharacterItem(character: Character) {
Surface(
shape = MaterialTheme.shapes.large,
color = MaterialTheme.colorScheme.surface,
shadowElevation = 4.dp,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Row(
modifier = Modifier
.padding(all = 12.dp)
.fillMaxWidth(1f),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(R.drawable.app_logo),
contentDescription = "Character image",
modifier = Modifier
.size(40.dp)
.clip(MaterialTheme.shapes.small)
)
Spacer(modifier = Modifier.width(12.dp))
Column {
Text(
text = "${character.firstName} ${character.secondName}",
style = MaterialTheme.typography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = if (character.isAlive) "Alive" else "Not alive",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
@Preview
@Composable
fun MainScreenPreview() {
MainScreen(listOf(Character("Rick", "Sanchez", true), Character("Morty", "", false)))
MainScreen()
}
\ No newline at end of file
package cz.fel.barysole.ackeetesttask.ui.characterlist
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import cz.fel.barysole.ackeetesttask.R
import cz.fel.barysole.ackeetesttask.ui.characterlist.model.CharacterInfo
@Composable
fun CharacterList(characterList: List<CharacterInfo>) {
Surface(
color = MaterialTheme.colorScheme.background
) {
LazyColumn(modifier = Modifier.padding(vertical = 4.dp)) {
items(characterList) { character ->
CharacterItem(character)
}
}
}
}
@Composable
fun CharacterItem(character: CharacterInfo) {
Surface(
shape = MaterialTheme.shapes.large,
color = MaterialTheme.colorScheme.surface,
shadowElevation = 4.dp,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Row(
modifier = Modifier
.padding(all = 12.dp)
.fillMaxWidth(1f),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(R.drawable.app_logo),
contentDescription = "Character image",
modifier = Modifier
.size(40.dp)
.clip(MaterialTheme.shapes.small)
)
Spacer(modifier = Modifier.width(12.dp))
Column {
Text(
text = "${character.firstName} ${character.secondName}",
style = MaterialTheme.typography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = if (character.isAlive) "Alive" else "Not alive",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
\ No newline at end of file
package cz.fel.barysole.ackeetesttask.ui.characterlist
import androidx.lifecycle.ViewModel
class CharacterListViewModel : ViewModel() {
}
\ No newline at end of file
package cz.fel.barysole.ackeetesttask.ui.characterlist.model
data class CharacterInfo(val firstName: String, val secondName: String, val isAlive: Boolean)
package cz.fel.barysole.ackeetesttask.ui.mainuielements
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import cz.fel.barysole.ackeetesttask.ui.navigation.Screen
@Composable
fun MyNavigationBar(navController: NavController, selectedScreen: Screen?) {
val navBarItems = listOf(
Screen.Characters,
Screen.Favorite
)
NavigationBar(
modifier = Modifier.shadow(8.dp),
containerColor = MaterialTheme.colorScheme.surface,
tonalElevation = 0.dp
) {
navBarItems.forEach {screen ->
NavigationBarItem(
icon = {
if (screen.iconResId != null) {
Icon(
painterResource(screen.iconResId),
contentDescription = stringResource(screen.nameResId),
modifier = Modifier.size(24.dp)
)
}
},
label = { Text(stringResource(screen.nameResId)) },
selected = selectedScreen == screen,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
}
)
}
}
}
\ No newline at end of file
package cz.fel.barysole.ackeetesttask.ui.mainuielements
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import cz.fel.barysole.ackeetesttask.ui.navigation.Screen
import cz.fel.barysole.ackeetesttask.ui.navigation.screenWithTopAppBarList
//Main screen contains the TopAppBar, which is experimental and is likely to change or to be removed in the future.
//But in the small test app it looks nice, i guess.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyTopAppBar(selectedScreen: Screen?) {
if (screenWithTopAppBarList.contains(selectedScreen)) {
TopAppBar(
modifier = Modifier.shadow(16.dp),
title = {
Text(
//there is no null in the screenWithTopAppBarList
stringResource(selectedScreen!!.nameResId),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
actions = {
if (selectedScreen == Screen.Characters) {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
imageVector = Icons.Default.Search,
contentDescription = "Search button"
)
}
}
},
colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.background)
)
}
}
\ No newline at end of file
package cz.fel.barysole.ackeetesttask.ui.navigation
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import cz.fel.barysole.ackeetesttask.R
sealed class Screen(val route: String, @StringRes val nameResId: Int, @DrawableRes val iconResId: Int? = null) {
object Characters : Screen("characters", R.string.characters, R.drawable.ic_rick_face)
object Favorite : Screen("favorite", R.string.favorite, R.drawable.baseline_star_24)
object CharacterDetail : Screen("characterdetail", R.string.character_detail)
}
val appScreenList = listOf(
Screen.Characters,
Screen.Favorite,
Screen.CharacterDetail
)
val screenWithTopAppBarList = listOf(
Screen.Characters,
Screen.Favorite,
Screen.CharacterDetail
)
\ No newline at end of file
<resources>
<string name="app_name">Ackee test task</string>
<string name="characters">Characters</string>
<string name="favorite">Favorite</string>
<string name="character_detail">Character Detail</string>
</resources>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment