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>

id现在虽然我在 Viewee 构造函数中收到一个非零值,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的 是 aView本身。

于 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 回答