Я пытался сделать именно это - я хотел, чтобы представление имело кнопку слева и кнопку справа, но в середине мог иметь произвольный контент (в зависимости от того, кто его включал). В основном это настраиваемая группа представлений, которая может иметь дочернее представление в макете XML и оборачивать эти дочерние представления другим макетом XML. Вот как я это сделал:
top_bar.xml: это представляет общий макет, чтобы обернуть вещи. Обратите внимание на LinearLayout (может быть любой макет) с идентификатором «addChildrenHere» - на него ссылаются позже.
<?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: основной макет. Это включает в себя пользовательскую группу просмотра (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: это относится к 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>
Наконец, WrappedLayout.java: это обрабатывает чтение пользовательских атрибутов и делает небольшую хакерскую попытку заставить 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);
}
}
}
Это работает, насколько я могу судить. Он не очень хорошо работает с редактором макетов Eclipse (я не совсем уверен, в чем проблема), но вы можете хорошо просматривать макет. Изменение дочерних элементов WrappedLayout, по-видимому, требует редактирования XML вручную.