search: improve ui

Improve the search UI by making it edge-to-edge and adding the
liftOnScroll idiom. It does come with the caveat of walking on
eggshells to get the liftOnScroll code working, but its okay. It
may be improved in the future.
This commit is contained in:
OxygenCobalt 2021-08-29 20:20:25 -06:00
parent e142c17fca
commit a4d2a8d48c
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 53 additions and 16 deletions

View file

@ -22,6 +22,7 @@ import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.annotation.MenuRes
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
@ -30,6 +31,7 @@ import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.databinding.FragmentDetailBinding
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.memberBinding
import org.oxycblt.auxio.util.applyEdge
import org.oxycblt.auxio.util.isLandscape
/**
@ -42,6 +44,10 @@ abstract class DetailFragment : Fragment() {
protected val binding by memberBinding(FragmentDetailBinding::inflate)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.applyEdge { bars ->
binding.detailAppbar.updatePadding(top = bars.top)
}
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
}

View file

@ -29,7 +29,6 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayoutMediator
import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R
@ -44,6 +43,7 @@ import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.util.applyEdge
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logE
import org.oxycblt.auxio.util.makeScrollingViewFade
/**
* The main "Launching Point" fragment of Auxio, allowing navigation to the detail
@ -74,16 +74,7 @@ class HomeFragment : Fragment() {
binding.homeAppbar.updatePadding(top = bars.top)
}
// There is basically no way to prevent the toolbar to draw under the status bar when
// it collapses, so do the next best thing and fade it out so it doesn't stick out like
// a sore thumb. As a side effect, this looks really cool.
binding.homeAppbar.addOnOffsetChangedListener(
AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
binding.homeToolbar.apply {
alpha = (height + verticalOffset) / height.toFloat()
}
}
)
binding.homeAppbar.makeScrollingViewFade(binding.homeToolbar)
binding.homeToolbar.setOnMenuItemClickListener { item ->
when (item.itemId) {

View file

@ -27,6 +27,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.ItemTouchHelper
import kotlinx.coroutines.NonDisposableHandle.parent
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentQueueBinding
import org.oxycblt.auxio.music.BaseModel
@ -105,6 +106,7 @@ class QueueFragment : Fragment() {
lastShuffle = isShuffling
binding.queueRecycler.scrollToPosition(0)
binding.queueAppbar.isLifted = false // Make sure lifted state changes.
}
}

View file

@ -24,6 +24,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.core.view.postDelayed
import androidx.core.view.updatePadding
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@ -42,9 +43,11 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.ui.newMenu
import org.oxycblt.auxio.util.applyEdge
import org.oxycblt.auxio.util.applySpans
import org.oxycblt.auxio.util.getSystemServiceSafe
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.makeScrollingViewFade
/**
* A [Fragment] that allows for the searching of the entire music library.
@ -79,6 +82,12 @@ class SearchFragment : Fragment() {
binding.lifecycleOwner = viewLifecycleOwner
binding.applyEdge { bars ->
binding.searchAppbar.updatePadding(top = bars.top)
}
binding.searchAppbar.makeScrollingViewFade(binding.searchToolbar)
binding.searchToolbar.apply {
val itemId = when (searchModel.filterMode) {
DisplayMode.SHOW_SONGS -> R.id.option_filter_songs
@ -131,18 +140,18 @@ class SearchFragment : Fragment() {
searchModel.searchResults.observe(viewLifecycleOwner) { results ->
searchAdapter.submitList(results) {
// We've just scrolled back to the top, reset the lifted state
// TODO: Maybe find a better way to keep scroll state when the search
// results didn't actually change.
binding.searchRecycler.scrollToPosition(0)
binding.searchAppbar.isLifted = false
}
if (results.isEmpty()) {
// If the data is empty, then the ability for the toolbar to collapse
// on scroll should be disabled.
binding.searchAppbar.setExpanded(true)
binding.searchRecycler.visibility = View.GONE
toolbarParams.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL
} else {
binding.searchRecycler.visibility = View.VISIBLE
toolbarParams.scrollFlags = defaultParams
}
}

View file

@ -32,11 +32,13 @@ import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import com.google.android.material.appbar.AppBarLayout
import org.oxycblt.auxio.R
/**
@ -140,6 +142,33 @@ fun @receiver:AttrRes Int.resolveAttr(context: Context): Int {
return color.resolveColor(context)
}
/**
* Make this [AppBarLayout] fade a scrolling [view] out when it collapses.
* This is mostly because I am unable to figure out how to get a collapsing view not
* to draw under the status bar in edge-to-edge mode.
*/
fun AppBarLayout.makeScrollingViewFade(view: View) {
addOnOffsetChangedListener(
AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
view.alpha = (view.height + verticalOffset) / view.height.toFloat()
}
)
}
/**
* Force-update this [AppBarLayout]'s lifted state. This is useful when the dataset changes
* and the lifted state must be updated.
*/
fun AppBarLayout.updateLiftedState(recycler: RecyclerView) {
post {
val coordinator = (parent as CoordinatorLayout)
(layoutParams as CoordinatorLayout.LayoutParams).behavior?.onNestedPreScroll(
coordinator, this, recycler, 0, 0, IntArray(2), 0
)
}
}
/**
* Apply edge-to-edge tweaks to the root of a [ViewBinding].
* @param onApply What to do when the system bar insets are provided

View file

@ -12,7 +12,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorSurface"
android:elevation="@dimen/elevation_normal">
app:liftOnScroll="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/search_toolbar"