【问题标题】:ListView scroll position not maintained after loading content using CWAC Endless Adapter使用 CWAC Endless Adapter 加载内容后,ListView 滚动位置未保持
【发布时间】:2014-08-18 12:30:56
【问题描述】:

更新

基本上,bbrakenhoff 已经回答了我的问题,但还有一件事需要解决。如何更新我的 EndlessFeedAdapter (mEndlsFidAdptr) 的内容?我需要清除该项目,然后重新加载。我正在使用 CWAC EndlessAdapter。是否有清除内容的技巧或者只编写一个方法会更容易?完成此操作后,应保持滚动位置。


我正在从服务器获取数据并在内容更改时更新我的​​ EndlessFeedAdapter。每次我更新我的适配器并重新加载内容时。问题是重新加载后我的列表会跳回顶部,因为我的滚动位置没有保持。我已经广泛尝试了 setSelection 和 setSelectionFromTop,但没有积极的结果。

适配器更新后如何保持滚动位置?

我一直在通过论坛寻找答案,但似乎没有任何效果。

这些我都试过了:Maintain/Save/Restore scroll position when returning to a ListView

这不起作用:

int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();


 // restore index and position
mList.setSelectionFromTop(index, top);

也不是这样:

// Save ListView state
Parcelable state = listView.onSaveInstanceState();

// Set new items
listView.setAdapter(adapter);

// Restore previous state (including selected item index and scroll position)
listView.onRestoreInstanceState(state);

也不是其他解决方案,例如设置可运行文件或设置 scrollPositionY。当我从不同的列表加载时,设置 notifyDataChanged 不起作用。

我的代码:

private void showFeed() {

    if (mFeedActivity.mInFeed) {
        mQuickReturnView.setVisibility(View.VISIBLE);
    } else {
        mQuickReturnView.setVisibility(View.GONE);
    }                                  

    Activity actvt= getActivity();
    if (actvt == null || mFeedListView == null) return;

    actvt.invalidateOptionsMenu();

    mFeedListView.setVisibility(View.VISIBLE);

    //updated with help from response
    if (mAdapter == null){
        mAdapter = new FeedAdapter(actvt, 0, mFeed.getItems().getFeedItemList(), this);
    } else {
        mAdapter.clear();
        mAdapter.addAll(mFeed.getItems().getFeedItemList());
        mAdapter.notifyDataSetChanged();
    }

    mEndlsFidAdptr = new EndlessFeedAdapter(actvt, mAdapter, R.layout.progress_row, mFeed.isShowMoreBar(),

    mEndlsFidAdptr.setRunInBackground(false);

    //Parcelable state = mFeedListView.onSaveInstanceState();
    mFeedListView.setAdapter(mEndlsFidAdptr);
    //mFeedListView.onRestoreInstanceState(state);



    mFeedListView.setSelectionFromTop(mFirstVisibleItem, mVisibleItemOffset);

    if(!(mFeedScope.equalsIgnoreCase(FeedScope.BOOKMARKS.xmlValue()) ||
            mFeedScope.equalsIgnoreCase(FeedScope.DOCUMENT.xmlValue()) ||
            mFeedScope.equalsIgnoreCase(FeedScope.NOTIFICATIONS.xmlValue()) ||
            mFeedScope.equalsIgnoreCase(FeedScope.RECEIVED_TASKS.xmlValue()) ||
            mFeedScope.equalsIgnoreCase(FeedScope.SEND_TASKS.xmlValue()))) {

  mFeedListView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);


        mFeedListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {

                mCanShowHide = scrollState == SCROLL_STATE_FLING;

            }


            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

                View v = mFeedListView.getChildAt(0);
                //View v = null;
                if(!mFeedActivity.mInFeed || v == null)
                    return;

                int top = v.getTop();

                if(mIsAnimating) {
                    mVisibleItemOffset = top;
                    mFirstVisibleItem = firstVisibleItem;
                    return;
                }

                boolean hide = false;
                boolean show = false;
                float stickyHeight = getResources().getDimension(R.dimen.sticky_height);

                if(firstVisibleItem == mFirstVisibleItem) {
                    if((top + stickyHeight) < mVisibleItemOffset) {
                        // Content scrolled down
                        // if shown then hide quickactionview
                        if(mQuickReturnShown) {
                            hide = true;
                        }

                    } else if (top > mVisibleItemOffset) {
                        // Content scrolled up
                        // if hidden then show quickactionview
                        if(!mQuickReturnShown) {
                            show = true;
                        }

                    }

                } else if(firstVisibleItem > mFirstVisibleItem) {
                    // Content scrolled down
                    // if shown then hide quickactionview
                    if(mQuickReturnShown) {
                        hide = true;
                    }

                } else if (firstVisibleItem < mFirstVisibleItem) {
                    // Content scrolled up
                    // if hidden then show quickactionview
                    if(!mQuickReturnShown) {
                        show = true;
                    }
                }

                if((show && mCanShowHide) || (top == 0 && !mQuickReturnShown)) {
                    mTranslateAnimation = new TranslateAnimation(0, 0, -mQuickReturnHeight, 0);
                    mTranslateAnimation.setDuration(DURATION_MILLIS);
                    mTranslateAnimation.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
                            mIsAnimating = true;
                        }

                        @Override
                        public void onAnimationEnd(Animation animation) {
                            mIsAnimating = false;
                            mQuickReturnShown = true;
                            mQuickReturnView.setVisibility(View.VISIBLE);
                        }

                        @Override
                        public void onAnimationRepeat(Animation animation) {

                        }
                    });

                    mQuickReturnView.startAnimation(mTranslateAnimation);
                }

                if(hide) {
                    mTranslateAnimation = new TranslateAnimation(0, 0, 0, -mQuickReturnHeight);
                    mTranslateAnimation.setDuration(DURATION_MILLIS);
                    mTranslateAnimation.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
                            mIsAnimating = true;
                        }

                        @Override
                        public void onAnimationEnd(Animation animation) {
                            mIsAnimating = false;
                            mQuickReturnShown = false;
                            mQuickReturnView.setVisibility(View.GONE);
                        }

                        @Override
                        public void onAnimationRepeat(Animation animation) { }
                    });


                    mQuickReturnView.startAnimation(mTranslateAnimation);
                }

                mVisibleItemOffset = top;
                mFirstVisibleItem = firstVisibleItem;
            }
        });
    } else {
        mFeedListView.setOnScrollListener(null);

    }

    mFeedListView.setSelectionFromTop(mFirstVisibleItem, mVisibleItemOffset);

    //mFeedListView.scrollTo(mCurrentX,mCurrentY);

    if(mFeed.getItems().getFeedItemList().size() == 0) {
        mEmptyFeedView.setVisibility(View.VISIBLE);
    }
}

【问题讨论】:

  • 你尝试使用getScrollPosition然后设置scrollPOsition吗?
  • 是的,我试过在onScroll方法中设置滚动位置,然后在方法的最后调用scrollTo(可以看到注释掉的实现)
  • 你在哪里添加新项目?
  • 它们是通过调用添加的 - mFeed.getItems().getFeedItemList()。这会调用我的休息服务。

标签: android listview scroll


【解决方案1】:

您是否在每次收到新数据时都调用方法 showFeed()?如果是,那么也许您可以尝试重新填充适配器,而不是每次都分配一个新的。

我不确切知道您在适配器中做什么,所以我将向您展示我是如何在我的一个应用中做到这一点的。

在我的活动/片段中,当我想用​​新项目更新列表时,我会这样做:

private void refreshCalendar(ArrayList<CalendarDay> newCalendar) {
    if (mAdapter == null) {
        mAdapter = new CalendarAdapter(getActivity(), newCalendar);
        mExpandableListView.setAdapter(mAdapter);
    }
    else {
        mAdapter.refill(newCalendar);
    }

    restoreInstanceState();
}

在适配器中:

public void refill(ArrayList<CalendarDay> newCalendar) {
    mCalendar.clear();
    mCalendar.addAll(newCalendar);
    notifyDataSetChanged();
}

【讨论】:

  • 我不会对你投反对票,因为我在同一条船上:) 如果 mEndlsFidAdaptr.notifyDataChanged() 不为空,请添加它?这有效,除了我使用多个片段,然后在此实施后不刷新......
  • 你简直让我大吃一惊!我的意思是 .notifyDataSetChanged(),即使切换到另一个片段,它也会加载完全相同的列表。
  • 在 bbrakenhoff 的帮助下,我能够在我的案例中使用调整他的代码来在刷新后不创建新的适配器。但是,我的代码仍然被扔到一个循环器中,然后它被重置到屏幕顶部。有什么想法吗?
【解决方案2】:

也许您可以尝试删除此行?

  mFeedListView.setSelectionFromTop(mFirstVisibleItem, mVisibleItemOffset);

编辑:您在代码中使用了此行两次。

编辑:在您问题的代码中,您只是刷新 mAdapter 而不是 mEndlsFidAdptr 。那个仍然被分配了一个新的。每次您为 ListView 分配一个新适配器时,它都会滚动回顶部。

【讨论】:

  • 不,不是这样。我不仅从一个位置删除了 .setSelectionFromTop,而且两个位置和我的滚动位置仍然被重置...
  • 是的,这就是问题所在。我似乎无法清除 mEndlsFidAdptr,然后像 mAdapter 一样对其进行更新。我只能在没有实际添加的情况下通知DataSetChanged...任何想法如何在不使用.clear 的情况下添加第二个适配器?
  • 那为什么不能清理呢?你使用的是数组还是列表?
  • 其实就是mAdapter使用的mFeed.getItems().getFeedItemList(),所以是一个List。我已经在那个列表上做了 .clear ......
  • 您在这样做时是否遇到异常?因为如果你有类似 List 的东西,你就不能修改它。你需要有一个 ArrayList.
【解决方案3】:

所以解决方案不是我正在寻找的合乎逻辑的地方。非常感谢 bbrakenhoff 为我指明了正确的方向!

我的类 showFeed() 是从另一个名为 downloadFeed() 的类中调用的.

虽然如果其他人有这个问题可能是一个很长的问题 - 要解决这个问题,只需创建一个变量来在用户执行 onClick 事件时保存可滚动列表的总大小。然后当再次调用 downloadFeed() 时,有更多的项目要下载而不是默认的 5 个。然后可以保持滚动位置,因为现在存在可见项目。

我最终使用了 mFeedListView.setSelectionFromTop(firstVisibleItem, positionOffset)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-06
    • 2014-03-29
    • 1970-01-01
    • 2014-11-18
    • 2012-10-16
    • 2015-02-20
    相关资源
    最近更新 更多