I am trying lesson 1
However, the first step Add a text element is not working, i just get a blank screen.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("Hello world!")
}
}
}
What may be happening, is that since you're not defining a theme to work with, the text color might be white, as well as the background.
If your background is white, try to replace
Text("Hello world!")
with
Text(text = "Hello world!", color = Color.Black)
Related
As shown above, the list of items, the text input field and the add button go up when the user open the keyboard,
I want the list of items to stay in position while the text input field and the add buton go up as it does.
code:
Activity:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
OlegarioLopezTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) { Navigation() }
}
}
}
The Navigation() func just call the Composable
Composable:
#Composable
fun ListScreen(
viewModel: MainScreenViewModel,
navController: NavController
) {
LazyColumn{...}
MainTextField(viewModel)
AddButton(viewModel)
}
Ensure that the activity's windowSoftInputMode is set to adjustResize:
<activity
android:name=".MyActivity"
android:windowSoftInputMode="adjustResize">
</activity>
In this way the activity's main window is always resized to make room for the soft keyboard on screen.
Then just use a layout as:
Column() {
LazyColumn(Modifier.weight(1f)) {
//..
}
Row(){
TextField()
Button()
}
}
This was exactly my problem with an extra twist that it only became an issue after the activity returned from pause.
A brand new state had no issue, but when resuming suddenly my entire mainactivity was being pushed up with the keyboard. SUPER weird behavior.
I actually set windowSoftInputMode to "adjustPan" abd it works as expected now, which is how it worked as a fresh activity.
I have create a simple example with six TextFields inside a LazyColumn, when you click the last TextField, the keyboard hides it, if you hide the keyboard and click again last TextField, works fine.
In the AndroidManifest I use "adjustPan"
android:windowSoftInputMode="adjustPan"
This is a capture when you click the last TextField first time, hides the last TextField
This is a capture when you click the last TextField second time, works correctly
This is the code
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestComposeTheme {
val numbers = listOf(1,2,3,4,5,6)
LazyColumn() {
items(numbers) { index->
TextField(index = index)
}
}
}
}
}
}
#Composable
fun TextField(index: Int){
var text by remember { mutableStateOf("Hello$index") }
TextField(
modifier = Modifier.padding(25.dp),
value = text,
onValueChange = { text = it },
label = { Text("TextField$index") }
)
}
Does anyone know if there is any way that the first time the last TextField is tapped, it would prevent the keyboard from hiding it
EDIT: There is a known issue:
192043120
This is already a known issue. https://issuetracker.google.com/issues/192043120
One hack to overcome this is use a column with verticalScroll
Column(Modifier.verticalScroll(rememberScrollState(), reverseScrolling = true){
// Content
}
put this in your app AndroidManifest.xml inside activity tag
<activity
...
android:windowSoftInputMode="adjustResize|stateVisible">
I want to programmatically change which tab is selected as the user scrolls past each "see more" item in the list below. How would I best accomplish this?
As Ryan M writes, you can use LazyListState.firstVisibleItemIndex. The magic of Compose is that you can just use it in an if statement and Compose will do the work. Look at the following example, which displays a different text based on the first visible item. Similarly, you can select a different tab based on the first visible item.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val listState = rememberLazyListState()
Column {
Text(if (listState.firstVisibleItemIndex < 100) "< 100" else ">= 100")
LazyColumn(state = listState) {
items(1000) {
Text(
text = "$it",
modifier = Modifier.fillMaxWidth(),
)
}
}
}
}
}
}
Just create a NestedScrollConnection and assign it to parent view nestedScroll modifier:
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.y
// called when you scroll the content
return Offset.Zero
}
}
}
Assign it to LazyColumn or to its parent composed view:
Modifier.nestedScroll(nestedScrollConnection)
Exemple:
LazyColumn(
...
modifier = Modifier.nestedScroll(nestedScrollConnection)
) {
items(...) {
Text("Your text...")
}
}
You can use the LazyListState.firstVisibleItemIndex property (obtained via rememberLazyListState and set as the state parameter to LazyColumn) and set the current tab based on that.
Reading that property is considered a model read in Compose, so it will trigger a recomposition whenever the first visible item changes.
If you instead want to do something more complex based on more than just the first visible item, you can use LazyListState.layoutInfo to get information about all visible items and their locations, rather than just the first.
I have created an Empty Compose Activity template using Android Studio Canara 2020.03. Here is the code of the file " MainActivity. kt":
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ButtonPage()
}
}
}
#Composable
fun ButtonPage(){
Button(onClick = {}){Text("Click to go next")}
}
#Composable
fun TextPage(){
Text("Second Page")
}
How do I modify the code so that when you click on this button, it draws only text? (That is, you need that when you click the button, the program draws other content by deleting this one first).
Jetpack Compose version 1.0.0-alpha09, jdk version 15, android version 11
Thank you in advance
A simple solution would be to have a variable that dictates the current screen.
var showSecondScreen by remember { mutableStateOf(false) }
if (!showSecondScreen) {
Button(onClick = {showSecondScreen = true}){Text("Click to go next")}
} else {
Text("Second screen")
}
This doesn't have to be a boolean, you could declare var currentScreen by remember { mutableStateOf("homeScreen") } and use a when block for which screen to show.
#Composable fun MyApp(currentScreen: String) {
when (currentScreen) {
"homeScreen" -> HomeScreen()
"secondScreen" -> SecondScreen()
}
}
So you can think of it less as a transaction and more as a stateful navigation.
But you'll soon realize that this doesn't handle back navigation, so you'll need a navigation library like jetpack compose navigation, decompose, compose-router, etc. Here's more information on jetpack compose navigation.
Suppose I have some activity with a jetpack-compose content
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ScrollableColumn(
modifier = Modifier
.fillMaxSize()
.border(4.dp, Color.Red)
) {
val (text, setText) = remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = setText,
label = {},
modifier = Modifier
.fillMaxWidth()
)
for (i in 0..100) {
Text("Item #$i")
}
}
}
}
}
If I were to launch this activity and focus on the TextField a software keyboard would pop up.
The interface, however, would not react to it. ScrollableColumn's bottom border (.border(4.dp, Color.Red)) would not be visible, as well as 100th item (Text("Item #$i")).
In other words, software keyboard overlaps content.
How can I make jetpack compose respect visible area changes (due to software keyboard)?
You can use the standard android procedure, but I don't know if a Compose specific way exists.
If you set the SoftInputMode to SOFT_INPUT_ADJUST_RESIZE, the Layout will resize on keyboard change.
class YourActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
setContent { /* Composable Content */ }
}
}
otherwise, you could use the flags in the manifest. See here for more information:
Move layouts up when soft keyboard is shown?
You can use Accompanist's inset library https://google.github.io/accompanist/insets
first use ProvideWindowInsets at the root of your composable hierarchy most of the time below your app theme compose and set windowInsetsAnimationsEnabled true
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
// content }
The use navigationBarsWithImePadding() modifier on TextField
OutlinedTextField(
// other params,
modifier = Modifier.navigationBarsWithImePadding() )
Finaly make sure to call WindowCompat.setDecorFitsSystemWindows(window, false) from your activity(inside onCreate). If you want Software keyboard on/off to animate set your activity's windowSoftInputMode adjustResize in AndroidManifests
<activity
android:name=".MyActivity"
android:windowSoftInputMode="adjustResize">
Besides Compose, no external libraries are needed for this now.
Set android:windowSoftInputMode=adjustResize on the activity in your manifest file
e.g.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".MyActivity"
android:windowSoftInputMode="adjustResize"/>
</application>
</manifest>
Then for the composable that you want the UI to react to, you can use Modifier.imePadding() which reacts to IME visibility
Box(Modifier.imePadding()) {
// content
}
I faced the same problem.
Use OnGlobalLayoutListener which will observe the actual IME rect size and will be triggered when the soft keyboard is fully visible.
Worked solution for me:
val bringIntoViewRequester = remember { BringIntoViewRequester() }
val scope = rememberCoroutineScope()
val view = LocalView.current
DisposableEffect(view) {
val listener = ViewTreeObserver.OnGlobalLayoutListener {
scope.launch { bringIntoViewRequester.bringIntoView() }
}
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
onDispose { view.viewTreeObserver.removeOnGlobalLayoutListener(listener) }
}
TextField(
modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester),
...
)
Origin here
I came up with idea of using accompanist insets library.
Someone could be interested because my approach does not modificate the contents of an application.
I link my post below:
(Compose UI) - Keyboard (IME) overlaps content of app
If you want your TextField scroll up when keyboard appears.
The following it work for me.
You need to add these
class YourActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
setContent { /* Composable Content */ }
}
And add this to your app/build.gradle
implementation "com.google.accompanist:accompanist-insets-ui:0.24.7-alpha"
In my case just adding android:windowSoftInputMode="adjustResize" to activity was enough to solve the problem.
It depends on how you build your UI. If yours screen's root is a vertically scrollable container or a Box, the keyboard resize might get managed automatically.
Use the modifier imePadding(). This will allow the specific compositions to adjust themselves when the keyboard pops up. This does not require you to set any flag on the activity