Динамически загружать AnimationDrawable в TextView - PullRequest
8 голосов
/ 23 февраля 2012

Мне нужно заменить фразы текста в a изображениями, а затем добавить это в TextView. Для обычных Drawables это не проблема, но когда Drawable является AnimationDrawable, я не знаю, где и когда вызывать .start();.

Вот как я добавляю текст в TextView:

textview.append(Html.fromHtml(textWithHtmlImgTags, imagegetter, null));

Теги изображений в textWithHtmlImgTags заменяются с помощью imagegetter:

new ImageGetter()
{
    @Override
    public Drawable getDrawable(String source) {

        if(source.endsWith("_ani"))
        {
            Log.i("cmv", "This is an animated drawable.");

            AnimationDrawable dra = (AnimationDrawable)res.getDrawable(sRes.get(source));
            dra.setBounds(0, 0, dra.getIntrinsicWidth(), dra.getIntrinsicHeight());
            dra.start(); // This doesn't work..

            return dra;
        }

        Drawable dr = res.getDrawable(sRes.get(source));
        dr.setBounds(0, 0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight());
        return dr;
    }

};

Мои AnimationDrawables добавлены, но они не анимированы (они прикреплены к кадру 1).

В документации сказано:

Важно отметить, что метод start (), вызываемый для AnimationDrawable, не может быть вызван во время метода onCreate () вашего Activity, поскольку AnimationDrawable еще не полностью присоединен к окну. Если вы хотите немедленно воспроизвести анимацию, не требуя взаимодействия, то вы можете вызвать ее из метода onWindowFocusChanged () в своей деятельности, который будет вызываться, когда Android фокусирует ваше окно.

Поскольку изображения добавляются динамически, я не думаю, что это имеет какое-либо отношение к onCreate (). Так что, я думаю, я называю мой .start(), когда мой ничья is not yet fully attached to the window, но где / когда / как должен Я называю это?

Заранее спасибо!

Ответы [ 3 ]

6 голосов
/ 20 апреля 2012

Я вышел с решением.В вашем пользовательском TextView:

(1) Сначала вы должны определить время начала и окончания анимации.

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();

    handleAnimationDrawable(true);
}

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();

    handleAnimationDrawable(false);
}

private void handleAnimationDrawable(boolean isPlay) {
    CharSequence text = getText();
    if (text instanceof Spanned) {
        Spanned span = (Spanned) text;
        ImageSpan[] spans = span.getSpans(0, span.length() - 1,
                ImageSpan.class);
        for (ImageSpan s : spans) {
            Drawable d = s.getDrawable();
            if (d instanceof AnimationDrawable) {
                AnimationDrawable animationDrawable = (AnimationDrawable) d;
                if (isPlay) {
                    animationDrawable.setCallback(this);
                    animationDrawable.start();
                } else {
                    animationDrawable.stop();
                    animationDrawable.setCallback(null);
                }
            }
        }
    }
}

(2) А затем реализовать свой собственный Drawable.Callback toвызвать перерисовку.

@Override
public void invalidateDrawable(Drawable dr) {
    invalidate();
}

@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
    if (who != null && what != null) {
        mHandler.postAtTime(what, when);
    }
}

@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
    if (who != null && what != null) {
        mHandler.removeCallbacks(what);
    }
}
1 голос
/ 13 февраля 2013

В представлении уже реализован Drawable.Callback, поэтому для отображения объектов AnimationDrawable в ImageSpan необходимы только следующие переопределения TextView:

//  comparing against drawables found in the spans is probably safer
@Override
protected boolean verifyDrawable(Drawable who) {
    return (super.verifyDrawable(who) || who instanceof AnimationDrawable);
}

//  again comparing against drawables found in the spans is probably safer
@Override
public void invalidateDrawable(Drawable drawable) {
    if (drawable instanceof AnimationDrawable) {
        onLayout(true, getLeft(), getTop(), getRight(), getBottom());
        invalidate();
        return;
    }
    super.invalidateDrawable(drawable);
}

Переопределение verifyDrawable необходимо, чтобы реализации суперкласса Drawable.Callback принимали неожиданные объекты Drawable и обрабатывали планирование.

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

Я также обнаружил, что по какой-то причине, если я создал ImageSpan со ссылкой на идентификатор ресурса анимации, ресурс не кэшировался и, казалось, перезагружался при каждом вызове ImageSpan.getDrawable (). Загружая ресурс, устанавливая его границы и передавая его конструктору ImageSpan, я избежал этой проблемы.

используйте onAttachedToWindow / onDetachedFromWindow, как предлагается в противном случае.

1 голос
/ 20 апреля 2012

Думаю, важный вопрос: когда вы делаете вызов?

textview.append(Html.fromHtml(textWithHtmlImgTags...

Если вы вызываете это после onCreate, тогда вы можете вызвать animation.start ().Если он вызывается в методе onCreate (), я бы:

  • сохранил список элементов рисования, сгенерированных в ImageGetter;
  • , вызвал start () для всех элементов рисования в onAttachedToWindow илиonStart (не уверен, работают ли они в функции onStart)
...