我做了如下
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
。
我错过了什么?
答案 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
。