Android - 编写自定义(复合)组件

问题

我正在开发的Android应用程序的主要活动已经变得非常大。这主要是因为它包含带有3个选项卡的aTabWidget。每个选项卡都有很多组件。活动必须立即控制所有这些组件。所以我认为你可以想象这个Activity有20个字段(几乎每个组件都有一个字段)。它还包含许多逻辑(单击侦听器,填充列表的逻辑等)。

我通常在基于组件的框架中做的是将所有内容拆分为自定义组件。然后,每个自定义组件都有明确的责任。它将包含它自己的一组组件以及与该组件相关的所有其他逻辑。

我试图弄清楚如何做到这一点,我在Android文档中发现了他们喜欢称之为"复合控制"的东西。 (请参阅http://developer.android.com/guide/topics/ui/custom-components.html#compound并滚动到"复合控件"部分)我想基于定义视图结构的XML文件创建这样的组件。

在文档中它说:

请注意,就像使用Activity一样,你可以使用声明式(基于XML)方法来创建包含的组件,也可以从代码中以编程方式嵌套它们。

嗯,这是个好消息!基于XML的方法正是我想要的!但它没有说明如何做到这一点,除了它"像一个活动"...但我在一个活动中做的是调用setContentView(...)从XML扩展视图。如果你是例如subclassLinearLayout,则该方法不可用。

所以我尝试像这样手动扩充XML:

public class MyCompoundComponent extends LinearLayout {

    public MyCompoundComponent(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.my_layout, this);
    }
}

这是有效的,除了我正在加载的XML具有LinearLayout声明为根元素的事实。这导致inflatedLinearLayout的孩子为MyCompoundComponent,其本身已经是aLinearLayout !!所以现在我们在MyCompoundComponent之间有一个冗余的LinearLayout和它实际需要的视图。

有人可以为我提供一个更好的方法来解决这个问题,避免实现冗余LinearLayout吗?


#1 热门回答(99 赞)

Use合并tag作为你的XML根目录

<merge xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Your Layout -->
</merge>

Check this article.


#2 热门回答(0 赞)

我认为你应该这样做的方法是使用你的类名作为XML根元素:

<com.example.views.MyView xmlns:....
       android:orientation="vertical" etc.>
    <TextView android:id="@+id/text" ... />
</com.example.views.MyView>

然后让你的类派生自你想要使用的任何布局。请注意,如果你使用此方法,则不要在此处使用布局inflater。

public class MyView extends LinearLayout
{
    public ConversationListItem(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }
    public ConversationListItem(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }


    public void setData(String text)
    {
        mTextView.setText(text);
    }

    private TextView mTextView;

    @Override
    protected void onFinishInflate()
    {
        super.onFinishInflate();

        mTextView = (TextView)findViewById(R.id.text);
    }
}

然后你可以正常使用XML布局中的视图。如果你想以编程方式创建视图,则必须自己对其进行充气:

MyView v = (MyView)inflater.inflate(R.layout.my_view, parent, false);

不幸的是,这不会让你做到v = new MyView(context),因为似乎没有办法解决嵌套布局问题,所以这不是一个完整的解决方案。你可以添加这样的方法到MyView,使它更好一点:

public static MyView create(Context context)
{
    return (MyView)LayoutInflater.fromContext(context).inflate(R.layout.my_view, null, false);
}

免责声明:我可能正在谈论完整的bollocks。