Показать анимированный GIF

Я хочу отображать анимированные GIF-изображения в моем приложении. Как я выяснил, сложный способ, которым Android не поддерживает анимированный GIF изначально.

Однако он может отображать анимацию с помощью AnimationDrawable:

Разработка> Руководства> Изображения и графика> Обзор Drawables

В примере используется анимация, сохраненная в виде кадров в ресурсах приложения, но мне нужно отобразить анимированный GIF-файл напрямую.

Мой план состоит в том, чтобы разбить анимированный GIF на кадры и добавить каждый кадр как нарисованный в AnimationDrawable.

Кто-нибудь знает, как извлечь кадры из анимированного GIF и конвертировать каждый из них в Drawable?

32 ответа

Решение

Android фактически может декодировать и отображать анимированные GIF-файлы, используя класс android.graphics.Movie.

Это не слишком документировано, но есть в SDK Reference. Более того, он используется в примерах в ApiDemos в примере BitmapDecode с некоторым анимированным флагом.

ОБНОВИТЬ:

Используйте скольжение:

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.0.0'
}

использование:

Glide.with(context).load(GIF_URI).into(new GlideDrawableImageViewTarget(IMAGE_VIEW));

см документы

Также поместите (main/assets/htmls/name.gif) [с помощью этого html-файла подгоните под размер]

<html style="margin: 0;">
<body style="margin: 0;">
<img src="name.gif" style="width: 100%; height: 100%" />
</body>
</html>

объявите в своем XML, например, так (main/res/layout/name.xml): [вы определяете размер, например]

<WebView
android:layout_width="70dp"
android:layout_height="70dp"
android:id="@+id/webView"
android:layout_gravity="center_horizontal" />

в вашей деятельности поместите следующий код внутри onCreate

web = (WebView) findViewById(R.id.webView); 
web.setBackgroundColor(Color.TRANSPARENT); //for gif without background
web.loadUrl("file:///android_asset/htmls/name.html");

если вы хотите загружать динамически, вы должны загрузить веб-представление с данными:

// or "[path]/name.gif" (e.g: file:///android_asset/name.gif for resources in asset folder), and in loadDataWithBaseURL(), you don't need to set base URL, on the other hand, it's similar to loadData() method.
String gifName = "name.gif";
String yourData = "<html style=\"margin: 0;\">\n" +
        "    <body style=\"margin: 0;\">\n" +
        "    <img src=" + gifName + " style=\"width: 100%; height: 100%\" />\n" +
        "    </body>\n" +
        "    </html>";
// Important to add this attribute to webView to get resource from outside.
webView.getSettings().setAllowFileAccess(true);

// Notice: should use loadDataWithBaseURL. BaseUrl could be the base url such as the path to asset folder, or SDCard or any other path, where your images or the other media resides related to your html
webView.loadDataWithBaseURL("file:///android_asset/", yourData, "text/html", "utf-8", null);
// Or if you want to load image from SD card or where else, here is the idea.
String base = Environment.getExternalStorageDirectory().getAbsolutePath().toString();
webView.loadDataWithBaseURL(base + '/', yourData, "text/html", "utf-8", null);

предложение: лучше загрузить GIF со статическими изображениями для получения дополнительной информации. https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html

Вот и все, я надеюсь, вы поможете.

Я решил проблему, разделив gif- анимацию на кадры перед сохранением в телефоне, поэтому мне не пришлось бы иметь дело с этим в Android.

Затем я загружаю каждый кадр в телефон, создаю из него Drawable, а затем создаю AnimationDrawable - очень похоже на пример из моего вопроса.

Я нашел очень простой способ, с хорошим и простым рабочим примером здесь

отображать анимированный виджет

Прежде чем заставить его работать, в коде необходимо выполнить некоторые действия.

В СЛЕДУЮЩИХ

    @Override
    public void onCreate(Bundle savedInstanceState){    
        super.onCreate(savedInstanceStated);   
        setContentView(new MYGIFView());
    }    
}

просто замени

setContentView(new MYGIFView());

в

setContentView(new MYGIFView(this));

И В

public GIFView(Context context) {
    super(context);

Предоставьте свой собственный файл анимации GIF

    is = context.getResources().openRawResource(R.drawable.earth);
    movie = Movie.decodeStream(is);
}

ЗАМЕНА ПЕРВОЙ ЛИНИИ В

public MYGIFView(Context context) {

по названию класса...

после внесения этих небольших изменений все должно работать как у меня...

надеюсь, это поможет

Glide 4.6

1. Загрузить gif

GlideApp.with(context)
            .load(R.raw.gif) // or url
            .into(imageview);

2. Получить файл объекта

GlideApp.with(context)
                .asGif()
                .load(R.raw.gif) //or url
                .into(new SimpleTarget<GifDrawable>() {
                    @Override
                    public void onResourceReady(@NonNull GifDrawable resource, @Nullable Transition<? super GifDrawable> transition) {

                        resource.start();
                      //resource.setLoopCount(1);
                        imageView.setImageDrawable(resource);
                    }
                });

Способы показать анимированный GIF на Android:

  • Кино класс. Как уже упоминалось выше, это довольно глючно.
  • WebView. Он очень прост в использовании и обычно работает. Но иногда это начинает плохо себя вести, и это всегда на некоторых неясных устройствах, которых у вас нет. Кроме того, вы не можете использовать несколько экземпляров в любом виде списка, потому что это влияет на вашу память. Тем не менее, вы можете рассматривать это как основной подход.
  • Пользовательский код для декодирования GIF-файлов в растровые изображения и отображения их как Drawable или ImageView. Я упомяну две библиотеки:

https://github.com/koral--/android-gif-drawable - декодер реализован на C, поэтому он очень эффективен.

https://code.google.com/p/giffiledecoder - декодер реализован на Java, поэтому с ним проще работать. Все еще достаточно эффективно, даже с большими файлами.

Вы также найдете много библиотек, основанных на классе GifDecoder. Это также Java-декодер, но он работает, загружая весь файл в память, поэтому он применим только к маленьким файлам.

Мне было очень тяжело работать с анимацией в Android. У меня были только следующие две работы:

  1. WebView
  2. ион

WebView работает нормально и действительно просто, но проблема в том, что он замедляет загрузку представления, и приложение не отвечает на секунду или около того. Мне это не понравилось. Поэтому я пробовал разные подходы (НЕ РАБОТАЛ):

  1. ImageViewEx устарела!
  2. Пикассо не загружал анимированный GIF
  3. http://android-gif-drawable/ выглядит великолепно, но это вызвало некоторые проблемы с проводным NDK в моем проекте. Это привело к тому, что моя локальная библиотека NDK перестала работать, и я не смог это исправить

Я имел некоторые взад и вперед с Ion; Наконец, у меня это работает, и это действительно быстро:-)

Ion.with(imgView)
  .error(R.drawable.default_image)
  .animateGif(AnimateGifMode.ANIMATE)
  .load("file:///android_asset/animated.gif");

Glide

Библиотека загрузчиков изображений для Android, рекомендованная Google.

  • Glide очень похож на Picasso, но это намного быстрее, чем Picasso.
  • Glide потребляет меньше памяти, чем Picasso.

Что у этого Glide есть, но у Пикассо нет

Возможность загрузки GIF-анимации в простой ImageView может быть самой интересной особенностью Glide. И да, вы не можете сделать это с Пикассо. Некоторые важные ссылки

  1. https://github.com/bumptech/glide
  2. http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

Попробуйте это, приведенный ниже код отображения GIF-файла в ProgressBar

loading_activity.xml (в папке Layout)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff" >

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:indeterminate="true"
        android:indeterminateDrawable="@drawable/custom_loading"
        android:visibility="gone" />

</RelativeLayout>

custom_loading.xml (в папке для рисования)

здесь я положил black_gif.gif(в папке drawable), вы можете поместить свой собственный GIF здесь

<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/black_gif"
    android:pivotX="50%"
    android:pivotY="50%" />

LoadingActivity.java(в папке res)

public class LoadingActivity extends Activity {

    ProgressBar bar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_loading);
        bar = (ProgressBar) findViewById(R.id.progressBar);
        bar.setVisibility(View.VISIBLE);

    }

}

Используйте ImageViewEx, библиотеку, которая делает использование GIF столь же простым, как использование ImageView,

Никто не упомянул библиотеку Ion или Glide. они работают очень хорошо.

Это проще в обращении по сравнению с WebView.

У меня был успех с решением, предложенным в этой статье, класс под названием GifMovieView, который оказывает View которые затем могут быть отображены или добавлены к конкретному ViewGroup, Ознакомьтесь с другими методами, представленными в частях 2 и 3 указанной статьи.

Единственным недостатком этого метода является то, что сглаживание в фильме не так уж и хорошо (должно быть побочным эффектом использования "теневого" Android Movie Учебный класс). Тогда вам лучше установить сплошной цвет фона в анимированном GIF.

Некоторые мысли по поводу примера BitmapDecode... В основном он использует древний, но довольно безликий класс Movie из android.graphics. В последних версиях API вам необходимо отключить аппаратное ускорение, как описано здесь. В противном случае это было для меня

<activity
            android:hardwareAccelerated="false"
            android:name="foo.GifActivity"
            android:label="The state of computer animation 2014">
</activity>

Вот пример BitmapDecode, сокращенный только с частью GIF. Вы должны сделать свой собственный виджет (вид) и нарисовать его самостоятельно. Не такой мощный, как ImageView.

import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.*;
import android.view.View;

public class GifActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new GifView(this));
    }

    static class GifView extends View {
        Movie movie;

        GifView(Context context) {
            super(context);
            movie = Movie.decodeStream(
                    context.getResources().openRawResource(
                            R.drawable.some_gif));
        }
        @Override
        protected void onDraw(Canvas canvas) {   
            if (movie != null) {
                movie.setTime(
                    (int) SystemClock.uptimeMillis() % movie.duration());
                movie.draw(canvas, 0, 0);
                invalidate();
            }
        }
    }
}

2 других метода, один с ImageView, другой с WebView, можно найти в этом прекрасном уроке. Метод ImageView использует лицензированный Apache android-gifview из Google Code.

@PointerNull дал хорошее решение, но оно не идеально. Он не работает на некоторых устройствах с большими файлами и показывает некорректную анимацию Gif с дельта-кадрами в предыдущей версии ICS. Я нашел решение без этих ошибок. Это библиотека с собственным декодированием для рисования: https://github.com/koral--/android-gif-drawable.

Только для Android API (Android Pie)28 и +

использовать AnimatedImageDrawable как

// ImageView from layout
val ima : ImageView = findViewById(R.id.img_gif)
// create AnimatedDrawable 
val decodedAnimation = ImageDecoder.decodeDrawable(
        // create ImageDecoder.Source object
        ImageDecoder.createSource(resources, R.drawable.tenor))
// set the drawble as image source of ImageView
ima.setImageDrawable(decodedAnimation)
// play the animation
(decodedAnimation as? AnimatedImageDrawable)?.start()

XML-код, добавьте ImageView

<ImageView
    android:id="@+id/img_gif"
    android:background="@drawable/ic_launcher_background" <!--Default background-->
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_width="200dp"
    android:layout_height="200dp" />

AnimatedImageDrawable является дочерью Drawable и создан ImageDecoder.decodeDrawable

ImageDecoder.decodeDrawable что дополнительно требует экземпляра ImageDecoder.Source создано ImageDecoder.createSource,

ImageDecoder.createSource может принимать источник только как имя, ByteBuffer, File, resourceId, URI, ContentResolver для создания исходного объекта и использует его для создания AnimatedImageDrawable как Drawable (полиморфный зов)

static ImageDecoder.Source  createSource(AssetManager assets, String fileName)
static ImageDecoder.Source  createSource(ByteBuffer buffer)
static ImageDecoder.Source  createSource(File file)
static ImageDecoder.Source  createSource(Resources res, int resId)
static ImageDecoder.Source  createSource(ContentResolver cr, Uri uri)

Примечание: вы также можете создать Bitmap используя ImageDecoder # decodeBitmap.

Выход:

AnimatedDrawable также поддерживает изменение размера, рамку и управление цветом

Поместите его в WebView, он должен отображать его правильно, так как браузер по умолчанию поддерживает файлы GIF. (Froyo+, если я не ошибаюсь)

Создайте пакет с именем "Utils" в папке src и создайте класс с именем "GifImageView".

public class GifImageView extends View {
    private InputStream mInputStream;
    private Movie mMovie;
    private int mWidth, mHeight;
    private long mStart;
    private Context mContext;
    public GifImageView(Context context) {
        super(context);
        this.mContext = context;
    }
    public GifImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        if (attrs.getAttributeName(1).equals("background")) {
            int id = Integer.parseInt(attrs.getAttributeValue(1).substring(1));
            setGifImageResource(id);
        }
    }
    private void init() {
        setFocusable(true);
        mMovie = Movie.decodeStream(mInputStream);
        mWidth = mMovie.width();
        mHeight = mMovie.height();
        requestLayout();
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(mWidth, mHeight);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        long now = SystemClock.uptimeMillis();
        if (mStart == 0) {
            mStart = now;
        }
        if (mMovie != null) {
            int duration = mMovie.duration();
            if (duration == 0) {
                duration = 1000;
            }
            int relTime = (int) ((now - mStart) % duration);
            mMovie.setTime(relTime);
            mMovie.draw(canvas, 0, 0);
            invalidate();
        }
    }
    public void setGifImageResource(int id) {
        mInputStream = mContext.getResources().openRawResource(id);
        init();
    }
    public void setGifImageUri(Uri uri) {
        try {
            mInputStream = mContext.getContentResolver().openInputStream(uri);
            init();
        } catch (FileNotFoundException e) {
            Log.e("GIfImageView", "File not found");
        }
    }
}

Теперь определите GifImageView в файле MainActivity: src/activity/MainActivity.class

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        GifImageView gifImageView = (GifImageView) findViewById(R.id.GifImageView);
        gifImageView.setGifImageResource(R.drawable.smartphone_drib);
    }
}

Теперь файл части пользовательского интерфейса: res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:gravity="center"
    android:background="#111E39"
    tools:context=".Activity.MainActivity">
    <com.android.animatedgif.Utils.GifImageView
        android:id="@+id/GifImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />

</RelativeLayout>

Код ресурса: пример Android

Существует два варианта загрузки анимированных GIF-файлов в наши приложения для Android.

1) Использование Glide для загрузки GIF в ImageView,

    String urlGif = "https://cdn.dribbble.com/users/263558/screenshots/1337078/dvsd.gif";
    //add Glide implementation into the build.gradle file.
    ImageView imageView = (ImageView)findViewById(R.id.imageView);
    Uri uri = Uri.parse(urlGif);
    Glide.with(getApplicationContext()).load(uri).into(imageView);

2) Использование HTML для загрузки GIF в WebView

Создайте HTML-файл с адресом.gif-файла:

<html style="margin: 0;">
<body style="margin: 0;">
<img src="https://..../myimage.gif" style="width: 100%; height: 100%" />
</body>
</html>

сохраните этот файл в каталоге активов:

Загрузите этот HTML-код в WebView вашего приложения:

    WebView webView =  (WebView)findViewById(R.id.webView);
    webView = (WebView) findViewById(R.id.webView);
    webView.loadUrl("file:///android_asset/html/webpage_gif.html");

Вот полный пример этих двух вариантов.

Я думаю, что лучшая библиотека для работы с GIF-файлами : koral

Использовал его, и я успешен, и эта библиотека посвящена GIF'S; но где как Пикассо и скольжение являются основой изображения общего назначения; так что я думаю, что разработчики этой библиотеки полностью сосредоточились на gif-файлах

Просто хотел добавить, что класс Movie теперь устарел.

Этот класс устарел на уровне API P.

Рекомендуется использовать это

AnimatedImageDrawable

Drawable для рисования анимированных изображений (например, GIF).

Используйте фреску. Вот как это сделать:

http://frescolib.org/docs/animations.html

Вот репо с образцом:

https://github.com/facebook/fresco/tree/master/samples/animation

Осторожно, фреска не поддерживает перенос содержимого!

Самый простой способ - можно рассмотреть приведенный ниже код

Мы можем воспользоваться Imageview setImageResource, см. Ниже код для того же.

Приведенный ниже код можно использовать для показа изображения в формате GIF, если у вас есть несколько изображений GIF. Просто разделите GIF-файл на отдельный PNG-файл из онлайн-инструмента и поместите изображение в рисунок, как показано ниже

image_1.png, image_2.png и т. д.

Есть обработчик для динамического изменения изображения.

int imagePosition = 1;
    Handler handler = new Handler();
        Runnable runnable = new Runnable() {
            public void run() {
                updateImage();
            }
        };




    public void updateImage() {

                appInstance.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        int resId = getResources().getIdentifier("image_" + imagePosition, "drawable", appInstance.getPackageName());
                        gifImageViewDummy.setImageResource(resId);
                        imagePosition++;
    //Consider you have 30 image for the anim
                        if (imagePosition == 30) {
//this make animation play only once
                            handler.removeCallbacks(runnable);

                        } else {
    //You can define your own time based on the animation
                            handler.postDelayed(runnable, 50);
                        }

//to make animation to continue use below code and remove above if else
// if (imagePosition == 30)
//imagePosition = 1;
// handler.postDelayed(runnable, 50);
// 
                    }
                });
              }

Я решил это, разделив gif на кадры и используя стандартную анимацию Android

Подобно тому, что сказал @Leonti, но с большей глубиной:

Чтобы решить ту же проблему, я открыл GIMP, скрыл все слои, кроме одного, экспортировал его как свое собственное изображение, а затем скрыл этот слой и отобразил следующий, и т. Д., Пока у меня не было отдельных файлов ресурсов для каждого из них., Затем я мог бы использовать их в качестве фреймов в XML-файле AnimationDrawable.

Что-то, что я сделал для показа картинок в приложениях. Я расширил ImageView, чтобы люди могли свободно использовать его атрибуты. Это может показать GIF-файлы из URL или из каталога активов. Библиотека также позволяет расширяемым классам наследовать от нее и расширять ее для поддержки различных методов инициализации gif.

https://github.com/Gavras/GIFView

На странице github есть небольшое руководство.

Также было опубликовано на Android Arsenal:

https://android-arsenal.com/details/1/4947

Пример использования:

Из XML:

<com.whygraphics.gifview.gif.GIFView xmlns:gif_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/main_activity_gif_vie"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:scaleType="center"
        gif_view:gif_src="url:http://pop.h-cdn.co/assets/16/33/480x264/gallery-1471381857-gif-season-2.gif" />

В деятельности:

    GIFView mGifView = (GIFView) findViewById(R.id.main_activity_gif_vie);

    mGifView.setOnSettingGifListener(new GIFView.OnSettingGifListener() {
                @Override
                public void onSuccess(GIFView view, Exception e) {
                    Toast.makeText(MainActivity.this, "onSuccess()", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onFailure(GIFView view, Exception e) {

        }
});

Установка GIF программно:

mGifView.setGifResource("asset:gif1");

Для экономии ресурсов существует библиотека glide для файлов. Понятия не имею, зачем использовать что-то еще, особенно веб-просмотр, чтобы показывать только изображение.Glide - идеальная и простая библиотека, которая подготавливает анимированные рисунки из gif и помещает их прямо в просмотр изображений. Сама логика анимации ручки gifdrawable.Gif имеют внутри lzw заархивированные необработанные данные rgb анимации. Нет причин для сложного использования веб-просмотра и управления большим количеством файлов, чтобы отображать только файл gif в приложении.

Если вы хотите использовать Glide для загрузки gif:

      Glide.with(this)
        .asGif()
        .load(R.raw.onboarding_layers) //Your gif resource
        .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.NONE))
        .listener(new RequestListener<GifDrawable>() {
            @Override
            public boolean onLoadFailed(@Nullable @org.jetbrains.annotations.Nullable GlideException e, Object model, Target<GifDrawable> target, boolean isFirstResource) {
                return false;
            }

            @Override
            public boolean onResourceReady(GifDrawable resource, Object model, Target<GifDrawable> target, DataSource dataSource, boolean isFirstResource) {
                resource.setLoopCount(1);
                return false;
            }
        })
        .into((ImageView) view.findViewById(R.id.layer_icons));

Простой способ отобразить анимированный GIF непосредственно из URL в макет вашего приложения - использовать класс WebView.

Шаг 1: в вашем макете XML

<WebView
android:id="@+id/webView"
android:layout_width="50dp"
android:layout_height="50dp"
/>

Шаг 2: в вашей деятельности

WebView wb;
wb = (WebView) findViewById(R.id.webView);
wb.loadUrl("https://.......);

Шаг 3: В вашем Manifest.XML сделайте интернет-разрешение

<uses-permission android:name="android.permission.INTERNET" />

Шаг 4. Если вы хотите, чтобы ваш GIF-фон был прозрачным и чтобы GIF-файл соответствовал вашему макету

wb.setBackgroundColor(Color.TRANSPARENT);
wb.getSettings().setLoadWithOverviewMode(true);
wb.getSettings().setUseWideViewPort(true);

Вы можете использовать библиотеку GifAnimationDrawable, которая находится по этой ссылке - https://github.com/Hipmob/gifanimateddrawable, и которая преобразует любой gif в AnimationDrawable. Наслаждаться:)

Другие вопросы по тегам