ScrollView在子元素更改后无法直接调整大小

14

我遇到了一个非常烦人的ScrollView调整大小的问题,而且我已经没有可能的解决方案了。

我有一个包含几个不同Fragment的FragmentPager,其中一个Fragment有一个ScrollView。带有ScrollView的Fragment由Spinner和包含其他View的LinearLayout(如SeekBar、Button、EditText等)的多行组成。根据Spinner中选择的选项,ScrollView会显示不同的视图。为此,某些视图的可见性会被设置为Gone,而其他视图则会被设置为Visible。这样做效果很好,但是事实上ScrollView在使用Spinner选择不同选项时似乎无法正确调整大小。

当ScrollView充满视图并且可以滚动时,如果用户选择一个显示少于填充ViewPort所需的View的选项,ScrollView仍然会滚动。当用户再次选择旧选项时,由于它采用了先前选项所需的大小,因此ScrollView现在无法滚动。然后,当用户再次选择相同的选项时,ScrollView突然变得可滚动,因为它现在已调整为实际所需的大小。

这里发生了什么?更好的方法是,我该如何解决这个烦人的问题?

我的布局:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/control_scroll"
    android:layout_width="fill_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:background="#99000000"
    android:fillViewport="true" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="20dp"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#DD000000"
            android:gravity="bottom"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/lamp_choose_tv"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="2dp"
                android:paddingLeft="5dp"
                android:text="@string/choose_lamp_text"
                android:textColor="#FFFFFF"
                android:textSize="14sp" />
        </LinearLayout>

        <Spinner
            android:id="@+id/lamp_select_spinner"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:textColor="#FFFFFF" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#DD000000"
            android:gravity="bottom"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/lamp_settings_tv"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="2dp"
                android:background="#44000000"
                android:paddingLeft="5dp"
                android:text="@string/lamp_settings_text"
                android:textColor="#FFFFFF"
                android:textSize="14sp" />
        </LinearLayout>
        <!-- Lamp name -->

        <LinearLayout
            android:id="@+id/naam_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="bottom"
            android:paddingBottom="3dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:paddingTop="3dp" >

            <TextView
                android:id="@+id/bridge_naam"
                style="@style/ConfigText"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/config_lightname"
                android:textColor="#FFFFFF"
                android:textSize="16sp" />

            <EditText
                android:id="@+id/lamp_naam_input"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="2"
                android:background="@drawable/tasstextfield"
                android:inputType="textNoSuggestions"
                android:textColor="#FFFFFF" >
            </EditText>
        </LinearLayout>

        <View
            android:id="@+id/separator"
            android:layout_width="fill_parent"
            android:layout_height="0.5dp"
            android:background="@android:color/black"
            android:visibility="visible" />
<!-- Rest of the views -->

我已经尝试过的方法:

  • 在父级ScrollView上使用ForceLayout/requestLayout
  • 使ScrollView失效
  • 在包含的LinearLayout上使用ForceLayout/requestLayout
  • 使LinearLayout失效
  • 使ScrollView的所有子元素失效
  • 在ScrollView的所有子元素上使用ForceLayout/requestLayout

你尝试过使用ListView而不是构建自己的列表视图吗?忽略该页面上的Loader示例--你可以简单地创建一个包含每个文本片段的String[]List<String>,并将其提供给ArrayAdapter,然后重写getView来控制外观。 - quietmint
我能理解你为什么会这样想。实际上,我已经停止使用TextViews,因为那只是用于测试目的,现在我正在使用不同的视图,如按钮、编辑框和拖动条。然而,问题仍然存在。 - Sander van't Veer
我认为问题在于ScrollView需要调整大小以适应新的视图。当设置内部视图为View.VISIBLE时,请在父ScrollView上调用requestLayout()forceLayout()来安排布局过程。 - DeeV
我更新了我的问题并附上了我已经尝试的东西,似乎第一次没有生效。遗憾的是,我已经在所有的视图上尝试了invalidate、requestLayout和forceLayout,但问题仍然存在。 - Sander van't Veer
嗯...你可以尝试另一种选择,不要使用View.GONE,而是根据需要添加/删除它们。这可能是最终强制调整大小的方法。虽然会增加一些额外开销,但并不比你已经尝试做的多。只需保留Views的引用,就不必重新填充它们。只需将它们放入即可。 - DeeV
显示剩余4条评论
4个回答

4

这可能不是最令人愉快的解决方案,但它可能有效。

使用处理程序发布延迟消息以再次刷新视图,就好像用户在单击刷新按钮后两次选择相同选项。

// The code when the user wants to update the views 
myButton.setOnClickListener(new OnClickListener{
  @Override
  public void onClick(View view) {
    updateView();
    Message m = Message.obtain();
    m.what = UPDATE_TAG;
    mHandler.sendMessageDelayed(msg, 200);
  }
})

...

final int UPDATE_TAG = 1;

public void updateView() {
  // Code where View.GONE and View.VISIBLE + invalidate is used.
}

Handler mHandler = new Handler {
  @Override
  public void handleMessage(Message input_msg) {
    if(msg.what == UPDATE_TAG) {
      updateView();
    }
  }
}

愉快吗?不是。但它确实有效!非常感谢,希望这个答案也能帮助其他人。 - Sander van't Veer
我只需要在更新的视图中改变可见性,不需要进行任何无效操作。 - MinceMan
我仍然认为这个 bug 一定有更好的解决方案。 - MinceMan

1
尝试使用这些更改。
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/control_scroll"
android:layout_width="fill_parent"
android:layout_height="wrap_content"

android:background="#99000000"
android:fillViewport="true" >

退出 android:layout_weight="1"



我尝试的第一件事是使用wrap_content,但这并没有解决问题。 - Sander van't Veer

0

我猜测LinearLayout使用某种缓存机制来避免每次测量子元素。或者我可能是错的。

然而,你可以尝试使用RelativeLayout代替一堆LinearLayout。这将解决你的问题,也会更加高效,因为嵌套的ViewGroup对性能不利。


0

您可以将ScrollView的高度设置为“wrap_content”,这样即使子视图发生变化,ScrollView也可以调整到新的设置


1
我尝试的第一件事,显然。但这并没有解决问题。 - Sander van't Veer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接