Dynamic recyclerview inflation for the library view and better swap handling

This commit is contained in:
len 2016-07-30 23:54:32 +02:00
parent fbd2235a51
commit e95fcf6172
9 changed files with 99 additions and 158 deletions

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.library
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager import android.support.v4.app.FragmentManager
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.ui.base.adapter.SmartFragmentStatePagerAdapter import eu.kanade.tachiyomi.ui.base.adapter.SmartFragmentStatePagerAdapter

View File

@ -8,7 +8,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.util.inflate import eu.kanade.tachiyomi.util.inflate
import kotlinx.android.synthetic.main.fragment_library_category.* import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import kotlinx.android.synthetic.main.item_catalogue_grid.view.* import kotlinx.android.synthetic.main.item_catalogue_grid.view.*
import java.util.* import java.util.*
@ -85,15 +85,16 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryFragment) :
*/ */
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LibraryHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LibraryHolder {
//depending on preferences, display a list or display a grid //depending on preferences, display a list or display a grid
if (parent.id == R.id.library_list) { if (parent is AutofitRecyclerView) {
val view = parent.inflate(R.layout.item_library_list)
return LibraryListHolder(view, this, fragment)
} else {
val view = parent.inflate(R.layout.item_catalogue_grid).apply { val view = parent.inflate(R.layout.item_catalogue_grid).apply {
val coverHeight = parent.itemWidth / 3 * 4
card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight) card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight)
gradient.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM) gradient.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM)
} }
return LibraryGridHolder(view, this, fragment) return LibraryGridHolder(view, this, fragment)
} else {
val view = parent.inflate(R.layout.item_library_list)
return LibraryListHolder(view, this, fragment)
} }
} }
@ -112,10 +113,4 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryFragment) :
holder.itemView.isActivated = isSelected(position) holder.itemView.isActivated = isSelected(position)
} }
/**
* Property to return the height for the covers based on the width to keep an aspect ratio.
*/
val coverHeight: Int
get() = fragment.recycler.itemWidth / 3 * 4
} }

View File

@ -1,24 +1,23 @@
package eu.kanade.tachiyomi.ui.library package eu.kanade.tachiyomi.ui.library
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.support.v7.widget.GridLayoutManager
import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView import android.support.v7.widget.RecyclerView
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 android.view.animation.AnimationUtils
import com.f2prateek.rx.preferences.Preference
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment
import eu.kanade.tachiyomi.ui.manga.MangaActivity import eu.kanade.tachiyomi.ui.manga.MangaActivity
import eu.kanade.tachiyomi.util.inflate
import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.util.toast
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import kotlinx.android.synthetic.main.fragment_library_category.* import kotlinx.android.synthetic.main.fragment_library_category.*
import rx.Subscription import rx.Subscription
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -50,26 +49,11 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
*/ */
private var libraryMangaSubscription: Subscription? = null private var libraryMangaSubscription: Subscription? = null
/**
* Subscription of the number of manga per row.
*/
private var numColumnsSubscription: Subscription? = null
/**
* subscription to view toggle
*/
private var toggleViewSubscription: Subscription? = null
/** /**
* Subscription of the library search. * Subscription of the library search.
*/ */
private var searchSubscription: Subscription? = null private var searchSubscription: Subscription? = null
/**
* display mode
*/
private var displayAsList: Boolean = false
companion object { companion object {
/** /**
* Key to save and restore [position] from a [Bundle]. * Key to save and restore [position] from a [Bundle].
@ -97,45 +81,31 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
override fun onViewCreated(view: View, savedState: Bundle?) { override fun onViewCreated(view: View, savedState: Bundle?) {
adapter = LibraryCategoryAdapter(this) adapter = LibraryCategoryAdapter(this)
//set up grid val recycler = if (preferences.libraryAsList().getOrDefault()) {
recycler.setHasFixedSize(true) (swipe_refresh.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply {
(recycler.layoutManager as GridLayoutManager).recycleChildrenOnDetach = true spanCount = libraryFragment.mangaPerRow
recycler.recycledViewPool = libraryFragment.pool }
recycler.adapter = adapter } else {
(swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply {
layoutManager = LinearLayoutManager(context)
}
}
//set up list (recycler.layoutManager as LinearLayoutManager).recycleChildrenOnDetach = true
library_list.setHasFixedSize(true) recycler.recycledViewPool = libraryFragment.pool
library_list.layoutManager = LinearLayoutManager(activity) recycler.setHasFixedSize(true)
library_list.recycledViewPool = libraryFragment.pool recycler.adapter = adapter
(library_list.layoutManager as LinearLayoutManager).recycleChildrenOnDetach = true swipe_refresh.addView(recycler)
library_list.adapter = adapter
if (libraryFragment.actionMode != null) { if (libraryFragment.actionMode != null) {
setSelectionMode(FlexibleAdapter.MODE_MULTI) setSelectionMode(FlexibleAdapter.MODE_MULTI)
} }
numColumnsSubscription = getColumnsPreferenceForCurrentOrientation().asObservable()
.doOnNext { recycler.spanCount = it }
.skip(1)
// Set again the adapter to recalculate the covers height
.subscribe { recycler.adapter = adapter }
searchSubscription = libraryPresenter.searchSubject.subscribe { text -> searchSubscription = libraryPresenter.searchSubject.subscribe { text ->
adapter.searchText = text adapter.searchText = text
adapter.updateDataSet() adapter.updateDataSet()
} }
toggleViewSubscription = preferences.libraryAsList().asObservable()
.subscribe { onViewModeChange(it) }
if (libraryPresenter.displayAsList != displayAsList) {
library_switcher.showNext()
displayAsList = libraryPresenter.displayAsList
}
library_switcher.inAnimation = AnimationUtils.loadAnimation(activity, android.R.anim.fade_in)
library_switcher.outAnimation = AnimationUtils.loadAnimation(activity, android.R.anim.fade_out)
if (savedState != null) { if (savedState != null) {
position = savedState.getInt(POSITION_KEY) position = savedState.getInt(POSITION_KEY)
adapter.onRestoreInstanceState(savedState) adapter.onRestoreInstanceState(savedState)
@ -157,9 +127,9 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
// Double the distance required to trigger sync // Double the distance required to trigger sync
swipe_refresh.setDistanceToTriggerSync((2 * 64 * resources.displayMetrics.density).toInt()) swipe_refresh.setDistanceToTriggerSync((2 * 64 * resources.displayMetrics.density).toInt())
swipe_refresh.setOnRefreshListener { swipe_refresh.setOnRefreshListener {
if (!LibraryUpdateService.isRunning(activity)) { if (!LibraryUpdateService.isRunning(context)) {
libraryPresenter.categories.getOrNull(position)?.let { libraryPresenter.categories.getOrNull(position)?.let {
LibraryUpdateService.start(activity, true, it) LibraryUpdateService.start(context, true, it)
context.toast(R.string.updating_category) context.toast(R.string.updating_category)
} }
} }
@ -169,9 +139,7 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
} }
override fun onDestroyView() { override fun onDestroyView() {
numColumnsSubscription?.unsubscribe()
searchSubscription?.unsubscribe() searchSubscription?.unsubscribe()
toggleViewSubscription?.unsubscribe()
super.onDestroyView() super.onDestroyView()
} }
@ -250,7 +218,7 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
libraryPresenter.onOpenManga() libraryPresenter.onOpenManga()
// Create a new activity with the manga. // Create a new activity with the manga.
val intent = MangaActivity.newIntent(activity, manga) val intent = MangaActivity.newIntent(context, manga)
startActivity(intent) startActivity(intent)
} }
@ -282,18 +250,6 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
} }
} }
/**
* Returns a preference for the number of manga per row based on the current orientation.
*
* @return the preference.
*/
fun getColumnsPreferenceForCurrentOrientation(): Preference<Int> {
return if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT)
libraryPresenter.preferences.portraitColumns()
else
libraryPresenter.preferences.landscapeColumns()
}
/** /**
* Sets the mode for the adapter. * Sets the mode for the adapter.
* *
@ -306,15 +262,6 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
} }
} }
fun onViewModeChange(isList: Boolean) {
//do nothing if the display does not need to change
if (isList == displayAsList) return
//else change view and display mode
library_switcher.showNext()
displayAsList = isList
}
/** /**
* Property to get the library fragment. * Property to get the library fragment.
*/ */

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.library
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.support.design.widget.TabLayout import android.support.design.widget.TabLayout
import android.support.v4.view.ViewPager import android.support.v4.view.ViewPager
@ -10,6 +11,7 @@ import android.support.v7.widget.RecyclerView
import android.support.v7.widget.SearchView import android.support.v7.widget.SearchView
import android.view.* import android.view.*
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.f2prateek.rx.preferences.Preference
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
@ -24,6 +26,7 @@ import eu.kanade.tachiyomi.util.toast
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_library.* import kotlinx.android.synthetic.main.fragment_library.*
import nucleus.factory.RequiresPresenter import nucleus.factory.RequiresPresenter
import rx.Subscription
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.IOException import java.io.IOException
@ -61,11 +64,6 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
*/ */
private var query: String? = null private var query: String? = null
/**
* Display mode of the library (list or grid mode).
*/
private var displayMode: MenuItem? = null
/** /**
* Action mode for manga selection. * Action mode for manga selection.
*/ */
@ -87,10 +85,18 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
*/ */
var isFilterUnread = false var isFilterUnread = false
/**
* Number of manga per row in grid mode.
*/
var mangaPerRow = 0
private set
/** /**
* A pool to share view holders between all the registered categories (fragments). * A pool to share view holders between all the registered categories (fragments).
*/ */
val pool = RecyclerView.RecycledViewPool() var pool = RecyclerView.RecycledViewPool()
private var numColumnsSubscription: Subscription? = null
companion object { companion object {
/** /**
@ -148,6 +154,12 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
} else { } else {
activeCategory = presenter.preferences.lastUsedCategory().getOrDefault() activeCategory = presenter.preferences.lastUsedCategory().getOrDefault()
} }
numColumnsSubscription = getColumnsPreferenceForCurrentOrientation().asObservable()
.doOnNext { mangaPerRow = it }
.skip(1)
// Set again the adapter to recalculate the covers height
.subscribe { reattachAdapter() }
} }
override fun onResume() { override fun onResume() {
@ -156,6 +168,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
} }
override fun onDestroyView() { override fun onDestroyView() {
numColumnsSubscription?.unsubscribe()
tabs.setupWithViewPager(null) tabs.setupWithViewPager(null)
tabs.visibility = View.GONE tabs.visibility = View.GONE
super.onDestroyView() super.onDestroyView()
@ -197,17 +210,6 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
} }
}) })
//set the icon for the display mode button
displayMode = menu.findItem(R.id.action_library_display_mode).apply {
val icon = if (preferences.libraryAsList().getOrDefault())
R.drawable.ic_view_module_white_24dp
else
R.drawable.ic_view_list_white_24dp
setIcon(icon)
}
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -257,27 +259,40 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
*/ */
private fun onFilterCheckboxChanged() { private fun onFilterCheckboxChanged() {
presenter.updateLibrary() presenter.updateLibrary()
adapter.notifyDataSetChanged()
adapter.refreshRegisteredAdapters() adapter.refreshRegisteredAdapters()
activity.supportInvalidateOptionsMenu() activity.supportInvalidateOptionsMenu()
} }
/** /**
* swap display mode * Swap display mode
*/ */
private fun swapDisplayMode() { private fun swapDisplayMode() {
presenter.swapDisplayMode() presenter.swapDisplayMode()
val isListMode = presenter.displayAsList reattachAdapter()
val icon = if (isListMode)
R.drawable.ic_view_module_white_24dp
else
R.drawable.ic_view_list_white_24dp
displayMode?.setIcon(icon)
} }
/**
* Reattaches the adapter to the view pager to recreate fragments
*/
private fun reattachAdapter() {
pool.clear()
pool = RecyclerView.RecycledViewPool()
val position = view_pager.currentItem
view_pager.adapter = adapter
view_pager.currentItem = position
}
/**
* Returns a preference for the number of manga per row based on the current orientation.
*
* @return the preference.
*/
private fun getColumnsPreferenceForCurrentOrientation(): Preference<Int> {
return if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT)
preferences.portraitColumns()
else
preferences.landscapeColumns()
}
/** /**
* Updates the query. * Updates the query.
@ -289,7 +304,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
// Notify the subject the query has changed. // Notify the subject the query has changed.
if (isResumed) { if (isResumed) {
presenter.searchSubject?.onNext(query) presenter.searchSubject.onNext(query)
} }
} }

View File

@ -11,8 +11,10 @@ import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
* @param listener a listener to react to the single tap and long tap events. * @param listener a listener to react to the single tap and long tap events.
*/ */
abstract class LibraryHolder(private val view: View, adapter: LibraryCategoryAdapter, listener: FlexibleViewHolder.OnListItemClickListener) abstract class LibraryHolder(private val view: View,
: FlexibleViewHolder(view, adapter, listener) { adapter: LibraryCategoryAdapter,
listener: FlexibleViewHolder.OnListItemClickListener)
: FlexibleViewHolder(view, adapter, listener) {
/** /**
* Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this

View File

@ -71,12 +71,6 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
*/ */
val downloadManager: DownloadManager by injectLazy() val downloadManager: DownloadManager by injectLazy()
/**
* display the library as a list?
*/
var displayAsList: Boolean = false
private set
companion object { companion object {
/** /**
* Id of the restartable that listens for library updates. * Id of the restartable that listens for library updates.
@ -95,16 +89,6 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
start(GET_LIBRARY) start(GET_LIBRARY)
} }
add(preferences.libraryAsList().asObservable().subscribe { setDisplayMode(it) })
}
/**
* Sets the display mode
*
* @param asList display as list or not
*/
fun setDisplayMode(asList: Boolean) {
displayAsList = asList
} }
/** /**
@ -305,6 +289,7 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
* Changes the active display mode. * Changes the active display mode.
*/ */
fun swapDisplayMode() { fun swapDisplayMode() {
val displayAsList = preferences.libraryAsList().getOrDefault()
preferences.libraryAsList().set(!displayAsList) preferences.libraryAsList().set(!displayAsList)
} }

View File

@ -1,36 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout
xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout <android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh" android:id="@+id/swipe_refresh"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ViewSwitcher
android:id="@+id/library_switcher"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<eu.kanade.tachiyomi.widget.AutofitRecyclerView
android:id="@+id/recycler"
style="@style/Theme.Widget.GridView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="140dp"
tools:listitem="@layout/item_catalogue_grid"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/library_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/material_component_lists_padding_above_list"
tools:listitem="@layout/item_library_list"/>
</ViewSwitcher>
</android.support.v4.widget.SwipeRefreshLayout> </android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout> </FrameLayout>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.widget.AutofitRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/library_grid"
style="@style/Theme.Widget.GridView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="140dp"
tools:listitem="@layout/item_catalogue_grid" />

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/library_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/material_component_lists_padding_above_list"
tools:listitem="@layout/item_library_list" />