четверг, 27 октября 2011 г.

Android. ImageView с поддержкой SVG


English translation

Source Code

Эта моя стать изначально была опубликована на сайте The Code Project. Оригинал Codeproject.

Введение

Как вы знаете, Андроид не поддерживает формат SVG. Однако у него есть масса преимуществ. Во первых - масштабируемость. Отпадает необходимость держать картинки в разных разрешениях. Нет необходимости, к примеру, масштабировать битмэп с потерей качества. SVG может быть отмасштабирована в любое разрешение и качество картинки будет тем же. Во-вторых, SVG - это обычный xml-файл, что в итоге размер файла намного меньше, чем та же картинка в растровом формате. Более того, картинку сожно изменять "на лету". SVG файл можно открыть в обычном текстовом редакторе и помотреть, как картинка формируется. И так далее... Но, так как, к сожалению, Android не поддерживает SVG, то придется немного повозиться с NDK. Хорошая новоость заключается в том, что возни будет немного. У нас имеются готовые библиотеки с открытым исходным кодом для растеризации SVG.

В интернете достаточно уроков о том, как работать с NDK. Не будем повторяться. Единственно, немного полезных замечаний:

  • Во-первых, необходим эклипс. Загрузить можно здесь: [1].
  • Или, как альтернативу, можно использовать [2]. Раньше я предпочитал ее, но в последнее время она без бубна работает не очень...
  • После установки эклипса, в нем нужно установить CDT плагин.
  • ADT плагин [3].
  • После этого, добавляем Eclipse Sequoyah[4] плагин, чтобы дебажить c/c++ код.
  • Для пользователей Windows необходимо установить cygwin[5] (Добавляем Cygwin/bin в PATH. В эклипсе - cinfigure build path, и устанавливаем build command в что-то типа "bash C:\AndroidNDK\ndk-build").
  • Android SDK [6].
  • Android NDK. Используем CrystaX NDK[7]. (В текущей версии NDK (6) от Google Android NDK libsvg собрать не удалось)
  • В настройках эклипса указываем пути к NDK и SDKIn
 Сначала используем библиотеку android-libsvg [8]. Кроме того, она зависит от libsvg, libpng, libjpeg, libexpat и zlib. Ее преимущество в том, что она поддерживает почти все фичи SVG. Создаем папку android-libsvg где-нибудь в файловой системе и в консоли (или cygwin) выполняем “bzr branch lp:libsvg-android”. Bazaar загрузит исходники.

Ok. Теперь создаем новый проект “ImageViewSvg”. В контекстном меню проекта выбираем AndroidTools/Add Native support. К проекту добавится папка “jni”. Удаляем из нее все и копируем содержимое папки “jni” из android-libsvg . Обновляем папку jni нашего проекта(F5). Посмотрим на файл Android.mk в папке “jni”. Что означают некоторые переменные:


  • LOCAL_PATH := $(call my-dir) – my-dyr макрос устанавливает LOCAL_PATH переменную, которая указывает где находятся файлы исходников(в текущем каталоге)
  • include $(CLEAR_VARS) – Очищает вс локальные переменные
  • LOCAL_MODULE – Имя библиотеки
  • LOCAL_CFLAGS – Устанавливает флаги компилятора пути к "include" файлам
  • LIBJPEG_SOURCES, … - Список всех файлов исходников, для каждой библиотеки, которая будет использоваться
  • LOCAL_LDLIBS – Линки к дополнительным библиотекам
  • LOCAL_SRC_FILES – Список всех исходников для всех библиотек
  • BUILD_SHARED_LIBRARY – Линк к mk файлу, который построит "shared" библиотку 


Более подробно смотрите файлANDROID-MK.TXT в NDK.
Иногда в WIndows, после рестарта эклипса, он не может запустить ndk-build. Необходимо переустановить "build command" в крнфигурации.

Далее создаем com.toolkits.libsvgandroid и копируем туда SvgRaster.java из проекта libsvg-android . Все практически готово.


Для поддержки классом ImageView SVG формата, необходимо перегрузить в нем некоторые методы. Но еще хотелось бы использовать стандартный атрибут android:src как SVG файл и устанавливать его из стандартной папки“drawable” вместо папки “raw”. Вначале изменим конструктор. Для доступа к android:src атрибуту, добавляем attrs.xml в res/values:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="ImageViewSvg">
  <attr name="android:src"/>
 </declare-styleable>
</resources>
 
Посмотрим на исходники конструктора класса ImageView . Имеем следующий код:

Drawable d = 
   a.getDrawable(com.android.internal.R.styleable.ImageView_src);
if (d != null) {
    setImageDrawable(d);
}
 



Далее посмотрим на метод setImageBitmap . Он просто вызывает setImageDrawable. Т.о. мы можем использовать его, если у нас будет подходящий битмэп. Правда еще необходимо достать файл из “drawable”. Ну, здесь - как два пальца об асфальт - – достаем ID ресурса из android:src атрибута и читаем его в поток. Далее, при помощи, libandroidsvg парсим SVG:

В итоге конструктор выглядит так:

public ImageViewSvg(Context context, AttributeSet attrs, int defStyle) {

  // Let's try load supported by ImageView formats
  super(context, attrs, defStyle);
        
        if(this.getDrawable() == null)
        {
         //  Get defined attributes
            TypedArray a = context.obtainStyledAttributes(attrs,
                    R.styleable.ImageViewSvg, defStyle, 0);
                        
            // Getting a file name
            CharSequence cs = 
                a.getText(R.styleable.ImageViewSvg_android_src);
            String file = cs.toString();
            
            // Is it SVG file?
            if (file.endsWith(".svg")) {
             
             // Retrieve ID of the resource
                int id = 
                    a.getResourceId(
                    R.styleable.ImageViewSvg_android_src, -1);
                if(id != -1){
                try {
                  // Get the input stream for the raw resource
                  InputStream inStream = 
                                     getResources().openRawResource(id);
                  int size = inStream.available();
                  
                  // Read into the buffer
                  byte[] buffer = new byte[size];
                  inStream.read(buffer);
      inStream.close();
      
      // And make a string
      mSvgContent = 
      EncodingUtils.getString
       (buffer, "UTF-8");
      
      // Parse it
               mSvgId = SvgRaster.svgAndroidCreate();
               SvgRaster.svgAndroidParseBuffer
     (mSvgId, mSvgContent.toString());
               SvgRaster.svgAndroidSetAntialiasing(mSvgId, true);
                                                
               mIsSvg = true; 

      
     } catch (IOException e) {
      mIsSvg = false;
      e.printStackTrace();
     }                 
                }
            }
        }
}
 
Другая проблема в том, что в SVG не указан его размер(Не всегда, в некоторых указан желаемый. См. Пы.Сы.). Более тогоImageView параметры лэйаута могут быть установлены в wrap_content, fill_parent , или конкретные размеры. Используем метод onSizeChanged . Единственная проблема - атрибут wrap_content . В этом случае размер будет 0. Нуобходимо будет заменить wrap_content наfill_parent на лету. К сожалению, это ничего не даст. Если взглянуть на исходники, то будет видно, что родительски лэйаут вытаскивает параметры прямо из атрибутов и вызывает метод setLayoutParams . Перегрузим его:

@Override 
public void setLayoutParams(ViewGroup.LayoutParams params){
 if(mIsSvg)
 {
  // replace WRAP_CONTENT if needed
  if(params.width == ViewGroup.LayoutParams.WRAP_CONTENT
    && getSuggestedMinimumWidth() == 0)
   params.width = ViewGroup.LayoutParams.FILL_PARENT;
  if(params.height == ViewGroup.LayoutParams.WRAP_CONTENT
    && getSuggestedMinimumHeight() == 0)
   params.height = ViewGroup.LayoutParams.FILL_PARENT;
 }
 super.setLayoutParams(params);
}
 
Также onSizeChanged:

@Override 
public void onSizeChanged(int w, int h, int ow, int oh){
 if(mIsSvg){
  //Create the bitmap to raster svg to
   Canvas canvas = new Canvas();
  mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  canvas.setBitmap(mBitmap);
  // Render SVG with use of libandroidsvg
  SvgRaster.svgAndroidRenderToArea(
   mSvgId, canvas,
   0, 0, canvas.getWidth(), canvas.getHeight()); 
  this.setImageBitmap(mBitmap);
 }
 else
  super.onSizeChanged(w, h, ow, oh);
}
 


И, в конце-концов, настала пора опробовать то, что получилось. Создадим такой лэйаут:

<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:background="#AA0000"
  android:layout_height="fill_parent" 
  android:layout_width="fill_parent"
  android:layout_weight="1.0" 
  android:gravity="center"
  >
  <com.imageviewsvg.controls.ImageViewSvg
   android:src="@drawable/lion" 
   android:layout_width="100dip"
   android:layout_height="100dip" 
   android:id="@+id/svgview"
   android:layout_gravity="center" 
   />
 </LinearLayout>
 

Запускаемся:




Отладка

Для отладки с/с++ кода следуйте инструкциям Кароса Соунто: [9]. Сначала там не все ясно. Несколько советов:
  • При настройке C++ конфигурации, приложение действительно должно быть app_process, не обращаем внимания на жалобы эклипса, что такого файла нет, Он будет создан позже.
  • Необходимо каждый раз запускать ndk-gdb при запуске приложения. Иногда команда должна быть ndk-gdb –adb=/tools/adb –force.
  • Не забываем ставить "debuggable" в манифесте.

Другой подход. Anti Grain Geometry.

Это еще одна библиотека для растеризации SVG :[10]. Единственнаая дополнительная к ней библиотека - libexpat. Она уже есть в проекте. В папке jni создаем еще одну:



Копируем соответствующие файлы из исходников agg в gpc/include/src. Там, в папке examples есть папкаsvg_viewer. Копируем из нее все, за исключением svg_test в папкуaggsvg jni . Единственно, эта библиотека поддерживает только простые SVG. Необходимо будет дописывать парсер. В папке aggsvg-android , создаем файл aggsvgandroid.cpp. Пример парсит SVG из файловой системы. Для строки, добавляем следующий метод в класс parser :

void parser::parse(const char *chars, int length){

     char msg[1024];

     XML_Parser p = XML_ParserCreate(NULL);
     if(p == 0)
     {
      throw exception("Couldn't allocate memory for parser");
     }

     XML_SetParamEntityParsing(p, XML_PARAM_ENTITY_PARSING_ALWAYS);
     XML_UseForeignDTD(p, true);

     XML_SetUserData(p, this);
     XML_SetElementHandler(p, start_element, end_element);
     XML_SetCharacterDataHandler(p, content);

     int done = 0;
     std::string str = std::string(chars);
     std::istringstream inputString(str);

     while(true){
      if(done)
       break;
            size_t len = inputString.readsome(m_buf, buf_size);
            done = len < buf_size;
            if(!XML_Parse(p, m_buf, len, done))
            {
                sprintf(msg,
                    "%s at line %d\n",
                    XML_ErrorString(XML_GetErrorCode(p)),
                    (int)XML_GetCurrentLineNumber(p));
                throw exception(msg);
            }
     }
        XML_ParserFree(p);

        char* ts = m_title;
        while(*ts)
        {
            if(*ts < ' ') *ts = ' ';
            ++ts;
        }
    }
 

В конце файла Android.mk , добавляем секцию для построения
еще одной библиотеки. Все довольно просто. ПРосто очищаем переменные после построения первой
библиотеки и настраиваем их для построения второй.
Класс для растеризации с использованием AGG:

class SvgRasterizer{
 agg::svg::path_renderer m_path;
    double m_min_x;
    double m_min_y;
    double m_max_x;
    double m_max_y;
    double m_x;
    double m_y;
    pix_format_e pixformat;
 agg::rendering_buffer m_rbuf_window;

public:
 SvgRasterizer(pix_format_e format, uint32_t width, 
   uint32_t height, void *pixels) : \
  m_path(), \
  m_min_x(0.0), \
  m_min_y(0.0), \
  m_max_x(0.0), \
  m_max_y(0.0), \
  pixformat(format)
 {
  m_rbuf_window.attach((unsigned char*)pixels, width, height, 4*width);
 }

 void parse_svg(const char* svg, int length){
  // Create parser
  agg::svg::parser p(m_path);
  // Parse SVG
  p.parse(svg, length);
  // Make all polygons CCW-oriented
  m_path.arrange_orientations();
  // Get bounds of the image defined in SVG
        m_path.bounding_rect(&m_min_x, &m_min_y, &m_max_x, &m_max_y);
 }

 void rasterize_svg()
 {
  typedef agg::pixfmt_rgba32 pixfmt;
  typedef agg::renderer_base<pixfmt> renderer_base;
  typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_solid;

        pixfmt pixf(m_rbuf_window);
        renderer_base rb(pixf);
        renderer_solid ren(rb);

        agg::rasterizer_scanline_aa<> ras;
        agg::scanline_p8 sl;
        agg::trans_affine mtx;

        double scl;
        // Calculate the scale the image to fit given bitmap
        if(m_max_y > m_max_x)
         scl = pixf.height()/m_max_y;
        else
         scl = pixf.width()/m_max_x;

        // Default gamma as is
        ras.gamma(agg::gamma_power(1.0));
        mtx *= agg::trans_affine_scaling(scl);

        m_path.expand(0.0);

        // Render image
        m_path.render(ras, sl, ren, mtx, rb.clip_box(), 1.0);

        ras.gamma(agg::gamma_none());
 }
};
 

В исходниках я добавил возможность протестировать обе библиотеки:




Заключение

Итак, имеется по меньшей мере два способа использования SVG в Андроиде. Основное преимущество libsvg-android в том, что она готова к использования, но более чем в три раза медленнее AGG, в которой сы вынуждены дорабатывать парсер. Помимо того, в AGG имеется масса возможностей для работы с изображениями. Я показал лишь как можно использовать ImageView в лэйауте, для программного использования необходимо будет еще перегрузить методы такие как setImageResource , к примеру.

На данный момент все, спасибо за внимание!


Ресурсы

  1. http://www.eclipse.org
  2. http://developer.motorola.com/docstools/motodevstudio
  3. http://developer.android.com/guide/developing/tools/adt.html
  4. http://www.eclipse.org/sequoyah/
  5. http://www.cygwin.com/
  6. http://developer.android.com/sdk/index.html
  7. http://www.crystax.net/en/android/ndk/6
  8. https://launchpad.net/libsvg-android
  9. http://www.eclipse.org/sequoyah/documentation/native_debug.php
  10. http://www.antigrain.com/



P.S. На Code Project вы также можете найти статью об использовании SVG как Drawable:
Drawable with SVG Support

Известные проблемы:

ibsvg-android не поддерживает представление цвета в виде rgb(255, 255, 255) (Inkscape). (По крайней мере на момент написания статьи.)
Проверьте файл и замените на fill: #ffffff

Возможно что-то еще. Проверяйте структуру SVG перед использованием.
Неподдреживаемые фичи будут выявляться только при отладке.


вторник, 25 октября 2011 г.

Анимированная заставка.


Анимированный сплеш-скрин для Android - приложения

English translation: Animated Splash Screen

Эта статья, написанная мной, изначально была опубликована на сайте Codeproject. Оригинал: Codeproject.

Исходники

Введение


     Каждому хочется, чтобы интерфейс его приложения выглядел привлекательно для пользователя. Множество программ, по крайней мере десктопных приложений (по большей части игры), используют заставки.  Это красиво и, более того, пока заставка находится на экране, вы можете инициализировать свое приложение. Существует много руководств, рассказывающих о том, как начать программировать под Андроид, нет смысла их повторять. Вы можете найти их в интернете в огромных количествах. Разберемся с тем, что касается конкретно программирования.


Начало.

Создайте новый проект в эклипсе со следующими настройками:

Project name : AdvancedSplashDemo
Build target:  Android 2.1
Application name: Advanced Splash Demo
Package name: Advanced Splash Demo
Create Activity: MainActivity – само приложение
 

Что мы имеем изначально - после того, как заставка отработала, она нам больше не нужна. Первая возникающая мысль - использовать активити, которая запускает основную и сама прекращает работу. Создадим лэйаут для заставки - просто LinearLayout с картинкой посередине. Создадим Android XML файл "splash.xml" в папке appfolder/res/layout.  Устанавливаем параметры в wrap_content, для того, чтобы это действительно выглядело как заставка. Выравниваем посередине:

 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/TheSplashLayout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center" >

    <ImageView
        android:id="@+id/SplashImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" >
    </ImageView>
</LinearLayout>
 

Значение аттрибута "layout_gravity" выставлено в "center" для того, чтобы картинка была посередине экрана. Добавляем картинку в папку appfolder/res/drawable и жмем  F5 на проекте.
В исходниках есть файл lnxins.png, и вы можете установить его как источник для ImageView.

Теперь разберемся с манифестом. Сейчас в нем есть только “.MainActivity”, указанная как стартовая. Заменим ей категорию на категорию по умолчанию и добавим "SplashActivity". Она теперь будет являться стартовой. Для этого откроем в манифесте закладку "Application"  и для "MainActivity" изменим категорию в "default". Рядом с окном "Application Nodes" жмем кнопку "Add...", выбираем из списка "Activity" и жмем "Ok". Рядом с появившимся пунктом выбираем "Name*" гиперлинк и вводим имя - SplashScreen.  В исходниках автоматически создастся новый класс. Теперь рядом жмем кнопку "Add...", чтобы добавить intent filter, action - MAIN и category - Launcher. В результате SplashScreen activity будет запущена первой.

В итоге, манифест должен выглядеть следующим образом:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.yourname.main"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>    
        <activity android:name="SplashScreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"></action>
                <category android:name="android.intent.category.LAUNCHER"></category>
            </intent-filter>
        </activity>
    </application>
</manifest> 
 

Немного программирования

Откроем SplashScreen.java файл. На данный момент в нем имеется только один перегруженный метод - onCreate. Перегрузим также onTouchEvent для того, чтобы дать пользователю возможность закрыть заставку в любой момент. И не забываем о синхронизации, чтобы приложение не крэшилось в неподходящий момент. В результате имеем:

public class SplashScreen extends Activity {
    
    /**
     * Поток для обработки сообщений заставки
     */
    private Thread mSplashThread;    

    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Лэйаут заставки
        setContentView(R.layout.splash);
        
        final SplashScreen sPlashScreen = this;   
        
        // Поток для ожидания событий заставки
        mSplashThread =  new Thread(){
            @Override
            public void run(){
                try {
                    synchronized(this){
                        // Ждем некоторое время, или выход по прикосновению
                    wait(5000);
                    }
                }
                catch(InterruptedException ex){                    
                }

                finish();
                
                // Запускаем основную форму
                Intent intent = new Intent();
                intent.setClass(sPlashScreen, MainActivity.class);
                startActivity(intent);
                                  
            }
        };
        
        mSplashThread.start();        
    }
        
    @Override
    public boolean onTouchEvent(MotionEvent evt)
    {
        if(evt.getAction() == MotionEvent.ACTION_DOWN)
        {
            synchronized(mSplashThread){
                mSplashThread.notifyAll();
            }
        }
        return true;
    }    
}


Немного усовершенствований

Сначала сделаем фон заставки прозрачным. В  папке appfolder/res/values, добавим новый Android XML файл styles.xml и определим в нем тему для прозрачности:

<resources>
    <style name="Theme.Transparent" parent="android:Theme">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:backgroundDimEnabled">false</item>
    </style>    
</resources>



Родительская тема - android:Theme, т.о. мы можем применить ее у к нашей активити. И, как вы видите, имена атрибутов довольно прозрачны, по ним можно понять их предназначение.
Далее, применим тему. В манифесте для SplashScreen activity установим "theme" атрибут:

<activity 
    android:name="SplashScreen"
    android:theme="@style/Theme.Transparent"            
>
    <intent-filter>
        <action android:name="android.intent.action.MAIN"></action>
        <category android:name="android.intent.category.LAUNCHER"></category>
    </intent-filter>
</activity> 


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

<activity android:name=".MainActivity"
          android:label="@string/app_name"
          android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
          >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>


Попробуем запустить то, что у нас получилось. Выглядит лучше. Теперь добавим плавное появление и исчезание. В папке appfolder/res еще одну - "anim" и добавим два Android XML файла – appear.xml и disappear.xml. Они будут определять альфа - анимацию.


Appear.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:interpolator="@android:anim/accelerate_interpolator"
        android:fromAlpha="0.0" android:toAlpha="1.0"
        android:duration="800"
    />
</set>


Disappear.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:interpolator="@android:anim/decelerate_interpolator"
        android:fromAlpha="1.0" android:toAlpha="0.0"
        android:duration="800"
    />
</set> 


Они просто изменяют альфа-канал объекта начиная с "fromAlpa" и заканчивая "toAlpha" в течение указанного промежутка времени. Добавим новый стиль в styles.xml:

<style name="Animations" parent="@android:Animation" />
    <style name="Animations.SplashScreen">
        <item name="android:windowEnterAnimation">@anim/appear</item>
        <item name="android:windowExitAnimation">@anim/disappear</item> 
    </style>
</style>

В итоге, "appear" анимация будет выполняться  при открытии окна, "disappear" - при закрытии. Добавим этот стиль к теме прозрачности:

<style name="Theme.Transparent" parent="android:Theme">
        ………
  <item name="android:windowAnimationStyle">
      @style/Animations.SplashScreen
  </item>
</style>


Зер гут, выглядит уже неплохо. И…

Не стреляйте в программиста, он рисует как может...

Теперь добавим еще немного анимации. Я художник от слова худо, поэтому использовал Script-Fu скрипт в Гимпе, чтобы сгенерировать хоть какую-то анимацию. Для начала убираем android:src атрибут в splash.xml. Теперь, в папке "drawable" , создадим flag.xml:

<?xml version="1.0" encoding="utf-8"?>
<animation-list     
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/flaganim"
    android:oneshot="false"
    >
    <item android:drawable="@drawable/f03" android:duration="100" />
    <item android:drawable="@drawable/f04" android:duration="100" />
    <item android:drawable="@drawable/f05" android:duration="100" />
    <item android:drawable="@drawable/f06" android:duration="100" />
    <item android:drawable="@drawable/f07" android:duration="100" />
    <item android:drawable="@drawable/f08" android:duration="100" />
    <item android:drawable="@drawable/f09" android:duration="100" />
    <item android:drawable="@drawable/f10" android:duration="100" />    
</animation-list>

Здесь просто набор фреймов, и "oneshot" атрибут говорит о том, что они будут повторяться в цикле. Чтобы запустить данную анимацию изменяем класс заставки - в "onCreate" методе меняем:

final ImageView splashImageView = 
        (ImageView) findViewById(R.id.SplashImageView);
 splashImageView.setBackgroundResource(R.drawable.flag);
 final AnimationDrawable frameAnimation = 
              (AnimationDrawable)splashImageView.getBackground(); 


Мы установили анимацию для заставки, но есть небольшая проблема - мы не можем запустить ее из метода "onCreate". Анимация должна быть запущена из GUI потока.  Для этого используем  “post” метод ImageView класса. Это добавить наш "Runnable" объект к очереди сообщений и , когда GUI поток будет доступен, запустит его:

splashImageView.post(new Runnable(){
            @Override
            public void run() {
                frameAnimation.start();                
            }            
        });


В итоге:


Это все. И программируем под Андроид в свое удовольствие :)

Спасибо за внимание!