背景
可以使用以下方法将RecyclerVIEw捕捉到其中心:
linearSnapHelper().attachToRecyclerVIEw(recyclerVIEw)
例:
MainActivity.kt
class MainActivity : AppCompatActivity() { overrIDe fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentVIEw(R.layout.activity_main) val inflater = LayoutInflater.from(this) recyclerVIEw.adapter = object : RecyclerVIEw.Adapter<RecyclerVIEw.VIEwHolder>() { overrIDe fun onBindVIEwHolder(holder: RecyclerVIEw.VIEwHolder, position: Int) { val textVIEw = holder.itemVIEw as TextVIEw textVIEw.setBackgroundcolor(if (position % 2 == 0) 0xffff0000.toInt() else 0xff00ff00.toInt()) textVIEw.text = position.toString() } overrIDe fun getItemCount(): Int { return 100 } overrIDe fun onCreateVIEwHolder(parent: VIEwGroup?, vIEwType: Int): RecyclerVIEw.VIEwHolder { val vIEw = inflater.inflate(androID.R.layout.simple_List_item_1, parent, false) as TextVIEw val cellSize = recyclerVIEw.wIDth / 3 vIEw.layoutParams.height = cellSize vIEw.layoutParams.wIDth = cellSize vIEw.gravity = Gravity.CENTER return object : RecyclerVIEw.VIEwHolder(vIEw) {} } } linearSnapHelper().attachToRecyclerVIEw(recyclerVIEw) }}
activity_main.xml中
<androID.support.v7.Widget.RecyclerVIEw androID:ID="@+ID/recyclerVIEw" 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" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" androID:orIEntation="horizontal" app:layoutManager="androID.support.v7.Widget.linearlayoutmanager"/>
它也可以将其捕捉到其他方面,就像在某些库中所做的那样,例如here.
还有一些库允许使用可以像VIEwPager一样工作的RecyclerVIEw,例如here.
问题
假设我有一个包含许多项目的RecyclerVIEw(在我的情况下为水平),我希望它将每个X项目(X是常量)视为一个单元,并对齐每个单元.
例如,如果我滚动一下,它可以捕捉到0项或X项,但不能捕捉它们之间的某些内容.
在某种程度上,它的行为与普通VIEwPager的情况类似,只是每个页面中都有X个项目.
例如,如果我们从上面编写的示例代码继续,假设X == 3,则捕捉将来自此空闲状态:
到这个空闲状态(如果我们滚动得足够多,否则将保持在以前的状态):
更多的投掷或滚动应该像在VIEwPager上一样处理,就像我上面提到的库一样.
向下一个捕捉点滚动更多(朝同一方向)将是到达项目“6”,“9”,依此类推……
我尝试了什么
我试图搜索替代库,我也尝试阅读有关此问题的文档,但我没有找到任何可能有用的文档.
也许可以通过使用VIEwPager,但我认为这不是最好的方式,因为VIEwPager不能很好地回收它的项目,我认为它在如何捕捉方面不如RecyclerVIEw灵活.
问题
>是否可以将RecyclerVIEw设置为捕捉每个X项目,将每个X项目视为单个页面以捕捉到?
当然,这些物品将为整个RecyclerVIEw提供足够的空间,均匀分布.
>假设有可能,当RecyclerVIEw即将捕捉到某个项目(包括拥有此项目)之前,如何捕获它之前我将如何获得回调?我问这个是因为它与我问here的问题有关.
Kotlin解决方案
基于“Cheticamp”答案(here)的工作Kotlin解决方案,无需验证您是否具有RecyclerVIEw大小,并且在示例中选择了网格而不是列表:
MainActivity.kt
class MainActivity : AppCompatActivity() { val USE_GRID = false // val USE_GRID = true val ITEMS_PER_PAGE = 4 var selectedItemPos = 0 overrIDe fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentVIEw(R.layout.activity_main) val inflater = LayoutInflater.from(this) recyclerVIEw.adapter = object : RecyclerVIEw.Adapter<RecyclerVIEw.VIEwHolder>() { overrIDe fun onBindVIEwHolder(holder: RecyclerVIEw.VIEwHolder, position: Int) { val textVIEw = holder.itemVIEw as TextVIEw textVIEw.setBackgroundcolor(if (position % 2 == 0) 0xffff0000.toInt() else 0xff00ff00.toInt()) textVIEw.text = if (selectedItemPos == position) "selected: $position" else position.toString() } overrIDe fun getItemCount(): Int { return 100 } overrIDe fun onCreateVIEwHolder(parent: VIEwGroup?, vIEwType: Int): RecyclerVIEw.VIEwHolder { val vIEw = inflater.inflate(androID.R.layout.simple_List_item_1, parent, false) as TextVIEw vIEw.layoutParams.wIDth = if (USE_GRID) recyclerVIEw.wIDth / (ITEMS_PER_PAGE / 2) else recyclerVIEw.wIDth / 4 vIEw.layoutParams.height = recyclerVIEw.height / (ITEMS_PER_PAGE / 2) vIEw.gravity = Gravity.CENTER return object : RecyclerVIEw.VIEwHolder(vIEw) { } } } recyclerVIEw.layoutManager = if (USE_GRID) GrIDLayoutManager(this, ITEMS_PER_PAGE / 2, GrIDLayoutManager.HORIZONTAL, false) else linearlayoutmanager(this, linearlayoutmanager.HORIZONTAL, false) val snapToBlock = SnapToBlock(recyclerVIEw, ITEMS_PER_PAGE) snapToBlock.attachToRecyclerVIEw(recyclerVIEw) snapToBlock.setSnapBlockCallback(object : SnapToBlock.SnapBlockCallback { overrIDe fun onBlockSnap(snapposition: Int) { if (selectedItemPos == snapposition) return selectedItemPos = snapposition recyclerVIEw.adapter.notifyDataSetChanged() } overrIDe fun onBlockSnapped(snapposition: Int) { if (selectedItemPos == snapposition) return selectedItemPos = snapposition recyclerVIEw.adapter.notifyDataSetChanged() } }) }}
SnapToBlock.kt
/**@param maxFlingBlocks Maxim blocks to move during most vigorous fling*/class SnapToBlock constructor(private val maxFlingBlocks: Int) : SnapHelper() { private var recyclerVIEw: RecyclerVIEw? = null // Total number of items in a block of vIEw in the RecyclerVIEw private var blocksize: Int = 0 // Maximum number of positions to move on a fling. private var maxpositionsToMove: Int = 0 // WIDth of a RecyclerVIEw item if orIEntation is horizonal; height of the item if vertical private var itemDimension: Int = 0 // Callback interface when blocks are snapped. private var snapBlockCallback: SnapBlockCallback? = null // When snapPing, used to determine direction of snap. private var priorFirstposition = RecyclerVIEw.NO_position // Our private scroller private var scroller: Scroller? = null // Horizontal/vertical layout helper private var orIEntationHelper: OrIEntationHelper? = null // LTR/RTL helper private var layoutDirectionHelper: LayoutDirectionHelper? = null @Throws(IllegalStateException::class) overrIDe fun attachToRecyclerVIEw(recyclerVIEw: RecyclerVIEw?) { if (recyclerVIEw != null) { this.recyclerVIEw = recyclerVIEw val layoutManager = recyclerVIEw.layoutManager as linearlayoutmanager orIEntationHelper = when { layoutManager.canScrollHorizontally() -> OrIEntationHelper.createHorizontalHelper(layoutManager) layoutManager.canScrollVertically() -> OrIEntationHelper.createVerticalHelper(layoutManager) else -> throw IllegalStateException("RecyclerVIEw must be scrollable") } scroller = Scroller(this.recyclerVIEw!!.context, sInterpolator) initItemDimensionIfNeeded(layoutManager) } super.attachToRecyclerVIEw(recyclerVIEw) } // Called when the target vIEw is available and we need to kNow how much more // to scroll to get it lined up with the sIDe of the RecyclerVIEw. overrIDe fun calculatedistancetoFinalSnap(layoutManager: RecyclerVIEw.LayoutManager, targetVIEw: VIEw): IntArray { val out = IntArray(2) initLayoutDirectionHelperIfNeeded(layoutManager) if (layoutManager.canScrollHorizontally()) out[0] = layoutDirectionHelper!!.getScrollToAlignVIEw(targetVIEw) if (layoutManager.canScrollVertically()) out[1] = layoutDirectionHelper!!.getScrollToAlignVIEw(targetVIEw) if (snapBlockCallback != null) if (out[0] == 0 && out[1] == 0) snapBlockCallback!!.onBlockSnapped(layoutManager.getposition(targetVIEw)) else snapBlockCallback!!.onBlockSnap(layoutManager.getposition(targetVIEw)) return out } private fun initLayoutDirectionHelperIfNeeded(layoutManager: RecyclerVIEw.LayoutManager) { if (layoutDirectionHelper == null) if (layoutManager.canScrollHorizontally()) layoutDirectionHelper = LayoutDirectionHelper() else if (layoutManager.canScrollVertically()) // RTL doesn't matter for vertical scrolling for this class. layoutDirectionHelper = LayoutDirectionHelper(false) } // We are flinging and need to kNow where we are heading. overrIDe fun findTargetSnapposition(layoutManager: RecyclerVIEw.LayoutManager, veLocityX: Int, veLocityY: Int): Int { initLayoutDirectionHelperIfNeeded(layoutManager) val lm = layoutManager as linearlayoutmanager initItemDimensionIfNeeded(layoutManager) scroller!!.fling(0, 0, veLocityX, veLocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE) return when { veLocityX != 0 -> layoutDirectionHelper!!.getpositionsToMove(lm, scroller!!.finalX, itemDimension) else -> if (veLocityY != 0) layoutDirectionHelper!!.getpositionsToMove(lm, scroller!!.finalY, itemDimension) else RecyclerVIEw.NO_position } } // We have scrolled to the neighborhood where we will snap. Determine the snap position. overrIDe fun findSnapVIEw(layoutManager: RecyclerVIEw.LayoutManager): VIEw? { // Snap to a vIEw that is either 1) toward the bottom of the data and therefore on screen, // or, 2) toward the top of the data and may be off-screen. val snapPos = calcTargetposition(layoutManager as linearlayoutmanager) val snapVIEw = if (snapPos == RecyclerVIEw.NO_position) null else layoutManager.findVIEwByposition(snapPos) if (snapVIEw == null) Log.d(TAG, "<<<<findSnapVIEw is returning null!") Log.d(TAG, "<<<<findSnapVIEw snapos=" + snapPos) return snapVIEw } // Does the heavy lifting for findSnapVIEw. private fun calcTargetposition(layoutManager: linearlayoutmanager): Int { val snapPos: Int initLayoutDirectionHelperIfNeeded(layoutManager) val firstVisiblePos = layoutManager.findFirstVisibleItemposition() if (firstVisiblePos == RecyclerVIEw.NO_position) return RecyclerVIEw.NO_position initItemDimensionIfNeeded(layoutManager) if (firstVisiblePos >= priorFirstposition) { // Scrolling toward bottom of data val firstCompleteposition = layoutManager.findFirstCompletelyVisibleItemposition() snapPos = if (firstCompleteposition != RecyclerVIEw.NO_position && firstCompleteposition % blocksize == 0) firstCompleteposition else roundDownToBlockSize(firstVisiblePos + blocksize) } else { // Scrolling toward top of data snapPos = roundDownToBlockSize(firstVisiblePos) // Check to see if target vIEw exists. If it doesn't, force a smooth scroll. // SnapHelper only snaps to existing vIEws and will not scroll to a non-existant one. // If limiting fling to single block, then the following is not needed since the // vIEws are likely to be in the RecyclerVIEw pool. if (layoutManager.findVIEwByposition(snapPos) == null) { val toScroll = layoutDirectionHelper!!.calculatedistancetoScroll(layoutManager, snapPos) recyclerVIEw!!.smoothScrollBy(toScroll[0], toScroll[1], sInterpolator) } } priorFirstposition = firstVisiblePos return snapPos } private fun initItemDimensionIfNeeded(layoutManager: RecyclerVIEw.LayoutManager) { if (itemDimension != 0) return val child = layoutManager.getChildAt(0) ?: return if (layoutManager.canScrollHorizontally()) { itemDimension = child.wIDth blocksize = getSpanCount(layoutManager) * (recyclerVIEw!!.wIDth / itemDimension) } else if (layoutManager.canScrollVertically()) { itemDimension = child.height blocksize = getSpanCount(layoutManager) * (recyclerVIEw!!.height / itemDimension) } maxpositionsToMove = blocksize * maxFlingBlocks } private fun getSpanCount(layoutManager: RecyclerVIEw.LayoutManager): Int = (layoutManager as? GrIDLayoutManager)?.spanCount ?: 1 private fun roundDownToBlockSize(trialposition: Int): Int = trialposition - trialposition % blocksize private fun roundUpToBlockSize(trialposition: Int): Int = roundDownToBlockSize(trialposition + blocksize - 1) overrIDe fun createScroller(layoutManager: RecyclerVIEw.LayoutManager): linearSmoothScroller? { return if (layoutManager !is RecyclerVIEw.SmoothScroller.ScrollVectorProvIDer) null else object : linearSmoothScroller(recyclerVIEw!!.context) { overrIDe fun onTargetFound(targetVIEw: VIEw, state: RecyclerVIEw.State?, action: RecyclerVIEw.SmoothScroller.Action) { val snapdistances = calculatedistancetoFinalSnap(recyclerVIEw!!.layoutManager, targetVIEw) val dx = snapdistances[0] val dy = snapdistances[1] val time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy))) if (time > 0) action.update(dx, dy, time, sInterpolator) } overrIDe fun calculateSpeedPerPixel(displayMetrics: displayMetrics): float = MILliSECONDS_PER_INCH / displayMetrics.densityDpi } } fun setSnapBlockCallback(callback: SnapBlockCallback?) { snapBlockCallback = callback } /* Helper class that handles calculations for LTR and RTL layouts. */ private inner class LayoutDirectionHelper { // Is the layout an RTL one? private val mIsRTL: Boolean constructor() { mIsRTL = VIEwCompat.getLayoutDirection(recyclerVIEw) == VIEwCompat.LAYOUT_DIRECTION_RTL } constructor(isRTL: Boolean) { mIsRTL = isRTL } /* Calculate the amount of scroll needed to align the target vIEw with the layout edge. */ fun getScrollToAlignVIEw(targetVIEw: VIEw): Int = if (mIsRTL) orIEntationHelper!!.getDecoratedEnd(targetVIEw) - recyclerVIEw!!.wIDth else orIEntationHelper!!.getDecoratedStart(targetVIEw) /** * Calculate the distance to final snap position when the vIEw corresponding to the snap * position is not currently available. * * @param layoutManager linearlayoutmanager or descendent class * @param targetPos - Adapter position to snap to * @return int[2] {x-distance in pixels, y-distance in pixels} */ fun calculatedistancetoScroll(layoutManager: linearlayoutmanager, targetPos: Int): IntArray { val out = IntArray(2) val firstVisiblePos = layoutManager.findFirstVisibleItemposition() if (layoutManager.canScrollHorizontally()) { if (targetPos <= firstVisiblePos) // scrolling toward top of data if (mIsRTL) { val lastVIEw = layoutManager.findVIEwByposition(layoutManager.findLastVisibleItemposition()) out[0] = orIEntationHelper!!.getDecoratedEnd(lastVIEw) + (firstVisiblePos - targetPos) * itemDimension } else { val firstVIEw = layoutManager.findVIEwByposition(firstVisiblePos) out[0] = orIEntationHelper!!.getDecoratedStart(firstVIEw) - (firstVisiblePos - targetPos) * itemDimension } } if (layoutManager.canScrollVertically() && targetPos <= firstVisiblePos) { // scrolling toward top of data val firstVIEw = layoutManager.findVIEwByposition(firstVisiblePos) out[1] = firstVIEw.top - (firstVisiblePos - targetPos) * itemDimension } return out } /* Calculate the number of positions to move in the RecyclerVIEw given a scroll amount and the size of the items to be scrolled. Return integral multiple of mBlockSize not equal to zero. */ fun getpositionsToMove(llm: linearlayoutmanager, scroll: Int, itemSize: Int): Int { var positionsToMove: Int positionsToMove = roundUpToBlockSize(Math.abs(scroll) / itemSize) if (positionsToMove < blocksize) // Must move at least one block positionsToMove = blocksize else if (positionsToMove > maxpositionsToMove) // Clamp number of positions to move so we don't get wild flinging. positionsToMove = maxpositionsToMove if (scroll < 0) positionsToMove *= -1 if (mIsRTL) positionsToMove *= -1 return if (layoutDirectionHelper!!.isDirectionToBottom(scroll < 0)) { // Scrolling toward the bottom of data. roundDownToBlockSize(llm.findFirstVisibleItemposition()) + positionsToMove } else roundDownToBlockSize(llm.findLastVisibleItemposition()) + positionsToMove // Scrolling toward the top of the data. } fun isDirectionToBottom(veLocityNegative: Boolean): Boolean = if (mIsRTL) veLocityNegative else !veLocityNegative } interface SnapBlockCallback { fun onBlockSnap(snapposition: Int) fun onBlockSnapped(snapposition: Int) } companion object { // Borrowed from VIEwPager.java private val sInterpolator = Interpolator { input -> var t = input // _o(t) = t * t * ((tension + 1) * t + tension) // o(t) = _o(t - 1) + 1 t -= 1.0f t * t * t + 1.0f } private val MILliSECONDS_PER_INCH = 100f private val TAG = "SnapToBlock" }}
更新
即使我已将an answer标记为已接受,因为它工作正常,我注意到它存在严重问题:
>平滑滚动似乎不能正常工作(不滚动到正确的位置).只滚动该工作是这样的(但具有“拖尾”效果):
(recyclerVIEw.layoutManager as linearlayoutmanager).scrollTopositionWithOffset(targetPos,0)
>当切换到RTL(从右到左)语言环境,例如希伯来语(“עברית”)时,它根本不让我滚动.
>我注意到onCreateVIEwHolder被大量调用.事实上,每次滚动时都会调用它,即使有时它应该回收VIEwHolders.这意味着有过多的视图创建,也可能意味着存在内存泄漏.
我试图自己解决这些问题,但到目前为止失败了.
如果有人知道如何修复它,我将授予额外的新奖励
更新:当我们修复了RTL / LTR时,我在这篇文章中更新了Kotlin解决方案.
更新:关于第3点,这似乎是因为recyclelerVIEw有一个视图池,它会很快被填充.为了解决这个问题,我们可以通过对我们在其中使用的每种视图类型使用recyclelerVIEw.getRecycledVIEwPool().setMaxRecycledVIEws(vIEwType,Integer.MAX_VALUE)来简单地扩大池大小.真的需要这个奇怪的事情.我已经将它发布到谷歌(here和here),但被拒绝,默认情况下该池应该是无限制的.最后,我决定至少要求为所有视图类型提供更方便的功能(here).
解决方法:
SnapHelper为您尝试的内容提供必要的框架,但需要扩展它以处理视图块.下面的SnapToBlock类扩展了SnapHelper以捕捉到视图块.在这个例子中,我对一个块使用了四个视图,但它可以更多或更少.
更新:代码已更改以适应GrIDLayoutManager以及linearlayoutmanager. Flinging现在被禁止,因此捕捉更多地列出了VIEwPager.现在支持水平和垂直滚动以及LTR和RTL布局.
更新:更改了平滑滚动插值器,使其更像VIEwPager.
更新:为前/后捕捉添加回调.
更新:添加对RTL布局的支持.
以下是示例应用的快速视频:
如下设置布局管理器:
// For linearlayoutmanager horizontal orIEntationrecyclerVIEw.setLayoutManager(new linearlayoutmanager(this, RecyclerVIEw.HORIZONTAL, false));// For GrIDLayoutManager vertical orIEntationrecyclerVIEw.setLayoutManager(new GrIDLayoutManager(this, SPAN_COUNT, RecyclerVIEw.VERTICAL, false));
添加以下内容以将SnapToBlock附加到RecyclerVIEw.
SnapToBlock snapToBlock = new SnapToBlock(mMaxFlingPages);snapToBlock.attachToRecyclerVIEw(recyclerVIEw);
mMaxFlingPages是允许一次抛出的最大块数(rowsCols * spans).
对于即将完成快照并已完成快照的回叫,请添加以下内容:
snapToBlock.setSnapBlockCallback(new SnapToBlock.SnapBlockCallback() { @OverrIDe public voID onBlockSnap(int snapposition) { ... } @OverrIDe public voID onBlockSnapped(int snapposition) { ... }});
SnapToBlock.java
/* The number of items in the RecyclerVIEw should be a multiple of block size; otherwise, the extra item vIEws will not be positioned on a block boundary when the end of the data is reached. Pad out with empty item vIEws if needed. Updated to accommodate RTL layouts. */public class SnapToBlock extends SnapHelper { private RecyclerVIEw mRecyclerVIEw; // Total number of items in a block of vIEw in the RecyclerVIEw private int mBlocksize; // Maximum number of positions to move on a fling. private int mMaxpositionsToMove; // WIDth of a RecyclerVIEw item if orIEntation is horizonal; height of the item if vertical private int mItemDimension; // Maxim blocks to move during most vigorous fling. private final int mMaxFlingBlocks; // Callback interface when blocks are snapped. private SnapBlockCallback mSnapBlockCallback; // When snapPing, used to determine direction of snap. private int mPriorFirstposition = RecyclerVIEw.NO_position; // Our private scroller private Scroller mScroller; // Horizontal/vertical layout helper private OrIEntationHelper mOrIEntationHelper; // LTR/RTL helper private LayoutDirectionHelper mLayoutDirectionHelper; // Borrowed from VIEwPager.java private static final Interpolator sInterpolator = new Interpolator() { public float getInterpolation(float t) { // _o(t) = t * t * ((tension + 1) * t + tension) // o(t) = _o(t - 1) + 1 t -= 1.0f; return t * t * t + 1.0f; } }; SnapToBlock(int maxFlingBlocks) { super(); mMaxFlingBlocks = maxFlingBlocks; } @OverrIDe public voID attachToRecyclerVIEw(@Nullable final RecyclerVIEw recyclerVIEw) throws IllegalStateException { if (recyclerVIEw != null) { mRecyclerVIEw = recyclerVIEw; final linearlayoutmanager layoutManager = (linearlayoutmanager) recyclerVIEw.getLayoutManager(); if (layoutManager.canScrollHorizontally()) { mOrIEntationHelper = OrIEntationHelper.createHorizontalHelper(layoutManager); mLayoutDirectionHelper = new LayoutDirectionHelper(VIEwCompat.getLayoutDirection(mRecyclerVIEw)); } else if (layoutManager.canScrollVertically()) { mOrIEntationHelper = OrIEntationHelper.createVerticalHelper(layoutManager); // RTL doesn't matter for vertical scrolling for this class. mLayoutDirectionHelper = new LayoutDirectionHelper(RecyclerVIEw.LAYOUT_DIRECTION_LTR); } else { throw new IllegalStateException("RecyclerVIEw must be scrollable"); } mScroller = new Scroller(mRecyclerVIEw.getContext(), sInterpolator); initItemDimensionIfNeeded(layoutManager); } super.attachToRecyclerVIEw(recyclerVIEw); } // Called when the target vIEw is available and we need to kNow how much more // to scroll to get it lined up with the sIDe of the RecyclerVIEw. @NonNull @OverrIDe public int[] calculatedistancetoFinalSnap(@NonNull RecyclerVIEw.LayoutManager layoutManager, @NonNull VIEw targetVIEw) { int[] out = new int[2]; if (layoutManager.canScrollHorizontally()) { out[0] = mLayoutDirectionHelper.getScrollToAlignVIEw(targetVIEw); } if (layoutManager.canScrollVertically()) { out[1] = mLayoutDirectionHelper.getScrollToAlignVIEw(targetVIEw); } if (mSnapBlockCallback != null) { if (out[0] == 0 && out[1] == 0) { mSnapBlockCallback.onBlockSnapped(layoutManager.getposition(targetVIEw)); } else { mSnapBlockCallback.onBlockSnap(layoutManager.getposition(targetVIEw)); } } return out; } // We are flinging and need to kNow where we are heading. @OverrIDe public int findTargetSnapposition(RecyclerVIEw.LayoutManager layoutManager, int veLocityX, int veLocityY) { linearlayoutmanager lm = (linearlayoutmanager) layoutManager; initItemDimensionIfNeeded(layoutManager); mScroller.fling(0, 0, veLocityX, veLocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); if (veLocityX != 0) { return mLayoutDirectionHelper .getpositionsToMove(lm, mScroller.getFinalX(), mItemDimension); } if (veLocityY != 0) { return mLayoutDirectionHelper .getpositionsToMove(lm, mScroller.getFinalY(), mItemDimension); } return RecyclerVIEw.NO_position; } // We have scrolled to the neighborhood where we will snap. Determine the snap position. @OverrIDe public VIEw findSnapVIEw(RecyclerVIEw.LayoutManager layoutManager) { // Snap to a vIEw that is either 1) toward the bottom of the data and therefore on screen, // or, 2) toward the top of the data and may be off-screen. int snapPos = calcTargetposition((linearlayoutmanager) layoutManager); VIEw snapVIEw = (snapPos == RecyclerVIEw.NO_position) ? null : layoutManager.findVIEwByposition(snapPos); if (snapVIEw == null) { Log.d(TAG, "<<<<findSnapVIEw is returning null!"); } Log.d(TAG, "<<<<findSnapVIEw snapos=" + snapPos); return snapVIEw; } // Does the heavy lifting for findSnapVIEw. private int calcTargetposition(linearlayoutmanager layoutManager) { int snapPos; int firstVisiblePos = layoutManager.findFirstVisibleItemposition(); if (firstVisiblePos == RecyclerVIEw.NO_position) { return RecyclerVIEw.NO_position; } initItemDimensionIfNeeded(layoutManager); if (firstVisiblePos >= mPriorFirstposition) { // Scrolling toward bottom of data int firstCompleteposition = layoutManager.findFirstCompletelyVisibleItemposition(); if (firstCompleteposition != RecyclerVIEw.NO_position && firstCompleteposition % mBlocksize == 0) { snapPos = firstCompleteposition; } else { snapPos = roundDownToBlockSize(firstVisiblePos + mBlocksize); } } else { // Scrolling toward top of data snapPos = roundDownToBlockSize(firstVisiblePos); // Check to see if target vIEw exists. If it doesn't, force a smooth scroll. // SnapHelper only snaps to existing vIEws and will not scroll to a non-existant one. // If limiting fling to single block, then the following is not needed since the // vIEws are likely to be in the RecyclerVIEw pool. if (layoutManager.findVIEwByposition(snapPos) == null) { int[] toScroll = mLayoutDirectionHelper.calculatedistancetoScroll(layoutManager, snapPos); mRecyclerVIEw.smoothScrollBy(toScroll[0], toScroll[1], sInterpolator); } } mPriorFirstposition = firstVisiblePos; return snapPos; } private voID initItemDimensionIfNeeded(final RecyclerVIEw.LayoutManager layoutManager) { if (mItemDimension != 0) { return; } VIEw child; if ((child = layoutManager.getChildAt(0)) == null) { return; } if (layoutManager.canScrollHorizontally()) { mItemDimension = child.getWIDth(); mBlocksize = getSpanCount(layoutManager) * (mRecyclerVIEw.getWIDth() / mItemDimension); } else if (layoutManager.canScrollVertically()) { mItemDimension = child.getHeight(); mBlocksize = getSpanCount(layoutManager) * (mRecyclerVIEw.getHeight() / mItemDimension); } mMaxpositionsToMove = mBlocksize * mMaxFlingBlocks; } private int getSpanCount(RecyclerVIEw.LayoutManager layoutManager) { return (layoutManager instanceof GrIDLayoutManager) ? ((GrIDLayoutManager) layoutManager).getSpanCount() : 1; } private int roundDownToBlockSize(int trialposition) { return trialposition - trialposition % mBlocksize; } private int roundUpToBlockSize(int trialposition) { return roundDownToBlockSize(trialposition + mBlocksize - 1); } @Nullable protected linearSmoothScroller createScroller(RecyclerVIEw.LayoutManager layoutManager) { if (!(layoutManager instanceof RecyclerVIEw.SmoothScroller.ScrollVectorProvIDer)) { return null; } return new linearSmoothScroller(mRecyclerVIEw.getContext()) { @OverrIDe protected voID onTargetFound(VIEw targetVIEw, RecyclerVIEw.State state, Action action) { int[] snapdistances = calculatedistancetoFinalSnap(mRecyclerVIEw.getLayoutManager(), targetVIEw); final int dx = snapdistances[0]; final int dy = snapdistances[1]; final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy))); if (time > 0) { action.update(dx, dy, time, sInterpolator); } } @OverrIDe protected float calculateSpeedPerPixel(displayMetrics displayMetrics) { return MILliSECONDS_PER_INCH / displayMetrics.densityDpi; } }; } public voID setSnapBlockCallback(@Nullable SnapBlockCallback callback) { mSnapBlockCallback = callback; } /* Helper class that handles calculations for LTR and RTL layouts. */ private class LayoutDirectionHelper { // Is the layout an RTL one? private final boolean mIsRTL; @TargetAPI(Build.VERSION_CODES.JELLY_BEAN_MR1) LayoutDirectionHelper(int direction) { mIsRTL = direction == VIEw.LAYOUT_DIRECTION_RTL; } /* Calculate the amount of scroll needed to align the target vIEw with the layout edge. */ int getScrollToAlignVIEw(VIEw targetVIEw) { return (mIsRTL) ? mOrIEntationHelper.getDecoratedEnd(targetVIEw) - mRecyclerVIEw.getWIDth() : mOrIEntationHelper.getDecoratedStart(targetVIEw); } /** * Calculate the distance to final snap position when the vIEw corresponding to the snap * position is not currently available. * * @param layoutManager linearlayoutmanager or descendent class * @param targetPos - Adapter position to snap to * @return int[2] {x-distance in pixels, y-distance in pixels} */ int[] calculatedistancetoScroll(linearlayoutmanager layoutManager, int targetPos) { int[] out = new int[2]; int firstVisiblePos; firstVisiblePos = layoutManager.findFirstVisibleItemposition(); if (layoutManager.canScrollHorizontally()) { if (targetPos <= firstVisiblePos) { // scrolling toward top of data if (mIsRTL) { VIEw lastVIEw = layoutManager.findVIEwByposition(layoutManager.findLastVisibleItemposition()); out[0] = mOrIEntationHelper.getDecoratedEnd(lastVIEw) + (firstVisiblePos - targetPos) * mItemDimension; } else { VIEw firstVIEw = layoutManager.findVIEwByposition(firstVisiblePos); out[0] = mOrIEntationHelper.getDecoratedStart(firstVIEw) - (firstVisiblePos - targetPos) * mItemDimension; } } } if (layoutManager.canScrollVertically()) { if (targetPos <= firstVisiblePos) { // scrolling toward top of data VIEw firstVIEw = layoutManager.findVIEwByposition(firstVisiblePos); out[1] = firstVIEw.gettop() - (firstVisiblePos - targetPos) * mItemDimension; } } return out; } /* Calculate the number of positions to move in the RecyclerVIEw given a scroll amount and the size of the items to be scrolled. Return integral multiple of mBlockSize not equal to zero. */ int getpositionsToMove(linearlayoutmanager llm, int scroll, int itemSize) { int positionsToMove; positionsToMove = roundUpToBlockSize(Math.abs(scroll) / itemSize); if (positionsToMove < mBlocksize) { // Must move at least one block positionsToMove = mBlocksize; } else if (positionsToMove > mMaxpositionsToMove) { // Clamp number of positions to move so we don't get wild flinging. positionsToMove = mMaxpositionsToMove; } if (scroll < 0) { positionsToMove *= -1; } if (mIsRTL) { positionsToMove *= -1; } if (mLayoutDirectionHelper.isDirectionToBottom(scroll < 0)) { // Scrolling toward the bottom of data. return roundDownToBlockSize(llm.findFirstVisibleItemposition()) + positionsToMove; } // Scrolling toward the top of the data. return roundDownToBlockSize(llm.findLastVisibleItemposition()) + positionsToMove; } boolean isDirectionToBottom(boolean veLocityNegative) { //noinspection SimplifiableConditionalExpression return mIsRTL ? veLocityNegative : !veLocityNegative; } } public interface SnapBlockCallback { voID onBlockSnap(int snapposition); voID onBlockSnapped(int snapposition); } private static final float MILliSECONDS_PER_INCH = 100f; @SuppressWarnings("unused") private static final String TAG = "SnapToBlock";}
上面定义的SnapBlockCallback接口可用于报告要捕捉的块开始处视图的适配器位置.如果视图不在屏幕上进行调用,则可能无法实例化与该位置关联的视图.
总结以上是内存溢出为你收集整理的android – 如何捕捉RecyclerView项目,以便每个X项目都被视为一个单元来捕捉?全部内容,希望文章能够帮你解决android – 如何捕捉RecyclerView项目,以便每个X项目都被视为一个单元来捕捉?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)