When you create a compound view and inflate an xml layout file for it like this:
public class CompundLayout extends LinearLayout{...}
this inflates an xml with root like this:
<LinearLayout ... />
you end up with a layout hierarchy with a LinearLayout inside a LinearLayout (or so I concluded when defining a tag string to the layout object in the xml cased my app to crash).
Am I wrong? is there a better way to do this and prevent this double layout?
There is a better way to avoid the double layout, alter your xml layout to replace the LinearLayout container with a "merge" container. Your xml layout will look something like this afterwards:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView ... />
<EditText ... />
...
</merge>
Related
I have a button I use a lot in one of my layouts. Or rather, I have like ten buttons all with the same text color, background color, text size, width, and height. I don't want to define all of these parameters for each button. So I want to be able to write something like...
<include view="#layout/standard_button"
android:id="#+id/button-id"
android:text="button-specific-text"/>
But of course, there is no include view="", there is only include layout, and include layout the xml file as a Layout, not a View, so setting the text is not possible and when I findViewById() in my activity, it would refer to a Layout and not a View.
Is there something like <include view="... ?
You can create a layout with your customized style button that you want to use multiple times, and wrap it in a <merge> tag like below sample.
And then you can reuse it over and over again using the include tag in any other layout.
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</merge>
Usage within any other layout, assuming the top layout named as my_button.xml
<include layout="#layout/my_button" />
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
Say I want a reusable border layout for Android.
border.xml
<LinearLayout ...>
<LinearLayout...>
This is where I want content to go
</LinearLayout...>
<FrameLayout .../>
</LinearLayout ...>
and Border.java inflates border.xml.
Now in another view, I want to draw things inside my border. Something like
homepage.xml
<Border ... >
<TextView .../>
<TextView .../>
</Border>
Is there a way to tell xml where to put the content of custom tags?
you could inflate a given ressource to your custom Border layout.
Use this inside your constructor
LayoutInflater.from(getContext()).inflate(R.layout.homepage, YOUR_SECOND_LINEARLAYOUT);
You can do this by making a class that extends say linearlayout. Inside that class you can inflate the border.xml
public class BorderView extends LinearLayout {
public BorderView(Context context) {
super(context);
View.inflate(context, R.layout.border, this);
}
Here is where I got this for an exampler:
How to Inflate Extended LinearLayout from XML
I have the following custom view which is based on RelativeLayout:
public class LearningModeRadioButton extends
RelativeLayout
implements
Checkable,
View.OnClickListener {
private void init(Context context, AttributeSet attrs) {
LayoutInflater.from(context).inflate(R.layout.rb_learning_mode, this, true);
}
}
R.layout.rb_learning_mode contents are:
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
>
<RadioButton
android:id="#+id/rb"
android:button="#drawable/sel_rb_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="#+id/tv_mode_title"
style="#style/text_regular"
android:layout_toRightOf="#+id/rb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="#+id/tv_description"
style="#style/text_small"
android:layout_below="#+id/tv_mode_title"
android:layout_alignLeft="#+id/tv_mode_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</merge>
It sort of works, but layout parameters (layout_xxx) are ignored. I could use another <RelativeLayout/> as root element of the layout but I want to avoid having extra level in view hierarchy.
So the question is: How do I make layout attributes inside <merge/> work?
For anyone that might still be struggling with this, you should specify the parentTag attribute inside the merge tag. You will also need to specify layout_height and layout_width to make it work.
<merge
tools:parentTag="android.widget.RelativeLayout"
tools:layout_width="match_parent"
tools:layout_height="match_parent"
>
// Child Views
</merge>
The editor should display everything properly now.
Merge useful for LinearLayout and FrameLayout its not suitable for RelativeLayout.
Obviously, using works in this case because the parent of an activity's content view is always a FrameLayout. You could not apply this trick if your layout was using a LinearLayout as its root tag for instance. The can be useful in other situations though.
check this:
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.