вторник, 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();                
            }            
        });


В итоге:


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

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

Комментариев нет:

Отправить комментарий