setSelectionFromTop有时工作,有时不工作

时间:2014-04-13 06:09:38

标签: android listview android-listview scroll

我有以下代码来计算列表更新前的列表滚动位置并返回到原始位置。我想根据列表视图中可见的最后一项进行对齐。

  • mList是我试图保持滚动位置的列表。
  • isScrolling是用户手动滚动列表视图时返回的函数。
  • mAssocPage.mListPosition是一个包含滚动信息的类。
    • .selectionBasemAssocPage.arrItems中的对象,用作计算滚动位置的基础。
    • .nPosExtra是列表视图中最后一个可见项的最高值。

此函数在mList的OnScrollListener中调用,因此将始终包含listview的位置。

来自https://github.com/GaulSori/artwave/blob/master/src/gaulsori/artwave/ui/activity/main/SinglePageView.java

public synchronized void calculateListPosition(){
    int nItemIndex=mList.getLastVisiblePosition()-mAdapter.getPaddingCount()-1;

    //Is there something to align?
    if(0<=nItemIndex && nItemIndex<mAssocPage.arrItems.size()){
        mAssocPage.mListPosition.setAtBottom(false);
        mAssocPage.mListPosition.selectionBase=mAssocPage.arrItems.get(nItemIndex);
        mAssocPage.mListPosition.nPosExtra=mList.getChildAt(mList.getChildCount()-1).getTop();
    }else{ // Force to top
        mAssocPage.mListPosition.selectionBase=null;
        mAssocPage.mListPosition.nPosExtra=0;
        mAssocPage.mListPosition.setAtBottom(nItemIndex>=mAssocPage.arrItems.size());
    }

}

如果列表视图位于顶部,如果用户没有滚动列表视图,则滚动返回功能会平滑地向上滚动到顶部。 +1用于delta是因为mList有标题。在这种情况下,arrItem不会丢失任何项目,因此保证0&lt; = nPos&lt; = mList.arrItems.size()。

public void returnListPosition(){
    if(mAssocPage.mListPosition.bInitialized){
        int delta=mAdapter.getPaddingCount()+1;
        if(mAssocPage.mListPosition.isAtBottom()){
            mList.setSelection(mAssocPage.arrItems.size()+delta);
        }else{
            int nPos=mAssocPage.mListPosition.selectionBase==null?0:Collections.binarySearch(mAssocPage.arrItems, mAssocPage.mListPosition.selectionBase);
            if(nPos<0){
                nPos=-nPos+1;
                mList.setSelection(nPos+delta);
            }else{
                mList.setSelectionFromTop(nPos+delta, mAssocPage.mListPosition.nPosExtra);
            }
            if(mAssocPage.mListPosition.isAtTop() && !isScrolling()){ // Move to top
                mList.smoothScrollToPosition(0);
                mAssocPage.mListPosition.nPosExtra=0;
                if(mAssocPage.arrItems.size()>0)
                    mAssocPage.mListPosition.selectionBase=mAssocPage.arrItems.get(0);
            }
        }
    }
}

mAssocList中的以下函数将通过替换为修改过的arrItems(arrToReplace)来修改旧的arrItems,如果列表可见,它将滚动列表。为了确保一次调用一个函数,我使用了synchronized

  • mAssocView是使用以上两个函数扩展View的类。
  • notifyAssocView会调用适配器的notifyDataSetChanged

将始终使用带有一个参数的函数调用该函数。

来自https://github.com/GaulSori/artwave/blob/master/src/gaulsori/artwave/items/SinglePage.java

private void applyTempEditor(final ArrayList<SpecialListItem> arrToReplace){
    applyTempEditor(arrToReplace, false);
}
private void applyTempEditor(final ArrayList<SpecialListItem> arrToReplace, boolean withoutSync){
    if(!withoutSync){
        synchronized(this){
            applyTempEditor(arrToReplace, true);
        }
        return;
    }
    if(mAssocView==null || mAssocView.getHandler()==null){
        arrItems=arrToReplace;
    }else if(Utils.isUiThread()){
        // FIXME Sometimes position returning does NOT work
        arrItems=arrToReplace;
        notifyAssocView();

        mAssocView.returnListPosition();
    }else{
        final Semaphore semp=new Semaphore(1);
        semp.drainPermits();
        if(mAssocView.getHandler().post(new Runnable() { @Override public void run() {

            applyTempEditor(arrToReplace, true);

            semp.release();
            }})==false)
            throw new RuntimeException("?????");
        semp.acquireUninterruptibly(); // Wait for UI is actually changed
    }
}

applyTempEditor经常在非UI线程中调用,滚动位置有时会保留,有时不会。但话又说回来,如果UI更改是以编程方式锁定的(如果用户正在触摸屏幕),则更改将被推迟,直到arrItems的修改完成,并且在用户释放手指之后,列表视图的位置将被保留。

  • isUiLocked()表示用户是否正在点击屏幕。

以下代码将在多个非UI线程中频繁运行。

if(isUiLocked()){
    return;
}
synchronized(this){
    arrItemsTempEditor=new ArrayList<SpecialListItem>(arrItems);
    applyNewItems(true, true, arrItemsTempEditor); // Modify arrItemsTempEditor ONLY
    applyTempEditor(arrItemsTempEditor); // Apply to arrItems
}
notifyAssocView();

我尝试在returnListPosition中包装mList.post(new Runnable(){ @Override public void run(){ ... } });,但我发现没有区别 - 列表有时不会滚动。我检查了每次通过在applyTempEditor中的returnListPosition和android.util.Log.d("artwave", "setSelectionFromTop")中添加android.util.Log.d("artwave", "update")来调用该函数。由于mlistPosition未在onScroll之外修改,因此滚动位置将返回到原始位置最终,但看到滚动跳转仍然很烦人。 setSelectionFromTop有时可以工作的原因是什么?

1 个答案:

答案 0 :(得分:0)

我添加了以下功能:

public void setListChanged(){
    mListChanged=true;
}

确保在下一次applyPageStatus()调用时重置列表的适配器。

public void applyPageStatus(){
    if(!Utils.isUiThread()){ // UI thread
        mHandler.sendEmptyMessage(HandlerMessages.MESSAGE_SINGLE_PAGE_APPLY_STATUS);
        return;
    }

    ...

    if(mListChanged){
        mList.setAdapter(null);
        mList.setAdapter(mAdapter);
        returnListPosition();
    }
}

通过删除适配器并重置,listview将立即对其子项进行更改。认为这是低效的,因为每次操作都需要处理很多视图,直到我有另一个解决方案,我将不得不坚持这个。

只有这个解决方案的后备是,如果列表正在投掷,它将会停止。