android– 如何捕捉RecyclerView项目,以便每个X项目都被视为一个单元来捕捉?

android– 如何捕捉RecyclerView项目,以便每个X项目都被视为一个单元来捕捉?,第1张

概述背景可以使用以下方法将RecyclerView捕捉到其中心:LinearSnapHelper().attachToRecyclerView(recyclerView)例:MainActivity.ktclassMainActivity:AppCompatActivity(){overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanc

背景

可以使用以下方法将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项目都被视为一个单元来捕捉?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1102656.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-28
下一篇 2022-05-28

发表评论

登录后才能评论

评论列表(0条)

保存