This lesson:

- understand how to use the LayoutInflater

 

After learning the SQLite, it’s a good moment to begin learning the List. But before this, it would be useful to learn about LayoutInflater. This knowledge will be useful for extended lists creation. I also recommend you to update your knowledge about LayoutParams in memory.

LayoutInflater - this is a class, which can create a View element from the layout files’ content. The inflate method is that one which does it. There are several implementations of this method with different parameters. But all of them use each other and have the same execution result - the view.

We will consider this implementation - public View inflate (int resource, ViewGroup root, boolean attachToRoot).

As we see, the method accepts three parameters to the input:
resource is a layout files’ ID, which will be used for a View creation. For example: R.layout.main.
root is a parent ViewGroup-element for the created view. This ViewGroup LayoutParams will be passed to the created view.
attachToRoot is a boolean field, which determines if the created view will be bound to root. In case of a true value the root will become a parent of created view. I.e. it is the same as the code root.addView(View). In case of a false value the view will only get LayoutParams from root, but won’t become it’s child.

Let’s try this in practice.

Let’s create a project:

Project name: P0401_LayoutInflater
Build Target: Android 2.3.3
Application name: LayoutInflater
Package name: ru.startandroid.develop.p0401layoutinflater
Create Activity: MainActivity

 

Open main.xml and code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <LinearLayout
        android:id="@+id/linLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Linear Layout: ">
        </TextView>
    </LinearLayout>
    <RelativeLayout
        android:id="@+id/relLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Relative Layout: ">
        </TextView>
    </RelativeLayout>
</LinearLayout>

Here are two ViewGroups on the screen - linLayout and relLayout. Both them have inner textView with relevant text.

 

Let’s create one more layout text.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tvLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="Layout with TextView">
</TextView>

Here is a simple TextView without ViewGroup. We will test LayoutInflater with it.

 

Open MainActivity.java and code:

package ru.startandroid.develop.p0401layoutinflater;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.TextView;

public class MainActivity extends Activity {
  
  final String LOG_TAG = "myLogs";
  
    /** Called when the activity is first created. */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        LayoutInflater ltInflater = getLayoutInflater();
        View view = ltInflater.inflate(R.layout.text, null, false);
        LayoutParams lp = view.getLayoutParams();
        
        Log.d(LOG_TAG, "Class of view: " + view.getClass().toString());
        Log.d(LOG_TAG, "LayoutParams of view is null: " + (lp == null));
        Log.d(LOG_TAG, "Text of view: " + ((TextView) view).getText());
    }
}

Using the getLayoutInflater method we get LayoutInflater, use it to get View element from text.xml and get LayoutParams from the view we have just created.

Take a look at the parameters we used to call the inflate method. We passed the layout’s id and null as a parent element, so the parent binding parameter value is false.

Let’s save and launch the app.

 

Nothing has changed on the screen, because we had converted the layout into a view but hadn’t put it anywhere. It just holds place in a memory.

Let’s look in logs:

Class of view: class android.widget.TextView
LayoutParams of view is null: true
Text of view: Layout with TextView

We can see a class of the created element - TextView. That’s alright, because it was in text.xml. In the next row we can see null instead of LayoutParams. It happened so because we passed null  to the inflate method as a parent. And exactly from parent the view should get LayoutParams. The third row shows TextView’s text. It is the same as in the  text.xml.

 

Let’s change the app a little. We will add our created element into main.xml linLayout. This is done simply by the addView command. 

LayoutParams lp = view.getLayoutParams();
        
LinearLayout linLayout = (LinearLayout) findViewById(R.id.linLayout);
linLayout.addView(view);
        
Log.d(LOG_TAG, "Class of view: " + view.getClass().toString());  

(add only selected rows)

 

We determined linLayout and added an element created with LayoutInflater in it.

Let’s save and launch the app. We will see, that the element was added to the screen into linLayout.

 

Now, let’s try to specify the parent (root) when calling the inflate method. Let’s rewrite onCreate:

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    LayoutInflater ltInflater = getLayoutInflater();

    LinearLayout linLayout = (LinearLayout) findViewById(R.id.linLayout);
    View view1 = ltInflater.inflate(R.layout.text, linLayout, false);
    LayoutParams lp1 = view1.getLayoutParams();

    Log.d(LOG_TAG, "Class of view1: " + view1.getClass().toString());
    Log.d(LOG_TAG, "Class of layoutParams of view1: " + lp1.getClass().toString());
    Log.d(LOG_TAG, "Text of view1: " + ((TextView) view1).getText());

    RelativeLayout relLayout = (RelativeLayout) findViewById(R.id.relLayout);
    View view2 = ltInflater.inflate(R.layout.text, relLayout, false);
    LayoutParams lp2 = view2.getLayoutParams();

    Log.d(LOG_TAG, "Class of view2: " + view2.getClass().toString());
    Log.d(LOG_TAG, "Class of layoutParams of view2: " + lp2.getClass().toString());
    Log.d(LOG_TAG, "Text of view2: " + ((TextView) view2).getText());
  }

We determine linLayout and relLayout and using LayoutInflater create two view-elements from text.xml layout. We specify root for the first one as linLayout and relLayout for the second. But the third parameter value (attachToRoot ) stays false. It means that the created view-element will get LayoutParams from the root-element, but wouldn’t bind to it.

 

Let’s save and launch the project. The screen is the same, because we didn’t add new elements and attachToRoot = false.

 

Look in logs:

Class of view1: class android.widget.TextView
Class of layoutParams of view1: class android.widget.LinearLayout$LayoutParams
Text of view1: Layout with TextView
Class of view2: class android.widget.TextView
Class of layoutParams of view2: class android.widget.RelativeLayout$LayoutParams
Text of view2: Layout with TextView

In logs we see, that created elements’ class is TextView. But the LayoutParams’ class differs. In the first case it is LinearLayout$LayoutParams,  because we specified linLayout as a root element within the inflate method, and this class’ object is the LinearLayout. In the second case the created elements’ LayoutParams class is RelativeLayout$LayoutParams, because we specified  relLayout as a root element (RelativeLayout class).

Now we have two ways to add to the screen the view1 and view2 we have created.

1) use the addView method

2) pass true value as the third parameter to the inflate method. In this case created view will be added to root

 

Let’s choose the second way and refactor the code:

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    LayoutInflater ltInflater = getLayoutInflater();

    LinearLayout linLayout = (LinearLayout) findViewById(R.id.linLayout);
    View view1 = ltInflater.inflate(R.layout.text, linLayout, true);
    LayoutParams lp1 = view1.getLayoutParams();

    Log.d(LOG_TAG, "Class of view1: " + view1.getClass().toString());
    Log.d(LOG_TAG, "Class of layoutParams of view1: " + lp1.getClass().toString());

    RelativeLayout relLayout = (RelativeLayout) findViewById(R.id.relLayout);
    View view2 = ltInflater.inflate(R.layout.text, relLayout, true);
    LayoutParams lp2 = view2.getLayoutParams();

    Log.d(LOG_TAG, "Class of view2: " + view2.getClass().toString());
    Log.d(LOG_TAG, "Class of layoutParams of view2: " + lp2.getClass().toString());
  }

We pass a true value as the third parameter within the inflate method and delete rows which displayed the TextView values in logs. Soon you’ll see why.

 

Let’s save and launch the app.

 

As you see, the TextView we created, have appeared within their parents, which we specified within the inflate method. Within the  RelativeLayout  the elements have overlapped each other, because we didn’t adjust the layout. At the moment, this is not essential.

 

Look in logs:

Class of view1: class android.widget.LinearLayout
Class of layoutParams of view1: class android.widget.LinearLayout$LayoutParams
Class of view2: class android.widget.RelativeLayout
Class of layoutParams of view2: class android.widget.LinearLayout$LayoutParams

Take a look at the elements’ class. In the first case it is the LinearLayout, in the second case it’s the RelativeLayout. I.e. the inflate method has returned the elements which we specified as root, but not the view-elements created from layout file. And the elements created from the layout, it has added to root as the child elements, similarly with the addView method. This happened because we have passed true as the third parameter (attachToRoot) to the inflate method.

Respectively, LayoutParams for view1 and view2  will be LinearLayout$LayoutParams,  because linLayout and relLayout have LinearLayout  as a parent and take LayoutParams from it.

 

To fix this topic we will make a more interesting example the next lesson.
 
In the next lesson:
- make your own version of the list


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня




Language