Align composable to bottom of another composable in Jetpack compose? - android-layout

I have 3 composable in Column and I want to align 2nd composable to bottom of 1st composable like this:
But currently i'm getting output like this:
Here is my current code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposePlaygroundTheme {
Column(modifier = Modifier
.fillMaxSize()) {
topControls("Game 1",34,Modifier.weight(1f))
centerControls()
bottomControls()
}
}
}
}
}
#Composable
fun topControls(gamename: String, totalTime: Int, modifier: Modifier) {
Column(modifier = modifier
.background(Color.LightGray)
.padding(start = 24.dp, end = 24.dp, top = 24.dp)) {
Row(modifier = Modifier.padding(end = 32.dp)) {
Image(
painter = painterResource(R.drawable.ic_launcher_foreground),
contentDescription = "",
Modifier
.height(32.dp)
.width(32.dp)
)
Text(
text = gamename,
modifier = Modifier
.weight(1f)
.align(Alignment.CenterVertically),
textAlign = TextAlign.Center
)
}
Text(text = totalTime.toString(),modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center,fontSize = 53.sp)
Image(
painter = painterResource(R.drawable.ic_launcher_background),
contentDescription = "",
Modifier.fillMaxSize(),
contentScale = ContentScale.FillBounds
)
}
}
#Composable
fun centerControls() {
Box(modifier = Modifier
.fillMaxWidth()) {
Divider(color = Color.Blue, thickness = 1.dp, modifier = Modifier.align(Alignment.Center))
Row(modifier = Modifier.padding(start = 20.dp, end = 20.dp)) {
Button(onClick = { },colors = ButtonDefaults.buttonColors(backgroundColor = Color.Black,contentColor = Color.White), modifier = Modifier
.weight(1f)
.padding(end = 20.dp)
.fillMaxWidth()) {
Text(text = "End Game",modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(), textAlign = TextAlign.Center,fontSize = 20.sp)
}
Image(painter = ColorPainter(Color.Gray), contentDescription = "", modifier = Modifier
.padding(horizontal = 25.dp)
.align(Alignment.CenterVertically)
.height(32.dp)
.width(32.dp))
}
}
}
#Composable
fun bottomControls() {
Spacer(modifier = Modifier.height(170.dp))
}
Is there anyway I can achieve this in jetpack compose without using Constraint layout?

As you need to place one view under an other one, you should use Box.
Using Modifier.align, you can specify position of your centerControls, in this case Alignment.BottomCenter.
Using Modifier.layout you can add offset, equal to half view size.
Column(modifier = Modifier
.fillMaxSize()
) {
Box(Modifier.weight(1f)) {
topControls("Game 1", 34, Modifier.fillMaxHeight())
centerControls(
Modifier
.align(Alignment.BottomCenter)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
placeable.place(0, placeable.height / 2)
}
}
)
}
bottomControls()
}
Result:

Something like this should work
ComposePlaygroundTheme {
Column(modifier = Modifier
.fillMaxSize()) {
topControls("Game 1",34)
Box(modifier = Modifier.weight(1f)) { // FrameLayout, weight will occupy rest of the content
centerControls()
bottomControls() // will be stacked up on top of topControls()
}
}
}

Related

Jetpack Compose view position and shadow preview is different to on when running on device

I have the below code, however the Preview is rendering the view different to when I run it on a AVD (Android Virtual Device). It looks there are 2 things different:
Firstly, the AVD is rending the view at the top of the screen, but the preview is rending the view in the centre of the screen. I've added contentAlignment = Alignment.Center to my AlertView Box which I believe should render it in the middle of the screen.
Secondly, the AVD is showing a weird shadow compared to the preview.
Images below of the preview and AVD:
//Preview Provider
#Preview(
showSystemUi = true,
device = Devices.PIXEL_4_XL,
uiMode = Configuration.UI_MODE_NIGHT_NO,
showBackground = true,
backgroundColor = 0xFFFFFFFF
)
#Preview(
showSystemUi = true,
device = Devices.PIXEL_4,
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true,
backgroundColor = 0xFF000000
)
#Composable
fun DefaultPreview() {
AlertView()
}
//AlertViewModel
class AlertViewModel : ViewModel() {
var showAlert: Boolean by mutableStateOf(false)
var alertIcon: ImageVector by mutableStateOf(Icons.Rounded.WifiOff)
var alertTitle: String by mutableStateOf("No internet connection")
var alertMessage: String by mutableStateOf("This is the alert message and it is purposely longer than one line to be displayed on screen")
var alertButtonText: String by mutableStateOf("This is the alert button text")
}
//--------------------------------------------------------------------------------------------------
//.onCreate
class AlertManager : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AlertView()
}
}
}
//AlertView
#Composable
fun AlertView(alertViewModel: AlertViewModel = viewModel()) {
Box(
contentAlignment = Alignment.Center
) {
Column(
verticalArrangement = Arrangement.spacedBy(10.dp),
modifier = Modifier
.padding(30.dp, 0.dp)
.background(
shape = RoundedCornerShape(15.dp),
color =
if (isSystemInDarkTheme()) {
Color.Black
} else {
Color.White
}
)
.shadow(
elevation = 1.dp,
shape = RoundedCornerShape(15.dp),
ambientColor = if (isSystemInDarkTheme()) {
Color.Black
} else {
Color.White
},
spotColor = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Gray
},
)
.padding(30.dp, 25.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(20.dp)
) {
Icon(
imageVector = alertViewModel.alertIcon,
contentDescription = null,
tint = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Black
},
)
Text(
text = alertViewModel.alertTitle,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Black
}
)
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(25.dp),
) {
Text(
text = alertViewModel.alertMessage,
color = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Black
}
)
Button(
onClick = {
alertViewModel.showAlert = false
},
colors = ButtonDefaults.buttonColors(
backgroundColor = if (isSystemInDarkTheme()) {
Color.White
} else {
Color.Black
}
),
shape = RoundedCornerShape(15.dp),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
contentPadding = PaddingValues(15.dp),
) {
Text(
text = alertViewModel.alertButtonText,
fontWeight = FontWeight.Bold,
color = if (isSystemInDarkTheme()) {
Color.Black
} else {
Color.White
}
)
}
}
}
}
}

Achieve Vertical Grid View in Jetpack Compose

My Code :::
#Composable
fun VerticalImageCards(toolsList: List<HealthTool>) {
val domainName = stringResource(id = R.string.media_url)
FlowRow(mainAxisSpacing = 8.dp,
crossAxisSpacing = 16.dp,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
mainAxisSize = SizeMode.Wrap) {
for (i in toolsList) {
val imgUrl = "$domainName${i.url}"
ToolsCardLayout(cardTitle = i.title, imgUrl = imgUrl)
}
}
}
#Composable
fun ToolsCardLayout(
imgUrl: String,
cardTitle: String,
) {
Box(modifier = Modifier.clickable {}) {
AsyncImage(model = imgUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxWidth())
Box(modifier = Modifier
.size(32.dp)
.align(Alignment.TopEnd)) {
Image(painter = painterResource(id = R.drawable.schedule),
contentDescription = null,
modifier = Modifier
.fillMaxSize()
.padding(top = 8.dp, end = 8.dp),
contentScale = ContentScale.Fit)
}
}
Card(modifier = Modifier
.height(44.dp)
.fillMaxWidth()) {
Text(text = cardTitle,
style = MaterialTheme.typography.h6,
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.clip(RoundedCornerShape(bottomStart = 8.dp, bottomEnd = 8.dp)))
}
}
Code Explanation:::
I'm getting a list of tools details which is having tool image, title, description from server.
imgURl -> url for image
cardTitle -> tool's name
Problem :::
Card is taking the whole width. Earlier, I used weight(1f) when the data was hardcoded and I don't have to retrieve it from backend. Also, I'm going to use it on the Homescreen, so the grid shouldn't be scrollable.
PLEASE VISIT THIS DRIVE LINK FOR MORE DETAILS, I have added code snippet and images + screen recording for better understanding
You can use official LazyVerticalGrid
#Composable
fun VerticalImageCards(toolsList: List<HealthTool>) {
val domainName = stringResource(id = R.string.media_url) {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = modifier,
state = rememberLazyGridState()
) {
items(toolsList) {
val imgUrl = "$domainName${it.url}"
ToolsCardLayout(cardTitle = it.title, imgUrl = imageUrl)
}
}
}

Can I use ActivityResultContracts.TakePicturePreview() to open front camera at first rather then back camera

Can I use ActivityResultContracts.TakePicturePreview() to open front camera at first rather then back camera.
#Composable
fun PickImage(onImagePicked: (Uri) -> Unit) {
var imageUri by remember { mutableStateOf<Uri?>(null) }
val launcher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.TakePicturePreview()) {
it?.let { _ -> imageUri = saveImage(it) }
}
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
imageUri?.let {
onImagePicked(it)
}
LaunchedEffect(key1 = Unit, block = {
launcher.launch()
})
}
}

How to wait result from OKHTTP response without freezing app Kotlin?

I am writing a small application in Kotlin with JetpackCompose and my application freezes (CountDownLatch) when a request is called, is there any way to avoid this?
Without CountDownLatch, okhttp does not have time to return the result before the method completes.
MainActivity.kt
fun infoServer() {
val viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
var txt = remember {
mutableStateOf(0)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.padding(10.dp)
) {
Text(
text = "Player Count ",
style = MaterialTheme.typography.body1
)
Text(
text = txt.value.toString(),
style = MaterialTheme.typography.body1,
fontWeight = FontWeight.Bold
)
}
Row(horizontalArrangement = Arrangement.Center) {
eveButton(
text = "Update",
onClick = ({
viewModel.updatePlayerCount()
txt.value = viewModel.res
}),
modifier = Modifier
.padding(10.dp)
)
eveButton(
text = "Clear",
onClick = ({
txt.value = 0
}),
modifier = Modifier
.padding(10.dp)
)
}
}
}
MainActivityViewModel.kt
class MainActivityViewModel : ViewModel() {
var res = 0
fun updatePlayerCount() {
res = MainActivityModel().updateServerPlayers()
}
}
MainACtivityModel.kt
class MainActivityModel {
var result = 0
fun updateServerPlayers(): Int {
val client = OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.callTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
val request = Request.Builder()
.url("https://esi.evetech.net/latest/status/?datasource=tranquility")
.method("GET", null)
.build()
var countDownLatch = CountDownLatch(1)
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d("Error Connection", e.toString())
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Error $response")
try {
var json =JSONObject(response.body!!.string())
countDownLatch.countDown()
result = json.getInt("players")
}
catch (e: Exception) {
Log.d("OKHTTP", e.toString())
}
}
}
})
countDownLatch.await()
Log.d("OKHTTP2", result.toString())
return result
}
}

How to change language locale in Jetpack Compose

I want to change language programmatically in Jetpack Compose. I've read quite some posts and watch videos but still can't find the way to do it. (The post and video are in Android view system.)
How to change language in kotlin (locale)
https://www.youtube.com/watch?v=xxPzi2h0Vvc
I want my app works like below image. After clicking the language, the whole app will change the language. Below code is the clickable's part. What should I do in this clickable part and MainActivity.kt?
#Composable
fun LanguageScreen(
navController: NavController,
) {
val context = LocalContext.current
val langList = arrayOf("English", "繁體中文", "简体中文", "日本語")
var items by remember {
mutableStateOf(
langList.map {
LanguageItem(
title = it,
isSelected = false
)
}
)
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
) {
items(items.size) { i ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
items = items.mapIndexed { j, item ->
if (i == j) {
item.copy(isSelected = true)
} else item.copy(isSelected = false)
}
if (i == 0) {
setLocaleLang("", context)
} else if (i == 1) {
setLocaleLang("zh-rTW", context)
} else if (i == 2) {
setLocaleLang("zh-rCN", context)
} else {
setLocaleLang("ja", context)
}
}
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text = items[i].title, fontSize = 20.sp)
if (items[i].isSelected) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = "Selected",
tint = Color.Blue,
modifier = Modifier.size(24.dp)
)
}
}
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(Color.LightGray)
)
}
}
}
fun setLocaleLang(lang: String, context: Context) {
val locale = Locale(lang)
Locale.setDefault(locale)
val resources = context.resources
val configuration = resources.configuration
configuration.setLocale(locale)
resources.updateConfiguration(configuration, resources.displayMetrics)
val editor = context.getSharedPreferences("Settings", Context.MODE_PRIVATE).edit()
editor.putString("My_Lang", lang)
editor.apply()
}
fun loadLocale(context: Context) {
val sharedPreferences = context.getSharedPreferences("Settings", Activity.MODE_PRIVATE)
val language = sharedPreferences.getString("My_Lang", "")
setLocaleLang(language!!, context)
}
MainActivity.kt
class MainActivity : ComponentActivity() {
#ExperimentalFoundationApi
override fun onCreate(savedInstanceState: Bundle?) {
loadLocale(this)
super.onCreate(savedInstanceState)
setContent {
SpanishTravelTheme {
Image: https://i.stack.imgur.com/y5kcO.png
Try this
val context = LocalContext.current
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
val locale = Locale(language) //Here I assume you have access to the language you want
Locale.setDefault(locale)
val resources = context.getResources()
val configuration = resources.getConfiguration()
configuration.locale = locale
resources.updateConfiguration(configuration, resources.getDisplayMetrics())
}
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
)
You can try this
Create a helper object
LocaleUtils.kt
object LocaleUtils {
// [AppPrefs] is sharedpreferences or datastore
fun setLocale(c: Context, pref: AppPrefs) = updateResources(c, pref.language ?: "en") //use locale codes
private fun updateResources(context: Context, language: String) {
context.resources.apply {
val locale = Locale(language)
val config = Configuration(configuration)
context.createConfigurationContext(configuration)
Locale.setDefault(locale)
config.setLocale(locale)
context.resources.updateConfiguration(config, displayMetrics)
}
}
}
Call setLocale inside setContent to change the language at runtime.
setContent {
LocaleUtils.setLocale(LocalContext.current, viewModel.pref)
To change the App language
fun changeAppLanguage(languageISO: String) {
sharedPrefs.edit().putString(LANGUAGE_KEY, languageISO).apply()
}
Please remember to use language ISO 639-1 Code
here's a list of Locale codes
https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/localization

Resources