web-dev-qa-db-ja.com

ビュー参照をAndroidカスタムビューに渡す方法は?

私は次のようにしました

1)スタイラブルを作成する

_<declare-styleable name="Viewee">
    <attr name="linkedView" format="reference"/>
</declare-styleable>
_

2)カスタムビューレイアウトの定義

_<LinearLayout
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:layout_width="fill_parent"
        Android:layout_height="wrap_content"
        Android:background="#ffc0">
    <TextView
            Android:id="@+id/custom_text"
            Android:layout_width="fill_parent"
            Android:layout_height="wrap_content"
            Android:text="[text]"
            />
</LinearLayout>
_

3)必要なクラスを作成する

_public class Viewee extends LinearLayout
{
public Viewee(Context context, AttributeSet attributeSet)
{
    super(context, attributeSet);
    View.inflate(context, R.layout.viewee, this);
    TextView textView = (TextView) findViewById(R.id.custom_text);
    TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.Viewee);
    int id = typedArray.getResourceId(R.styleable.Viewee_linkedView, 0);
    if (id != 0)
    {
        View view = findViewById(id);
        textView.setText(((TextView) view).getText().toString());
    }

    typedArray.recycle();
}
}
_

そして最後に以下のような活動で

_<LinearLayout
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        xmlns:app="http://schemas.Android.com/apk/res/com.ns"
        Android:layout_width="fill_parent"
        Android:layout_height="fill_parent"
        Android:orientation="vertical">
    <TextView
            Android:id="@+id/tvTest"
            Android:layout_width="fill_parent"
            Android:layout_height="wrap_content"
            Android:text="Android"/>
    <com.ns.Viewee
            Android:layout_width="fill_parent"
            Android:layout_height="fill_parent"
            app:linkedView="@+id/tvTest"
            />
</LinearLayout>
_

vieweeコンストラクターでゼロ以外のidを受信しましたが、findViewById(id)がnullに戻り、NullPointerExceptionが発生します。

私は何が欠けていますか?

ここで説明したように行いました

22
anonim

答えが見つかりました!

問題はfindViewById(id)と私がそれを呼んだ場所にありました。 findViewByIdは子ビューのみを検索し、 documentation が言うように、上位階層レベルにビューは存在しません。したがって、getRootView().findViewById(id)のようなものを呼び出す必要がありますが、これはnullも返します。これは、それが正しいとは言えなかったためです。

VieweeコンストラクターではViewee自体がまだルートに接続されていないため、呼び出しによってNullPointerExceptionが発生します。

したがって、構築後にどこか別の場所でgetRootView().findViewById(id)を呼び出すと、正常に機能し、両方とも"@+id/tvTest"および"@id/tvTest" 正しいです。私はそれをテストしました!

答えは次のとおりです

public class Viewee extends LinearLayout
{
    public Viewee(Context context, AttributeSet a)
    {
        super(context, attributeSet);
        View.inflate(context, R.layout.main6, this);
        TextView textView = (TextView) findViewById(R.id.custom_text);
        TypedArray t = context.obtainStyledAttributes(a, R.styleable.Viewee);
        int id = t.getResourceId(R.styleable.Viewee_linkedView, 0);
        if (id != 0)
        {
            _id = id;
        }

        t.recycle();
    }

    private int _id;

    public void Foo()
    {
        TextView textView = (TextView) findViewById(R.id.custom_text);
        View view = getRootView().findViewById(_id);
        textView.setText(((TextView) view).getText().toString());
    }
}

Fooは、アクティビティなどの別の場所で参照IDを介して添付ビューを処理する必要がある場合に呼び出されます。

クレジットは完全に この投稿 で貢献した人たちに行きます。質問を送信する前に、その投稿を見たことがありませんでした。

21
anonim

これは古い質問ですが、すべてをカスタムビューにカプセル化したかったので、これを行う別の方法を追加すると思いました。

階層の上位にあるビューを取得する別の方法である外部から呼び出す代わりに、代わりにonAttachedToWindow()にフックしました。

public class MyCustomView extends LinearLayout {
    private int siblingResourceId;
    private View siblingView;

    public MyCustomView(Context context, AttributeSet a) {
        super(context, attributeSet);
        inflate(context, R.layout.main6, this);
        TextView textView = (TextView) findViewById(R.id.custom_text);
        TypedArray t = context.obtainStyledAttributes(a, R.styleable.Viewee);
        siblingResourceId = t.getResourceId(R.styleable.MyCustomView_siblingResourceId, NO_ID);
        t.recycle();
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (siblingResourceId != NO_ID) {
            siblingView = ((View) getParent()).findViewById(siblingResourceId);
        }
    }
}

onAttachedToWindowはかなり早い段階で呼び出されますが、ビュー階層全体が落ち着くのに十分遅いようです。それは少なくとも私のニーズには完璧に機能し、もう少し制御されており、機能するために外部からの対話を必要としません;-)

編集: Kotlinコードが追加されました

class MyCustomView(context: Context, attributeSet: AttributeSet) : LinearLayout(context, attributeSet) {
    private val siblingResourceId: Int
    private lateinit var siblingView: View

    // All other constructors left out for brevity.

    init {
        inflate(context, R.layout.main6, this)
        val textView = findViewById<TextView>(R.id.custom_text)
        val t = context.obtainStyledAttributes(a, R.styleable.Viewee)
        siblingResourceId = t.getResourceId(R.styleable.MyCustomView_siblingResourceId, NO_ID)
        t.recycle()
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        if (siblingResourceId != NO_ID) {
            siblingView = (parent as View).findViewById(siblingResourceId) 
        }
    }
}

注:このカスタムparentViewView自体であると想定しています。

13
Darwind

説明したAndroid:idapp:linkedView="@+id/tvTestに設定されます。ただし、@+id/tvTestは、「tvTest」という名前の新しいIDを作成するために使用されます。あなたがしたいのはapp:linkedView="@id/tvTestを使うことです。

2
Jason L