正确访问自定义LinearLayout

时间:2015-06-23 07:38:53

标签: android android-layout android-context android-custom-attributes

所以我正在玩android,并陷入了死胡同。

这个问题可能与this one有关,因为它的答案可能是我的问题的一个很好的解决方法,但我对我的代码失败的原因感兴趣。我可能对观点有一些严重的误解。

所以,我有这个西洋双陆棋板:

backgammon board with chips stacked on the top and the bottom

我想在每次移动中执行动画,为此我需要在屏幕上获取移动芯片的原点和目标坐标。

因此,我必须确保点表现为堆栈,新视图必须插入倒置点的底部,并且在下行点的顶部。 由于任何一点的重力都是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);

这看起来像我的同义词,虽然显然不是,但总是回归真实。

任何想法?

提前致谢

[编辑]

异常的堆栈跟踪:

--------- 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.access$800(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中嵌套视图:

1 个答案:

答案 0 :(得分:0)

好的,我开始工作了

我的代码中存在两个问题。

  1. 墨菲的第九定律:自然总是存在隐藏的缺陷: 这是错误的

    返回mUpsideDown? super.getChildAt(index):super.getChildAt(super.getChildCount() - index + 1);

  2. 我试图在子列表结束后访问两个位置。它应该是

    super.getChildAt(super.getChildCount() - (index + 1));
    

    super.getChildAt(super.getChildCount() - index - 1);
    

    这就是例外的原因。当填充我的LinearLayout时,Android正试图在此时膨胀null。

    其次,

    @Override
    public void addView(View child) {
        if(mUpsideDown) {
            super.addView(child);
        }
        else {
            super.addView(child, 0);
        }
    }
    

    没有工作,没有覆盖这个方法给了我预期的行为,我猜android在使用LinearLayouts时使用了getChildAt(),因此我可以按常规方式插入子视图,这样它们就可以堆叠在点的顶部而不管它是什么& #39;方向。

    我访问自定义属性的方式还可以(这是我提出的问题的答案)

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PointView);
    
    mUpsideDown = a.getBoolean(R.styleable.PointView_upsideDown, false);
    a.recycle();
    

    谢谢,@ Ravi Thapliyal,感兴趣