Android - запись пользовательского (составного) компонента

В Android-приложении, которое я сейчас разрабатываю, есть основной вид деятельности, который стал довольно большим. Это происходит главным образом потому, что он содержит TabWidget с 3 вкладками. На каждой вкладке имеется немало компонентов. Деятельность должна контролировать все эти компоненты сразу. Поэтому я думаю, вы можете себе представить, что эта активность имеет 20 полей (поле для почти каждого компонента). Кроме того, он содержит много логики (щелчок слушателей, логика для заполнения списков и т.д.).

То, что я обычно делаю в инфраструктурах на основе компонентов, состоит в том, чтобы разбить все на пользовательские компоненты. Тогда каждый пользовательский компонент будет иметь четкую ответственность. Он будет содержать собственный набор компонентов и всю другую логику, связанную с этим компонентом.

Я попытался выяснить, как это можно сделать, и я нашел в документации Android что-то, что им нравится называть "Compound Control". (См. http://developer.android.com/guide/topics/ui/custom-components.html#compound и перейдите к разделу "Compound Controls" ). Я хотел бы создать такой компонент на основе XML файла, определяющего представление структура.

В документации говорится:

Обратите внимание, что точно так же, как с помощью Activity, вы можете использовать либо декларативный (Основанный на XML) подход к созданию содержащиеся компоненты, или вы можете вставить они программно из вашего кода.

Хорошо, что хорошие новости! Основанный на XML подход - это именно то, что я хочу! Но он не говорит, как это сделать, за исключением того, что это "как с Activity"... Но то, что я делаю в Activity, вызывает setContentView(...), чтобы раздуть представления из XML. Этот метод недоступен, если вы, например, подкласс LinearLayout.

Итак, я попытался раздуть 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, объявленный как корневой элемент. Это приводит к тому, что надутый LinearLayout является дочерним элементом MyCompoundComponent, который сам уже является LinearLayout!! Итак, теперь у нас есть избыточный LinearLayout между MyCompoundComponent и представлениями, которые ему действительно нужны.

Может кто-нибудь, пожалуйста, предоставит мне лучший способ приблизиться к этому, избегая дублирования LinearLayout?

Ответ 1

Используйте тег merge как ваш корень XML

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

Проверьте эту статью.

Ответ 2

Я думаю, что вы должны это делать, это использование имени класса в качестве корневого элемента XML:

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

И тогда ваш класс будет получен из любого макета, который вы хотите использовать. Обратите внимание: если вы используете этот метод, здесь вы не используете разводку макета.

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);
}

Отказ от ответственности: я могу говорить о полных ошибках.