Представления Android — как настроить свободный элемент представления через XML?

Это то, чего я пытаюсь достичь:

  1. Используйте TextView в TabWidget (т.е. вызовите TabHost.TabSpec.setIndicator(View) вместо TabHost.TabSpec.setIndicator(String)).

  2. И я хочу настроить этот TextView в XML, а не создавать его программно.

Итак, изначально в моем макете XML у меня было:

<TabHost
    android:id="@+id/mainTabHost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="30px"/>
    <FrameLayout
        android:id="@android:id/tabcontent"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:paddingTop="30px">
    </FrameLayout>
</TabHost>

Теперь я хочу где-нибудь разместить конфигурацию TextView. На самом деле он не «принадлежит» нигде в иерархии макета. Но, поскольку XML макета должен быть допустимым XML, конечно, он не может иметь более одного корневого элемента. Поэтому я вынужден где-то вложить TextView. Поэтому я просто поместил его на один уровень ниже корня (TabHost):

<TabHost
    android:id="@+id/mainTabHost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="30px"/>
    <TextView
        android:id="@+id/tvTabIndicatorPreferences"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:text="@string/tab_name_preferences"/>
    <FrameLayout
        android:id="@android:id/tabcontent"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:paddingTop="30px">
    </FrameLayout>
</TabHost>

И в моем коде я делаю:

final TabHost mainTabHost = (TabHost) this.findViewById(R.id.mainTabHost);
mainTabHost.setup();
final TextView tvTabIndicatorPreferences = (TextView) this.findViewById(R.id.tvTabIndicatorPreferences);
final TabHost.TabSpec tabSpecPreferences = mainTabHost.newTabSpec("tabPreferences");
tabSpecPreferences.setIndicator(tvTabIndicatorPreferences);
tabSpecPreferences.setContent(R.id.scrlTblPreferences);
mainTabHost.addTab(tabSpecPreferences);

но тут есть проблема. Когда я пытаюсь запустить приложение, я получаю исключение, которое говорит

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

что вполне логично и разумно. TextView уже является потомком TabHost. я не могу просто установить его как дочерний элемент другого элемента. (Примечание: размещение TextView, вложенного в TabWidget, дает то же самое.)

поэтому я вынужден позвонить mainTabHost.removeView(tvTabIndicatorPreferences) перед вызовом setIndicator.

На самом деле это работает, но этот способ кажется мне очень неправильным. Во-первых, я должен поместить TextView в XML, где он не принадлежал, а затем в коде удалить его из того места, где он не принадлежал, прежде чем использовать его.

В таком сценарии, каков правильный способ добиться этого?


person Edwin Lee    schedule 01.02.2010    source источник


Ответы (1)


Шаг 1. Вытяните TextView в отдельный файл макета (например, res/layout/indicator.xml).

Шаг 2. Позвоните tabSpecPreferences.setIndicator(getLayoutInflater().inflate(R.layout.indicator, null));

Шаг № 3: Нет шага № 3.

person CommonsWare    schedule 01.02.2010
comment
Спасибо! Раньше я делал то, что вы говорите в шаге № 1, но в № 2 я пытался напрямую вызвать findViewById (...), что дало мне NPE. Поэтому я подумал, что все связанные элементы представления должны находиться в одном файле макета. - person Edwin Lee; 02.02.2010
comment
о, я также обнаружил, что, делая это таким образом, я действительно могу повторно использовать TextView в макете XML для ОБЕИХ вкладок. Как это так? я имею в виду, не будет ли у TextView 2 родителя? (или inflate() создает разные экземпляры представления из одной и той же конфигурации?) - person Edwin Lee; 02.02.2010
comment
Каждый вызов inflate создает новое дерево объектов Java. - person CommonsWare; 02.02.2010
comment
Спасибо! Я собирался опубликовать еще один вопрос о том, как динамически создавать N номеров элементов представления, настроенных с помощью XML. Но теперь я знаю! - person Edwin Lee; 03.02.2010