NullPointerException в мутированном методе Drawable для Android 1.6 - PullRequest
3 голосов
/ 11 марта 2011

При использовании mutate в Android 1.6 со списком состояния Drawable я всегда получал исключение Null Pointer. Он работает безупречно на более высоких версиях Android. Я использую стандартный список android list_selector_background в качестве изменяемого объекта для изменения, и мне нужно изменить его, в противном случае нажатые фоны становятся довольно грязными -> все или некоторые из них выделяются при нажатии.

Проект находится здесь:

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

И соответствующий фрагмент кода здесь: 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();

Трассировка стека ниже:

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)

1 Ответ

9 голосов
/ 11 марта 2011

Кажется, в 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 1 и изменить только одну строку:

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

на

<item android:state_window_focused="false" android:drawable="@android:color/transparent" />
  1. Скопируйте 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.x36

Вуаля.Скомпилируйте и запустите.Это должно работать сейчас.

...