我已经尝试了一段时间让smoothScrollToPositionFromTop()工作,但它并不总是滚动到正确的位置。
我有一个ListView(有10个条目)的布局,边上有10个按钮,所以我可以滚动到列表中的每个条目。通常,当我向后或向前滚动一个位置时,效果很好,但是当我试图向后或向前滚动超过3个位置时,ListView并不完全在所选位置结束。当它失败时,它通常会减少0.5到1.5个项目,并且当滚动失败时,这是不可预测的。
我还检查了在通知fyDataSetChanged在android中不起作用后的平滑ScrollToPotion,但此修复对我不起作用,我不更改任何数据。
我真的很想自动滚动到选定的列表项,但不是我不知道如何。以前有没有人遇到过这个问题,并且知道如何解决它?
如四楼google issuetracker页面所述:https://issue tracker . Google . com/issues/36952786
前面给出的解决方法,“目前的解决方法是在启动滚动时监听SCROLL_STATE_IDLE,并将ScrollToPositionFromTop再次平滑到相同的位置。”也不总是有效。
实际上,使用SCROLL_STATE_IDLE调用onScrollStateChanged并不一定意味着滚动已经完成。因此,它仍然不能保证Listview每次都滚动到正确的位置,尤其是当列表项视图的高度不完全相同时。
经过研究,我发现了另一种完全正确和合理的方法。众所周知,Listview提供了一种方法scrollListBy(int y),它使我们能够立即以y像素向上滚动Listview。然后,在计时器的帮助下,我们可以自己平滑正确地滚动列表。
我们需要做的第一件事是计算每个列表项视图的高度,包括屏幕外的视图。由于之前已经知道列表数据和子视图的类型,因此可以计算每个列表项视图的高度。因此,给定一个要平滑滚动的目标位置,我们可以计算其在y方向上的滚动距离。此外,应在完成对 ListView 的初始化后进行计算。
第二件事是结合一个计时器和scrollListBy(int)方法。实际上我们可以使用android.os.Handler.的sendemptyMessageDelayed()方法。因此,解决方案可以是:
/**
* Created by CaiHaozhong on 2017/9/29.
*/
public class ListViewSmoothScroller {
private final static int MSG_ACTION_SCROLL = 1;
private final static int MSG_ACTION_ADJUST = 2;
private ListView mListView = null;
/* The accumulated height of each list item view */
protected int[] mItemAccumulateHeight = null;
protected int mTimeStep = 20;
protected int mHeaderViewHeight;
private int mPos;
private Method mTrackMotionScrollMethod = null;
protected int mScrollUnit = 0;
protected int mTotalMove = 0;
protected int mTargetScrollDis = 0;
private Handler mMainHandler = new Handler(Looper.getMainLooper()){
public void handleMessage(Message msg) {
int what = msg.what;
switch (what){
case MSG_ACTION_SCROLL: {
int scrollDis = mScrollUnit;
if(mTotalMove + mScrollUnit > mTargetScrollDis){
scrollDis = mTargetScrollDis - mTotalMove;
}
if(Build.VERSION.SDK_INT >= 19) {
mListView.scrollListBy(scrollDis);
}
else{
if(mTrackMotionScrollMethod != null){
try {
mTrackMotionScrollMethod.invoke(mListView, -scrollDis, -scrollDis);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
mTotalMove += scrollDis;
if(mTotalMove < mTargetScrollDis){
mMainHandler.sendEmptyMessageDelayed(MSG_ACTION_SCROLL, mTimeStep);
}else {
mMainHandler.sendEmptyMessageDelayed(MSG_ACTION_ADJUST, mTimeStep);
}
break;
}
case MSG_ACTION_ADJUST: {
mListView.setSelection(mPos);
break;
}
}
}
};
public ListViewSmoothScroller(Context context, ListView listView){
mListView = listView;
mScrollUnit = Tools.dip2px(context, 60);
mPos = -1;
try {
mTrackMotionScrollMethod = AbsListView.class.getDeclaredMethod("trackMotionScroll", int.class, int.class);
}catch (NoSuchMethodException ex){
ex.printStackTrace();
mTrackMotionScrollMethod = null;
}
if(mTrackMotionScrollMethod != null){
mTrackMotionScrollMethod.setAccessible(true);
}
}
/* scroll to a target position smoothly */
public void smoothScrollToPosition(int pos){
if(mListView == null)
return;
if(mItemAccumulateHeight == null || pos >= mItemAccumulateHeight.length){
return ;
}
mPos = pos;
mTargetScrollDis = mItemAccumulateHeight[pos];
mMainHandler.sendEmptyMessage(MSG_ACTION_SCROLL);
}
/* call after initializing ListView */
public void doMeasureOnLayoutChange(){
if(mListView == null){
return;
}
int headerCount = mListView.getHeaderViewsCount();
/* if no list item */
if(mListView.getChildCount() < headerCount + 1){
return ;
}
mHeaderViewHeight = 0;
for(int i = 0; i < headerCount; i++){
mHeaderViewHeight += mListView.getChildAt(i).getHeight();
}
View firstListItemView = mListView.getChildAt(headerCount);
computeAccumulateHeight(firstListItemView);
}
/* calculate the accumulated height of each list item */
protected void computeAccumulateHeight(View firstListItemView){
int len = listdata.size();// count of list item
mItemAccumulateHeight = new int[len + 2];
mItemAccumulateHeight[0] = 0;
mItemAccumulateHeight[1] = mHeaderViewHeight;
int currentHeight = mHeaderViewHeight;
for(int i = 2; i < len + 2; i++){
currentHeight += getItemHeight(firstListItemView);
mItemAccumulateHeight[i] = currentHeight;
}
}
/* get height of a list item. You may need to pass the listdata of the list item as parameter*/
protected int getItemHeight(View firstListItemView){
// Considering the structure of listitem View and the list data in order to calculate the height.
}
}
完成对ListView的初始化后,我们调用doInstrureOnLayoutChange()方法。之后,我们可以通过SmulthScrollToPotions(int pos)方法滚动ListView。我们可以像这样调用doInstrureOnLayoutChange()方法:
mListAdapter.notifyDataSetChanged();
mListView.post(new Runnable() {
@Override
public void run() {
mListViewSmoothScroller.doMeasureOnLayoutChange();
}
});
最后,我们的列表视图可以平滑地滚动到目标位置,更重要的是,可以正确地滚动。
下面是该解决方案的一个实现。
void smoothScrollToPositionFromTopWithBugWorkAround(final AbsListView listView,
final int position,
final int offset,
final int duration){
//the bug workaround involves listening to when it has finished scrolling, and then
//firing a new scroll to the same position.
//the bug is the case that sometimes smooth Scroll To Position sort of misses its intended position.
//more info here : https://code.google.com/p/android/issues/detail?id=36062
listView.smoothScrollToPositionFromTop(position, offset, duration);
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(scrollState==OnScrollListener.SCROLL_STATE_IDLE){
listView.setOnScrollListener(null);
listView.smoothScrollToPositionFromTop(position, offset, duration);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
这是一个已知的错误。看到https://code.google.com/p/android/issues/detail?id=36062
但是,我实施了此解决方法来处理可能发生的所有边缘情况:
首先调用snthScrollToPositionFromTop(位置)
,然后,当滚动完成时,调用setSelect(位置)
。后者调用通过直接跳转到所需位置来纠正不完整的滚动。这样做用户仍然会觉得它正在被动画滚动到这个位置。
我在两个helper方法中实现了此解决方案:
平滑滚动到位置从顶部()
public static void smoothScrollToPositionFromTop(final AbsListView view, final int position) {
View child = getChildAtPosition(view, position);
// There's no need to scroll if child is already at top or view is already scrolled to its end
if ((child != null) && ((child.getTop() == 0) || ((child.getTop() > 0) && !view.canScrollVertically(1)))) {
return;
}
view.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
view.setOnScrollListener(null);
// Fix for scrolling bug
new Handler().post(new Runnable() {
@Override
public void run() {
view.setSelection(position);
}
});
}
}
@Override
public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
final int totalItemCount) { }
});
// Perform scrolling to position
new Handler().post(new Runnable() {
@Override
public void run() {
view.smoothScrollToPositionFromTop(position, 0);
}
});
}
getChildAtPosition()
public static View getChildAtPosition(final AdapterView view, final int position) {
final int index = position - view.getFirstVisiblePosition();
if ((index >= 0) && (index < view.getChildCount())) {
return view.getChildAt(index);
} else {
return null;
}
}
我不明白为什么我的结果不同: 我有表订单和列价格(在mysql中键入double)。 数据库中的价格值为13.5。 查询: 结果是:1.21 查询: 结果是1.22 不舍入:结果为1.215 因此,一轮后的正确结果是1.22。 为什么查询
我想使用查找从一个集合中获取一些数据并将其放入另一个集合中。 在localfield或foreignfield中写什么都不重要,因为它从player_game_stats中获取所有数据并将其插入player集合中的每个文档中。我想检查localfield和foreignField是否相等,但lookup不检查这一点。我对mongodb使用NoSqlBooster
最后是持久性上下文配置: 我很感谢你的帮助。
问题内容: 我有以下状态: 然后我更新状态: 由于setState是假设要合并的,所以我希望它是: 但是它吃掉了id,状态为: 这是预期的行为吗?仅更新嵌套状态对象的一个属性的解决方案是什么? 问题答案: 我认为不做递归合并。 您可以使用当前状态的值构造一个新状态,然后调用该状态: 我在这里使用过函数function(来自underscore.js库),通过创建状态的浅表副本来防止对该状态的现
我正在学习,并试图理解是什么使它比其他框架和库提供的解决方案更“快”和更特别。 我意识到以下几点: 以及React如何运行一个diff以采取最少的步骤来确定“更改”并相应地响应/重新呈现,而不是在其他框架/库中进行传统的“脏检查”操作。 以声明方式编程,而不是通过“可观察”模式强制实现。 因此,从概念上讲,上面的陈述对我来说都很好,但是,当我考虑现实生活中的用例和实践时,我无法想象其中的好处: >