Android invalidateDrawable() не работает

У меня есть куча рисования в пользовательском представлении. Я хочу, чтобы пользователь мог нажимать на один или несколько рисунков, и он меняет цвета. В настоящее время каждый чертеж является просто StateListDrawable с двумя состояниями: state_pressed и не давить. Каждый раз, когда я нажимаю на отрисовку, setState возвращает true, поэтому я предполагаю, что оно действительно изменилось, но я не вижу изменения рисованного изображения. Является invalidateDrawable ничего не делая? Что я делаю неправильно? Как я могу перерисовать тот нарисованный при нажатии без необходимости вызова customView.invalidate() и перерисовывать все это каждый раз? Первоначально я делал это, но обнаружил, что мое приложение работает очень медленно / неэффективно. Спасибо!

Поток:

Custom View (contains set of our custom class - TouchKey)
- Custom class TouchKey containing drawable and info
- Upon press or release, custom class finds which drawable to change

Вот код для нажатия кнопки внутри TouchKey класс (MyTouch - это пользовательский класс, отслеживающий все прикосновения на устройстве Android):

public void pressed(MyTouch touch) {
    boolean successfulStateChange = this.drawable.setState(new int[]{android.
                                                           R.attr.state_pressed});
    this.customView.invalidateDrawable(drawable);
}

public void released(MyTouch touch) {
    boolean successfulStateChange = this.drawable.setState(new int[]{-android.
                                                           R.attr.state_pressed});
    this.customView.invalidateDrawable(drawable);
}

Как мой StateListDrawable рисуется в моем обычном представлении:

public class CustomView extends View {

    private TreeMap<Integer, TouchKey> keymap;

    /* Initialization Code Stuff Here - call drawKey */

    // StateListDrawable Creation
    private StateListDrawable drawKey(Canvas canvas, int bounds_l, 
                                  int bounds_t, int bounds_r, int bounds_b) 
        throws Resources.NotFoundException, XmlPullParserException, IOException {

        StateListDrawable key = new StateListDrawable();
        key.addState(new int[]{android.R.attr.state_pressed}, 
                     ContextCompat.getDrawable(mContext, R.drawable.key_pressed));
        key.addState(new int[]{-android.R.attr.state_pressed}, 
                     ContextCompat.getDrawable(mContext, R.drawable.key_released));    

        key.setBounds(bound_l, bounds_t, bounds_r, bounds_b);
        key.draw(canvas);

        return key;
    }
}

1 ответ

Решение

Я делал это изначально, но обнаружил, что мое приложение работает очень медленно / неэффективно

Если вы сделаете это, у вас есть большие преимущества в каком-то месте вашего кода, или (я полагаю) не масштабирует изображения перед рисованием. Поэтому постарайтесь найти логику в вашем коде, которая имеет огромное преимущество в системе. Так как View.invalidate() такой быстрый метод.

Другое 0,02$:

Я полагаю, что вы разрабатываете что-то вроде клавиатуры. Для этого случая вам нужно сделать недействительной только область вашего холста.

View.invalidate(new Rect(0, 0, 49, 49));

У меня тоже была проблема, но в конце концов я ее решаю. Причиной этой проблемы является то, что Drawable объект, который вы используете в контексте не устанавливает это Bounds по телефону setBounds(Rect), так что это Bounds является Rect(0,0,0,0) по умолчанию. Это причина invalidateDrawable() из View который Drawable прилагается не работает.

Увидеть View.invalidateDrawable():

@Override
public void invalidateDrawable(@NonNull Drawable drawable) {
    if (verifyDrawable(drawable)) {
        final Rect dirty = drawable.getDirtyBounds();
        final int scrollX = mScrollX;
        final int scrollY = mScrollY;

        invalidate(dirty.left + scrollX, dirty.top + scrollY,
                dirty.right + scrollX, dirty.bottom + scrollY);
        rebuildOutline();
    }
}

Посмотрите галочку Drawable.getDirtyBounds():

  /**
 * Return the drawable's dirty bounds Rect. Note: for efficiency, the
 * returned object may be the same object stored in the drawable (though
 * this is not guaranteed).
 * <p>
 * By default, this returns the full drawable bounds. Custom drawables may
 * override this method to perform more precise invalidation.
 *
 * @return The dirty bounds of this drawable
 */
@NonNull
public Rect getDirtyBounds() {
    return getBounds();
}

Так что Rect грязный Rect(0,0,0,0), если вы не настроены Drawable.Bounds.Следовательно View.invalidate() не работает

Итак, что вам нужно сделать, это настроить Drawable.Bounds в каком-то месте в коде, как это:

@Override
public void draw(@NonNull Canvas canvas) {
    Rect localRect = canvas.getClipBounds();
    this.setBounds(localRect);
    ...
}
Другие вопросы по тегам