Pagination loading indicator

pull/26/head
Koitharu 6 years ago
parent 3539e6a892
commit e01b74ee3d

@ -12,13 +12,15 @@ abstract class BoundsScrollListener(private val offsetTop: Int, private val offs
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
val layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return val layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
if (firstVisibleItemPosition == RecyclerView.NO_POSITION) {
return
}
if (firstVisibleItemPosition <= offsetTop) { if (firstVisibleItemPosition <= offsetTop) {
onScrolledToStart(recyclerView) onScrolledToStart(recyclerView)
return
} }
val visibleItemCount = layoutManager.childCount val visibleItemCount = layoutManager.childCount
val totalItemCount = layoutManager.itemCount val totalItemCount = layoutManager.itemCount
if (visibleItemCount + firstVisibleItemPosition >= totalItemCount - offsetBottom && firstVisibleItemPosition >= 0) { if (visibleItemCount + firstVisibleItemPosition >= totalItemCount - offsetBottom) {
onScrolledToEnd(recyclerView) onScrolledToEnd(recyclerView)
} }
} }

@ -2,24 +2,31 @@ package org.koitharu.kotatsu.ui.common.list
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
class PaginationScrollListener(offset: Int, private val callback: Callback) : BoundsScrollListener(0, offset) { class PaginationScrollListener(offset: Int, private val callback: Callback) :
BoundsScrollListener(0, offset) {
private var lastTotalCount = 0 private var lastTotalCount = 0
override fun onScrolledToStart(recyclerView: RecyclerView) = Unit override fun onScrolledToStart(recyclerView: RecyclerView) = Unit
override fun onScrolledToEnd(recyclerView: RecyclerView) { override fun onScrolledToEnd(recyclerView: RecyclerView) {
val total = recyclerView.adapter?.itemCount ?: 0 val total = callback.getItemsCount()
if (total > lastTotalCount) { if (total > lastTotalCount) {
callback.onRequestMoreItems(total)
lastTotalCount = total lastTotalCount = total
callback.onRequestMoreItems(total)
} else if (total < lastTotalCount) { } else if (total < lastTotalCount) {
lastTotalCount = total lastTotalCount = total
} }
} }
fun reset() {
lastTotalCount = 0
}
interface Callback { interface Callback {
fun onRequestMoreItems(offset: Int) fun onRequestMoreItems(offset: Int)
fun getItemsCount(): Int
} }
} }

@ -4,7 +4,7 @@ import android.view.ViewGroup
class ProgressBarAdapter : BaseRecyclerAdapter<Boolean, Unit>() { class ProgressBarAdapter : BaseRecyclerAdapter<Boolean, Unit>() {
var isVisible: Boolean var isProgressVisible: Boolean
get() = dataSet.isNotEmpty() get() = dataSet.isNotEmpty()
set(value) { set(value) {
if (value == dataSet.isEmpty()) { if (value == dataSet.isEmpty()) {
@ -20,5 +20,5 @@ class ProgressBarAdapter : BaseRecyclerAdapter<Boolean, Unit>() {
override fun onCreateViewHolder(parent: ViewGroup) = ProgressBarHolder(parent) override fun onCreateViewHolder(parent: ViewGroup) = ProgressBarHolder(parent)
override fun onGetItemId(item: Boolean) = 1L override fun onGetItemId(item: Boolean) = -1L
} }

@ -8,11 +8,27 @@ import org.koitharu.kotatsu.R
class ProgressBarHolder(parent: ViewGroup) : class ProgressBarHolder(parent: ViewGroup) :
BaseViewHolder<Boolean, Unit>(parent, R.layout.item_progress) { BaseViewHolder<Boolean, Unit>(parent, R.layout.item_progress) {
private var pendingVisibility: Int = View.GONE
private val action = Runnable {
progressBar?.visibility = pendingVisibility
pendingVisibility = View.GONE
}
override fun onBind(data: Boolean, extra: Unit) { override fun onBind(data: Boolean, extra: Unit) {
progressBar.visibility = if (data) { val visibility = if (data) {
View.VISIBLE View.VISIBLE
} else { } else {
View.INVISIBLE View.INVISIBLE
} }
if (visibility != progressBar.visibility && visibility != pendingVisibility) {
progressBar.removeCallbacks(action)
pendingVisibility = visibility
progressBar.postDelayed(action, 400)
}
}
override fun onRecycled() {
progressBar.removeCallbacks(action)
super.onRecycled()
} }
} }

@ -9,10 +9,7 @@ import androidx.core.view.GravityCompat
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.*
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_list.* import kotlinx.android.synthetic.main.fragment_list.*
@ -28,6 +25,7 @@ import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.ui.common.BaseFragment import org.koitharu.kotatsu.ui.common.BaseFragment
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
import org.koitharu.kotatsu.ui.common.list.ProgressBarAdapter
import org.koitharu.kotatsu.ui.common.list.decor.ItemTypeDividerDecoration import org.koitharu.kotatsu.ui.common.list.decor.ItemTypeDividerDecoration
import org.koitharu.kotatsu.ui.common.list.decor.SectionItemDecoration import org.koitharu.kotatsu.ui.common.list.decor.SectionItemDecoration
import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration
@ -38,14 +36,19 @@ import org.koitharu.kotatsu.utils.UiUtils
import org.koitharu.kotatsu.utils.ext.* import org.koitharu.kotatsu.utils.ext.*
abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list), abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
MangaListView<E>, MangaListView<E>, PaginationScrollListener.Callback, OnRecyclerItemClickListener<Manga>,
PaginationScrollListener.Callback, OnRecyclerItemClickListener<Manga>,
SharedPreferences.OnSharedPreferenceChangeListener, OnFilterChangedListener, SharedPreferences.OnSharedPreferenceChangeListener, OnFilterChangedListener,
SectionItemDecoration.Callback, SwipeRefreshLayout.OnRefreshListener { SectionItemDecoration.Callback, SwipeRefreshLayout.OnRefreshListener {
private val settings by inject<AppSettings>() private val settings by inject<AppSettings>()
private val adapterConfig = MergeAdapter.Config.Builder()
.setIsolateViewTypes(true)
.setStableIdMode(MergeAdapter.Config.StableIdMode.SHARED_STABLE_IDS)
.build()
private var adapter: MangaListAdapter? = null private var adapter: MangaListAdapter? = null
private var progressAdapter: ProgressBarAdapter? = null
private var paginationListener : PaginationScrollListener? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -56,10 +59,11 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
adapter = MangaListAdapter(this) adapter = MangaListAdapter(this)
progressAdapter = ProgressBarAdapter()
paginationListener = PaginationScrollListener(4, this)
recyclerView.setHasFixedSize(true) recyclerView.setHasFixedSize(true)
initListMode(settings.listMode) initListMode(settings.listMode)
// recyclerView.adapter = adapter recyclerView.addOnScrollListener(paginationListener!!)
recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
swipeRefreshLayout.setOnRefreshListener(this) swipeRefreshLayout.setOnRefreshListener(this)
recyclerView_filter.setHasFixedSize(true) recyclerView_filter.setHasFixedSize(true)
recyclerView_filter.addItemDecoration(ItemTypeDividerDecoration(view.context)) recyclerView_filter.addItemDecoration(ItemTypeDividerDecoration(view.context))
@ -72,6 +76,8 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
override fun onDestroyView() { override fun onDestroyView() {
adapter = null adapter = null
progressAdapter = null
paginationListener = null
settings.unsubscribe(this) settings.unsubscribe(this)
super.onDestroyView() super.onDestroyView()
} }
@ -123,6 +129,7 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
} }
override fun onListChanged(list: List<Manga>) { override fun onListChanged(list: List<Manga>) {
paginationListener?.reset()
adapter?.replaceData(list) adapter?.replaceData(list)
if (list.isEmpty()) { if (list.isEmpty()) {
setUpEmptyListHolder() setUpEmptyListHolder()
@ -130,11 +137,16 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
} else { } else {
layout_holder.isVisible = false layout_holder.isVisible = false
} }
progressAdapter?.isProgressVisible = list.isNotEmpty()
recyclerView.callOnScrollListeners() recyclerView.callOnScrollListeners()
} }
override fun onListAppended(list: List<Manga>) { override fun onListAppended(list: List<Manga>) {
adapter?.appendData(list) adapter?.appendData(list)
progressAdapter?.isProgressVisible = list.isNotEmpty()
if (list.isNotEmpty()) {
layout_holder.isVisible = false
}
recyclerView.callOnScrollListeners() recyclerView.callOnScrollListeners()
} }
@ -231,11 +243,19 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver) recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver)
adapter?.listMode = mode adapter?.listMode = mode
recyclerView.layoutManager = when (mode) { recyclerView.layoutManager = when (mode) {
ListMode.GRID -> GridLayoutManager(ctx, UiUtils.resolveGridSpanCount(ctx)) ListMode.GRID -> {
val spanCount = UiUtils.resolveGridSpanCount(ctx)
GridLayoutManager(ctx, spanCount).apply {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int) = if (position < getItemsCount())
1 else spanCount
}
}
}
else -> LinearLayoutManager(ctx) else -> LinearLayoutManager(ctx)
} }
recyclerView.recycledViewPool.clear() recyclerView.recycledViewPool.clear()
recyclerView.adapter = adapter recyclerView.adapter = MergeAdapter(adapterConfig, adapter, progressAdapter)
recyclerView.addItemDecoration( recyclerView.addItemDecoration(
when (mode) { when (mode) {
ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL) ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL)
@ -252,6 +272,8 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
recyclerView.firstItem = position recyclerView.firstItem = position
} }
override fun getItemsCount() = adapter?.itemCount ?: 0
final override fun isSection(position: Int): Boolean { final override fun isSection(position: Int): Boolean {
return position == 0 || recyclerView_filter.adapter?.run { return position == 0 || recyclerView_filter.adapter?.run {
getItemViewType(position) != getItemViewType(position - 1) getItemViewType(position) != getItemViewType(position - 1)

@ -7,10 +7,7 @@ import android.view.View
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.*
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -27,6 +24,7 @@ import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.ui.common.BaseBottomSheet import org.koitharu.kotatsu.ui.common.BaseBottomSheet
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
import org.koitharu.kotatsu.ui.common.list.ProgressBarAdapter
import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.ui.details.MangaDetailsActivity import org.koitharu.kotatsu.ui.details.MangaDetailsActivity
import org.koitharu.kotatsu.utils.UiUtils import org.koitharu.kotatsu.utils.UiUtils
@ -38,12 +36,18 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
SharedPreferences.OnSharedPreferenceChangeListener, Toolbar.OnMenuItemClickListener { SharedPreferences.OnSharedPreferenceChangeListener, Toolbar.OnMenuItemClickListener {
private val settings by inject<AppSettings>() private val settings by inject<AppSettings>()
private val adapterConfig = MergeAdapter.Config.Builder()
.setIsolateViewTypes(true)
.setStableIdMode(MergeAdapter.Config.StableIdMode.SHARED_STABLE_IDS)
.build()
private var adapter: MangaListAdapter? = null private var adapter: MangaListAdapter? = null
private var progressAdapter: ProgressBarAdapter? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
adapter = MangaListAdapter(this) adapter = MangaListAdapter(this)
progressAdapter = ProgressBarAdapter()
initListMode(settings.listMode) initListMode(settings.listMode)
recyclerView.adapter = adapter recyclerView.adapter = adapter
recyclerView.addOnScrollListener(PaginationScrollListener(4, this)) recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
@ -66,6 +70,7 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
override fun onDestroyView() { override fun onDestroyView() {
settings.unsubscribe(this) settings.unsubscribe(this)
adapter = null adapter = null
progressAdapter = null
super.onDestroyView() super.onDestroyView()
} }
@ -123,6 +128,7 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
override fun onListChanged(list: List<Manga>) { override fun onListChanged(list: List<Manga>) {
adapter?.replaceData(list) adapter?.replaceData(list)
textView_holder.isVisible = list.isEmpty() textView_holder.isVisible = list.isEmpty()
progressAdapter?.isProgressVisible = list.isNotEmpty()
recyclerView.callOnScrollListeners() recyclerView.callOnScrollListeners()
} }
@ -131,6 +137,7 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
if (list.isNotEmpty()) { if (list.isNotEmpty()) {
textView_holder.isVisible = false textView_holder.isVisible = false
} }
progressAdapter?.isProgressVisible = list.isNotEmpty()
recyclerView.callOnScrollListeners() recyclerView.callOnScrollListeners()
} }
@ -138,6 +145,8 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).show() Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT).show()
} }
override fun getItemsCount() = adapter?.itemCount ?: 0
override fun onInitFilter( override fun onInitFilter(
sortOrders: List<SortOrder>, sortOrders: List<SortOrder>,
tags: List<MangaTag>, tags: List<MangaTag>,
@ -171,10 +180,18 @@ abstract class MangaListSheet<E> : BaseBottomSheet(R.layout.sheet_list),
recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver) recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver)
adapter?.listMode = mode adapter?.listMode = mode
recyclerView.layoutManager = when (mode) { recyclerView.layoutManager = when (mode) {
ListMode.GRID -> GridLayoutManager(ctx, UiUtils.resolveGridSpanCount(ctx)) ListMode.GRID -> {
val spanCount = UiUtils.resolveGridSpanCount(ctx)
GridLayoutManager(ctx, spanCount).apply {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int) = if (position < getItemsCount())
1 else spanCount
}
}
}
else -> LinearLayoutManager(ctx) else -> LinearLayoutManager(ctx)
} }
recyclerView.adapter = adapter recyclerView.adapter = MergeAdapter(adapterConfig, adapter, progressAdapter)
recyclerView.addItemDecoration( recyclerView.addItemDecoration(
when (mode) { when (mode) {
ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL) ListMode.LIST -> DividerItemDecoration(ctx, RecyclerView.VERTICAL)

@ -102,6 +102,10 @@ class FeedFragment : BaseFragment(R.layout.fragment_tracklogs), FeedView,
} }
} }
override fun getItemsCount(): Int {
return adapter?.itemCount ?: 0
}
override fun onRequestMoreItems(offset: Int) { override fun onRequestMoreItems(offset: Int) {
presenter.loadList(offset) presenter.loadList(offset)
} }

@ -25,13 +25,21 @@ class FeedPresenter : BasePresenter<FeedView>() {
val list = withContext(Dispatchers.IO) { val list = withContext(Dispatchers.IO) {
repository.getTrackingLog(offset, 20) repository.getTrackingLog(offset, 20)
} }
viewState.onListChanged(list) if (offset == 0) {
viewState.onListChanged(list)
} else {
viewState.onListAppended(list)
}
} catch (e: CancellationException) { } catch (e: CancellationException) {
} catch (e: Throwable) { } catch (e: Throwable) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
e.printStackTrace() e.printStackTrace()
} }
viewState.onListError(e) if (offset == 0) {
viewState.onListError(e)
} else {
viewState.onError(e)
}
} finally { } finally {
viewState.onLoadingStateChanged(false) viewState.onLoadingStateChanged(false)
} }

@ -23,9 +23,7 @@ class LocalListFragment : MangaListFragment<File>(), ActivityResultCallback<Uri>
private val presenter by moxyPresenter(factory = ::LocalListPresenter) private val presenter by moxyPresenter(factory = ::LocalListPresenter)
override fun onRequestMoreItems(offset: Int) { override fun onRequestMoreItems(offset: Int) {
if (offset == 0) { presenter.loadList(offset)
presenter.loadList()
}
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {

@ -37,7 +37,11 @@ class LocalListPresenter : BasePresenter<MangaListView<File>>() {
super.onFirstViewAttach() super.onFirstViewAttach()
} }
fun loadList() { fun loadList(offset: Int) {
if (offset != 0) {
viewState.onListAppended(emptyList())
return
}
presenterScope.launch { presenterScope.launch {
viewState.onLoadingStateChanged(true) viewState.onLoadingStateChanged(true)
try { try {

@ -1,16 +1,12 @@
package org.koitharu.kotatsu.ui.search.global package org.koitharu.kotatsu.ui.search.global
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEmpty
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import moxy.presenterScope import moxy.presenterScope
import org.koitharu.kotatsu.domain.MangaSearchRepository import org.koitharu.kotatsu.domain.MangaSearchRepository
import org.koitharu.kotatsu.ui.common.BasePresenter import org.koitharu.kotatsu.ui.common.BasePresenter
import org.koitharu.kotatsu.ui.list.MangaListView import org.koitharu.kotatsu.ui.list.MangaListView
import org.koitharu.kotatsu.utils.ext.onFirst
import java.io.IOException import java.io.IOException
class GlobalSearchPresenter : BasePresenter<MangaListView<Unit>>() { class GlobalSearchPresenter : BasePresenter<MangaListView<Unit>>() {
@ -26,23 +22,27 @@ class GlobalSearchPresenter : BasePresenter<MangaListView<Unit>>() {
fun startSearch(query: String) { fun startSearch(query: String) {
presenterScope.launch { presenterScope.launch {
viewState.onLoadingStateChanged(isLoading = true) viewState.onLoadingStateChanged(isLoading = true)
var isFirstCall = true
repository.globalSearch(query) repository.globalSearch(query)
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
.catch { e -> .catch { e ->
if (e is IOException) { if (e is IOException) {
viewState.onError(e) viewState.onError(e)
} }
} }.filterNot { x -> x.isEmpty() }
.onFirst {
viewState.onListChanged(emptyList())
viewState.onLoadingStateChanged(isLoading = false)
}
.onEmpty { .onEmpty {
viewState.onListChanged(emptyList()) viewState.onListChanged(emptyList())
viewState.onLoadingStateChanged(isLoading = false) viewState.onLoadingStateChanged(isLoading = false)
} }.onCompletion {
.collect { viewState.onListAppended(emptyList())
viewState.onListAppended(it) }.collect {
if (isFirstCall) {
isFirstCall = false
viewState.onListChanged(it)
viewState.onLoadingStateChanged(isLoading = false)
} else {
viewState.onListAppended(it)
}
} }
} }
} }

@ -9,6 +9,7 @@
android:id="@+id/progressBar" android:id="@+id/progressBar"
style="@style/Widget.AppCompat.ProgressBar" style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:visibility="invisible"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" /> android:layout_gravity="center" />

Loading…
Cancel
Save