24

私は次のようにしました

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>

idVieweeの請負業者でゼロ以外の値を受け取りましたが、 findViewById(id)nullに戻って発生しNullPointerExceptionます。

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

私はここで説明されているようにそれをしました

4

3 に答える 3

21

答えが見つかりました!

問題はfindViewById(id)、私がそれを呼んだ場所でした。ドキュメントfindViewByIdが言うように、上位階層レベルに存在するビューではなく、子ビューのみを探します。したがって、次のようなものを呼び出す必要がありますが、呼び出した場所が正しくなかったため、それも返されます。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 を介して添付ビューを処理する必要がある場合に呼び出されます。

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

于 2012-06-14T22:14:08.790 に答える
16

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

外部から呼び出す代わりに、ビューを階層の上位に取得する別の方法として、次のようにフックしまし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) 
        }
    }
}

注:parentこのカスタムViewの はそれ自体であると想定していViewます。

于 2016-01-19T23:21:02.060 に答える
2

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

于 2012-06-14T19:16:52.940 に答える