Как сослаться на один элемент управления из другого во время инфляции?

Я пытаюсь сослаться на родственный элемент управления через XML.

Чтобы объявить атрибут для ссылки на идентификатор из MyTextView:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyTextView">
        <attr name="valueTextViewId" format="reference" />
    </declare-styleable>
</resources>

fragment_example.xml — как использовать настраиваемый атрибут:

<!-- Declare a "Title" text view that references a "Value" -->
<com.example.MyTextView
    android:id="@+id/foo"
    example:valueTextViewId="@id/bar"
    ... />

<!-- Depending on the "text" attribute of this "Value" textview -->
<!-- Do something within "Title" textview -->
<com.example.MyTextView android:id="@+id/bar" />

MyFragment.java — расширение элементов управления

public View onCreateView(LayoutInflater inflater, ViewGroup container, 
        Bundle savedInstanceState) {
        // calls MyTextView Ctor
    View v = inflater.inflate(R.layout.fragment_example, container, false);
}

Конструктор класса MyTextView — во время надувания сделайте что-нибудь с указанным текстовым представлением:

public TextView(Context context, AttributeSet attrs) {
    super(context, attrs);

    TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.MyTextView);
    int refId = a.getResourceId(R.styleable.MyTextView_valueTextViewId);

    // Updated to use context
    if (refId > -1 && context instanceof Activity) {
        Activity a = (Activity)context;
        View v = a.findViewById(refId);

        // THE PROBLEM: v is null
        if (v != null) {
            // In my case, I want to check if the "Value" textview
            // is empty. If so I will set "this" textColor to gray
        }
    }
}

в этом примере v всегда null. Я предполагаю, что во время инфляции макета элементы управления еще не добавлены. Еще одна вещь, которую следует отметить, это то, что это находится в Fragment, поэтому это может быть причиной того, что я не могу найти представление в родительской активности.

Можно ли ссылаться на элемент управления из другого, подобного этому?


person Tom Fobear    schedule 23.05.2013    source источник


Ответы (4)


Можно ли ссылаться на элемент управления из другого, подобного этому?

Можно сослаться на другой View из View.

Но выполнять проверку свойств в конструкторе объекта View не рекомендуется.
Нет гарантии, что конкретный объект View будет создан раньше любого другого во время Просмотр инфляции.

Сравните эти два макета:
first_layout.xml

<com.example.MyTextView
    ...
    android:id="@+id/foo"
    example:valueTextViewId="@+id/bar" />

<com.example.MyTextView
    ...
    android:id="@+id/bar" />

second_layout.xml

<com.example.MyTextView
    ...
    android:id="@+id/bar" />

<com.example.MyTextView
    ...
    android:id="@+id/foo"
    example:valueTextViewId="@+id/bar" />

В этом примере ясно, что проверка свойств из конструктора не будет работать ни в одном из этих макетов.

Я согласен с тем, что можно хранить ссылку на другой View внутри View:

public MyTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyTextView);
    mReferenceId = a.getResourceId(R.styleable.MyTextView_valueTextViewId);
    ...
}

private int mReferenceId;

public View getReferenceViewFromActivity() {
    if (getContext() instanceof Activity) {
        return ((Activity)getContext()).findViewById(mReferenceId);
    return null;
}

public View getReferenceView(View view) {
    return view.findViewById(mReferenceId);
}

Но вам определенно следует выполнять любую проверку свойств в пределах Activity или Fragment:

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    MyTextView myTextView = (MyTextView)view.findViewById(R.id.foo);
    MyReferenceView refView = (MyReferenceView)myTextView.getReferenceView(view);
    //
    // do property checking
    //
}
person EricRobertBrewer    schedule 07.06.2013

если текстовое представление с идентификатором bar доступно в пределах идентификатора bar, вы можете сделать что-то вроде этого.

<com.example.MyTextView
    android:id="@+id/foo"
    ...
    android:tag="bar" />

<com.example.MyTextView android:id="@+id/bar" />

и

public TextView(Context context, AttributeSet attrs) {
    super(context, attrs);

      int barId = getResources().getIdentifier(getTag(), "id", packageName);
      TextView bar = mActivity.findViewById(barId);

    if (bar.getText() == "") {
        // Gray out this "title" textview
        setColor(android.R.color.gray);
    }

    // maybe set a text change listener to bar to make it future-proof
}

я бы просто передал идентификатор как тег в вашем MyTextView, поэтому вам не нужно создавать новый атрибут.

person r2DoesInc    schedule 23.05.2013
comment
Это интересно и начало. Однако у меня нет возможности передать mActivity, когда я использую LayoutInflator. Это заставляет меня задуматься, является ли атрибут контекста в Ctor самим действием... - person Tom Fobear; 23.05.2013
comment
Это может быть. проверьте через if(context instanceof Activity) и затем закиньте его ((Activity) context) - person r2DoesInc; 23.05.2013
comment
Возможно, не делайте эту логику в конструкторе. Добавьте метод в MyTextView для setActivityValue(mActivity), а затем, после того, как действие было явно задано, выполните свою логику сокрытия или манипулирования другим текстовым представлением, как можете. - person r2DoesInc; 23.05.2013
comment
Я обновил вопрос. Контекст - это деятельность. Однако я раздуваю fragment в этой деятельности. - person Tom Fobear; 26.05.2013

Сохраните ссылки в своих личных полях в конструкторе. В вашем случае сохраните refId в приватном поле. Затем используйте их в функции onMeasure.


Я бы посоветовал вам не использовать ваш подход к получению ссылочного представления. Если вы посмотрите на обычные случаи, такие как дочерний элемент RelativeView layout_toLeftOf, вы заметите, что идентификатор всегда является одним из дочерних элементов RelativeView и, следовательно, родственным текущему представлению. В этом случае легко получить представление, получив родителя, используя getParent(), приведя его к ViewGroup (не обязательно) и найдя представление со ссылкой, используя findViewById. Если вы можете сделать такое ограничение (id должен быть родственным), вы, возможно, сможете избавиться от этой зависимости от контекста, что в конечном итоге сделает ваше представление непригодным для использования. Вероятно, вам лучше установить представление с помощью setBla() вместо использования собственного атрибута (может быть, выдать ошибку времени выполнения в вашем onMeasure, если этот установщик не был вызван).

person Sherif elKhatib    schedule 08.06.2013

Насколько я знаю, нет никакой гарантии, что макет Activity's будет создан методом Fragment's onCreateView().

Вместо этого вы можете попробовать этот подход:

@Override
public void onActivityCreated(Bundle savedInstanceState){
    mView.customMethodToSetTextColor(...)
}

onActivityCreated() вызывается после завершения создания Activity и после onCreateView() в Fragment жизненном цикле .

person onosendai    schedule 08.06.2013