【问题标题】:how to work around change in list item layout behavior change between sdk 17 and 18?如何解决 sdk 17 和 18 之间列表项布局行为变化的变化?
【发布时间】:2014-02-21 22:09:11
【问题描述】:

我创建了一个简单的应用程序来说明在 SDK 17 和 SDK 18 之间包装在 RelativeLayout 中时 LinearLayout 的行为方式的变化。首先,屏幕截图:

targetSdkVersion 为“17”时:

targetSdkVersion 为“18”时:

这个活动的布局是:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00ffff"
    android:orientation="vertical" >

    <include layout="@layout/test_list_item"/>

    <ListView
        android:id="@+id/listview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:overScrollMode="never"
        android:scrollingCache="false" >
    </ListView>

</LinearLayout>

text_list_item.xml 是:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#ff0000" >

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="#ffff00"
        android:padding="10dp"
        android:text="foobar"
        android:textColor="#000000" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignBottom="@id/text"
        android:layout_alignTop="@id/text"
        android:background="#00ff00"
        android:orientation="horizontal"
        android:weightSum="1.0" >

        <View
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="0.5"
            android:background="#0000ff" />
    </LinearLayout>

</RelativeLayout>

Activity 的 onCreate() 如下所示:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);
    findViewById(R.id.text).bringToFront();
    final ListView mListView = (ListView) findViewById(R.id.listview);
    mListView.setAdapter(new TestAdapter());
}

TestAdapter 看起来像这样:

public class TestAdapter extends BaseAdapter {

    private static final int COUNT = 3;

    @Override
    public int getCount() {
        return COUNT;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public boolean areAllItemsEnabled() {
        return true;
    }

    @Override
    public boolean isEnabled(int position) {
        return false;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_list_item, null);
        view.findViewById(R.id.text).bringToFront();
        return view;
    }

当 target = 17 时,LinearLayout 的子视图在位于 ListView 内部和在 ListView 外部时都被正确调整为可用宽度的 50%。当 target = 18 用作列表项的布局时,它不会调整大小。在这种情况下,它的宽度保持在“0dp”并且永远不会调整大小。

理想情况下,我希望以 SDK 19 为目标。不过,为了做到这一点,我以某种方式适应这种行为变化。有什么想法可以实现吗?

顺便说一句,布局如此复杂和“怪异”是有原因的;我意识到有更直接的方法可以实现这种特殊的视觉效果。

编辑

提供更多关于我为什么使用这种病态布局的信息:它用于“仪表”。 LinearLayout 中的 View 使用 ScaleAnimation 进行拉伸或收缩,使其占据总可用宽度的所需百分比。棘手的是我需要在仪表顶部覆盖文本,并且我希望仪表的高度由文本确定。

因此,外部的 RelativeLayout 应该“与其父级一样宽”,但只能“与其包含的 TextView 一样高”。该 RelativeLayout 内部的 LinearLayout 应该与其父级一样宽和高。 LinearLayout 中的 View 应该和包含它的 LinearLayout 一样高,但只有一半宽。

这正是我在 SDK 18+ 中针对 SDK 17和/或时得到的行为,当布局不用作 ListView 项的内容时。

编辑

发在开发者群:

https://groups.google.com/forum/#!msg/android-developers/KuoY5Tklyms/TbNq6ndD_PwJ

【问题讨论】:

  • 似乎没有为 ListView 内的视图调用 onLayout()?我想知道有什么方法可以验证吗?也许您可以在填充 ListView 后调用它,看看它是否“修复”了一些事情。

标签: android android-layout android-listview android-linearlayout android-compatibility


【解决方案1】:

改变

final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_list_item, null);

到:

final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_list_item, parent, false);

而且我认为你的运气会更好。


更新

好吧,这就是我可以告诉你的。

如果您启动 Hierarchy View,您将看到提供蓝色背景的 View 在“内部 ListView”场景中的高度为 0。根据您问题的其他答案,宽度是正确的,这表明问题与重量无关。

如果您查看 the source code to RelativeLayout,他们确实在 android:targetSdkVersion 17 与 18+ 之间的分界线上添加了一些改变行为的智能。我的猜测是,在某些与ListView 容器相关的情况下,您正在绊倒mAllowBrokenMeasureSpecs,但这只是一个猜测。我有足够的时间去摸索容器的所有布局/测量内容,当它是一个简单的容器时,我写的,当我睡了一整夜——在有限的睡眠中理解 RelativeLayout 超出了我的范围能力。我已经戳了戳你的样本,尝试了各种方法,看看它们是否能解决问题,但没有结果。

关于你在我戳这个时发布的编辑:

  • 原则上,基本布局不需要是RelativeLayoutFrameLayout 也应该可以工作,因为您需要做的就是能够在 Z 轴上堆叠东西并能够使您的标签居中。话虽如此,当我沿着这条路走下去时,我得到了更奇怪的结果,尽管这些结果似乎与 targetSdkVersion 无关,所以我可能只是在某个地方搞砸了

  • 即使你坚持使用RelativeLayout,你也不应该需要LinearLayout。在运行时,你知道你的宽度,所以你应该动画仪表的宽度,而不是它的重量。因此,只需将View 放在RelativeLayout(或FrameLayout)中就足够了。

但我会欣然同意这是一种奇怪的行为。

【讨论】:

  • 似乎没有什么不同。不过,无论如何,我可能应该按照您推荐的方式进行操作。另外,我发现另一个人遇到了(我认为)同样的问题:stackoverflow.com/questions/17993415/…
  • @user190758:如果在布局资源中使用RelativeLayout作为根元素,强烈推荐使用三参数inflate()方法。这就是为什么我认为它可能会解决您在这里遇到的问题。如果您创建了一个完整的项目(ZIP 文件、GitHub 存储库等)来重现您的问题,我有兴趣看看它。
  • 我明天会把它打包并在此处发布一个 zip 文件。感谢您的观看!
  • 决定我等不及了。给你:drive.google.com/file/d/0B6DvDY2BvxUTZHUxTHkzNUZvVDg
  • RelativeLayout 文档中可能有一条线索,内容如下: 注意:在平台版本 17 及更低版本中,RelativeLayout 受到测量错误的影响,该错误可能导致使用不正确的 MeasureSpec 值测量子视图. ... 这是在将 RelativeLayout 容器放置在滚动容器(例如 ScrollView 或 Horizo​​ntalScrollView)中时触发的。如果将未配备以使用 MeasureSpec 模式 UNSPECIFIED 正确测量的自定义视图放置在 RelativeLayout 中,则无论如何这将静默工作,因为 RelativeLayout 将传递一个非常大的 AT_MOST MeasureSpec。
【解决方案2】:

看起来因为它只是一个视图,不需要渲染任何内容,因此跳过了一些优化测量方法。将 View 更改为 TextView 并设置一些文本,您会看到它确实按预期显示。我确实理解 LinearLayout 以不同的方式呈现视图(不确定那是正确的还是 ListView 是正确的)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff0000" >

<TextView
    android:id="@+id/text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:background="#ffff00"
    android:padding="10dp"
    android:text="foobar"
    android:textColor="#000000" />

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignBottom="@id/text"
    android:layout_alignTop="@id/text"
    android:background="#00ff00"
    android:orientation="horizontal"
    android:weightSum="1.0" >

    <TextView
        android:id="@+id/testView"
        android:layout_width="0dp"
        android:layout_height="fill_parent"
        android:layout_weight="0.5"
        android:background="#0000ff"
        android:singleLine="false" />
</LinearLayout>

 @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_list_item, null);

        view.findViewById(R.id.text).bringToFront();
        TextView testView = (TextView)view.findViewById(R.id.testView);
        testView.setText("some test text for viewNo:"+String.valueOf(position));

        return view;
    }

【讨论】:

  • 看起来你甚至不需要文本。采用完整的项目(请参阅我的回答中的 cmets)并简单地将 View 替换为 TextView 在外部和内部场景之间给出相同的结果。很好的发现!希望@user190758 可以重现这些结果。
  • 是的,但是它是单行渲染的,所以我添加了 2 行文本。
  • 我想我实际上曾经尝试过,但它暴露了一些与我的动画绘制方式有关的其他问题(在旧设备上)。我可以再试一次。还将尝试“空间”视图,因为这似乎是应该使用的那种情况。或者可能是 ImageView,只是不设置图像。
  • 回想起来,我不应该接受这个答案。当内部视图是 TextView 而不是 View 时,SDK 17 和 SDK 18 之间的行为仍然不同。运行我在 CommonsWare 的响应下发布的示例项目。它使用内部 Textview,并且 SDK 18 中的布局仍然“看起来不正确”(与 SDK 17 相比)。
  • 使用 textView.requestLayout() 或设置一些文本。它试图在渲染时优化调用。我猜你只是想知道哪里出了问题,而不是寻找其他方法来显示你发布的内容(因为还有其他方法可以做到)。
【解决方案3】:

我认为layout_weight(尝试int numbers >= 0)的使用方式已经改变。与android:layout_width="0dp"(尝试match_parent)结合使用会更加不稳定。

weight 参数的另一个问题是,某些设备处理它的方式与其他设备不同。

一般来说,我建议仅使用 LinearLayouts,如果可能的话不要使用权重。 你可以用gravity(例如gravity="center")、layout_gravitypaddingmargin做同样的事情,但它可以保存工作。

编辑 - 示例 1:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#ff0000" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#00ff00"
        android:orientation="horizontal">

        <View
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="#0000ff" />

        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:background="#ffff00"
            android:padding="10dp"
            android:text="foobar"
            android:textColor="#000000" />

        <View
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="#00ff00" />
    </LinearLayout>

</LinearLayout>

例子2:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#ff0000" >

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:background="#ffff00"
        android:padding="10dp"
        android:text="foobar"
        android:textColor="#000000" />

</LinearLayout>

【讨论】:

  • “权重参数的另一个问题是,某些设备处理它的方式与其他设备不同”——您对此的证明是,究竟是什么?我有大约 50 台 Android 设备,所以我很想看看你声称的行为的演示。
  • 同上。我从未见过它在 per-device 级别上有不同的表现。但是,正如这个问题所示,不同 SDK 版本的行为有时会有所不同。
猜你喜欢
  • 2013-08-02
  • 1970-01-01
  • 1970-01-01
  • 2022-07-11
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-22
相关资源
最近更新 更多