几天前,我遇到了一个问题,我的视图的一部分被键盘重叠了。
假设我们有 3 个不同的对话框(可以是任何内容),如下所示:
当我想写任何东西时,最后一个对话框被键盘覆盖:
并且无法查看用户写的内容。这是我的代码:
@Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(PrimaryLight)
.fillMaxSize()
) {
BuildWordsScreenContents()
}
}
@Composable
fun BuildWordsScreenContents() {
Column(
Modifier
.fillMaxSize()
.padding(all = 16.dp)
) {
val inputBoxModifier = Modifier
.clip(RoundedCornerShape(10.dp))
.background(Primary)
.weight(12f)
.wrapContentHeight()
InputBlock("Dialog1", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog2", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog3", inputBoxModifier)
}
}
@Composable
fun InputBlock(dialogText: String, inputBlockModifier: Modifier) {
Column(modifier = inputBlockModifier) {
Text(
dialogText,
fontSize = 30.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
var text by remember { mutableStateOf("") }
TextField(
value = text,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center),
onValueChange = { text = it },
label = { Text("Label") }
)
}
}
这个问题似乎与我的类似,但答案修改了我想避免的视图内容:
现在我已经知道如何解决这个问题了,我分享我的方法作为答案
https://google.github.io/accompanist/insets/
为了开始处理问题,您需要向 gradle 添加依赖项(当前版本是 0.22.0-rc)。
dependencies {
implementation "com.google.accompanist:accompanist-insets:0.22.0-rc"
}
然后您需要使用
ProvideWindowInsets
将您的内容包装在您的活动中
setContent {
ProvideWindowInsets {
YourTheme {
//YOUR CONTENT HERE
}
}
}
此外,您需要在活动 onCreate() 函数中添加以下行:
WindowCompat.setDecorFitsSystemWindows(window, false)
更新:尽管建议使用此功能,但根据我的经验,它可能会使此方法不起作用。如果您遇到任何问题,您可能需要删除此行。
现在您的项目已设置为使用Insets
在接下来的步骤中,我将使用我在问题中提供的代码
首先,您需要用
包裹您的主栏ProvideWindowInsets(windowInsetsAnimationsEnabled = true)
然后让我们稍微修改一下修饰符,添加:
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
正如你所看到的,我的方法中的技巧是使用verticalScroll()。主栏的最终代码应如下所示:
@Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(PrimaryLight)
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
.fillMaxSize()
) {
BuildWordsScreenContents()
}
}
}
现在我们来修改
fun BuildWordsScreenContents()
中Column的修饰符
主要修改是我们通过以下方式提供屏幕高度:
.height(LocalConfiguration.current.screenHeightDp.dp)
这意味着我们的柱的高度将完美适合我们的屏幕。 因此,当键盘未打开时,该列将无法滚动
有完整代码:
@Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(PrimaryLight)
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
.fillMaxSize()
) {
BuildWordsScreenContents()
}
}
}
@Composable
fun BuildWordsScreenContents() {
Column(
Modifier
.height(LocalConfiguration.current.screenHeightDp.dp)
.padding(all = 16.dp)
) {
val inputBoxModifier = Modifier
.clip(RoundedCornerShape(10.dp))
.background(Primary)
.weight(12f)
.wrapContentHeight()
InputBlock("Dialog1", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog2", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog3", inputBoxModifier)
}
}
@Composable
fun InputBlock(dialogText: String, inputBlockModifier: Modifier) {
Column(modifier = inputBlockModifier) {
Text(
dialogText,
fontSize = 30.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
var text by remember { mutableStateOf("") }
TextField(
value = text,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center),
onValueChange = { text = it },
label = { Text("Label") }
)
}
}
最终代码允许我们向下滚动视图:
对于低于 30 的 API,您需要修改 AndroidManifest.xml 文件
在android:windowSoftInputMode="adjustResize"
中才能使其发挥作用。它不会调整组件的大小,但必须使这种方法发挥作用
清单应如下所示:
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize"
请随时给我任何如何改进我的问题的提示。 AFAIK 这个问题和 android 一样古老,我想创建一个快速教程如何管理它。快乐编码!
这是我的解决方案,使用 Compose 1.2.0 中的实验功能
在
build.gradle
(:项目)
...
ext {
compose_version = '1.2.0-beta03'
}
...
在
build.gradle
(:应用程序)
...
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.compose.foundation:foundation-layout:$compose_version"
...
}
在
AndroidManifest.xml
<activity
...
android:windowSoftInputMode="adjustResize" >
在
AuthScreen.kt
@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
@Composable
fun AuthScreen(
val focusManager = LocalFocusManager.current
val coroutineScope = rememberCoroutineScope()
// Setup the handles to items to scroll to.
val bringIntoViewRequesters = mutableListOf(remember { BringIntoViewRequester() })
repeat(6) {
bringIntoViewRequesters += remember { BringIntoViewRequester() }
}
val buttonViewRequester = remember { BringIntoViewRequester() }
fun requestBringIntoView(focusState: FocusState, viewItem: Int) {
if (focusState.isFocused) {
coroutineScope.launch {
delay(200) // needed to allow keyboard to come up first.
if (viewItem >= 2) { // force to scroll to button for lower fields
buttonViewRequester.bringIntoView()
} else {
bringIntoViewRequesters[viewItem].bringIntoView()
}
}
}
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.navigationBarsPadding()
.imePadding()
.padding(10.dp)
.verticalScroll(rememberScrollState())
) {
repeat(6) { viewItem ->
Row(
modifier = Modifier
.bringIntoViewRequester(bringIntoViewRequesters[viewItem]),
) {
TextField(
value = "",
onValueChange = {},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) }),
modifier = Modifier
.onFocusEvent { focusState ->
requestBringIntoView(focusState, viewItem)
},
)
}
}
Button(
onClick = {},
modifier = Modifier
.bringIntoViewRequester(buttonViewRequester)
) {
Text(text = "I'm Visible")
}
}
}
尝试用谷歌搜索这样的关键字:Modifier.statusBarsPadding()、systemBarsPadding()、navigationBarsPadding()。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
makeStatusBarTransparent()
//WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
Box(
Modifier
.background(Color.Blue)
.fillMaxSize()
.padding(top = 10.dp, bottom = 10.dp)
.statusBarsPadding() //systemBarsPadding
) {
//Box(Modifier.background(Color.Green).navigationBarsPadding()) {
Greeting("TopStart", Alignment.TopStart)
Greeting("BottomStart", Alignment.BottomStart)
Greeting("TopEnd", Alignment.TopEnd)
Greeting("BottomEnd", Alignment.BottomEnd)
//}
}
}
/* setContent {
MyComposeApp1Theme {
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = Color.Red) {
Box(Modifier
.fillMaxSize()
.padding(top = 34.dp)
) {
Greeting("Android")
}
}
}
}*/
}
}
@Composable
fun Greeting(name: String, contentAlignment: Alignment) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = contentAlignment
) {
Text(
text = "Hello $name!",
Modifier
.background(color = Color.Cyan)
)
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
MyComposeApp1Theme {
Greeting("Android", Alignment.TopStart)
}
}
@Suppress("DEPRECATION")
fun Activity.makeStatusBarTransparent() {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
statusBarColor = android.graphics.Color.GREEN//android.graphics.Color.TRANSPARENT
}
}
val Int.dp
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
toFloat(),
Resources.getSystem().displayMetrics
)
我确实喜欢这样:
确保您
AndroidMainifest.xml
设置:
android:windowSoftInputMode="adjustResize"
添加此功能(在屏幕内或在其他文件中):
@Composable
fun rememberImeState(): State<Boolean> {
val imeState = remember {
mutableStateOf(false)
}
val view = LocalView.current
DisposableEffect(view) {
val listener = ViewTreeObserver.OnGlobalLayoutListener {
val isKeyboardOpen = ViewCompat.getRootWindowInsets(view)
?.isVisible(WindowInsetsCompat.Type.ime()) ?: true
imeState.value = isKeyboardOpen
}
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
onDispose {
view.viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
return imeState
}
val imeState = rememberImeState()
val scrollState = rememberScrollState()
LaunchedEffect(key1 = imeState.value) {
if (imeState.value) {
scrollState.animateScrollTo(scrollState.maxValue, tween(300))
}
}
Column(
modifier = modifier.verticalScroll(scrollState),
verticalArrangement = Arrangement.Top,
horizontalAlignment = CenterHorizontally
) {
UserSelection(
onClick = { onClick(it) }
)
}