正确访问自定义 LinearLayout 的自定义属性
Properly acessing custom attributes of custom LinearLayout
所以我在玩 android 并进入了死胡同。
这个问题可能与 this one 有关,因为它的答案可能是解决我的问题的一个很好的解决方法,但我对我的代码失败的原因很感兴趣。我可能对视图有一些严重的误解。
所以,我有这个西洋双陆棋棋盘:
我想在每次移动时都执行动画,为此我需要在屏幕上获取移动筹码的起点和终点坐标。
因此,我必须确保点表现为堆栈,必须在倒置点的底部和倒置点的顶部插入新视图。
由于任何点的重力是 TOP 或 BOTTOM 取决于它在屏幕上的位置,我可以这样做以获得这个属性,但它似乎是私有的:see this answer in the related post
所以我创建了一个自定义 LinearLayout :
<resources>
<declare-styleable name="PointView">
<attr name="upsideDown" format="boolean" />
</declare-styleable>
</resources>
它覆盖了 LinearLayout
public class PointView extends LinearLayout {
private boolean mUpsideDown = false;
public PointView(Context context) {
super(context);
}
public PointView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public PointView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PointView);
mUpsideDown = a.getBoolean(R.styleable.PointView_upsideDown, false);
a.recycle();
}
@Override
public View getChildAt(int index) {
return mUpsideDown ? super.getChildAt(index) : super.getChildAt(super.getChildCount() - index + 1);
}
@Override
public void addView(View child) {
if(mUpsideDown) {
super.addView(child);
}
else {
super.addView(child, 0);
}
}
}
它们像这样包含在我的 Activity 的 XML 中:
<spacebar.backgammoid.PointView
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="@drawable/black_point"
android:layout_weight="1"
android:focusableInTouchMode="false"
android:id="@+id/point13"
android:weightSum="5"
android:clipChildren="false"
android:gravity="top"
app:upsideDown="true"/>
现在,当在运行时调用 init() 时,我得到一个异常:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.resolveLayoutParams()' on a null object reference
看起来我的上下文在 :
中为空
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PointView);
我不知道为什么,所以我无法访问我的自定义属性 upsideDown。
正在尝试从 attrs 访问 属性 :
attrs.getAttributeBooleanValue(R.styleable.PointView_upsideDown, false);
对我来说这看起来像是一个同义词,虽然它显然不是,但总是返回 true。
有什么想法吗?
提前致谢
[编辑]
异常的堆栈跟踪:
--------- beginning of crash
06-23 10:04:34.972 2036-2036/spacebar.backgammoid E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: spacebar.backgammoid, PID: 2036
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.resolveLayoutParams()' on a null object reference
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.View.setLayoutParams(View.java:11473)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:273)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3055)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2395)
at android.app.ActivityThread.access0(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5257)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
[编辑 2]
我以编程方式用芯片填充点,在 Activity 中填充 XML 在设计时抛出相同的异常。
<spacebar.backgammoid.PointView ...>
<ImageView ... />
</spacebar.backgammoid.PointView>
然而,如果我使用常规 LinearLayouts 而不是我的自定义视图,一切正常,并且没有错误。
<LinearLayout ...>
<ImageView ... />
</LinearLayout>
此外,我不知道它是否相关,但是 android-studio 设计师不允许我在我的 PoinView 中嵌套视图:
好的,我成功了
我的代码中有两个问题。
墨菲第九定律:大自然总是站在隐藏的缺陷一边:
这是错误的
return 颠倒? super.getChildAt(索引) : super.getChildAt(super.getChildCount() - 索引 + 1);
我试图访问子列表末尾后的两个位置。应该是
super.getChildAt(super.getChildCount() - (index + 1));
或
super.getChildAt(super.getChildCount() - index - 1);
这就是异常的原因。 Android 在填充我的 LinearLayout 时试图在那个时候膨胀 null。
其次,
@Override
public void addView(View child) {
if(mUpsideDown) {
super.addView(child);
}
else {
super.addView(child, 0);
}
}
没有工作,不重写这个方法给了我预期的行为,我猜 android 在膨胀 LinearLayouts 时使用 getChildAt(),因此我可以以常规方式插入子视图,以便它们堆叠在顶部无论其方向如何。
我访问自定义属性的方式没有问题(这是我的问题的答案)
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PointView);
mUpsideDown = a.getBoolean(R.styleable.PointView_upsideDown, false);
a.recycle();
谢谢@Ravi Thapliyal 的关注
所以我在玩 android 并进入了死胡同。
这个问题可能与 this one 有关,因为它的答案可能是解决我的问题的一个很好的解决方法,但我对我的代码失败的原因很感兴趣。我可能对视图有一些严重的误解。
所以,我有这个西洋双陆棋棋盘:
我想在每次移动时都执行动画,为此我需要在屏幕上获取移动筹码的起点和终点坐标。
因此,我必须确保点表现为堆栈,必须在倒置点的底部和倒置点的顶部插入新视图。 由于任何点的重力是 TOP 或 BOTTOM 取决于它在屏幕上的位置,我可以这样做以获得这个属性,但它似乎是私有的:see this answer in the related post
所以我创建了一个自定义 LinearLayout :
<resources>
<declare-styleable name="PointView">
<attr name="upsideDown" format="boolean" />
</declare-styleable>
</resources>
它覆盖了 LinearLayout
public class PointView extends LinearLayout {
private boolean mUpsideDown = false;
public PointView(Context context) {
super(context);
}
public PointView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public PointView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PointView);
mUpsideDown = a.getBoolean(R.styleable.PointView_upsideDown, false);
a.recycle();
}
@Override
public View getChildAt(int index) {
return mUpsideDown ? super.getChildAt(index) : super.getChildAt(super.getChildCount() - index + 1);
}
@Override
public void addView(View child) {
if(mUpsideDown) {
super.addView(child);
}
else {
super.addView(child, 0);
}
}
}
它们像这样包含在我的 Activity 的 XML 中:
<spacebar.backgammoid.PointView
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="@drawable/black_point"
android:layout_weight="1"
android:focusableInTouchMode="false"
android:id="@+id/point13"
android:weightSum="5"
android:clipChildren="false"
android:gravity="top"
app:upsideDown="true"/>
现在,当在运行时调用 init() 时,我得到一个异常:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.resolveLayoutParams()' on a null object reference
看起来我的上下文在 :
中为空 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PointView);
我不知道为什么,所以我无法访问我的自定义属性 upsideDown。
正在尝试从 attrs 访问 属性 :
attrs.getAttributeBooleanValue(R.styleable.PointView_upsideDown, false);
对我来说这看起来像是一个同义词,虽然它显然不是,但总是返回 true。
有什么想法吗?
提前致谢
[编辑]
异常的堆栈跟踪:
--------- beginning of crash
06-23 10:04:34.972 2036-2036/spacebar.backgammoid E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: spacebar.backgammoid, PID: 2036
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.resolveLayoutParams()' on a null object reference
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.ViewGroup.resolveLayoutParams(ViewGroup.java:6116)
at android.view.View.setLayoutParams(View.java:11473)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:273)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3055)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2395)
at android.app.ActivityThread.access0(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5257)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
[编辑 2]
我以编程方式用芯片填充点,在 Activity 中填充 XML 在设计时抛出相同的异常。
<spacebar.backgammoid.PointView ...>
<ImageView ... />
</spacebar.backgammoid.PointView>
然而,如果我使用常规 LinearLayouts 而不是我的自定义视图,一切正常,并且没有错误。
<LinearLayout ...>
<ImageView ... />
</LinearLayout>
此外,我不知道它是否相关,但是 android-studio 设计师不允许我在我的 PoinView 中嵌套视图:
好的,我成功了
我的代码中有两个问题。
墨菲第九定律:大自然总是站在隐藏的缺陷一边: 这是错误的
return 颠倒? super.getChildAt(索引) : super.getChildAt(super.getChildCount() - 索引 + 1);
我试图访问子列表末尾后的两个位置。应该是
super.getChildAt(super.getChildCount() - (index + 1));
或
super.getChildAt(super.getChildCount() - index - 1);
这就是异常的原因。 Android 在填充我的 LinearLayout 时试图在那个时候膨胀 null。
其次,
@Override
public void addView(View child) {
if(mUpsideDown) {
super.addView(child);
}
else {
super.addView(child, 0);
}
}
没有工作,不重写这个方法给了我预期的行为,我猜 android 在膨胀 LinearLayouts 时使用 getChildAt(),因此我可以以常规方式插入子视图,以便它们堆叠在顶部无论其方向如何。
我访问自定义属性的方式没有问题(这是我的问题的答案)
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PointView);
mUpsideDown = a.getBoolean(R.styleable.PointView_upsideDown, false);
a.recycle();
谢谢@Ravi Thapliyal 的关注