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.FragmentManager
import eu.kanade.tachiyomi.data.database.models.Category
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.data.database.models.Manga
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 java.util.*
@ -85,15 +85,16 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryFragment) :
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LibraryHolder {
//depending on preferences, display a list or display a grid
if (parent.id == R.id.library_list) {
val view = parent.inflate(R.layout.item_library_list)
return LibraryListHolder(view, this, fragment)
} else {
if (parent is AutofitRecyclerView) {
val view = parent.inflate(R.layout.item_catalogue_grid).apply {
val coverHeight = parent.itemWidth / 3 * 4
card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight)
gradient.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM)
}
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)
}
/**
* 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
import android.content.res.Configuration
import android.os.Bundle
import android.support.v7.widget.GridLayoutManager
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import com.f2prateek.rx.preferences.Preference
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
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.fragment.BaseFragment
import eu.kanade.tachiyomi.ui.manga.MangaActivity
import eu.kanade.tachiyomi.util.inflate
import eu.kanade.tachiyomi.util.toast
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import kotlinx.android.synthetic.main.fragment_library_category.*
import rx.Subscription
import uy.kohesive.injekt.injectLazy
@ -50,26 +49,11 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
*/
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.
*/
private var searchSubscription: Subscription? = null
/**
* display mode
*/
private var displayAsList: Boolean = false
companion object {
/**
* 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?) {
adapter = LibraryCategoryAdapter(this)
//set up grid
recycler.setHasFixedSize(true)
(recycler.layoutManager as GridLayoutManager).recycleChildrenOnDetach = true
recycler.recycledViewPool = libraryFragment.pool
recycler.adapter = adapter
val recycler = if (preferences.libraryAsList().getOrDefault()) {
(swipe_refresh.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply {
spanCount = libraryFragment.mangaPerRow
}
} else {
(swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply {
layoutManager = LinearLayoutManager(context)
}
}
//set up list
library_list.setHasFixedSize(true)
library_list.layoutManager = LinearLayoutManager(activity)
library_list.recycledViewPool = libraryFragment.pool
(library_list.layoutManager as LinearLayoutManager).recycleChildrenOnDetach = true
library_list.adapter = adapter
(recycler.layoutManager as LinearLayoutManager).recycleChildrenOnDetach = true
recycler.recycledViewPool = libraryFragment.pool
recycler.setHasFixedSize(true)
recycler.adapter = adapter
swipe_refresh.addView(recycler)
if (libraryFragment.actionMode != null) {
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 ->
adapter.searchText = text
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) {
position = savedState.getInt(POSITION_KEY)
adapter.onRestoreInstanceState(savedState)
@ -157,9 +127,9 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
// Double the distance required to trigger sync
swipe_refresh.setDistanceToTriggerSync((2 * 64 * resources.displayMetrics.density).toInt())
swipe_refresh.setOnRefreshListener {
if (!LibraryUpdateService.isRunning(activity)) {
if (!LibraryUpdateService.isRunning(context)) {
libraryPresenter.categories.getOrNull(position)?.let {
LibraryUpdateService.start(activity, true, it)
LibraryUpdateService.start(context, true, it)
context.toast(R.string.updating_category)
}
}
@ -169,9 +139,7 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
}
override fun onDestroyView() {
numColumnsSubscription?.unsubscribe()
searchSubscription?.unsubscribe()
toggleViewSubscription?.unsubscribe()
super.onDestroyView()
}
@ -250,7 +218,7 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli
libraryPresenter.onOpenManga()
// Create a new activity with the manga.
val intent = MangaActivity.newIntent(activity, manga)
val intent = MangaActivity.newIntent(context, manga)
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.
*
@ -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.
*/

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.library
import android.app.Activity
import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import android.support.design.widget.TabLayout
import android.support.v4.view.ViewPager
@ -10,6 +11,7 @@ import android.support.v7.widget.RecyclerView
import android.support.v7.widget.SearchView
import android.view.*
import com.afollestad.materialdialogs.MaterialDialog
import com.f2prateek.rx.preferences.Preference
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
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.fragment_library.*
import nucleus.factory.RequiresPresenter
import rx.Subscription
import uy.kohesive.injekt.injectLazy
import java.io.IOException
@ -61,11 +64,6 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
*/
private var query: String? = null
/**
* Display mode of the library (list or grid mode).
*/
private var displayMode: MenuItem? = null
/**
* Action mode for manga selection.
*/
@ -87,10 +85,18 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
*/
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).
*/
val pool = RecyclerView.RecycledViewPool()
var pool = RecyclerView.RecycledViewPool()
private var numColumnsSubscription: Subscription? = null
companion object {
/**
@ -148,6 +154,12 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
} else {
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() {
@ -156,6 +168,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
}
override fun onDestroyView() {
numColumnsSubscription?.unsubscribe()
tabs.setupWithViewPager(null)
tabs.visibility = View.GONE
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 {
@ -257,27 +259,40 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
*/
private fun onFilterCheckboxChanged() {
presenter.updateLibrary()
adapter.notifyDataSetChanged()
adapter.refreshRegisteredAdapters()
activity.supportInvalidateOptionsMenu()
}
/**
* swap display mode
* Swap display mode
*/
private fun swapDisplayMode() {
presenter.swapDisplayMode()
val isListMode = presenter.displayAsList
val icon = if (isListMode)
R.drawable.ic_view_module_white_24dp
else
R.drawable.ic_view_list_white_24dp
displayMode?.setIcon(icon)
reattachAdapter()
}
/**
* 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.
@ -289,7 +304,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
// Notify the subject the query has changed.
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.
*/
abstract class LibraryHolder(private val view: View, adapter: LibraryCategoryAdapter, listener: FlexibleViewHolder.OnListItemClickListener)
: FlexibleViewHolder(view, adapter, listener) {
abstract class LibraryHolder(private val view: View,
adapter: LibraryCategoryAdapter,
listener: FlexibleViewHolder.OnListItemClickListener)
: FlexibleViewHolder(view, adapter, listener) {
/**
* 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()
/**
* display the library as a list?
*/
var displayAsList: Boolean = false
private set
companion object {
/**
* Id of the restartable that listens for library updates.
@ -95,16 +89,6 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
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.
*/
fun swapDisplayMode() {
val displayAsList = preferences.libraryAsList().getOrDefault()
preferences.libraryAsList().set(!displayAsList)
}

View file

@ -1,36 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="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>
</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" />