I have several layout files that are mostly the same, except for one section. Is there a way that I can have the common XML all in one place; instead of copy/pasting, and having to update a bunch of files when I want to make 1 change?
I know that I can include XML from other XML files, but the common code isn't an internal control; it is the outer wrapper; so include doesn't work. Basically, I have a bunch of files that all look like this:
<LinearLayout
android:id="@+id/row"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<ImageView android:layout_height="26dp"
android:id="@+id/checkImage"
android:layout_width="26dp"
android:layout_alignParentTop="true"
android:scaleType="fitCenter"/>
<!-- Different types of views go here depending on which layout file it is -->
<ImageButton android:layout_height="fill_parent"
android:id="@+id/playButton"
android:layout_width="42dp"
android:src="@drawable/play_button"
android:scaleType="center"
android:background="#00000000"/>
</LinearLayout>
Basically, I want to do what ASP.Net does with Master Pages. Is there any option for this?
The solution was pretty easy.
You need to extend "Activity" Class, in onCreate()
function SetContentView to your base xml layout and also need to override setContentView in base Activity Class
For Example:
1.Create base_layout.xml
with the below code
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/image_view_01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxHeight="50dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/base_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</LinearLayout>
</LinearLayout>
BaseActivity.java
public class BaseActivity extends Activity {
ImageView image;
LinearLayout baseLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.base_layout);
this.image = (ImageView) this.findViewById(R.id.image_view_01);
this.baseLayout = (LinearLayout) this.findViewById(R.id.base_layout);
this.image.setImageResource(R.drawable.header);
}
@Override
public void setContentView(int id) {
LayoutInflater inflater = (LayoutInflater)getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(id, this.baseLayout);
}
}
and SomeActivity.java
public class SomeActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.some_layout);
//rest of code
}
}
The only thing I noticed so far was that when requesting a progress bar (requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) this needs to be done before calling super.onCreate. I think this is because nothing can be drawn yet before calling this function.
This worked great for me and hopefully you will find this useful in your own coding.
Maybe you could use one main layout XML file and then add/remove other widgets dynamically through code as needed.
I was trying to do exactly this - I wanted a view that had a button on the left and a button on the right, but could have arbitrary content in the middle (depending on who was including it). Basically a custom view group that could have child view in the XML layout, and would wrap those child views with another XML layout. Here is how I did it:
top_bar.xml: This represents the common layout to wrap things with. Note the LinearLayout (could be any layout) with an ID "addChildrenHere" - it is referenced later.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/topBarLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="left" />
<LinearLayout
android:id="@+id/addChildrenHere"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="right" />
</LinearLayout>
main.xml: The main layout. This includes a custom viewgroup (WrappedLayout) with a few children. Note how it declares a custom XML namespace, and sets two custom attributes on the WrappedLayout tag (these say which layout to wrap the children with, and where within that layout the children of this node should be placed).
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:karl="http://schemas.android.com/apk/res/karl.test"
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<karl.test.WrappedLayout
android:id="@+id/topBarLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
karl:layoutToInflate="@layout/top_bar"
karl:childContainerID="@+id/addChildrenHere">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is a child of the special wrapper."
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is another child; you can put anything here."
android:textAppearance="?android:attr/textAppearanceMedium" />
</karl.test.WrappedLayout>
</LinearLayout>
attrs.xml: This goes in res/values. This defines the custom XML attributes used in the XML above.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="WrappedLayout">
<attr name="layoutToInflate" format="integer"/>
<attr name="childContainerID" format="integer"/>
</declare-styleable>
</resources>
Finally, WrappedLayout.java: This handles reading the custom attributes, and doing a bit of hackery to make addView() actually add the views in a different place.
package karl.test;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
public class WrappedLayout extends FrameLayout
{
///Attempts to add children to this layout will actually get forwarded through to mChildContainer.
///This would be final, but it's actually used indirectly by the constructor before it's initialised.
private ViewGroup mChildContainer;
public WrappedLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
//read the custom attributes
final int layoutToInflate;
final int childContainerID;
{
final TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.WrappedLayout);
layoutToInflate = styledAttributes.getResourceId(R.styleable.WrappedLayout_layoutToInflate, 0);
childContainerID = styledAttributes.getResourceId(R.styleable.WrappedLayout_childContainerID, 0);
styledAttributes.recycle();
}
if(layoutToInflate == 0
|| childContainerID == 0)
{
Log.e("Error", "WrappedLayout.WrappedLayout(): Error reading custom attributes from XML. layoutToInflate = " + layoutToInflate + ", childContainerID =" + childContainerID);
}
else
{
//inflate the layout and (implicitly) add it as a child view
final View inflatedLayout = View.inflate(context, layoutToInflate, this);
//grab the reference to the container to pass children through to
mChildContainer = (ViewGroup)inflatedLayout.findViewById(childContainerID);
}
}
///All the addView() overloads eventually call this method.
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params)
{
if(mChildContainer == null)
{
//still inflating - we're adding one of the views that makes up the wrapper structure
super.addView(child, index, params);
}
else
{
//finished inflating - forward the view through to the child container
mChildContainer.addView(child, index, params);
}
}
}
This works, as far as I can tell. It doesn't work very well with the Eclipse layout editor (I'm not quite sure what the problem is), but you can view the layout fine. Changing the children of the WrappedLayout seems to require editing the XML manually.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With