Fix RecyclerView height in CoordinatorLayout for non touch-mode

Any `RecyclerView`s with an app bar in a `CoordinatorLayout` would end up going off-screen due to the layout behavior implementing an offset by using a transform which would not correctly handle focusing on off-screen objects. This has now been fixed by manually adjusting height to be clipped to what is visible on the screen.
This commit is contained in:
PixelyIon 2022-03-11 16:35:41 +05:30
parent 3ae62c7fcc
commit e2cae74425
4 changed files with 60 additions and 7 deletions

View File

@ -8,7 +8,9 @@ package emu.skyline
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.view.ViewTreeObserver
import androidx.appcompat.app.AppCompatActivity
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import emu.skyline.databinding.SettingsActivityBinding
@ -33,8 +35,33 @@ class SettingsActivity : AppCompatActivity() {
setSupportActionBar(binding.titlebar.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
window.decorView.findViewById<View>(android.R.id.content).viewTreeObserver.addOnTouchModeChangeListener { isInTouchMode ->
if (!isInTouchMode) binding.titlebar.appBarLayout.setExpanded(false)
var layoutDone = false // Tracks if the layout is complete to avoid retrieving invalid attributes
binding.coordinatorLayout.viewTreeObserver.addOnTouchModeChangeListener { isTouchMode ->
val layoutUpdate = { ->
val params = binding.settings.layoutParams as CoordinatorLayout.LayoutParams
if (!isTouchMode) {
binding.titlebar.appBarLayout.setExpanded(true)
params.height = binding.coordinatorLayout.height - binding.titlebar.toolbar.height
} else {
params.height = CoordinatorLayout.LayoutParams.MATCH_PARENT
}
binding.settings.layoutParams = params
binding.settings.requestLayout()
}
if (!layoutDone) {
binding.coordinatorLayout.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
// We need to wait till the layout is done to get the correct height of the toolbar
binding.coordinatorLayout.viewTreeObserver.removeOnGlobalLayoutListener(this)
layoutUpdate()
layoutDone = true
}
})
} else {
layoutUpdate()
}
}
supportFragmentManager

View File

@ -9,7 +9,10 @@ import android.content.Intent
import android.graphics.Canvas
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.view.ViewTreeObserver
import androidx.appcompat.app.AppCompatActivity
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.marginTop
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
@ -182,13 +185,34 @@ class ControllerActivity : AppCompatActivity() {
binding.controllerList.layoutManager = layoutManager
binding.controllerList.adapter = adapter
binding.controllerList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView : RecyclerView, dx : Int, dy : Int) {
super.onScrolled(recyclerView, dx, dy)
var layoutDone = false // Tracks if the layout is complete to avoid retrieving invalid attributes
binding.coordinatorLayout.viewTreeObserver.addOnTouchModeChangeListener { isTouchMode ->
val layoutUpdate = { ->
val params = binding.controllerList.layoutParams as CoordinatorLayout.LayoutParams
if (!isTouchMode) {
binding.titlebar.appBarLayout.setExpanded(true)
params.height = binding.coordinatorLayout.height - binding.titlebar.toolbar.height
} else {
params.height = CoordinatorLayout.LayoutParams.MATCH_PARENT
}
if (layoutManager.findLastCompletelyVisibleItemPosition() == adapter.itemCount - 1) binding.titlebar.appBarLayout.setExpanded(false)
binding.controllerList.layoutParams = params
binding.controllerList.requestLayout()
}
})
if (!layoutDone) {
binding.coordinatorLayout.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
// We need to wait till the layout is done to get the correct height of the toolbar
binding.coordinatorLayout.viewTreeObserver.removeOnGlobalLayoutListener(this)
layoutUpdate()
layoutDone = true
}
})
} else {
layoutUpdate()
}
}
val dividerItemDecoration = object : DividerItemDecoration(this, DividerItemDecoration.VERTICAL) {
override fun onDraw(canvas : Canvas, parent : RecyclerView, state : RecyclerView.State) {

View File

@ -2,6 +2,7 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".input.ControllerActivity">

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">