如何将视图引用传递给android自定义视图?

时间:2012-06-14 19:14:43

标签: android custom-view

我做了如下

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

我错过了什么?

I did it as described here

3 个答案:

答案 0 :(得分:20)

我找到了答案!

问题在于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());
    }
}
当需要通过活动等其他地方的引用ID处理附加视图时,会调用

Foo

完全归功于那些在this post贡献的人。在提交问题之前我没有看过那篇文章。

答案 1 :(得分:11)

我知道这是一个老问题,但我想我会添加另一种方法,因为我想将所有内容封装到我的自定义视图中。

我没有从外部打电话,而是在层次结构中获得更高层次视图的另一种方式,而是选择了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的{​​{1}}本身就是View

答案 2 :(得分:2)

您描述的android:id设置为app:linkedView="@+id/tvTest。但是,@+id/tvTest用于创建名为“tvTest”的新ID。您要做的是使用app:linkedView="@id/tvTest