So I'm playing around with android and got to a dead-end.
This question may be related to this one, since its answer could be a good workaround for my problem, but I'm interested on the reason why my code is failing. I may have some severe misconceptions about Views.
So, I have this backgammon board:
I will want to perform animations on every move, and for that I'll need to get the moving chip's origin and destination coordinates on the screen.
I have therefore to be sure that points behave as stacks, new views have to be inserted on the bottom of the upside-down points, and in the top of downside-ups. Since the gravity of any point is TOP or BOTTOM depending on it's position on the screen, I would be able to do this getting this attribute, but it seems to be private: see this answer in the related post
So I created a custom LinearLayout :
<resources>
<declare-styleable name="PointView">
<attr name="upsideDown" format="boolean" />
</declare-styleable>
</resources>
Which overrides 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);
}
}
}
They're included in my Activity's XML like this:
<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"/>
Now, when init() is called during the runtime, I get an exception:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.resolveLayoutParams()' on a null object reference
Looks like my context is null in :
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PointView);
And I don't have any clue about why, and so I can't access my custom attribute upsideDown.
Trying to get access to the property from attrs :
attrs.getAttributeBooleanValue(R.styleable.PointView_upsideDown, false);
Which looks like a synonym to me, although it's clearly not, is always returning true.
Any Idea?
Thanks in advance
[EDIT]
The exception's stacktrace :
--------- 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)
[EDIT 2]
I was populating the points with chips programmatically, populating in the Activity's XML throws the same exception on design-time.
<spacebar.backgammoid.PointView ...>
<ImageView ... />
</spacebar.backgammoid.PointView>
Yet, if I use regular LinearLayouts instead of my custom view everything works fine, and there's no error.
<LinearLayout ...>
<ImageView ... />
</LinearLayout>
Also, , I don't know if it's relevant, but the android-studio designer doesn't allow me to nest Views in my PoinView:
Okay, I got it working
There are two issues in my code.
Murphy's Ninth Law: Nature always sides with the hidden flaw: This is just wrong
return mUpsideDown ? super.getChildAt(index) : super.getChildAt(super.getChildCount() - index + 1);
I was trying to access two positions after the end of the children list. It should be
or
That was the reason for the exception. Android was trying to inflate null at that point, when populating my LinearLayout.
Secondly,
Was not working, not overriding this method gives me the expected behavior, I guess android uses getChildAt() when inflating LinearLayouts, and thus I can insert children views the regular way so they stack in the top of the point regardless of its orientation.
The way I was accessing my custom attributes was OK (which is the answer of my question as it was formulated)
Thanks, @Ravi Thapliyal, for your interest