可以使用以下命令将 RecyclerView 捕捉到其中心:
LinearSnapHelper().attachToRecyclerView(recyclerView)
示例:
主活动. 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"/>
也可以将其捕捉到另一侧,就像在一些库中所做的那样,比如这里。
也有一些库允许RecyclerView像ViewPager一样工作,比如这里。
假设我有一个包含许多项目的RecyclView(在我的例子中是水平的),我希望它将每个X项目(X是常数)视为一个单元,并与每个单元对齐。
例如,如果我滚动一点,它可以捕捉到0项或X项,但不能捕捉到它们之间的东西。
在某种程度上,它的行为类似于普通的ViewPager,只是每个页面都有X个项目。
例如,如果我们继续我上面写的示例代码,假设X==3,快照将来自这种空闲状态:
到这个空闲状态(如果我们滚动得足够多,否则会停留在以前的状态) :
应该像在ViewPager上一样处理更多的投掷或滚动,就像我上面提到的库一样。
滚动更多(在同一方向)到下一个对齐点将到达项目“6”,“9”,依此类推...
我试图搜索替代库,我也试图阅读有关此的文档,但我没有找到任何可能有用的东西。
使用ViewPager也可能是可能的,但我认为这不是最好的方法,因为ViewPager不能很好地回收其项目,而且我认为它在如何捕捉方面不如RecyclizerView灵活。
>
是否可以将 RecyclerView 设置为贴靠每个 X 个项目,将每个 X 个项目视为要对齐到的单个页面?
当然,这些物品将均匀地占据整个RecyclerView的足够空间。
假设这是可能的,当回收人员视图即将捕捉到某个项目时,包括在它被捕捉之前拥有这个项目时,我将如何获得回调?我问这个问题是因为它与我在这里问的同一个问题有关。
基于“Cheticamp”答案的工作静态编程语言解决方案(此处),无需验证您是否具有回收站视图大小,并且可以选择具有网格而不是列表,在示例中:
主活动. 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"
}
}
尽管我已经将答案标记为已接受,因为它运行良好,但我注意到它存在严重问题:
>
平滑滚动似乎不能正常工作(不能滚动到正确的位置)。只有滚动才能正常工作(但有“涂抹”效果):
(recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(targetPos,0)
当切换到希伯来语(“עברית”)等RTL(从右到左)语言环境时,它根本不允许我滚动。
我注意到,onCreateViewHolder
被称为很多。事实上,每次我滚动时它都会被调用,甚至有时候它本应该循环使用viewholder。这意味着视图创建过多,也可能意味着内存泄漏。
我自己也试过修复,但到目前为止失败了。
如果这里有人知道如何修复它,我会给予额外的新赏金
更新:当我们获得RTL / LTR的修复程序时,我在这篇文章中更新了Kotlin解决方案。
更新:关于第3点,这似乎是因为回收器视图有一个视图池,它很快就被填满了。为了处理这个问题,我们可以简单地扩大池的大小,方法是为我们在其中的每个视图类型使用recyclerView.getRecycledViewPool(). setMaxRecycledViews(viewType,Integer.MAX_VALUE)
。这确实是很需要的奇怪的事情。我已经将其发布到Google(这里和这里),但被拒绝默认情况下池应该是无限的。最后,我决定至少要求有一个更方便的函数来为所有视图类型执行此操作(这里)。
这个图书馆对https://github.com/TakuSemba/MultiSnapRecyclerView很有用
//Adding multisnap to the recyclerview
val multiSnapHelper = MultiSnapHelper(MultiSnapHelper.DEFAULT_GRAVITY, 1, 200F)
multiSnapHelper.attachToRecyclerView(recyclerView)
上述代码是为您的活动通过代码
<com.takusemba.multisnaprecyclerview.MultiSnapRecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:msrv_gravity="start" or center, end
app:msrv_interval="2" items to scroll over
app:msrv_ms_per_inch="100" /> // speed of scrolling through.
这是同样的方式,但在xml中这是您的选择
所有这些信息都来自文档
我会做类似的事情
>
RecyclerView内部的块滚动(例如,如何禁用RecyclerView滚动?
创建手势甩动Detecor并将其附加到回收器查看
应该有效:)
SnapHelper
为您正在尝试的内容提供了必要的框架,但需要对其进行扩展以处理视图块。下面的类 SnapToBlock
扩展了 SnapHelper
以捕捉到视图块。在此示例中,我对一个块使用了四个视图,但它可以或多或少。
更新:代码已更改以适应GridLayoutManager
以及LinearLayoutManager
。现在禁止翻转,因此抓拍工作更多列出ViewPager
。现在支持水平和垂直滚动以及LTR和RTL布局。
更新:将平滑滚动插值器更改为更像<code>ViewPager</code>。
更新:添加用于前/后捕捉的回调。
更新:添加对RTL布局的支持。
这是示例应用程序的快速视频:
设置布局管理器,如下所示:
// For LinearLayoutManager horizontal orientation
recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
// For GridLayoutManager vertical orientation
recyclerView.setLayoutManager(new GridLayoutManager(this, SPAN_COUNT, RecyclerView.VERTICAL, false));
添加以下内容以将SnapToBlock
附加到RecyclView
。
SnapToBlock snapToBlock = new SnapToBlock(mMaxFlingPages);
snapToBlock.attachToRecyclerView(recyclerView);
mMaxFlingPages
是允许一次抛出的最大块数(行Cols * 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";
}
上面定义的< code>SnapBlockCallback接口可用于报告视图在要捕捉的块的开始处的适配器位置。如果视图在屏幕之外,则当进行调用时,与该位置相关联的视图可能不会被实例化。
如果您有这样的字符串: < code >【hello world】这是【最好的。家]是个好地方。 如何仅提取括号[]中的每个单词(由空格分隔)。现在我有这个工作 https://regex101.com/r/Tgokeq/2 哪个返回: 你好世界 最好的。家 但我想要: 你好 世界 该 最好的 。主页 PS:我知道我可以在一个foreach中做字符串拆分,但我不想要在正则表达式本身中使用它,就像这
我需要一些关于我的GridLayoutManager的帮助。 如果我有一个项目,它应该在中间正常显示。 对于两个项目,它们应该彼此相邻。从三到四个项目的示例,它应该像正方形一样显示。3个项目的示例4个项目的示例 如果我是对的,spanCount 2应该就是这种情况。但是,如果有5个项目(所以连续3个),那就有问题了 从5个项目(及以上)开始,每个项目应该是连续3个,例如有5个项目,如果底部行的项目
最后是我的适配器类
如何创建一个选项菜单,如以下屏幕截图: 点击RecyclerView项目的“更多”图标后,应打开选项菜单! 我的尝试是这样的: 但这会导致问题,因为如果我点击RecyclerView item More按钮,则会单击完整项目。。。 这是我的RecyclerViewOnTouchListener: 我没有发现任何类似的问题,所以我希望你能帮助我!
问题内容: 我有两个在android studio中制作的单独的android应用程序项目,我正在尝试将项目1合并到项目2中,以使其全部成为一个应用程序,我该怎么做呢?我知道如何在Eclipse中做到这一点,但在android studio中却不行。请注意,这两个项目都是android应用程序,都不是android库。 任何帮助都将是惊人的! 当我将项目1导入到项目模块中时,在生成成绩时出现此错误