如何为Android Layout文件创建可重用的xml包装器

时间:2022-05-22 05:25:31

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?

我有几个大致相同的布局文件,除了一个部分。有没有办法让我可以在一个地方拥有共同的XML;而不是复制/粘贴,并且当我想要进行1次更改时必须更新一堆文件?

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:

我知道我可以从其他XML文件中包含XML,但是公共代码不是内部控件;它是外包装;所以包括不起作用。基本上,我有一堆文件,看起来像这样:

<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?

基本上,我想做ASP.Net对Master Pages的处理。这有什么选择吗?

4 个解决方案

#1


4  

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

您需要将onCreate()函数SetContentView中的“Activity”类扩展为基本xml布局,还需要覆盖基本Activity类中的setContentView

For Example:

例如:

1.Create base_layout.xml with the below code

1.使用以下代码创建base_layout.xml

<?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>    
  1. Create BaseActivity.java
  2. 创建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

和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.

到目前为止我唯一注意到的是当请求进度条(requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS))时,这需要在调用super.onCreate之前完成。我想这是因为在调用这个函数之前还没有任何东西可以绘制。

This worked great for me and hopefully you will find this useful in your own coding.

这对我很有用,希望你会发现这对你自己的编码很有用。

#2


1  

Maybe you could use one main layout XML file and then add/remove other widgets dynamically through code as needed.

也许您可以使用一个主布局XML文件,然后根据需要通过代码动态添加/删除其他小部件。

#3


1  

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:

我试图做到这一点 - 我想要一个左侧有一个按钮,右侧有一个按钮的视图,但中间可能有任意内容(取决于谁包括它)。基本上是一个自定义视图组,它可以在XML布局中具有子视图,并将使用另一个XML布局包装这些子视图。我是这样做的:

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.

top_bar.xml:这表示包装内容的通用布局。请注意具有ID“addChildrenHere”的LinearLayout(可以是任何布局) - 稍后会引用它。

<?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).

main.xml:主要布局。这包括一个带有几个孩子的自定义视图组(WrappedLayout)。请注意它如何声明自定义XML命名空间,并在WrappedLayout标记上设置两个自定义属性(这些属性表示用于包装子项的布局,以及该布局中应放置此节点的子项的位置)。

<?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.

attrs.xml:这是res / values。这定义了上面XML中使用的自定义XML属性。

<?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.

最后,WrappedLayout.java:它处理读取自定义属性,并做一些hackery使addView()实际上在不同的地方添加视图。

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.

据我所知,这是有效的。 Eclipse布局编辑器不能很好地工作(我不太确定问题是什么),但你可以很好地查看布局。更改WrappedLayout的子项似乎需要手动编辑XML。

#4


0  

Have you looked at Applying Styles and Themes?

你看过应用样式和主题吗?

#1


4  

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

您需要将onCreate()函数SetContentView中的“Activity”类扩展为基本xml布局,还需要覆盖基本Activity类中的setContentView

For Example:

例如:

1.Create base_layout.xml with the below code

1.使用以下代码创建base_layout.xml

<?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>    
  1. Create BaseActivity.java
  2. 创建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

和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.

到目前为止我唯一注意到的是当请求进度条(requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS))时,这需要在调用super.onCreate之前完成。我想这是因为在调用这个函数之前还没有任何东西可以绘制。

This worked great for me and hopefully you will find this useful in your own coding.

这对我很有用,希望你会发现这对你自己的编码很有用。

#2


1  

Maybe you could use one main layout XML file and then add/remove other widgets dynamically through code as needed.

也许您可以使用一个主布局XML文件,然后根据需要通过代码动态添加/删除其他小部件。

#3


1  

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:

我试图做到这一点 - 我想要一个左侧有一个按钮,右侧有一个按钮的视图,但中间可能有任意内容(取决于谁包括它)。基本上是一个自定义视图组,它可以在XML布局中具有子视图,并将使用另一个XML布局包装这些子视图。我是这样做的:

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.

top_bar.xml:这表示包装内容的通用布局。请注意具有ID“addChildrenHere”的LinearLayout(可以是任何布局) - 稍后会引用它。

<?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).

main.xml:主要布局。这包括一个带有几个孩子的自定义视图组(WrappedLayout)。请注意它如何声明自定义XML命名空间,并在WrappedLayout标记上设置两个自定义属性(这些属性表示用于包装子项的布局,以及该布局中应放置此节点的子项的位置)。

<?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.

attrs.xml:这是res / values。这定义了上面XML中使用的自定义XML属性。

<?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.

最后,WrappedLayout.java:它处理读取自定义属性,并做一些hackery使addView()实际上在不同的地方添加视图。

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.

据我所知,这是有效的。 Eclipse布局编辑器不能很好地工作(我不太确定问题是什么),但你可以很好地查看布局。更改WrappedLayout的子项似乎需要手动编辑XML。

#4


0  

Have you looked at Applying Styles and Themes?

你看过应用样式和主题吗?