【问题标题】:Keeping the last visible item of a ListView when the size of the ListView changes当 ListView 的大小发生变化时,保留 ListView 的最后一个可见项
【发布时间】:2012-02-19 17:43:34
【问题描述】:

我想要的很简单,至少我认为它会很简单。 我只想要一个 EditText 位于屏幕底部的窗口,而其余空间则由 ListView 填充。 不幸的是,它没有按我的预期工作。我想要的是下面的图像。在 XML 中是否有任何简单的方法可以做到这一点,或者我应该为此编写一些特殊的代码?

我有问题的 Android 源代码。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
    android:id="@+id/demolist"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
     >
</ListView>
    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:layout_weight="0">
    </EditText>
</LinearLayout >

【问题讨论】:

  • 你找到方法了吗?我正在寻找完全相同的行为。

标签: android listview layout


【解决方案1】:

你想要什么

没有键盘的最后一个可见项目

使用键盘的最后一个可见项

输入较大的最后一个可见项目

我是这样做的。

总结

  • 在列表视图中设置 android:stackFromBottom="true"android:transcriptMode="alwaysScroll"
  • 在Activity中设置android:windowSoftInputMode="adjustPan"
  • 在添加新聊天时调用 adapter.notifyDataSetChanged();,以便列表视图始终滚动到最后一个位置
  • 在 EditText 中设置 android:inputType="textMultiLine" & android:maxLines="4" 以增加文本框

详情

活动

<activity android:name="MainActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan"></activity>

聊天视图

<RelativeLayout
    android:id="@+id/chat_header"
    android:layout_width="fill_parent"
    android:layout_height="40dp"
    android:padding="3dp" >

    <Button
        android:id="@+id/btnBackChat"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:layout_alignParentLeft="true"
        android:background="@drawable/back_button" />

    <TextView
        android:id="@+id/txt_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/btnBackChat"
        android:layout_centerHorizontal="true"
        android:textColor="@android:color/white"
        android:textSize="18sp" />

    <ImageView
        android:id="@+id/imgViewProfileChat"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:layout_alignParentRight="true"
        />
</RelativeLayout>

<ListView
    android:id="@+id/listView1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_above="@+id/form"
    android:layout_below="@+id/chat_header"
    android:stackFromBottom="true"
    android:transcriptMode="alwaysScroll"/>

<RelativeLayout
    android:id="@+id/form"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:orientation="vertical" >

    <ImageButton
        android:id="@+id/btnAttach"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:contentDescription="@string/attach"
        android:src="@drawable/attach_icon" />

    <EditText
        android:id="@+id/txtChat"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@+id/btnSend"
        android:layout_toRightOf="@+id/btnAttach"
        android:inputType="textMultiLine"
        android:maxLines="4" />

    <Button
        android:id="@+id/btnSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="@string/send" />
</RelativeLayout>

【讨论】:

  • 正如我在另一条评论中已经提到的,我宁愿避免使用 android:windowSoftInputMode="adjustPan"。另外,您的解决方案不是总是滚动到列表底部吗?当窗口缩小时,我希望列表视图窗口底部的任何项目都保持在窗口底部,NOT 让列表保持滚动到列表的末尾。 (注意,我的应用程序不是像 OP 那样的聊天应用程序——我希望将新项目插入到用户正在查看的列表中,而不是在底部。)
  • 您也可以在此解决方案中使用 android:windowSoftInputMode="adjustResize"。然后操作栏不会消失。
【解决方案2】:

我想你可能想在显示键盘之前使用AdapterView.getLastVisiblePosition()

这里有两个解决方案:

  • 在 EditText 上单击
  • 感谢监听您的 ListView 的 OnScrollListener(及其 onScroll 函数)

然后在 EditText 焦点后使用AdapterView.setSelection 跳转到此项(或索引 - 2 或索引 - 3)。

【讨论】:

  • 奖励赏金,因为他是唯一理解这个问题的人。我已经尝试过使用 OnScrollListener,并使用 OnLayoutChangeListener 来检测键盘的外观。我又试了一次,但我仍然对结果不满意。 SetSelection() 和 smoothScrollToPosition() 等似乎不可靠:-( 我想我可能不得不完全放弃这个 UI。
  • 也不要忘记请求焦点。 lstMessages.requestFocus();然后清除它并在需要时请求其他元素焦点
  • 你给我the right clue to achieve this and keep adjustResize,谢谢。我只是通过使用 setSelectionFromTop() 而不是 setSelection() 来显示编辑文本上方的最后一项来进行一些更改。
【解决方案3】:

您可以在 AndroidManifest.xml 中使用android:windowSoftInputMode="adjustPan",如下所示

 <activity android:name="com.example.SimpleListView"
            android:label="@string/app_name"
            android:windowSoftInputMode="adjustPan" >

这应该会产生您想要的输出。官方android documentation.

如果您想自动滚动到列表视图的末尾,您可以选择为列表视图指定脚本模式(如果您正在使用某些聊天功能)。您也可以查看android:transcriptMode="normal",看看是否合适。

一个相关的SO answer。官方android doc

<ListView
android:id="@+id/comments"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transcriptMode="alwaysScroll">

这应该可以解决您的大部分问题,也许除了我没有测试过的最后一个问题(在图片中)。

【讨论】:

  • 我已经尝试过adjustPan,但这会将操作栏滚动到视野之外,并且在键盘可见的情况下滚动列表会很尴尬。不幸的是,转录模式也没有用——我不希望它滚动到列表的底部。 (我认为 OP 已经很好地说明了这一点,这就是为什么我在他的帖子中添加了赏金而不是开始我自己的帖子。)我需要最后一个 visible 项目在键盘出现。
  • Hmm.. 很棘手,您可以尝试检测软键盘高度并手动调整列表视图的高度,设置最后一个可见位置(如果可能,使用 setSelectionFromTop)。虽然有点骇人听闻的解决方案。相关 SO 帖子stackoverflow.com/questions/13534365/…
【解决方案4】:

只需重写 ListView#onSizeChanged(int w, int h, int oldw, int oldh)的方法,如下: PS:你的ListView的高度在任何时候都不能为0,如果可能为0,则不起作用。

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (getChildCount() == 0) {
        return;
    }
    final int lastVisiblePosition = getLastVisiblePosition();
    int firstVisiblePosition = getFirstVisiblePosition();

    View last = getChildAt(lastVisiblePosition - firstVisiblePosition);
    int top = last.getTop();
    final int newTop = top + (h - oldh);
    Log.d(getClass().getSimpleName(), "onSizeChanged(" + w + ", " + h + ", " + oldw + ", " + oldh + ")");
    if (mFocusSelector == null) {
        mFocusSelector = new FocusSelector();
    }
    post(mFocusSelector.setup(lastVisiblePosition, newTop));
    super.onSizeChanged(w, h, oldw, oldh);
}

private class FocusSelector implements Runnable {
    private int mPosition;
    private int mPositionTop;

    public FocusSelector setup(int position, int top) {
        mPosition = position;
        mPositionTop = top;
        return this;
    }

    public void run() {
        setSelectionFromTop(mPosition, mPositionTop);
    }
}

【讨论】:

    【解决方案5】:

    实际上,通过在清单android:windowSoftInputMode="adjustResize"ListView 声明android:transcriptMode="normal" 中添加Activity 声明,我得到了您需要的相同行为。用于键盘和方向更改。

    【讨论】:

      【解决方案6】:

      我也需要这个功能。这是我的尝试,但效果不佳——滚动有点不稳定(并不总是最终到达应有的位置),并且在旋转屏幕时会干扰滚动位置的保留。

      @Override
      public void onResume() {
          super.onResume();
          getListView().addOnLayoutChangeListener(stickyLastItemHandler);
      }
      
      @Override
      public void onPause() {
          super.onPause();
          getListView().removeOnLayoutChangeListener(stickyLastItemHandler);
      }
      
      View.OnLayoutChangeListener stickyLastItemHandler = new View.OnLayoutChangeListener() {
          @Override
          public void onLayoutChange(View v,
                  int left, int top, int right, int bottom,
                  int oldLeft, int oldTop, int oldRight, int oldBottom) {
      
              final int heightChange = (bottom - top) - (oldBottom - oldTop);
              if (heightChange != 0) {
                  ((AbsListView)v).smoothScrollBy(-heightChange, 1000);
              }
          }
      };
      

      【讨论】:

      • 您希望最后一个项目如何表现,捕捉以使其完全可见或保持当前状态(如一半,只有几个像素等)?
      • 捕捉到完全可见可能是最整洁的,我想。我的应用程序是一个任务列表,使用编辑框应该在最后一个可见任务的下方添加一个新任务。 (在输入文本后,我会将新项目向上滚动到视图中。)
      【解决方案7】:

      可以通过xml来实现:

      <ListView
          android:stackFromBottom="true"
          android:layout_height=0dp
          android:layout_weight=1 />
      

      从底部堆叠将使 ListView 默认向下滚动, 设置 ListView 的权重将填充父垂直 LinearLayout 中所有可能的空间。

      【讨论】:

      • 不幸的是,这不能满足我的需要。列表最初会滚动到底部,但是当软键盘出现时,其行为与没有此标志时的行为相同。
      【解决方案8】:

      我为 Android 创建了一个布局引擎,它允许您监听键盘隐藏和可见状态的变化(以及许多其他核心功能)。该库位于http://phil-brown.github.io/AbLE/

      只需将jar 添加到您的libs 目录(如果该文件夹不存在,则创建此文件夹),而不是从Activity 继承,而是从AbLEActivity 继承。接下来,重写 onKeyboardShownonKeyboardHidden 方法,在这些方法中,您可以根据需要向上或向下设置内容视图的动画:

      @Override
      public void onKeyboardShown() {
          //animate up
      }
      
      @Override
      public void onKeyboardHidden() {
          //animate down
      }
      

      【讨论】:

      • 这给了我什么 OnLayoutChangeListener 没有?
      【解决方案9】:
      Follow these steps
      --------------------
      1. Update your activity in AndroidManifest.xml file like below
      <activity
                  android:name=".MyListActivity"
                  android:windowSoftInputMode="adjustResize" />
      
      2. Set your list view with like below.
      mListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
      mListView.setStackFromBottom(true);
      

      希望它能解决你的问题。

      【讨论】:

        【解决方案10】:

        假设您知道列表数据何时发生更改,您可以通过将列表选择设置为最后一行来手动告诉列表滚动到底部。像这样的:

        private void scrollMyListViewToBottom() {
            myListView.post(new Runnable() {
                @Override
                public void run() {
                    // Select the last row so it will scroll into view...
                    myListView.setSelection(myListAdapter.getCount() - 1) ;
                }
            });
        }
        

        或者你也可以试试这个:

        ChatAdapter adapter = new ChatAdapter(this);
        
        ListView lview = (ListView) findViewById(R.id.chatList);
        lview .setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
        lview .setAdapter(adapter);
        
        adapter.registerDataSetObserver(new DataSetObserver() {
            @Override
            public void onChanged() {
                super.onChanged();
                lv.setSelection(adapter.getCount() - 1);    
            }
        });
        

        【讨论】:

        • 另一个没有阅读问题的人。我不希望它滚动到列表底部。
        • 那么您到底想要什么,请您以图形模式分享...谢谢
        • 问题中的图表完美地解释了它。特别注意标有“无键盘”和“我想要这个”的图表,并注意“最后一个可见项目”在这些图表中的位置。请注意,“最后一个可见项”不是列表中的最后一项。
        【解决方案11】:

        在您的 ListAdapter 中,您可以执行以下操作:

           public View getView(int position, View convertView, ViewGroup parent) {
        
        //buttonMore
        if( position >= super.getCount()  )
              return textField ;
        
            ....
        

        这样,您将始终将列表中的最后一项作为文本字段。

        【讨论】:

        • 没有。请仔细看我的照片。我特别提到它不是最后一个项目,而是最后一个“可见”项目。查看图片的最左侧。
        • 你会得到你想要的。我给你的解决方案使文本字段成为列表中的最后一项。如果不可见,因为用户没有向下滚动到列表的末尾,那么它是不可见的......
        【解决方案12】:

        你需要设置选择位置......但是 - 以一种奇怪的方式,我真的不明白为什么 - 你需要用 post() 来做

        public void ScrollToLast() {
            yourList.clearFocus();
            yourList.post(new Runnable() {
                @Override
                public void run() {
                    yourList.setSelection(items.size() - 1);
                }
            });
        }
        

        所以你可以为你的editText添加一个TextWatcher,你可以在那里调用这个方法......所以你的列表将滚动到底部。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-07
          相关资源
          最近更新 更多