Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I access views from layout containing merge tag is included in another layout?

I use Android Studio 3.6-RC1 and build tools version 3.6.0-rc01 and encountered an issue with ViewBinding feature:

I have activity_test.xml file with the following markup:

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

    <include
        android:id="@+id/view_merged"
        layout="@layout/merge_view" />
</LinearLayout>

And merge_view.xml with following markup:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Merge view" />
</merge>

Activity code looks like the following:

class TestActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityTestBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.viewMerged.label.text = "New text"

    }
}

Problem is when I try to access TextView from merged layout, the app throws an exception with the message java.lang.NullPointerException: Missing required view with ID: viewMerged.

The generated binding class looks like the following:

public final class ActivityTestBinding implements ViewBinding {
  @NonNull
  private final LinearLayout rootView;

  @NonNull
  public final MergeViewBinding viewMerged;

  private ActivityTestBinding(@NonNull LinearLayout rootView,
      @NonNull MergeViewBinding viewMerged) {
    this.rootView = rootView;
    this.viewMerged = viewMerged;
  }

  @Override
  @NonNull
  public LinearLayout getRoot() {
    return rootView;
  }

  @NonNull
  public static ActivityTestBinding inflate(@NonNull LayoutInflater inflater) {
    return inflate(inflater, null, false);
  }

  @NonNull
  public static ActivityTestBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup parent, boolean attachToParent) {
    View root = inflater.inflate(R.layout.activity_test, parent, false);
    if (attachToParent) {
      parent.addView(root);
    }
    return bind(root);
  }

  @NonNull
  public static ActivityTestBinding bind(@NonNull View rootView) {
    // The body of this method is generated in a way you would not otherwise write.
    // This is done to optimize the compiled bytecode for size and performance.
    String missingId;
    missingId: {
      View viewMerged = rootView.findViewById(R.id.view_merged);
      if (viewMerged == null) {
        missingId = "viewMerged";
        break missingId;
      }
      MergeViewBinding viewMergedBinding = MergeViewBinding.bind(viewMerged);
      return new ActivityTestBinding((LinearLayout) rootView, viewMergedBinding);
    }
    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
  }
}

Am I missing something or there is no way to access views from included layouts with tags or it is not yet shipped in Android Studio 3.6-RC1?

like image 924
Oleksii Yerastov Avatar asked Jan 15 '20 14:01

Oleksii Yerastov


People also ask

Can we embed one layout inside another layout?

To efficiently re-use complete layouts, you can use the <include/> and <merge/> tags to embed another layout inside the current layout. Reusing layouts is particularly powerful as it allows you to create reusable complex layouts.

Which layout allows us to display items on the screen relative to each other?

RelativeLayout : is a ViewGroup that displays child views in relative positions.

Which part of the layout editor contains various views and view groups?

Palette: Contains various views and view groups that you can drag into your layout.


1 Answers

I wrote an article about using ViewBinding with <merge> tag which you can find here

Basically, what you need to do is

  • Don't give <include> tag any ID.
  • Call bind() method of generated merge layout binding, passing the root view of the layout you included your layout in.
  • Access your view from the object of merge binding

For example, you have merge_view.xml so you'll have MergeViewBinding class generated and this is how you will access the view from this layout.

class TestActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityTestBinding.inflate(layoutInflater)
        val mergeBinding = MergeViewBinding.bind(binding.root)
        setContentView(binding.root)
        mergeBinding.label.text = "New text"
    }
}
like image 136
Somesh Kumar Avatar answered Oct 09 '22 05:10

Somesh Kumar