NullPointerException in Drawable mutate method of Android 1.6

When using mutate on Android 1.6 with Drawable list state, I always got a Null Pointer exception. It works flawlessly on higher versions of Android. I use the default android list_selector_background as portable for mutation, and I need to mutate it, otherwise the pressed backgrounds become pretty dirty → all or some of them become highlighted when pressed.

The project is here:

http://code.google.com/p/tree-view-list-android/

And the relevant code snippet here: http://code.google.com/p/tree-view-list-android/source/browse/src/pl/polidea/treeview/AbstractTreeViewAdapter.java#205

return activity.getResources()
                    .getDrawable(android.R.drawable.list_selector_background)
                    .mutate();

Stack trace below:

3-11 11:37:39.973: ERROR/AndroidRuntime(5304): java.lang.NullPointerException
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.graphics.drawable.StateListDrawable.mutate(StateListDrawable.java:227)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at pl.polidea.treeview.AbstractTreeViewAdapter.getDrawableOrDefaultBackground(AbstractTreeViewAdapter.java:201)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at pl.polidea.treeview.AbstractTreeViewAdapter.populateTreeItem(AbstractTreeViewAdapter.java:210)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at pl.polidea.treeview.AbstractTreeViewAdapter.getView(AbstractTreeViewAdapter.java:153)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.AbsListView.obtainView(AbsListView.java:1273)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.ListView.makeAndAddView(ListView.java:1658)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.ListView.fillDown(ListView.java:637)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.ListView.fillFromTop(ListView.java:694)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.ListView.layoutChildren(ListView.java:1516)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.AbsListView.onLayout(AbsListView.java:1112)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.view.View.layout(View.java:6569)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:998)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.LinearLayout.onLayout(LinearLayout.java:918)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.view.View.layout(View.java:6569)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.view.View.layout(View.java:6569)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:998)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.LinearLayout.onLayout(LinearLayout.java:918)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.view.View.layout(View.java:6569)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.view.View.layout(View.java:6569)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.view.ViewRoot.performTraversals(ViewRoot.java:979)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1613)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.os.Handler.dispatchMessage(Handler.java:99)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.os.Looper.loop(Looper.java:123)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at android.app.ActivityThread.main(ActivityThread.java:4203)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at java.lang.reflect.Method.invokeNative(Native Method)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at java.lang.reflect.Method.invoke(Method.java:521)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
03-11 11:37:39.973: ERROR/AndroidRuntime(5304):     at dalvik.system.NativeStart.main(Native Method)
+3
1

, Android 1.6 . Android 1.6, :

android.graphics.drawable.StateListDrawable.java:
...
public Drawable mutate() {
    if (!mMutated && super.mutate() == this) {
        final int[][] sets = mStateListState.mStateSets;
        final int count = sets.length;
        mStateListState.mStateSets = new int[count][];
        for (int i = 0; i < count; i++) {
            mStateListState.mStateSets[i] = sets[i].clone(); //NPE causing line
        }
        mMutated = true;
    }
    return this;
}
...

mStateListState.mStateSets[i] = sets[i].clone(); //NPE causing line

clone() NullPointerException

Android > 1.6 :

public Drawable mutate() {
        if (!mMutated && super.mutate() == this) {
            final int[][] sets = mStateListState.mStateSets;
            final int count = sets.length;
            mStateListState.mStateSets = new int[count][];
            for (int i = 0; i < count; i++) {
                final int[] set = sets[i];
                if (set != null) {
                    mStateListState.mStateSets[i] = set.clone();
                }
            }
            mMutated = true;
        }
        return this;
    }

Android 1.6 . , mStateSets [i] :

android.graphics.drawable.DrawableContainer.DrowableContainerState:
... 
public final int addChild(Drawable dr) {
        final int pos = mNumChildren;

        if (pos >= mDrawables.length) {
            growArray(pos, pos+10); //Interesting line
        }

        dr.setVisible(false, true);
        dr.setCallback(mOwner);

        mDrawables[pos] = dr;
        mNumChildren++;
        mChildrenChangingConfigurations |= dr.getChangingConfigurations();
        mHaveOpacity = false;
        mHaveStateful = false;

        mConstantPadding = null;
        mPaddingChecked = false;
        mComputedConstantSize = false;

        return pos;
    }
...

xml. , mStateListState.mStateSets N * 10.

list_selector_background.xml, , android.R.drawable.list_selector_background:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_window_focused="false"
        android:drawable="@color/transparent" />

    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
    <item android:state_focused="true" android:state_enabled="false"
        android:state_pressed="true"
        android:drawable="@drawable/list_selector_background_disabled" />
    <item android:state_focused="true" android:state_enabled="false"
        android:drawable="@drawable/list_selector_background_disabled" />

    <item android:state_focused="true" android:state_pressed="true"
        android:drawable="@drawable/list_selector_background_transition" />
    <item android:state_focused="false" android:state_pressed="true"
        android:drawable="@drawable/list_selector_background_transition" />

    <item android:state_focused="true"
        android:drawable="@drawable/list_selector_background_focus" />

</selector>

6 ! , ? 1. list_selector_background.xml {Project}/res/drowable/ Android one :

<item android:state_window_focused="false"
    android:drawable="@color/transparent" />

<item android:state_window_focused="false" android:drawable="@android:color/transparent" />
  • Android :

    list_selector_background_disabled.9.png list_selector_background_focus.9.png list_selector_background_longpress.9.png list_selector_background_pressed.9.png list_selector_background_transition.xml

  • list_selector_background.xml, , (6 + 4 = 10):

    <item android:animationCache="true"
        android:drawable="@android:color/transparent" />
    
    <item android:animationCache="false"
        android:drawable="@android:color/transparent" />
    
    <item android:alwaysDrawnWithCache="false"
        android:drawable="@android:color/transparent" />
    
    <item android:alwaysDrawnWithCache="true"
        android:drawable="@android:color/transparent" />
    

. . .

+9

All Articles