Add bottom menu

Add a tab menu on the bottom of the screen that can switch between all fragments on the ViewPager.
This commit is contained in:
OxygenCobalt 2020-08-31 18:55:25 -06:00
parent 736e335ccf
commit d5e6b813a9
12 changed files with 161 additions and 30 deletions

View file

@ -66,6 +66,9 @@ dependencies {
// Image loading // Image loading
implementation 'io.coil-kt:coil:0.12.0' implementation 'io.coil-kt:coil:0.12.0'
// Material
implementation 'com.google.android.material:material:1.3.0-alpha02'
// Lint // Lint
ktlint "com.pinterest:ktlint:0.37.2" ktlint "com.pinterest:ktlint:0.37.2"
} }

View file

@ -3,7 +3,6 @@ package org.oxycblt.auxio
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -11,7 +10,7 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) // AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
Log.d(this::class.simpleName, "Activity Created.") Log.d(this::class.simpleName, "Activity Created.")
} }

View file

@ -5,13 +5,19 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import org.oxycblt.auxio.databinding.FragmentMainBinding import org.oxycblt.auxio.databinding.FragmentMainBinding
import org.oxycblt.auxio.library.LibraryFragment import org.oxycblt.auxio.library.LibraryFragment
import org.oxycblt.auxio.songs.SongsFragment import org.oxycblt.auxio.songs.SongsFragment
import org.oxycblt.auxio.theme.getAccentTransparency
import org.oxycblt.auxio.theme.getDeselectedTransparency
import org.oxycblt.auxio.theme.toColor
class MainFragment : Fragment() { class MainFragment : Fragment() {
@ -20,6 +26,21 @@ class MainFragment : Fragment() {
private val libraryFragment: LibraryFragment by lazy { LibraryFragment() } private val libraryFragment: LibraryFragment by lazy { LibraryFragment() }
private val songsFragment: SongsFragment by lazy { SongsFragment() } private val songsFragment: SongsFragment by lazy { SongsFragment() }
private val colorSelected: Int by lazy {
R.color.blue.toColor(requireContext())
}
private val colorDeselected: Int by lazy {
getAccentTransparency(
requireContext(), R.color.blue, getDeselectedTransparency(R.color.blue)
)
}
private val tabIcons = listOf(
R.drawable.ic_library,
R.drawable.ic_music
)
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -32,11 +53,54 @@ class MainFragment : Fragment() {
val adapter = PagerAdapter(requireActivity()) val adapter = PagerAdapter(requireActivity())
binding.viewPager.adapter = adapter binding.viewPager.adapter = adapter
// Link the ViewPager & Tab View
TabLayoutMediator(binding.tabs, binding.viewPager) { tab, position ->
tab.icon = ContextCompat.getDrawable(requireContext(), tabIcons[position])
// Set the icon tint to deselected if its not the default tab
if (position > 0) {
tab.icon?.setTint(colorDeselected)
}
// Init the fragment
fragmentAt(position)
}.attach()
// Set up the selected/deselected colors
binding.tabs.addOnTabSelectedListener(
object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
tab.icon?.setTint(colorSelected)
}
override fun onTabUnselected(tab: TabLayout.Tab) {
tab.icon?.setTint(colorDeselected)
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
}
)
binding.tabs.getTabAt(
binding.viewPager.offscreenPageLimit
)
Log.d(this::class.simpleName, "Fragment Created.") Log.d(this::class.simpleName, "Fragment Created.")
return binding.root return binding.root
} }
private fun fragmentAt(position: Int): Fragment {
return when (position) {
0 -> libraryFragment
1 -> songsFragment
else -> libraryFragment
}
}
private inner class PagerAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) { private inner class PagerAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
override fun getItemCount(): Int = shownFragments.size override fun getItemCount(): Int = shownFragments.size
@ -44,12 +108,7 @@ class MainFragment : Fragment() {
Log.d(this::class.simpleName, "Switching to fragment $position.") Log.d(this::class.simpleName, "Switching to fragment $position.")
if (shownFragments.contains(position)) { if (shownFragments.contains(position)) {
return when (position) { return fragmentAt(position)
0 -> libraryFragment
1 -> songsFragment
else -> libraryFragment
}
} }
// Not sure how this would happen but it might // Not sure how this would happen but it might

View file

@ -79,7 +79,7 @@ fun ImageView.getCoverArt(any: Any) {
load(uri) { load(uri) {
crossfade(true) crossfade(true)
placeholder(android.R.color.transparent) placeholder(android.R.color.transparent)
error(R.drawable.music_icon) error(R.drawable.ic_music)
} }
} }

View file

@ -137,6 +137,8 @@ class MusicSorter(
unknownGenre.numArtists = artists.size unknownGenre.numArtists = artists.size
genres.add(unknownGenre)
Log.d( Log.d(
this::class.simpleName, this::class.simpleName,
"${unknownArtists.size} albums were placed into an unknown genre." "${unknownArtists.size} albums were placed into an unknown genre."

View file

@ -1,12 +1,11 @@
package org.oxycblt.auxio.recycler package org.oxycblt.auxio.recycler
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.theme.getAccentTransparency
import org.oxycblt.auxio.theme.getDayNightTransparency
// Apply a custom vertical divider // Apply a custom vertical divider
fun RecyclerView.applyDivider() { fun RecyclerView.applyDivider() {
@ -17,21 +16,11 @@ fun RecyclerView.applyDivider() {
div.setDrawable( div.setDrawable(
ColorDrawable( ColorDrawable(
getDividerDrawable(this) getAccentTransparency(
context, R.color.divider_color, getDayNightTransparency()
)
) )
) )
addItemDecoration(div) addItemDecoration(div)
} }
private fun getDividerDrawable(recycler: RecyclerView): Int {
val isDark = AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES
// Depending on the theme use a different opacity for the divider
val alpha = if (isDark) 45 else 85
return ColorUtils.setAlphaComponent(
ContextCompat.getColor(recycler.context, R.color.divider_color),
alpha
)
}

View file

@ -0,0 +1,33 @@
package org.oxycblt.auxio.theme
import android.content.Context
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import org.oxycblt.auxio.R
fun getDayNightTransparency(): Int {
val isDark = AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES
// Depending on the theme use a different opacity for the divider
return if (isDark) 45 else 85
}
fun getDeselectedTransparency(color: Int): Int {
return if (color == R.color.yellow) 100 else 150
}
fun getAccentTransparency(context: Context, color: Int, alpha: Int): Int {
return ColorUtils.setAlphaComponent(
ContextCompat.getColor(context, color),
alpha
)
}
fun Int.toColor(context: Context): Int {
return try {
ContextCompat.getColor(context, this)
} catch (e: Exception) {
ContextCompat.getColor(context, android.R.color.white)
}
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/colorPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,7h-3v5.5c0,1.38 -1.12,2.5 -2.5,2.5S10,13.88 10,12.5s1.12,-2.5 2.5,-2.5c0.57,0 1.08,0.19 1.5,0.51L14,5h4v2zM4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6z" />
</vector>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:gravity="center"
android:left="16dp"
android:right="16dp">
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="4dp" />
<size android:height="2dp" />
</shape>
</item>
</layer-list>

View file

@ -1,13 +1,32 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <layout 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">
<FrameLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:orientation="vertical">
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager" android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="0dp"
</FrameLayout> android:layout_weight="1" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_menu_size"
android:layout_gravity="bottom"
android:background="?android:attr/windowBackground"
app:tabIndicatorColor="?android:attr/colorPrimary"
app:tabGravity="fill"
app:tabMode="fixed"
app:tabIconTint="?android:attr/colorPrimary"
app:tabIconTintMode="src_in"
app:tabIndicator="@drawable/indicator"
app:tabRippleColor="?android:attr/colorPrimary" />
</LinearLayout>
</layout> </layout>

View file

@ -10,5 +10,7 @@
<dimen name="cover_size_compact">44dp</dimen> <dimen name="cover_size_compact">44dp</dimen>
<dimen name="cover_size_normal">56dp</dimen> <dimen name="cover_size_normal">56dp</dimen>
<dimen name="tab_menu_size">38dp</dimen>
<dimen name="elevation_normal">4dp</dimen> <dimen name="elevation_normal">4dp</dimen>
</resources> </resources>