在Android中LayoutInflater的用途是什么?


当前回答

LayoutInflator做什么?

当我第一次开始Android编程时,我真的对LayoutInflater和findViewById感到困惑。有时我们用一种,有时用另一种。

LayoutInflater用于从您的xml布局之一创建一个新的视图(或布局)对象。 findViewById只是给你一个已经创建的视图的引用。你可能认为你还没有创建任何视图,但无论何时你在onCreate中调用setContentView,活动的布局及其子视图在幕后被膨胀(创建)。

如果视图已经存在,那么使用findViewById。如果不是,那么使用LayoutInflater创建它。

例子

这是我做的一个小项目,显示LayoutInflater和findViewById在行动。在没有特殊代码的情况下,布局如下所示。

蓝色的正方形是一个自定义布局插入到主布局包含(见这里更多)。它是自动膨胀的,因为它是内容视图的一部分。如您所见,代码没有什么特别之处。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

现在让我们膨胀(创建)自定义布局的另一个副本并将其添加进来。

LayoutInflater inflater = getLayoutInflater();
View myLayout = inflater.inflate(R.layout.my_layout, mainLayout, false);

为了膨胀新的视图布局,我所做的只是告诉膨胀器我的xml文件的名称(my_layout),我想要添加它的父布局(mainLayout),以及我实际上还不想添加它(false)。(我也可以设置父为空,但然后我的自定义布局的根视图的布局参数将被忽略。)

这里是它的背景。

public class MainActivity extends AppCompatActivity {

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

        // inflate the main layout for the activity
        setContentView(R.layout.activity_main);

        // get a reference to the already created main layout
        LinearLayout mainLayout = (LinearLayout) findViewById(R.id.activity_main_layout);

        // inflate (create) another copy of our custom layout
        LayoutInflater inflater = getLayoutInflater();
        View myLayout = inflater.inflate(R.layout.my_layout, mainLayout, false);

        // make changes to our custom layout and its subviews
        myLayout.setBackgroundColor(ContextCompat.getColor(this, R.color.colorAccent));
        TextView textView = (TextView) myLayout.findViewById(R.id.textView);
        textView.setText("New Layout");

        // add our custom layout to the main layout
        mainLayout.addView(myLayout);
    }
}

注意findViewById是如何仅在布局已经膨胀之后使用的。

附加的代码

下面是上面示例的xml。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main_layout"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <!-- Here is the inserted layout -->
    <include layout="@layout/my_layout"/>

</LinearLayout>

my_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:background="@color/colorPrimary">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:padding="5dp"
        android:textColor="@android:color/white"
        android:text="My Layout"/>

</RelativeLayout>

你什么时候需要LayoutInflater

大多数人最常使用它的时候是在RecyclerView中。(关于列表或网格,请参阅这些RecyclerView示例。)您必须为列表或网格中的每一个可见项目扩展一个新的布局。 如果您想以编程方式添加一个复杂的布局(就像我们在示例中所做的那样),您也可以使用布局膨胀器。您可以在代码中完成这一切,但是先用xml定义它,然后再对它进行膨胀要容易得多。

其他回答

LayoutInflator做什么?

当我第一次开始Android编程时,我真的对LayoutInflater和findViewById感到困惑。有时我们用一种,有时用另一种。

LayoutInflater用于从您的xml布局之一创建一个新的视图(或布局)对象。 findViewById只是给你一个已经创建的视图的引用。你可能认为你还没有创建任何视图,但无论何时你在onCreate中调用setContentView,活动的布局及其子视图在幕后被膨胀(创建)。

如果视图已经存在,那么使用findViewById。如果不是,那么使用LayoutInflater创建它。

例子

这是我做的一个小项目,显示LayoutInflater和findViewById在行动。在没有特殊代码的情况下,布局如下所示。

蓝色的正方形是一个自定义布局插入到主布局包含(见这里更多)。它是自动膨胀的,因为它是内容视图的一部分。如您所见,代码没有什么特别之处。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

现在让我们膨胀(创建)自定义布局的另一个副本并将其添加进来。

LayoutInflater inflater = getLayoutInflater();
View myLayout = inflater.inflate(R.layout.my_layout, mainLayout, false);

为了膨胀新的视图布局,我所做的只是告诉膨胀器我的xml文件的名称(my_layout),我想要添加它的父布局(mainLayout),以及我实际上还不想添加它(false)。(我也可以设置父为空,但然后我的自定义布局的根视图的布局参数将被忽略。)

这里是它的背景。

public class MainActivity extends AppCompatActivity {

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

        // inflate the main layout for the activity
        setContentView(R.layout.activity_main);

        // get a reference to the already created main layout
        LinearLayout mainLayout = (LinearLayout) findViewById(R.id.activity_main_layout);

        // inflate (create) another copy of our custom layout
        LayoutInflater inflater = getLayoutInflater();
        View myLayout = inflater.inflate(R.layout.my_layout, mainLayout, false);

        // make changes to our custom layout and its subviews
        myLayout.setBackgroundColor(ContextCompat.getColor(this, R.color.colorAccent));
        TextView textView = (TextView) myLayout.findViewById(R.id.textView);
        textView.setText("New Layout");

        // add our custom layout to the main layout
        mainLayout.addView(myLayout);
    }
}

注意findViewById是如何仅在布局已经膨胀之后使用的。

附加的代码

下面是上面示例的xml。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main_layout"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <!-- Here is the inserted layout -->
    <include layout="@layout/my_layout"/>

</LinearLayout>

my_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:background="@color/colorPrimary">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:padding="5dp"
        android:textColor="@android:color/white"
        android:text="My Layout"/>

</RelativeLayout>

你什么时候需要LayoutInflater

大多数人最常使用它的时候是在RecyclerView中。(关于列表或网格,请参阅这些RecyclerView示例。)您必须为列表或网格中的每一个可见项目扩展一个新的布局。 如果您想以编程方式添加一个复杂的布局(就像我们在示例中所做的那样),您也可以使用布局膨胀器。您可以在代码中完成这一切,但是先用xml定义它,然后再对它进行膨胀要容易得多。

当你在ListView中使用自定义视图时,你必须定义行布局。 你创建了一个xml,你放置android小部件,然后在适配器的代码中,你必须这样做:

public MyAdapter(Context context, List<MyObject> objects) extends ArrayAdapter {
  super(context, 1, objects);
  /* We get the inflator in the constructor */
  mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
  View view;
  /* We inflate the xml which gives us a view */
  view = mInflater.inflate(R.layout.my_list_custom_row, parent, false);

  /* Get the item in the adapter */
  MyObject myObject = getItem(position);

  /* Get the widget with id name which is defined in the xml of the row */
  TextView name = (TextView) view.findViewById(R.id.name);

  /* Populate the row's xml with info from the item */
  name.setText(myObject.getName());

  /* Return the generated view */
  return view;
}

在官方文档中阅读更多信息。

下面是另一个与前一个类似的示例,但扩展了它以进一步演示它可以提供的膨胀参数和动态行为。

假设你的ListView行布局可以有可变数量的textview。因此,首先膨胀基本项视图(就像前面的例子一样),然后在运行时动态循环添加textview。使用android:layout_weight额外对齐一切完美。

以下是布局资源:

list_layout.xml

<?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="wrap_content" 
    android:orientation="horizontal" >
    <TextView 
        android:id="@+id/field1"
        android:layout_width="0dp"  
        android:layout_height="wrap_content" 
        android:layout_weight="2"/>
    <TextView 
        android:id="@+id/field2"
        android:layout_width="0dp"  
        android:layout_height="wrap_content" 
        android:layout_weight="1"
/>
</LinearLayout>

schedule_layout.xml

<?xml version="1.0" encoding="utf-8"?>
   <TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="0dp"  
    android:layout_height="wrap_content" 
    android:layout_weight="1"/>

重写BaseAdapter类扩展中的getView方法

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = activity.getLayoutInflater();
    View lst_item_view = inflater.inflate(R.layout.list_layout, null);
    TextView t1 = (TextView) lst_item_view.findViewById(R.id.field1);
    TextView t2 = (TextView) lst_item_view.findViewById(R.id.field2);
    t1.setText("some value");
    t2.setText("another value");

    // dinamically add TextViews for each item in ArrayList list_schedule
    for(int i = 0; i < list_schedule.size(); i++){
        View schedule_view = inflater.inflate(R.layout.schedule_layout, (ViewGroup) lst_item_view, false);
        ((TextView)schedule_view).setText(list_schedule.get(i));
        ((ViewGroup) lst_item_view).addView(schedule_view);
    }
    return lst_item_view;
}

注意不同的膨胀方法调用:

inflater.inflate(R.layout.list_layout, null); // no parent
inflater.inflate(R.layout.schedule_layout, (ViewGroup) lst_item_view, false); // with parent preserving LayoutParams

Layout inflater是一个读取xml外观描述并将其转换为基于java的View对象的类。

充气机的作用

它接受一个xml布局作为输入(比如说)并将其转换为视图对象。

为什么需要

让我们设想一个场景,我们需要创建一个自定义列表视图。现在每一行都是自定义的。但是我们怎么做呢?不可能将xml布局分配给列表视图的一行。我们创建了一个View对象。因此,我们可以访问其中的元素(textview,imageview等),也分配对象作为列表视图行

无论何时我们需要在某处分配视图类型对象我们有自定义xml设计我们只需将它转换为object by inflater并使用它。