ViewBinding - How to switch/choose layout dynamically (<include>)? - android

I was planning on having two layout versions for the profile fragment, one for new user and one for logged in user.
How would I dynamically switch/choose the necessary layout in the Fragment's onCreateView() method?
The most straight-forward idea that comes to mind is to use two <include> layouts and hiding one of them in onCreateView() depending on variables. But I was wondering if there is a smarter approach.
Current layout xml:
The main content is inside the second FrameLayout and I'd like to have two versions to choose from.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<ImageView
android:id="#+id/profile_monk_head_imageview"
android:layout_width="match_parent"
android:layout_height="200dp"
android:contentDescription="#string/profile_monk_image_content_desc"
android:src="#drawable/monk_head" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- This is what I want to switch out -->
<FrameLayout ... >
</androidx.core.widget.NestedScrollView>
</FrameLayout>

Use DataBindingUtil version inflate if layoutId is unknown in advance ( a.k.a dynamic binding). Otherwise, use the generated Binding's inflate method to ensure type-safe inflation.
Crosslink reference

Related

Change layout in 'Include' programmatically using Kotlin

I am writing a simple game and I'm wanting the main screen to have a choice of 3 layouts, for 2 handed, right handed or left handed.
I have an include for the controls. However I'm struggling to get the layout to change programatically. Been searching since last night but cannot find a way to do it, is it even possible?
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".GamePlay">
<include
android:layout_width="match_parent"
android:layout_height="88dp"
layout="#layout/hand_two" <!-- this is what needs to change depending on settings -->
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:id="#+id/handLayout"/>
</androidx.constraintlayout.widget.ConstraintLayout>
From the animusmind's answer:
Use a ViewStub instead of include:
<ViewStub
android:id="#+id/layout_stub"
android:inflatedId="#+id/message_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.75" />
Then in code, get a reference to the stub, set its layout resource, and inflate it:
ViewStub stub = (ViewStub) findViewById(R.id.layout_stub);
stub.setLayoutResource(R.layout.whatever_layout_you_want);
View inflated = stub.inflate();
Answer collected from kcoppock
If you have multiple views which you would like to load them on demand programatically, ViewStub is one of the solution
please visit like link viewStub for multiple view

Layout inside another layout is not accessible from the activity

I have multiple layouts that are defined in hierarchical order, The issue is when i try to access item that is defined in an inner layout, it gives me NullPointerException.
This is my layout structure.
R.layout.activity_map
-> <include layout="content_map">
-> <include layout="terrace_parent_map">
-> <include layout="terrace_collection_map">
Now terrace_collection_map has constraint layout, that i need to access from the activity.
Following is the layout.
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/constraintTerraceLayout"
>
</android.support.constraint.ConstraintLayout>
But when i try to get id of constraintLayout, it returns null.
constraintTerraceLayout= findViewById(R.id.constraintTerraceLayout);
**Edit - terrace_collection_map **
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/constraintTerraceLayout"
>
<ImageView
android:id="#+id/imageView30"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="#drawable/terrace" />
</android.support.constraint.ConstraintLayout>
Try by adding the <merge /> tag that helps you to eliminate redundant view groups in your view hierarchy when including one layout within another.
For example, if your main layout is a vertical LinearLayout in which two consecutive views can be re-used in multiple layouts, then the re-usable layout in which you place the two views requires its own root view.
So use <merge> tag, if activity_map.xml has LinearLayout as a parent tag. Then change your terrace_collection_map.xml parent tag as like below code :
<merge xmlns:android="http://schemas.android.com/apk/res/android">
//..........Your Custom Layout Design...........
</merge>
This link helps you briefly, https://developer.android.com/training/improving-layouts/reusing-layouts

Custom view xml versus its usage

I have a custom view CustomSettingEntry this is its xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layout_custom_setting_entry_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/custom_setting_entry_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
</LinearLayout>
Then I use it in a fragment and assign it an id:
<com.mypackage.name.CustomSettingEntry
android:id="#+id/layout_notification_setting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:setting_title="#string/title"
app:setting_subtitle="#string/subtitle" />
My question is what's the difference between the LinearLayout with id layout_custom_setting_entry_container and the usage with id layout_notification_setting? do they refer to the same thing?
If I set a click listener on the custom view inside the fragment, then later in some condition disabled clicking on the root LinearLayout inside the custom view, will this stop the listener?
I understand your custom view is a subclass of LinearLayout which inflates an xml layout. If so, the resulting view hierarchy is
[LinearLayout (actually a CustomSettingEntry), id : layout_notification_setting]
[LinearLayout, id : layout_custom_setting_entry_container]
...
So there are two levels of viewgroups, each one with it own id.
By the way this is inefficient because, the outer viewgroup (your custom class) has only one child (the root of the xml).
On solution is to use a <merge> as root of the xml layout to skip one level. See Optimize by merging or Inflating layout for your custom view
for more details.

Z-ordering of child views with respect to their parent

Background
In my app I have a lot of fragments that share the same static overlay. Each fragment can be resized dynamically (i.e. its weight may change) so I want the size of my overlay to be in sync with it. Therefore it makes sense to either make them siblings or, as it occurred to me, put one inside another. The first approach works fine but it implies introducing one extra ViewGroup which seems redundant (or does it not?). The latter is the one I'm having problems with.
Problem
Consider these two layouts.
Container R.id.container is where I put my fragment in runtime with FragmentManager.
Fragment R.id.overlay is my overlay.
The difference is which one is the parent.
Layout A.
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.example.OverlayFragment"
android:id="#+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Layout B.
<fragment class="com.example.OverlayFragment"
android:id="#+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</fragment>
In both cases my overlay ends up below container (by z-axis). That is, container keeps overlapping it regardless of its role in hierarchy.
What are the rules for defining views's z-order in situations like this? Is it just the order of their initialization? I couldn't google it out.
Thanks.
From documentation:
When the system creates this activity layout, it instantiates each
fragment specified in the layout and calls the onCreateView() method
for each one, to retrieve each fragment's layout. The system inserts
the View returned by the fragment directly in place of the
element.
So I think you should use something like this:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<fragment class="com.example.OverlayFragment"
android:id="#+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

Use ViewStub as generic layout in android

As I mentioned in the title, I would like to use ViewStub as a generic layout. What I mean is, currently I have my base activity class and base fragment class to be extended for other activities or fragments. So since I have generic classes, I wonder if I can have a generic layout. Is it possible?
Example layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".activities.Activity_Main">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="#layout/toolbar" />
<ViewStub
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/viewStub"
/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Where #+id/viewStub content will change according to desired layout.
Yes, you can do that programmatically by using the setLayoutResource() method to which you pass the desired layout id.
ViewStub stub = (ViewStub) findViewById(R.id.viewStub);
stub.setLayoutResource(R.layout.some_layout);
View inflatedLayout = stub.inflate();
When inflate() is invoked, the ViewStub is replaced by the inflated View and the inflated View is returned. This lets applications get a reference to the inflated View without executing an extra findViewById().
Here is the reference.

Categories

Resources