mirror of
https://github.com/nextcloud/android.git
synced 2024-11-25 22:55:46 +03:00
Merge pull request #10174 from nextcloud/new-fastscroll-lib
Fast scrolling fixes
This commit is contained in:
commit
db85ebba94
10 changed files with 347 additions and 33 deletions
|
@ -272,7 +272,7 @@ dependencies {
|
|||
implementation "com.google.android.exoplayer:exoplayer:$exoplayerVersion"
|
||||
implementation "com.google.android.exoplayer:extension-okhttp:$exoplayerVersion"
|
||||
|
||||
implementation 'com.simplecityapps:recyclerview-fastscroll:2.0.1'
|
||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.8'
|
||||
|
||||
// Shimmer animation
|
||||
implementation 'io.github.elye:loaderviewlibrary:3.0.0'
|
||||
|
|
61
app/src/main/java/com/nextcloud/utils/view/FastScroll.kt
Normal file
61
app/src/main/java/com/nextcloud/utils/view/FastScroll.kt
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Nextcloud Android Library is available under MIT license
|
||||
*
|
||||
* @author Álvaro Brey Vilas
|
||||
* Copyright (C) 2022 Álvaro Brey Vilas
|
||||
* Copyright (C) 2022 Nextcloud GmbH
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.nextcloud.utils.view
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import me.zhanghai.android.fastscroll.FastScroller
|
||||
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
||||
|
||||
object FastScroll {
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun applyFastScroll(recyclerView: RecyclerView, viewHelper: FastScroller.ViewHelper? = null) {
|
||||
val builder = FastScrollerBuilder(recyclerView).useMd2Style()
|
||||
if (viewHelper != null) {
|
||||
builder.setViewHelper(viewHelper)
|
||||
}
|
||||
builder.build()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fixAppBarForFastScroll(appBarLayout: AppBarLayout, content: ViewGroup) {
|
||||
val contentLayoutInitialPaddingBottom = content.paddingBottom
|
||||
appBarLayout.addOnOffsetChangedListener(
|
||||
AppBarLayout.OnOffsetChangedListener { _, offset ->
|
||||
content.setPadding(
|
||||
content.paddingLeft,
|
||||
content.paddingTop,
|
||||
content.paddingRight,
|
||||
contentLayoutInitialPaddingBottom + appBarLayout.totalScrollRange + offset
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -25,14 +25,13 @@ import android.content.Context;
|
|||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* Extends RecyclerView to show a custom view if no data is available Inspired by http://alexzh.com/tutorials/how-to-setemptyview-to-recyclerview
|
||||
*/
|
||||
public class EmptyRecyclerView extends FastScrollRecyclerView {
|
||||
public class EmptyRecyclerView extends RecyclerView {
|
||||
private View mEmptyView;
|
||||
private boolean hasFooter = false;
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ import com.nextcloud.client.network.ConnectivityService;
|
|||
import com.nextcloud.client.preferences.AppPreferences;
|
||||
import com.nextcloud.client.utils.IntentUtil;
|
||||
import com.nextcloud.java.util.Optional;
|
||||
import com.nextcloud.utils.view.FastScroll;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.databinding.FilesBinding;
|
||||
|
@ -137,7 +138,6 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.view.MenuItemCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
@ -266,6 +266,9 @@ public class FileDisplayActivity extends FileActivity
|
|||
mSwitchAccountButton.setOnClickListener(v -> showManageAccountsDialog());
|
||||
|
||||
|
||||
FastScroll.fixAppBarForFastScroll(binding.appbar.appbar, binding.rootLayout);
|
||||
|
||||
|
||||
// Init Fragment without UI to retain AsyncTask across configuration changes
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
TaskRetainerFragment taskRetainerFragment =
|
||||
|
|
|
@ -45,7 +45,7 @@ import com.owncloud.android.utils.FileSortOrder
|
|||
import com.owncloud.android.utils.FileStorageUtils
|
||||
import com.owncloud.android.utils.theme.ThemeColorUtils
|
||||
import com.owncloud.android.utils.theme.ThemeDrawableUtils
|
||||
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView.SectionedAdapter
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
|
||||
|
@ -58,8 +58,8 @@ class GalleryAdapter(
|
|||
transferServiceGetter: ComponentsGetter,
|
||||
themeColorUtils: ThemeColorUtils,
|
||||
themeDrawableUtils: ThemeDrawableUtils
|
||||
) : SectionedRecyclerViewAdapter<SectionedViewHolder>(), CommonOCFileListAdapterInterface, SectionedAdapter {
|
||||
private var files: List<GalleryItems> = mutableListOf()
|
||||
) : SectionedRecyclerViewAdapter<SectionedViewHolder>(), CommonOCFileListAdapterInterface, PopupTextProvider {
|
||||
var files: List<GalleryItems> = mutableListOf()
|
||||
private val ocFileListDelegate: OCFileListDelegate
|
||||
private var storageManager: FileDataStorageManager
|
||||
|
||||
|
@ -122,7 +122,7 @@ class GalleryAdapter(
|
|||
return files.size
|
||||
}
|
||||
|
||||
override fun getSectionName(position: Int): String {
|
||||
override fun getPopupText(position: Int): String {
|
||||
return DisplayUtils.getDateByPattern(
|
||||
files[getRelativePosition(position).section()].date,
|
||||
context,
|
||||
|
|
|
@ -74,7 +74,6 @@ import com.owncloud.android.utils.theme.CapabilityUtils;
|
|||
import com.owncloud.android.utils.theme.ThemeAvatarUtils;
|
||||
import com.owncloud.android.utils.theme.ThemeColorUtils;
|
||||
import com.owncloud.android.utils.theme.ThemeDrawableUtils;
|
||||
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
@ -90,14 +89,14 @@ import androidx.annotation.Nullable;
|
|||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider;
|
||||
|
||||
/**
|
||||
* This Adapter populates a RecyclerView with all files and folders in a Nextcloud instance.
|
||||
*/
|
||||
public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||
implements DisplayUtils.AvatarGenerationListener,
|
||||
CommonOCFileListAdapterInterface,
|
||||
FastScrollRecyclerView.SectionedAdapter {
|
||||
CommonOCFileListAdapterInterface, PopupTextProvider {
|
||||
|
||||
private static final int showFilenameColumnThreshold = 4;
|
||||
private final String userId;
|
||||
|
@ -909,7 +908,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getSectionName(int position) {
|
||||
public String getPopupText(int position) {
|
||||
OCFile file = getItem(position);
|
||||
|
||||
if (file == null) {
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.nextcloud.utils.view.FastScroll;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
|
@ -35,6 +36,7 @@ import com.owncloud.android.ui.adapter.CommonOCFileListAdapterInterface;
|
|||
import com.owncloud.android.ui.adapter.GalleryAdapter;
|
||||
import com.owncloud.android.ui.asynctasks.GallerySearchTask;
|
||||
import com.owncloud.android.ui.events.ChangeMenuEvent;
|
||||
import com.owncloud.android.ui.fragment.util.GalleryFastScrollViewHelper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
@ -109,26 +111,14 @@ public class GalleryFragment extends OCFileListFragment {
|
|||
themeColorUtils,
|
||||
themeDrawableUtils);
|
||||
|
||||
// val spacing = resources.getDimensionPixelSize(R.dimen.media_grid_spacing)
|
||||
// binding.list.addItemDecoration(MediaGridItemDecoration(spacing))
|
||||
setRecyclerViewAdapter(mAdapter);
|
||||
|
||||
|
||||
GridLayoutManager layoutManager = new GridLayoutManager(getContext(), getColumnsCount());
|
||||
// ((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||
// @Override
|
||||
// public int getSpanSize(int position) {
|
||||
// if (position == getAdapter().getItemCount() - 1 ||
|
||||
// position == 0 && getAdapter().shouldShowHeader()) {
|
||||
// return ((GridLayoutManager) layoutManager).getSpanCount();
|
||||
// } else {
|
||||
// return 1;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
mAdapter.setLayoutManager(layoutManager);
|
||||
getRecyclerView().setLayoutManager(layoutManager);
|
||||
|
||||
FastScroll.applyFastScroll(getRecyclerView(), new GalleryFastScrollViewHelper(getRecyclerView(), mAdapter));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -59,6 +59,7 @@ import com.nextcloud.client.network.ClientFactory;
|
|||
import com.nextcloud.client.preferences.AppPreferences;
|
||||
import com.nextcloud.client.utils.Throttler;
|
||||
import com.nextcloud.common.NextcloudClient;
|
||||
import com.nextcloud.utils.view.FastScroll;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
|
@ -429,6 +430,8 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
|||
);
|
||||
|
||||
setRecyclerViewAdapter(mAdapter);
|
||||
|
||||
FastScroll.applyFastScroll(getRecyclerView());
|
||||
}
|
||||
|
||||
protected void prepareCurrentSearch(SearchEvent event) {
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* Nextcloud Android Library is available under MIT license
|
||||
*
|
||||
* @author Álvaro Brey Vilas
|
||||
* Copyright (C) 2022 Álvaro Brey Vilas
|
||||
* Copyright (C) 2022 Nextcloud GmbH
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.ui.fragment.util
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.view.MotionEvent
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
|
||||
import androidx.recyclerview.widget.RecyclerView.SimpleOnItemTouchListener
|
||||
import com.afollestad.sectionedrecyclerview.ItemCoord
|
||||
import com.owncloud.android.datamodel.GalleryItems
|
||||
import com.owncloud.android.ui.adapter.GalleryAdapter
|
||||
import me.zhanghai.android.fastscroll.FastScroller
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
import me.zhanghai.android.fastscroll.Predicate
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* Custom ViewHelper to get fast scroll working on gallery, which has a gridview and variable height (due to headers)
|
||||
*
|
||||
* Copied from me.zhanghai.android.fastscroll.RecyclerViewHelper and heavily modified for gallery structure
|
||||
*/
|
||||
class GalleryFastScrollViewHelper(
|
||||
private val mView: RecyclerView,
|
||||
private val mPopupTextProvider: PopupTextProvider?
|
||||
) : FastScroller.ViewHelper {
|
||||
// used to calculate paddings
|
||||
private val mTempRect = Rect()
|
||||
|
||||
private val layoutManager by lazy { mView.layoutManager as GridLayoutManager }
|
||||
|
||||
// header is always 1st in the adapter
|
||||
private val headerHeight by lazy { getItemHeight(0) }
|
||||
// the 2nd element is always an item
|
||||
private val rowHeight by lazy { getItemHeight(1) }
|
||||
|
||||
private val columnCount by lazy { layoutManager.spanCount }
|
||||
|
||||
private fun getItemHeight(position: Int): Int {
|
||||
if (mView.childCount <= position) {
|
||||
return 0
|
||||
}
|
||||
val itemView = mView.getChildAt(position)
|
||||
mView.getDecoratedBoundsWithMargins(itemView, mTempRect)
|
||||
return mTempRect.height()
|
||||
}
|
||||
|
||||
override fun addOnPreDrawListener(onPreDraw: Runnable) {
|
||||
mView.addItemDecoration(object : ItemDecoration() {
|
||||
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
onPreDraw.run()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun addOnScrollChangedListener(onScrollChanged: Runnable) {
|
||||
mView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
onScrollChanged.run()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun addOnTouchEventListener(onTouchEvent: Predicate<MotionEvent?>) {
|
||||
mView.addOnItemTouchListener(object : SimpleOnItemTouchListener() {
|
||||
override fun onInterceptTouchEvent(recyclerView: RecyclerView, event: MotionEvent): Boolean {
|
||||
return onTouchEvent.test(event)
|
||||
}
|
||||
|
||||
override fun onTouchEvent(recyclerView: RecyclerView, event: MotionEvent) {
|
||||
onTouchEvent.test(event)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getScrollRange(): Int {
|
||||
val headerCount = getHeaderCount()
|
||||
val rowCount = getRowCount()
|
||||
|
||||
if (headerCount == 0 || rowCount == 0) {
|
||||
return 0
|
||||
}
|
||||
val totalHeaderHeight = headerCount * headerHeight
|
||||
val totalRowHeight = rowCount * rowHeight
|
||||
return mView.paddingTop + totalHeaderHeight + totalRowHeight + mView.paddingBottom
|
||||
}
|
||||
|
||||
private fun getHeaderCount(): Int {
|
||||
val adapter = mView.adapter as GalleryAdapter
|
||||
return adapter.sectionCount
|
||||
}
|
||||
|
||||
private fun getRowCount(): Int {
|
||||
val adapter = mView.adapter as GalleryAdapter
|
||||
if (adapter.sectionCount == 0) return 0
|
||||
// in each section, the final row may contain less than the max of items
|
||||
return adapter.files.sumOf { itemCountToRowCount(it.files.size) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates current absolute offset depending on view state (first visible element)
|
||||
*/
|
||||
override fun getScrollOffset(): Int {
|
||||
val firstItemPosition = getFirstItemAdapterPosition()
|
||||
if (firstItemPosition == RecyclerView.NO_POSITION) {
|
||||
return 0
|
||||
}
|
||||
|
||||
val adapter = mView.adapter as GalleryAdapter
|
||||
val itemCoord: ItemCoord = adapter.getRelativePosition(firstItemPosition)
|
||||
val isHeader = itemCoord.relativePos() == -1
|
||||
|
||||
val seenRowsInPreviousSections = adapter.files
|
||||
.subList(0, itemCoord.section())
|
||||
.sumOf { itemCountToRowCount(it.files.size) }
|
||||
val seenRowsInThisSection = if (isHeader) 0 else itemCountToRowCount(itemCoord.relativePos())
|
||||
val totalSeenRows = seenRowsInPreviousSections + seenRowsInThisSection
|
||||
|
||||
val seenHeaders = when {
|
||||
isHeader -> itemCoord.section() // don't count the current section header
|
||||
else -> itemCoord.section() + 1
|
||||
}
|
||||
|
||||
val firstItemTop = getFirstItemOffset()
|
||||
|
||||
val totalRowOffset = totalSeenRows * rowHeight
|
||||
val totalHeaderOffset = seenHeaders * headerHeight
|
||||
return mView.paddingTop + totalHeaderOffset + totalRowOffset - firstItemTop
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls to an absolute offset
|
||||
*/
|
||||
override fun scrollTo(offset: Int) {
|
||||
mView.stopScroll()
|
||||
val offsetTmp = offset - mView.paddingTop
|
||||
val (position, remainingOffset) = findPositionForOffset(offsetTmp)
|
||||
scrollToPositionWithOffset(position, -remainingOffset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an absolute offset, returns the closest position to that offset (without going over it),
|
||||
* and the remaining offset
|
||||
*/
|
||||
private fun findPositionForOffset(offset: Int): Pair<Int, Int> {
|
||||
val adapter = mView.adapter as GalleryAdapter
|
||||
|
||||
// find section
|
||||
val sectionStartOffsets = getSectionStartOffsets(adapter.files)
|
||||
val previousSections = sectionStartOffsets.filter { it <= offset }
|
||||
|
||||
val section = previousSections.size - 1
|
||||
val sectionStartOffset = previousSections.last()
|
||||
|
||||
// now calculate where to scroll within the section
|
||||
var remainingOffset = offset - sectionStartOffset
|
||||
val positionWithinSection: Int
|
||||
if (remainingOffset <= headerHeight) {
|
||||
// header position
|
||||
positionWithinSection = -1
|
||||
} else {
|
||||
// row position
|
||||
remainingOffset -= headerHeight
|
||||
val rowCount = remainingOffset / rowHeight
|
||||
if (rowCount > 0) {
|
||||
val rowStartIndex = rowCount * columnCount
|
||||
positionWithinSection = rowStartIndex
|
||||
|
||||
remainingOffset -= rowCount * rowHeight
|
||||
} else {
|
||||
positionWithinSection = 0 // first item
|
||||
}
|
||||
}
|
||||
val absolutePosition = adapter.getAbsolutePosition(section, positionWithinSection)
|
||||
return Pair(absolutePosition, remainingOffset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the offset heights at which the section corresponding to that index starts
|
||||
*/
|
||||
private fun getSectionStartOffsets(files: List<GalleryItems>): List<Int> {
|
||||
val sectionHeights =
|
||||
files.map { headerHeight + itemCountToRowCount(it.files.size) * rowHeight }
|
||||
val sectionStartOffsets = sectionHeights.indices.map { i ->
|
||||
when (i) {
|
||||
0 -> 0
|
||||
else -> sectionHeights.subList(0, i).sum()
|
||||
}
|
||||
}
|
||||
return sectionStartOffsets
|
||||
}
|
||||
|
||||
private fun itemCountToRowCount(itemsCount: Int): Int {
|
||||
return ceil(itemsCount.toDouble() / columnCount).toInt()
|
||||
}
|
||||
|
||||
override fun getPopupText(): String? {
|
||||
var popupTextProvider: PopupTextProvider? = mPopupTextProvider
|
||||
if (popupTextProvider == null) {
|
||||
val adapter = mView.adapter
|
||||
if (adapter is PopupTextProvider) {
|
||||
popupTextProvider = adapter
|
||||
}
|
||||
}
|
||||
if (popupTextProvider == null) {
|
||||
return null
|
||||
}
|
||||
val position = getFirstItemAdapterPosition()
|
||||
return if (position == RecyclerView.NO_POSITION) {
|
||||
null
|
||||
} else popupTextProvider.getPopupText(position)
|
||||
}
|
||||
|
||||
private fun getFirstItemAdapterPosition(): Int {
|
||||
if (mView.childCount == 0) {
|
||||
return RecyclerView.NO_POSITION
|
||||
}
|
||||
val itemView = mView.getChildAt(0)
|
||||
return layoutManager.getPosition(itemView)
|
||||
}
|
||||
|
||||
private fun getFirstItemOffset(): Int {
|
||||
if (mView.childCount == 0) {
|
||||
return RecyclerView.NO_POSITION
|
||||
}
|
||||
val itemView = mView.getChildAt(0)
|
||||
mView.getDecoratedBoundsWithMargins(itemView, mTempRect)
|
||||
return mTempRect.top
|
||||
}
|
||||
|
||||
private fun scrollToPositionWithOffset(position: Int, offset: Int) {
|
||||
var offsetTmp = offset
|
||||
// LinearLayoutManager actually takes offset from paddingTop instead of top of RecyclerView.
|
||||
offsetTmp -= mView.paddingTop
|
||||
layoutManager.scrollToPositionWithOffset(position, offsetTmp)
|
||||
}
|
||||
}
|
|
@ -33,12 +33,7 @@
|
|||
<com.owncloud.android.ui.EmptyRecyclerView
|
||||
android:id="@+id/list_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:fastScrollPopupBgColor="@color/color_accent"
|
||||
app:fastScrollPopupTextColor="@color/login_text_color"
|
||||
app:fastScrollThumbColor="@color/color_accent"
|
||||
app:fastScrollAutoHide="true"
|
||||
app:fastScrollAutoHideDelay="1500" />
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<include
|
||||
|
|
Loading…
Reference in a new issue