为什么LayoutInflater忽略了我指定的layout_width和layout_height布局参数?

177
我曾经也遇到了使用LayoutInflater的困难,其他人也是如此:如何在运行时使用LayoutInflator添加视图?
为什么LayoutInflater会忽略我指定的布局参数?例如,为什么我的资源XML中的layout_width和layout_height值没有被遵守?

真的吗-那会不会很疼,还是我选错了地方?只是想分享一下我的结果。必须承认,在常见问题解答页面上并没有特别提到教程。 - andig
3
请将英文文本翻译成中文:查看常见问题解答中第一个问题的最后一段。将其作为问题发布,然后将教程作为答案发布。当然,您可以编辑此问题并将教程剪切粘贴到答案中。如果您能修复这个问题,我会点赞的。 - bigstones
3个回答

409
我已经调查了这个问题,参考了LayoutInflater文档并设置了一个小的演示项目。以下教程展示了如何使用LayoutInflater动态填充布局。
在我们开始之前,先看一下 LayoutInflater.inflate() 参数的样式:
  • resource: 要加载的 XML 布局资源的 ID(例如, R.layout.main_page)。
  • root: 用于生成的视图层次结构的可选父级视图(如果attachToRoottrue),或者仅是提供一组LayoutParams值以用于返回的层次结构的根视图的对象(如果attachToRootfalse)。
  • attachToRoot: 是否应将充气层次结构附加到根参数? 如果为false,则仅使用root来创建XML中根视图的正确子类LayoutParams

  • 返回值: 充气层次结构的根View。如果提供了root并且attachToRoottrue,则此值为root;否则它是填充的XML文件的根。

现在我们来看样例布局和代码。
主要布局(main.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</LinearLayout>

如果从XML中成功应用布局参数 (red.xml),则此容器中添加了一个单独的TextView,以小红色方块的形式可见:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="25dp"
    android:layout_height="25dp"
    android:background="#ff0000"
    android:text="red" />
现在,LayoutInflater 是用于多种调用参数的。
public class InflaterTest extends Activity {

    private View view;

    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setContentView(R.layout.main);
      ViewGroup parent = (ViewGroup) findViewById(R.id.container);

      // result: layout_height=wrap_content layout_width=match_parent
      view = LayoutInflater.from(this).inflate(R.layout.red, null);
      parent.addView(view);

      // result: layout_height=100 layout_width=100
      view = LayoutInflater.from(this).inflate(R.layout.red, null);
      parent.addView(view, 100, 100);

      // result: layout_height=25dp layout_width=25dp
      // view=textView due to attachRoot=false
      view = LayoutInflater.from(this).inflate(R.layout.red, parent, false);
      parent.addView(view);

      // result: layout_height=25dp layout_width=25dp 
      // parent.addView not necessary as this is already done by attachRoot=true
      // view=root due to parent supplied as hierarchy root and attachRoot=true
      view = LayoutInflater.from(this).inflate(R.layout.red, parent, true);
    }
}

参数变化的实际结果在代码中有记录。

概述:如果调用LayoutInflater时未指定根视图,那么inflate调用将忽略XML中的布局参数。如果使用不为null并且attachRoot=true的根视图调用inflate,则会加载布局参数,但返回的是根对象,这会阻止进一步的布局更改(除非您可以使用findViewById()找到它)。 因此,您最可能要使用的调用约定是:

loadedView = LayoutInflater.from(context)
                .inflate(R.layout.layout_to_load, parent, false);

为了帮助解决布局问题,强烈推荐使用Layout Inspector


23
非常好的回答。我已经从事Android工作三年了,仍然从中学到了一些东西。 - Reuben Scratton
@andig 为什么“在不指定根视图的情况下调用LayoutInflater会导致从xml中忽略布局参数”的问题?这是什么原因? - Paweł Brewczynski
1
@andig:ActivityContext 的子类,答案中的示例都在 Activity 的范围内,因此为了简化操作,我用 this 替换了 getBaseContext(),因为在这个答案中,它们是等效的。 - Marcin Orlowski
5
容器的layout_width和layout_height都设置为"match_parent";然后注释说"// result: layout_height=wrap_content layout_width=match_parent"。我有遗漏什么吗? - Marian Paździoch
1
感谢您进行研究并分享结果!此外,我支持玛丽安的问题。 - LarsH
显示剩余10条评论

9
andig是正确的,LayoutInflater忽略您的layout_params的常见原因是因为没有指定根。许多人认为可以将null传递给root,这在一些情况下是可以接受的,比如对话框,在创建时无法访问root。然而,一个好的规则是,如果您有root,请将其提供给LayoutInflater。
我写了一篇深入的博客文章,您可以在这里查看:

https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/


1
这在一些场景下是可以接受的,比如对话框。那就是我所需要的。 - rookieDeveloper

0

想要在上面的主要答案中添加
我尝试跟着做,但我的recyclerView开始将每个项目拉伸到屏幕上
我不得不在充气后添加下一行以达到目标

itemLayoutView.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT));

我已经通过xml添加了这些参数,但它没有正常工作
而加上这一行就没问题了


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