Android 使用反向无止境滚动添加到ListView时保持滚动位置

Android 使用反向无止境滚动添加到ListView时保持滚动位置,android,listview,scroll,endlessadapter,Android,Listview,Scroll,Endlessadapter,我正在构建一个类似于Android的聊天应用程序,类似于Hangouts。为此,我使用了一个垂直列表视图,其中包含stackFromBottom=true和transcriptMode=“normal” 列表按旧邮件(顶部)到新邮件(底部)排序。在正常状态下,ListView滚动到底部,显示最年轻的消息 一旦用户滚动到列表顶部,ListView使用反向无限滚动适配器加载更多(旧)消息。加载旧邮件后,它们将添加到列表的顶部 所需的行为是,当旧消息添加到列表顶部时,ListView将其滚动位置保持在

我正在构建一个类似于Android的聊天应用程序,类似于Hangouts。为此,我使用了一个垂直列表视图,其中包含
stackFromBottom=true
transcriptMode=“normal”

列表按旧邮件(顶部)到新邮件(底部)排序。在正常状态下,ListView滚动到底部,显示最年轻的消息

一旦用户滚动到列表顶部,ListView使用反向无限滚动适配器加载更多(旧)消息。加载旧邮件后,它们将添加到列表的顶部

所需的行为是,当旧消息添加到列表顶部时,ListView将其滚动位置保持在列表底部。也就是说,将旧消息添加到顶部时,滚动视图应显示与添加旧消息之前显示的相同消息

不幸的是,这不起作用。ListView没有将滚动位置保持到底部,而是将滚动位置保持在列表顶部。也就是说,将较旧的消息添加到列表后,列表将显示最早(即最旧)的消息

在向列表视图顶部添加新项目时,是否有一种简单的方法告诉列表视图将其滚动位置保持在底部而不是顶部

更新

public class MyListActivity extends Activity {
  int topIndex = 1;

  int bottomIndex = 20;

  protected final void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_list_activity);

    final ListView list = (ListView) findViewById(R.id.list);
    list.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
    list.setStackFromBottom(true);

    final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1);
    for (int i = topIndex; i <= bottomIndex; i++) {
      adapter.add(Integer.toString(i));
    }
    list.setAdapter(adapter);

    list.setOnItemClickListener(new OnItemClickListener() {
      @Override
      public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        adapter.insert(Integer.toString(--topIndex), 0);
      }
    });

    list.setOnItemLongClickListener(new OnItemLongClickListener() {
      @Override
      public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        adapter.add(Integer.toString(++bottomIndex));
        return true;
      }
    });
  }
}
出于演示目的,我尽可能地减少了用例和代码。下面的示例创建一个简单的字符串数组列表。单击一个项目将在列表顶部添加另一个项目。长时间单击一个项目将在列表底部添加另一个项目

在列表底部添加项目(长时间单击)时,一切正常。在列表顶部添加项目(简单单击)时,有3种情况:

  • 如果列表滚动到底部,则一切正常。将项目添加到顶部后,列表仍会滚动到底部
  • 如果列表未滚动到底部(或顶部),则列表将保持其滚动y位置(从顶部)。这会使列表显示为向上“跳转”一项。在这种情况下,我希望列表不要“向上跳”,也就是说,我希望它保持从底部开始的滚动y位置
  • 如果列表滚动到顶部,则与案例2中的情况相同。首选行为与案例2相同
(最后两个案例事实上是相同的。我将它们分开,因为案例2中可以更好地演示问题。)

代码

public class MyListActivity extends Activity {
  int topIndex = 1;

  int bottomIndex = 20;

  protected final void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_list_activity);

    final ListView list = (ListView) findViewById(R.id.list);
    list.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
    list.setStackFromBottom(true);

    final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1);
    for (int i = topIndex; i <= bottomIndex; i++) {
      adapter.add(Integer.toString(i));
    }
    list.setAdapter(adapter);

    list.setOnItemClickListener(new OnItemClickListener() {
      @Override
      public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        adapter.insert(Integer.toString(--topIndex), 0);
      }
    });

    list.setOnItemLongClickListener(new OnItemLongClickListener() {
      @Override
      public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        adapter.add(Integer.toString(++bottomIndex));
        return true;
      }
    });
  }
}
公共类MyListActivity扩展活动{
int topIndex=1;
指数=20;
创建时受保护的最终void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.my_list_活动);
最终ListView列表=(ListView)findViewById(R.id.list);
list.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
list.setStackFromBottom(true);
最终ArrayAdapter=新的ArrayAdapter(这是android.R.layout.simple_list_item_1,android.R.id.text1);
for(int i=topIndex;i arg0,视图arg1,int arg2,长arg3){
add(Integer.toString(++bottomIndex));
返回true;
}
});
}
}

首先获取firstVisiblePosition(或倒转后的最后一个位置。您需要自己尝试检查),然后获取添加到列表中的视图顶部,然后使用setSe;方法,可以滚动到所需的位置

代码:


编辑以了解更多信息:

我想我发现了你的问题。我使用独立列表作为XML中的唯一视图复制了您描述的用例:

<ListView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
</ListView>

我通过将布局嵌套在父布局中,解决了遵循所需用例的问题:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

</FrameLayout>

将记录插入适配器后,您可以尝试使用

   yourListView.setSelection(yourAdapter.getCount() - 1);

我已经弄明白了。无论您在哪里获得新数据,在更改适配器中的数据之前,我都会调用以下命令:

int firstVisibleItem = list.getFirstVisiblePosition();
int oldCount = adapter.getCount();
View view = list.getChildAt(0);
int pos = (view == null ? 0 :  view.getBottom());
然后我叫:

adapter.setData(data);

list.setSelectionFromTop(firstVisibleItem + adapter.getCount() - oldCount + 1, pos);

当我的活动初始化时,我将适配器设置为列表一次。之后,将新数据添加到现有适配器中,后跟notifyDataChanged()。正如我所说,列表已经保持滚动位置。然而,它是从顶部这样做的。我需要它来保持从底部滚动的位置。这里的差异不是100%清楚。因为在您的问题中,您指出“即,当较旧的消息添加到顶部时,滚动视图应显示与添加较旧消息之前显示的相同的消息”;如果保持滚动位置,则保持当前滚动位置-周期。如果滚动位置没有改变,那么从“顶部位置”和“底部位置”保持滚动位置实际上没有什么区别。将新项目添加到ListView时,它将保持滚动y位置。这意味着在添加项目后,它将被向下滚动到与以前完全相同的数量。将项目添加到列表底部时,这非常有效。也就是说,在添加新项目之前和之后(在底部),相同的项目都是可见的。但是,由于我正在将项目添加到列表的顶部,所以在添加项目(到顶部)之后,维护的滚动y将显示不同的项目。我已经测试了两个用例,即addToTop是adapter.insert(项目,0);和addToBottom是适配器。添加(项);,我的测试项目不是这样的。另外,我使用android:stackFromBottom=“true”和android:transcriptMode=“normal”扩展了ListView,我得到了您想要的功能-因此,如果没有看到源代码,很难说问题出在哪里。明天我将尝试复制代码的重要部分。代码的一个区别是我没有使用ArrayAdapter。我正在使用BaseAdapter的自定义子类。H