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

View file

@ -1,6 +1,5 @@
package org.oxycblt.auxio.detail package org.oxycblt.auxio.detail
import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater 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.music.MusicViewModel
import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.toColor import org.oxycblt.auxio.theme.disable
class AlbumDetailFragment : Fragment() { class AlbumDetailFragment : Fragment() {
@ -50,6 +49,8 @@ class AlbumDetailFragment : Fragment() {
} }
) )
// --- UI SETUP ---
binding.lifecycleOwner = this binding.lifecycleOwner = this
binding.detailModel = detailModel binding.detailModel = detailModel
binding.album = detailModel.currentAlbum.value!! binding.album = detailModel.currentAlbum.value!!
@ -64,6 +65,25 @@ class AlbumDetailFragment : Fragment() {
findNavController().navigateUp() 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 // If the album was shown directly from LibraryFragment, Then enable the ability to
// navigate upwards to the parent artist // navigate upwards to the parent artist
if (args.enableParentNav) { if (args.enableParentNav) {
@ -84,27 +104,6 @@ class AlbumDetailFragment : Fragment() {
binding.albumArtist.setBackgroundResource(R.drawable.ripple) 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.") Log.d(this::class.simpleName, "Fragment created.")
return binding.root return binding.root

View file

@ -1,6 +1,5 @@
package org.oxycblt.auxio.detail package org.oxycblt.auxio.detail
import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
@ -10,13 +9,12 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.toColor import org.oxycblt.auxio.theme.disable
class ArtistDetailFragment : Fragment() { class ArtistDetailFragment : Fragment() {
@ -56,19 +54,28 @@ class ArtistDetailFragment : Fragment() {
} }
) )
// --- UI SETUP ---
binding.lifecycleOwner = this binding.lifecycleOwner = this
binding.detailModel = detailModel binding.detailModel = detailModel
binding.artist = detailModel.currentArtist.value!! 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 { binding.artistAlbumRecycler.apply {
adapter = albumAdapter adapter = albumAdapter
applyDivider() applyDivider()
setHasFixedSize(true) setHasFixedSize(true)
} }
binding.artistToolbar.setNavigationOnClickListener { // --- VIEWMODEL SETUP ---
findNavController().navigateUp()
}
detailModel.artistSortMode.observe(viewLifecycleOwner) { mode -> detailModel.artistSortMode.observe(viewLifecycleOwner) { mode ->
Log.d(this::class.simpleName, "Updating sort mode to $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.") Log.d(this::class.simpleName, "Fragment created.")
return binding.root return binding.root

View file

@ -1,6 +1,5 @@
package org.oxycblt.auxio.detail package org.oxycblt.auxio.detail
import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
@ -10,13 +9,12 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.toColor import org.oxycblt.auxio.theme.disable
class GenreDetailFragment : Fragment() { class GenreDetailFragment : Fragment() {
@ -56,18 +54,29 @@ class GenreDetailFragment : Fragment() {
} }
) )
// --- UI SETUP ---
binding.lifecycleOwner = this binding.lifecycleOwner = this
binding.detailModel = detailModel binding.detailModel = detailModel
binding.genre = detailModel.currentGenre.value binding.genre = detailModel.currentGenre.value
binding.genreArtistRecycler.adapter = artistAdapter
binding.genreArtistRecycler.applyDivider()
binding.genreArtistRecycler.setHasFixedSize(true)
binding.genreToolbar.setNavigationOnClickListener { binding.genreToolbar.setNavigationOnClickListener {
findNavController().navigateUp() 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 -> detailModel.genreSortMode.observe(viewLifecycleOwner) { mode ->
Log.d(this::class.simpleName, "Updating sort mode to $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.") Log.d(this::class.simpleName, "Fragment created.")
return binding.root return binding.root

View file

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

View file

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

View file

@ -1,11 +1,13 @@
package org.oxycblt.auxio.theme package org.oxycblt.auxio.theme
import android.content.Context import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.text.SpannableString import android.text.SpannableString
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.util.TypedValue import android.util.TypedValue
import android.view.MenuItem import android.view.MenuItem
import android.widget.ImageButton
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat 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 // Apply a custom vertical divider
fun RecyclerView.applyDivider() { fun RecyclerView.applyDivider() {
val div = DividerItemDecoration( val div = DividerItemDecoration(

View file

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