Does LayoutInflater work with a layout xml file that uses includes? - android

Here's the rub: I have a layout that uses the include tag. The layout is simple ->
<LinearLayout>
<TextView ...>
<LinearLayout>
<include ...>
</LinearLayout>
</LinearLayout>
Now the included file is nothing more than:
<LinearLayout>
<TextView ... android:id="#+id/inner_text">
</LinearLayout>
Now, if I try to access the TextView inner_text in either the create or onStart call, it throws a NPE. I have tried using LayoutInflater to infate the included xml file and then access inner_text but to no avail - it always fails.
So, the question is : does LayoutInflater work with included xml files of the parent xml file? What I would like to do is grab the contents of the included xml file - and set the whole thing to be either visible or not visible based on preferences.
Nothing seems to allow me to grab the TextView object.
Now, when I include the xml as just a nested element in the parent file (not using the include tag) - then it accesses that LinearLayout just fine - but that defeats my purpose of trying to make the layout of the view dynamic - that is, I can change the contents of the child included layout at will - and not have to do any changes to the parent layout.
Any help, pointers, suggestions -> greatly appreciated.

As suggestion I think you can have in the main xml some Layout component left empty, them in runtime, inflate the main xml, then the included xml and add the content of the included in the space you left in the main.
But I think you example have to work, internally the Android uses the inflater to inflate resources. Can you edit and tell a bit more? Code examples can be helpfully.

well, very strange, when you inflate a view from any xml resource (if xml resources have or have not nested resources with include) the view must hold all the parsed xml. So, yes, LayoutInflater work with included xml files of the parent xml file.
i copied here the way i'm doing now...
my generic layout (a header with text, progress bar and image)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<ImageView
android:id="#+id/genericHeaderLogoPin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="#drawable/logo_pin"/>
<TextView
android:id="#+id/genericHeaderTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="#id/genericHeaderLogoPin"
android:textSize="18dip"
android:textStyle="bold"
android:textColor="#FF0C9994"/>
<ProgressBar
android:id="#+id/genericHeaderProgressBar"
android:layout_width="25dip"
android:layout_height="25dip"
android:layout_centerVertical="true"
android:layout_toLeftOf="#id/genericHeaderLogoPin"
style="#android:style/Widget.ProgressBar.Inverse"
android:visibility="gone"/>
</RelativeLayout>
my parent layout... include the generic layout and a ListView
<?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"
android:background="#drawable/background_new_search_activity_1">
<include layout="#layout/generic_header_layout" android:id="#+id/listaBusquedasHeader"/>
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"/>
</LinearLayout>
my onCreate method that inflate the parent layout mentioned above:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
requestWindowFeature(Window.FEATURE_NO_TITLE);
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.lista_busquedas_activity, null);
setContentView(view);
listView = (ListView) findViewById(android.R.id.list);
listView.setOnItemClickListener(listItemClickListener);
((TextView)findViewById(R.id.genericHeaderTitle)).setText(R.string.messagesActivityTitle);
}
and that´s all, i hope this helps you.

Thanx for the help - I discovered what my problem was - I was trying to inflate the included file - not the parent container - so that's why it never found the elements. I wish the documentation would have mentioned that - logically I would think the parent xml file would load - but the included files may need inflating - guess not.
Works now thanks to your code examples you submitted.
Thanx.

Related

Reuse a rendered layout in some other layout at run time

I am using a RecylerView inside a layout l1.xml. I am including this l1.xml inside l2.xml using include tag.
I update this RecyclerView after an api call but l2.xml is not showing the updated RecyclerView.
Is there a way to forcibly ask the parent to refresh?
invalidate(), refreshDrawableState(); on the parent layout didn't help?
Is there a smarter way to use a rendered layout in multiple places?
l1.xml
...
...
<LinearLayout
android:id="#+id/feed"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4"
android:background="#color/light_primary_background">
<include layout="#layout/events_list"/>
</LinearLayout>
...
...
events_list.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/events_recycler_view"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
I update the events_recycler_view after an API call and the events_list.xml is updated but the include in l1.xml is not updated
Yes you can always use LayoutInflater to inflate a view, but the view must have the ids and type matching the id and type defined in your java code.
Check out this link for how to use layout inflater
http://www.programcreek.com/java-api-examples/android.view.LayoutInflater

How do I avoid repetitive TextView code?

I want to include the below layout in a main layout file, at multiple points, but at each usage, I want to change ONLY the "android:text" attribute of the text view inside the relative layout (as seen below). How can I achieve that?
P.S. I know how to include it in the main layout. This includes the relative layout (as seen below), but the main purpose of creating another layout file is because the code (of the textview) is being repeatedly used in the main layout, and the only attribute that differs is "android:text" between these repeated text views.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/order_id_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test"
android:fontFamily="sans-serif-light"
android:padding="20dp"
android:textSize="20sp"
android:textStyle="bold" />
</RelativeLayout>
in your another Layout file you can use this .
<include layout="#layout/main_layout"/>
And From your activity class you can set text by this.
TextView tv = (TextView) findViewById(R.id.order_id_label)
tv.setText("New Text");
This is the only way you can do this .
If all TextView element arguments are the same you could define this component in a separate file using <merge> </merge> directive and then <include layout="" />
Check here how to reuse
But if any of the TextView argument is changing, i.e. android:text attribute, the best way is to separate all other TextView attributes to custom style and reuse this custom style in different xml layout files
Check here how to use styles

How does <merge> tag work internally

I am trying to understand how does <merge> tag internally work. I have studied some examples using View Hierarchy tool. So I understand basic using and how does it work on higher level, but I wanna to study more about this tag and inflating views in general.
So let's consider some simple layouts
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bg"
android:gravity="center_horizontal">
<include layout="#layout/titlebar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/some_text"
android:text="#string/hello"
android:padding="10dp" />
</RelativeLayout>
And of course titlebar.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/chrome" />
</merge>
In this case we have following result.
Let's change our titlebar.xml
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/some_text"
android:src="#drawable/chrome" />
The result is really forthcoming.
Let's do more changes in our activity_main.xml file.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bg"
android:gravity="center_horizontal">
<include layout="#layout/titlebar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/bug"
android:text="#string/hello"
android:padding="10dp" />
</LinearLayout>
And in titlebar.xml I left everything the same.
So in this case we have some weird things
1. Now our root layout is LinearLayout. We have specified nonexistent attribute android:layout_below="#+id/some_text" in titlebar file
In the same line we have another issue #+id/some_text doesn't exist anymore, now we have #+id/bug.
Let's look at the result
Here I have some questions :
We are can use any attribute (for any layout) in merged file,but if the viewgroup(layout) where this part is included (in our case activity_main.xml) doesn't have such attributes, what how this situation is solved, just ignored as we can see in result ?
We have hardcoded id of view above imageview,it works if id is exists, but if it doesn't , as we can see in result it is also just being ignored
So I have already written a lot.
To sum up, where works of XML parser exactly with merged and include tags is described, also it will be good to know where sources of XML parser is located, to look through it.
I will be grateful for everyone who read this line and can suggest something or give advice.
Thx in advance.
If an <include> tag includes both layout_width and layout_height, it will override all of the root view's (of the file it is including) layout parameters.
A merge is a way to avoid an additional depth in the view hierarchy - an XML layout file must only have 1 root - so it must either have a single View, a ViewGroup which can include additional Views or a <merge>. When you're including a layout with multiple Views inside a ViewGroup, you may be adding extra unneeded complexity to your hierarchy - for example, 2 vertically orientated LinearLayouts may not be required. The merge allows you to remove the extra ViewGroup, and merge its Views into the ViewGroup where it is included.
Including a layout with a merge with a single View is the same as just including a layout with the single View directly, except that with the merge there isn't a 'root view' of the layout, so the include will not override its layout parameters (I believe).
When a View or ViewGroup is inflated into a ViewGroup which doesn't support the layout parameters that the child specifies, the parameters are just dropped. This occurs when including or inflating (using a layout inflater).
If a child View or ViewGroup is added to a ViewGroup (using addView), it is possible that the child already has layout parameters assigned, and if they aren't compatible with the ViewGroup it is being added to, it may result in a class cast exception later on during measuring / layout.
Your second question is to do with RelativeLayouts layout rules, and its behaviour can be defined using android:layout_alignWithParentIfMissing

Android Layout Templating [duplicate]

This question already has an answer here:
Custom Layout in android
(1 answer)
Closed 2 years ago.
I'm looking to a simple way to do layout templating in android.
I already check include and merge techniques without success.
(I think that it's possible creating custom Layouts and defining by code this behavior, but i wondered if that could be done by xml)
I want to define something like this:
[globalLayout]
<linearLayout params=xxx>
<linearLayout params=yyy>
<?yied ?>
</linearLayout>
</linearLayout>
[customView1]
<Linearlayout>
<ImageView />
<Button/>
</LinearLayout>
[customView2]
<Linearlayout>
<Button/>
<Button/>
<Button/>
</LinearLayout>
(these 3 xml should be reusable)
[HomeLayout]
<?include globalLayout >
<?include customView1 />
</include>
[ParamsLayout]
<?include globalLayout >
<?include customView2 />
</include>
The thing is that i want to have a reusable layout, if a perform a small change, it will affect all dependent views. somethink linked to "partial views or templating" in other languages.
Could anyone help me?
I have done something like this before by using view stub.
You can inflate any view you like inside that view.
<GlobalLayout>
<ViewStub>
<GlobalLayout>
Use LayoutInflater to do something like this:
On the Activity's onCreate:
super.onCreate(savedInstanceState);
setContentView(new TemplateInflater(this).apply(R.layout.products)
.onViewGroup(R.id.replace_here).ofTemplate(R.layout.template));
An implementation snippet:
public View ofTemplate(int templateId) {
LayoutInflater inflater = LayoutInflater.from(context);
View root = inflater.inflate(templateId, null);
View content = inflater.inflate(contentId, null);
((ViewGroup)root.findViewById(viewGroupId)).addView(content);
return root;
}
An example of a working code is in my git: https://github.com/erichegt/AndroidTemplating
I think this code will solve your problem, but you should use Fragments instead. You can have one Activity associated with a template and a Fragment to inflate it.
ViewStub is pretty straightforward and can cover basic layout templating needs.
It serves as a placeholder for some other layout which you can specify and inflate at runtime and then:
The inflated View is added to the ViewStub's parent with the ViewStub's layout parameters.
Here is example from one of my projects. In my layout template I have:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout ... >
...
<ViewStub
android:layout_width="0dp"
android:layout_height="0dp"
android:id="#+id/button_1_stub"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/split_guideline"
app:layout_constraintLeft_toRightOf="#+id/primary_left_guideline"
app:layout_constraintRight_toLeftOf="#+id/primary_right_guideline">
</ViewStub>
....
</android.support.constraint.ConstraintLayout>
... then, when I inflate it I am setting actual button layout that I need and inflate stub:
View contentView = inflater.inflate(R.layout.activity_main_template, null);
ViewStub button1Stub = contentView.findViewById(R.id.button_1_stub);
button1Stub.setLayoutResource(R.layout.work_button);
button1Stub.inflate();
... which inserts layout from R.layout.work_button instead of stub, imposing layout constraints I defined on the R.id.button_1_stub.

How to specify id when uses include in layout xml file

In my layout xml file, I have included other layout xml file (each
with a different android id).
<include layout="#layout/view_contact_name" android:id="+id/test1"/>
<include layout="#layout/view_contact_name" android:id="+id/test2"/>
But when I run it in the emulator, and start Hierarchy Viewer, each of
the layout still shows 'NO_ID', and in my code, I have
findViewById(R.id.test1) and findViewById(R.id.test2) both returns null.
Can anyone please help me with my problem ?
Specify the ID in the <include>
<include layout="#layout/test" android:id="#+id/test1" />
Then use two findViewById to access fields in the layout
View test1View = findViewById(R.id.test1);
TextView test1TextView = (TextView) test1View.findViewById(R.id.text);
Using that approach, you can access any field in any include you have.
I found out, that if you are using <merge> tag in your include layout, then the ID of include transfers to the merge tag which is not real view.
So either remove merge, or replace it with some layout.
Tor Norbye wrote:
The <include> tag is not a real view, so findByView will not find it. The #id attribute (and any other attributes you've set on the include tag) gets applied on the root tag of the included layout instead. So your activity.getView(R.id.included1) should in fact be the <TextView> itself.
Romain Guy indicates that you can override the ID of an included layout by putting an android:id attribute inside the <include> tag.
<include android:id="#+id/cell1" layout="#layout/workspace_screen" />
I think the top answer misses the most important point and might mislead people into thinking the <include/> tag creates a View that holds the include contents.
The key point is that include's id is passed to the root view of the include's layout file.
Meaning that this:
// activity_main.xml
<include layout="#layout/somelayout" android:id="#+id/someid"/>
// somelayout.xml
<?xml version="1.0" encoding="utf-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
Becomes this:
// activity_main.xml
<ImageView
android:id="#+id/someid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
yes is like this, but careful when the layout inserted in include field is a custom one and you want to access that root layout. That layout in this case #layout/test test, is actually returned in first line.
test test1View = (test)findViewById(R.id.test1);
you must set id each include tag
included child element set a
new id. if you look how to generate new id, look at this entry:
https://stackoverflow.com/a/15442898/1136117
Problem is we try to use id which is not declared in current layout file.
Instead of declaring again, id can be simply referred using #+id/. If you refactor original id name through Android Studio it does refactor in included layout as well.
<include layout="#layout/toolbar"/>
<TextView
android:id="#+id/txt_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
**android:layout_below="#+id/toolbar"**
android:layout_marginTop="16dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"/>
In a case of using <RecyclerView> find the id of <include> by using an instance of inflated view or else it will return null.
public class ViewHolder extends RecyclerView.ViewHolder {
private mTextView;
public ViewHolder(View view) {
super(view);
View include_1 = view.findViewById(R.id.include_1);
mTextView = (TextView) include_1.findViewById(R.id.text_id);
}
}
If you have set id to either root tag of included layout then you can use that id
or you can set id to included layout.
But you can not set id to both it may throw exception.
<include layout="#layout/view_contact_name" android:id="+id/test1"/>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
....
</LinearLayout>
Or
<include layout="#layout/view_contact_name"/>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="#+id/llBottomMainView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
....
</LinearLayout>
When talking about include you either have an id on the root view inside the included layout file or on the include line itself and not on both. For example:
<include layout="#layout/layout1" android:id="#+id/layout1"/>
Layout 1 file
<RelativeLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/layout2">
</RelativeLayout>
The above example is wrong because technically you have two id's declared for the same layout. So what you have to do is pick which element will have the id.
To specify the id when you are including a xml file is like setting it to any xml element
Example:
*list_layout.xml*
`<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvNames"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>`
*activity_main.xml*
`<RelativeLayout
tools:context=".MainActivity">
<include
layout="#layout/list_layout"
android:id="#+id/myList" />
</RelativeLayout>`
Now if you want to get that to use in .kt file, just use normally findViewById
Exemplo
*MainActivity.kt*
`val myList: RecycleView = findViewById(R.id.myList)`
Wow, I can't believe this question doesn't have the right answer yet. It's simple tags suck. You can only change things that start with android:layout_ which android:id doesn't match. So the answer is you can't. Sorry. What you can do instead is create a class that will be a ViewGroup which will inflate the included views inside, then add that as a tag in your layout, but that's about it.

Categories

Resources