Uso de gif animado en Android

En Android se suele usar un ImageView para dibujar una imagen. La imagen puede provenir de un recurso interno, de una imagen externa, o de un objeto drawable que se le pase directamente al método .setImageDrawable().

En cualquier caso, en la mayoría de dispositivos no será posible mostrar en este tipo de contenedor un gif animado, ya que según la documentación tan solo se representará el primer frame de la imagen animada.

El modo de representar correctamente una animación en android es usando un recurso creado específicamente para ello. De los dos tipos de animaciones que se pueden crear en Android la que nos interesa en esta ocasión es la que se conoce como Frame Animation. La idea es sencilla y es muy similar a la de los GIF animados: un archivo xml define qué imágenes se muestran y durante cuánto tiempo, las imágenes deben de encontrarse accesibles como recursos internos de la aplicación. Para más información véase la documentación sobre Frame Animation.

La cuestión es que hay un montón de GIFs animados que podemos querer incluir en nuestra aplicación para Android, así que es necesario hacer una conversión de un formato a otro de un modo más o menos automatizado.

El siguiente guión en python extrae los frames completos así como la información temporal y espacial de cada uno de ellos regenerando imágenes en caso de tratarse de un GIF optimizado, para formar un recurso de imagen completo para Android. La animación así creada funcionará correctamente en cualquier dispositivo en cualquier versión de Android. Para más información véase la clase AnimationDrawable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
 
import sys, os
import subprocess
 
# When no delay is specified in a gif file, 100ms is default
DEFAULT_DELAY_MS = 100
 
input_file = None
output_file = None
 
try:
    input_file = sys.argv[1]
    output_file = "%s.xml" % (input_file.split(".")[0],)
except:
    print "Usage: %s <input.gif>" % (sys.argv[0],)
    sys.exit(1)
 
 
class Anim(object):
    def __init__(self):
        self.content = []
 
    def add_header(self):
        self.content.append('<?xml version="1.0" encoding="utf-8"?>\n')
        self.content.append('<animation-list\n')
        self.content.append('    xmlns:android="http://schemas.android.com/apk/res/android"\n')
        self.content.append('    android:oneshot="false">\n')
 
    def add_item(self, resource, duration):
        self.content.append(
            '    <item android:drawable="@drawable/%s" android:duration="%d" />\n' %
            (resource, duration))
 
    def add_footer(self):
        self.content.append('</animation-list>\n')
 
    def write_file(self):
        file_descriptor = open(output_file, "w")
        file_descriptor.writelines(self.content)
        file_descriptor.close()
 
 
class Gif(object):
    def __init__(self, gif):
        self.input_file = gif
        gif_info = subprocess.Popen(
            ["gifsicle", "--info", input_file],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)
 
        (gif_info_out, gif_info_error) = gif_info.communicate()
 
        self.gif_info_lines = gif_info_out.splitlines()
 
    def get_info(self):
        return self.gif_info_lines
 
    def explode(self):    
        return_code = subprocess.call(
            ["gifsicle", "--explode", "--unoptimize", self.input_file])
 
        return not return_code
 
    def to_png(self, gif_frame):
        return_code = subprocess.call(["gif2png", "-d", "-s", "-O", gif_frame])
 
        return return_code
 
 
def main():
    print "Processing %s..." % (input_file,)
 
    anim = Anim()
    anim.add_header() 
 
    gif = Gif(input_file)
 
    if not gif.explode():
        print "Error exploding gif input file %s" % (input_file,)
        #sys.exit(1)
 
    info_lines = gif.get_info()
 
    counter = 0
    for line in info_lines:
        line = line.strip()
 
        if line.startswith("disposal"):
            line_items = line.split()
 
            if len(line_items) == 4:
                milliseconds = int(round(float(line_items[3][:-1]) * 1000))
            else:
                milliseconds = DEFAULT_DELAY_MS
 
            # Rename and convert gif to optimized png
            renamed_gif = "%s_%03d.gif" % (input_file.split(".")[0], counter)
            os.rename("%s.%03d" % (input_file, counter), renamed_gif)
            result = gif.to_png(renamed_gif)
 
            anim.add_item(renamed_gif.split(".")[0], milliseconds)
            counter = counter + 1
 
    anim.add_footer()
    anim.write_file()
 
    os.unlink(input_file)
    sys.exit(0)
 
if __name__ == "__main__":
    main()

Es necesario tener instalado gifsicle, y para la optimización en tamaño y conversión a png de las imágenes resultantes es necesario tener también instalado, y en el $PATH, gif2png.

Descargar getanim.py.

Etiquetas: , , , ,

Archivado en:Android

2 comentarios en “Uso de gif animado en Android”