Standardize fragment creation

Standardize the code layout for fragment creation.
This commit is contained in:
OxygenCobalt 2020-10-04 10:53:03 -06:00
parent 711d9a0b00
commit 7928347f9b
8 changed files with 133 additions and 118 deletions

View file

@ -33,10 +33,6 @@ class MainFragment : Fragment() {
): View? {
val binding = FragmentMainBinding.inflate(inflater)
binding.lifecycleOwner = viewLifecycleOwner
binding.mainViewPager.adapter = PagerAdapter()
val colorActive = accent.first.toColor(requireContext())
val colorInactive = getTransparentAccent(
requireContext(),
@ -44,6 +40,11 @@ class MainFragment : Fragment() {
getInactiveAlpha(accent.first)
)
// --- UI SETUP ---
binding.lifecycleOwner = viewLifecycleOwner
binding.mainViewPager.adapter = PagerAdapter()
// Link the ViewPager & Tab View
TabLayoutMediator(binding.mainTabs, binding.mainViewPager) { tab, position ->
tab.icon = ContextCompat.getDrawable(requireContext(), tabIcons[position])

View file

@ -1,6 +1,5 @@
package org.oxycblt.auxio.detail
import android.content.res.ColorStateList
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
@ -16,7 +15,7 @@ import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.toColor
import org.oxycblt.auxio.theme.disable
class AlbumDetailFragment : Fragment() {
@ -50,6 +49,8 @@ class AlbumDetailFragment : Fragment() {
}
)
// --- UI SETUP ---
binding.lifecycleOwner = this
binding.detailModel = detailModel
binding.album = detailModel.currentAlbum.value!!
@ -64,6 +65,25 @@ class AlbumDetailFragment : Fragment() {
findNavController().navigateUp()
}
// Don't enable the sort button if there's only one song [or less]
if (detailModel.currentAlbum.value!!.numSongs < 2) {
binding.albumSortButton.disable(requireContext())
}
// -- VIEWMODEL SETUP ---
detailModel.albumSortMode.observe(viewLifecycleOwner) { mode ->
Log.d(this::class.simpleName, "Updating sort mode to $mode")
// Update the current sort icon
binding.albumSortButton.setImageResource(mode.iconRes)
// Then update the sort mode of the album adapter.
songAdapter.submitList(
mode.getSortedSongList(detailModel.currentAlbum.value!!.songs)
)
}
// If the album was shown directly from LibraryFragment, Then enable the ability to
// navigate upwards to the parent artist
if (args.enableParentNav) {
@ -84,27 +104,6 @@ class AlbumDetailFragment : Fragment() {
binding.albumArtist.setBackgroundResource(R.drawable.ripple)
}
detailModel.albumSortMode.observe(viewLifecycleOwner) { mode ->
Log.d(this::class.simpleName, "Updating sort mode to $mode")
// Update the current sort icon
binding.albumSortButton.setImageResource(mode.iconRes)
// Then update the sort mode of the album adapter.
songAdapter.submitList(
mode.getSortedSongList(detailModel.currentAlbum.value!!.songs)
)
}
// Don't enable the sort button if there's only one song [or less]
if (detailModel.currentAlbum.value!!.numSongs < 2) {
binding.albumSortButton.imageTintList = ColorStateList.valueOf(
R.color.inactive_color.toColor(requireContext())
)
binding.albumSortButton.isEnabled = false
}
Log.d(this::class.simpleName, "Fragment created.")
return binding.root

View file

@ -1,6 +1,5 @@
package org.oxycblt.auxio.detail
import android.content.res.ColorStateList
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
@ -10,13 +9,12 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.toColor
import org.oxycblt.auxio.theme.disable
class ArtistDetailFragment : Fragment() {
@ -56,19 +54,28 @@ class ArtistDetailFragment : Fragment() {
}
)
// --- UI SETUP ---
binding.lifecycleOwner = this
binding.detailModel = detailModel
binding.artist = detailModel.currentArtist.value!!
binding.artistToolbar.setNavigationOnClickListener {
findNavController().navigateUp()
}
// Disable the sort button if there is only one album [Or less]
if (detailModel.currentArtist.value!!.numAlbums < 2) {
binding.artistSortButton.disable(requireContext())
}
binding.artistAlbumRecycler.apply {
adapter = albumAdapter
applyDivider()
setHasFixedSize(true)
}
binding.artistToolbar.setNavigationOnClickListener {
findNavController().navigateUp()
}
// --- VIEWMODEL SETUP ---
detailModel.artistSortMode.observe(viewLifecycleOwner) { mode ->
Log.d(this::class.simpleName, "Updating sort mode to $mode")
@ -82,15 +89,6 @@ class ArtistDetailFragment : Fragment() {
)
}
// Don't enable the sort button if there is only one album [Or less]
if (detailModel.currentArtist.value!!.numAlbums < 2) {
binding.artistSortButton.imageTintList = ColorStateList.valueOf(
R.color.inactive_color.toColor(requireContext())
)
binding.artistSortButton.isEnabled = false
}
Log.d(this::class.simpleName, "Fragment created.")
return binding.root

View file

@ -1,6 +1,5 @@
package org.oxycblt.auxio.detail
import android.content.res.ColorStateList
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
@ -10,13 +9,12 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.toColor
import org.oxycblt.auxio.theme.disable
class GenreDetailFragment : Fragment() {
@ -56,18 +54,29 @@ class GenreDetailFragment : Fragment() {
}
)
// --- UI SETUP ---
binding.lifecycleOwner = this
binding.detailModel = detailModel
binding.genre = detailModel.currentGenre.value
binding.genreArtistRecycler.adapter = artistAdapter
binding.genreArtistRecycler.applyDivider()
binding.genreArtistRecycler.setHasFixedSize(true)
binding.genreToolbar.setNavigationOnClickListener {
findNavController().navigateUp()
}
// Disable the sort button if there is only one artist [Or less]
if (detailModel.currentGenre.value!!.numArtists < 2) {
binding.genreSortButton.disable(requireContext())
}
binding.genreArtistRecycler.apply {
adapter = artistAdapter
applyDivider()
setHasFixedSize(true)
}
// --- VIEWMODEL SETUP ---
detailModel.genreSortMode.observe(viewLifecycleOwner) { mode ->
Log.d(this::class.simpleName, "Updating sort mode to $mode")
@ -80,15 +89,6 @@ class GenreDetailFragment : Fragment() {
)
}
// Don't enable the sort button if there is only one artist [Or less]
if (detailModel.currentGenre.value!!.numArtists < 2) {
binding.genreSortButton.imageTintList = ColorStateList.valueOf(
R.color.inactive_color.toColor(requireContext())
)
binding.genreSortButton.isEnabled = false
}
Log.d(this::class.simpleName, "Fragment created.")
return binding.root

View file

@ -6,7 +6,6 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.view.forEach
@ -50,70 +49,74 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
navToItem(it)
}
// Toolbar setup
binding.libraryToolbar.overflowIcon = ContextCompat.getDrawable(
requireContext(), R.drawable.ic_sort_none
)
// --- UI SETUP ---
binding.libraryToolbar.menu.apply {
val item = findItem(R.id.action_search)
val searchView = item.actionView as SearchView
binding.libraryToolbar.apply {
overflowIcon = ContextCompat.getDrawable(
requireContext(), R.drawable.ic_sort_none
)
searchView.findViewById<ImageView>(androidx.appcompat.R.id.search_go_btn)
.setImageResource(R.drawable.ic_back)
setOnMenuItemClickListener {
if (it.itemId != R.id.action_search) {
libraryModel.updateSortMode(it)
} else {
// Do whatever this is in order to make the SearchView focusable.
(it.actionView as SearchView).isIconified = false
searchView.setOnQueryTextListener(this@LibraryFragment)
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
libraryModel.updateSearchFocusStatus(hasFocus)
item.isVisible = !hasFocus
// Then also do a basic animation
TransitionManager.beginDelayedTransition(
binding.libraryToolbar, Fade()
)
it.expandActionView()
}
true
}
item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
// When opened, update the adapter to the SearchAdapter, and make the sorting
// group invisible. The query is also reset, as if the Auxio process is
// killed in the background while still on the search adapter, then the
// search query will stick around if its opened again
// TODO: Couldn't you just try to restore the search state on restart?
binding.libraryRecycler.adapter = searchAdapter
setGroupVisible(R.id.group_sorting, false)
libraryModel.resetQuery()
menu.apply {
val item = findItem(R.id.action_search)
val searchView = item.actionView as SearchView
return true
searchView.setOnQueryTextListener(this@LibraryFragment)
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
libraryModel.updateSearchFocusStatus(hasFocus)
item.isVisible = !hasFocus
}
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
// When closed, make the sorting icon visible again, change back to
// LibraryAdapter, and reset the query.
binding.libraryRecycler.adapter = libraryAdapter
setGroupVisible(R.id.group_sorting, true)
libraryModel.resetQuery()
item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
// When opened, update the adapter to the SearchAdapter, and make the
// sorting group invisible. The query is also reset, as if the Auxio process
// is killed in the background while still on the search adapter, then the
// search query will stick around if its opened again
// TODO: Couldn't you just try to restore the search state on restart?
binding.libraryRecycler.adapter = searchAdapter
setGroupVisible(R.id.group_sorting, false)
libraryModel.resetQuery()
return true
}
})
}
return true
}
binding.libraryToolbar.setOnMenuItemClickListener {
if (it.itemId != R.id.action_search) {
libraryModel.updateSortMode(it)
} else {
// Do whatever this is in order to make the SearchView focusable.
(it.actionView as SearchView).isIconified = false
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
// When closed, make the sorting icon visible again, change back to
// LibraryAdapter, and reset the query.
binding.libraryRecycler.adapter = libraryAdapter
setGroupVisible(R.id.group_sorting, true)
libraryModel.resetQuery()
// Then also do a basic animation
TransitionManager.beginDelayedTransition(
binding.libraryToolbar, Fade()
)
it.expandActionView()
return true
}
})
}
true
}
// RecyclerView setup
binding.libraryRecycler.adapter = libraryAdapter
binding.libraryRecycler.applyDivider()
binding.libraryRecycler.setHasFixedSize(true)
binding.libraryRecycler.apply {
adapter = libraryAdapter
applyDivider()
setHasFixedSize(true)
}
// --- VIEWMODEL SETUP ---
libraryModel.sortMode.observe(viewLifecycleOwner) { mode ->
Log.d(this::class.simpleName, "Updating sort mode to $mode")

View file

@ -25,14 +25,18 @@ class SongsFragment : Fragment() {
): View? {
val binding = FragmentSongsBinding.inflate(inflater)
binding.songRecycler.adapter = SongAdapter(
musicModel.songs.value!!,
ClickListener { song ->
Log.d(this::class.simpleName, song.name)
}
)
binding.songRecycler.applyDivider()
binding.songRecycler.setHasFixedSize(true)
// --- UI SETUP ---
binding.songRecycler.apply {
adapter = SongAdapter(
musicModel.songs.value!!,
ClickListener { song ->
Log.d(this::class.simpleName, song.name)
}
)
applyDivider()
setHasFixedSize(true)
}
Log.d(this::class.simpleName, "Fragment created.")

View file

@ -1,11 +1,13 @@
package org.oxycblt.auxio.theme
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.ColorDrawable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.util.TypedValue
import android.view.MenuItem
import android.widget.ImageButton
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
@ -91,6 +93,14 @@ fun MenuItem.applyColor(color: Int) {
}
}
fun ImageButton.disable(context: Context) {
imageTintList = ColorStateList.valueOf(
R.color.inactive_color.toColor(context)
)
isEnabled = false
}
// Apply a custom vertical divider
fun RecyclerView.applyDivider() {
val div = DividerItemDecoration(

View file

@ -18,7 +18,7 @@
<item name="android:textColor">?android:attr/colorPrimary</item>
</style>
<style name="Toolbar.SearchViewStyle" parent="Widget.AppCompat.SearchView"/>
<style name="Toolbar.SearchViewStyle" parent="Widget.AppCompat.SearchView" />
<style name="DetailHeader">
<item name="android:textAppearance">?android:attr/textAppearanceLarge</item>