Поиск по этому блогу

воскресенье, 5 февраля 2012 г.

Создаем живые обои для Android

Не буду разглагольствовать сильно много, а сразу буду говорить по делу.

Живые обои – это анимированные интерактивные обои для главного экрана Android, по сути они схожи с другими приложениями для Android и могут использовать тот же самый функционал API.
Чтобы создать собственные живые обои необходимо создать XML файл, содержащий описание приложения, кроме того в нем могут быть указано изображение предварительного просмотра и ссылка на активити настроек.




Любые живые обои создаются на основе сервиса WallpaperService который включает в себя ряд функций/методов которые мало чем отличаются от класса SurfaceView. Вот какие функции имеет класс WallpaperService:
  • onCreate()
  • onSurfaceCreated()
  • onVisibilityChanged()
  • onOffsetsChanged()
  • onTouchEvent()
  • onCommand()
Как и в разработке игр — в разработке обоев используется старый добрый Canvas на основе которого рисуются все возможные объекты. 

Для того что бы создать живые обои нужно обязательно в AndroidManifest'e указать наличие прав на «android.permission.BIND_WALLPAPER» и что наше приложение использует функционал android.software.live_wallpaper. Если этого не сделать, приложение сможет быть установлено на устройствах, которые не поддерживают живые обои, что не желательно. 

Создаем живые обои


Создаем проект — Eclipse — File — New — Android Project — NameProject. И убираем галочку «Create Activity», так как у нас нет активити — оно нам не нужно. Добавьте папку /res/xml и в него файл mywallpaper.xml. В этом файле будет содержаться описание ваших обоев и графика для предварительного просмотра. Этот файл ресурсов будет указан в файле AndroidManifest.xml. Если вы укажете атрибут android:thumbnail, указывающий на ресурс типа drawable, это даст возможность показать уменьшенное изображение ваших обоев.

Нам нужно внести изменения в манифест, открываем AndroidManifest, выделяем «Ctrl + A» и удаляем. Вставляем код который ниже:

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest 
        xmlns:android="http://schemas.android.com/apk/res/android"
        package="live.wallpaper"
        android:versionCode="1"
        android:versionName="1.0">
        <application 
                android:icon="@drawable/ic_launcher" 
                android:label="@string/app_name">
                <service 
                        android:name="LiveWallpaperService"
                        android:enabled="true"
                        android:icon="@drawable/ic_launcher"
                        android:label="@string/app_name"
                        android:permission="android.permission.BIND_WALLPAPER">
                        <intent-filter android:priority="1" >
                                <action android:name="android.service.wallpaper.WallpaperService" />
                        </intent-filter>
                        <meta-data 
                                android:name="android.service.wallpaper" 
                                android:resource="@xml/mywallpaper" />
                </service>
                <activity 
                        android:label="@string/app_name" 
                        android:name=".LiveWallpaperSettings" 
                        android:theme="@android:style/Theme.Light.WallpaperSettings"
                        android:exported="true" 
                        android:configChanges="orientation"/>
        </application>
        <uses-sdk android:minSdkVersion="8" />
</manifest> 


Дальше нам нужно в созданный файл mywallpaper.xml внести данные о пред просмотре обоине которую пользователь будет устанавливать.
Вот как выглядит этот файл:

mywallpaper.xml
<?xml version="1.0" encoding="UTF-8"?>
<wallpaper 
        xmlns:android="http://schemas.android.com/apk/res/android"  
        android:thumbnail="@drawable/ic_launcher" 
        android:description="@string/description"
        android:settingsActivity="live.wallpaper.LiveWallpaperSettings"/>


Дальше берем вот эти изображения:



и закидываем в папку res/drawable, они нам понадобятся для красоты обоев :)

Создаем класс Bubble.java который будет задавать направление наших бульб.

package live.wallpaper;
import java.util.Random;
import android.graphics.Bitmap;
import android.graphics.Canvas;
public class Bubble
{
        /**Позиция по Х и У*/
        public int x;
        public int y;
        
        /**Выоста и ширина*/
        public int widht;
        public int height;
        
        /**Скорость*/
        public int speed;
        
        /**Угол полета*/
        public double angle;
        
        Bitmap bmp;
        LiveWallpaperPainting pm;
        
        /**Конструктор*/
        public Bubble(LiveWallpaperPainting pm, Bitmap bmp) {
                this.pm = pm;
                this.bmp = bmp;
                
                /**По "х" у нас будем появляться рандомно*/
                Random rnd = new Random(System.currentTimeMillis());
                this.y = 1000;
                this.x = rnd.nextInt(800);
                
                /**Скорость рандомная*/
                this.speed = rnd.nextInt(15 - 5) + 15;
                
                /**Задаем размер бульбашек*/
                this.widht = 75;
                this.height = 75;
                
                angle = getRandomAngle();
        }
        
        /**Движение объектов*/
        public void update() {
                y -= Math.abs(speed * Math.cos(angle));
                x -= Math.abs(speed * Math.sin(angle));
        }
        
        /**Задаем рандомный угол полета*/
        private int getRandomAngle() {
        Random rnd = new Random(System.currentTimeMillis());
        return rnd.nextInt(1) * 90 + 90 / 2 + rnd.nextInt(15) + 5;
    }
        
        /**Рисуем*/
        public void onDraw(Canvas c) {
                update();
                c.drawBitmap(bmp, x, y, null);
        }
        
        /**Проверка на столкновения*/
    public boolean isCollition(float x2, float y2) {
          return x2 > x && x2 < x + widht && y2 > y && y2 < y + height;
    }
}


Думаю из комментариев понятно что делаю функции и что какая переменная обозначает, если не понятно пишите в комментарии я объясню что к чему.
Далее создаем класс LiveWallpaperPainting.java который будет рисовать все что токо захотим. Он будет иметь следующий код:

package live.wallpaper;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
public class LiveWallpaperPainting extends Thread implements Runnable{

        private SurfaceHolder surfaceHolder;
        private Context context;
        
        /** Состояние потоков */
        private boolean wait;
        private boolean run;
        
        /** Выосота и ширина сцены */
        private int width;
        private int height;
        
        /**Скоре, достижение :)*/
        private int score = 0;

        /**Список бульбашек который будет бесконечным*/
        public List<Bubble> bubble = new ArrayList<Bubble>();
        
        /**Это если захотишь сделать что бы выводило спрайт после взрыва*/
        //private List<Boms> temps = new ArrayList<Boms>();

        //private Bitmap blood;
        
        /**Позиция нажатия на экран*/
        private float posX;
        private float posY;
        
        /**Объект рисовалки наших достижений*/
        private Paint mScorePaint;
        
        /**Фоновый рисунок*/
        private Bitmap bg;
        
        /**Бульбаки*/
        private Bitmap bubbles;
  
        /**Конструктор*/
        public LiveWallpaperPainting(SurfaceHolder surfaceHolder, Context context, int radius) {
                this.surfaceHolder = surfaceHolder;
                this.context = context;

                /**Запускаем поток*/
                this.wait = true;
                
                /*Рисуем всякое разное*/
                bubbles = BitmapFactory.decodeResource(context.getResources(), R.drawable.bubble);
                //blood = BitmapFactory.decodeResource(context.getResources(), R.drawable.blood1);
                bg = BitmapFactory.decodeResource(context.getResources(), R.drawable.bg);
                
             // стили для вывода счета
                mScorePaint = new Paint();
                mScorePaint.setTextSize(20);
                mScorePaint.setStrokeWidth(1);
                mScorePaint.setStyle(Style.FILL);
                mScorePaint.setColor(Color.WHITE);
        }

        /**
         * Ставим на паузу анимацию 
         */
        public void pausePainting() {
                this.wait = true;
                synchronized(this) {
                        this.notify();
                }
        }

        /**
         * Запускаем поток когда сняли с паузы
         */
        public void resumePainting() {
                this.wait = false;
                synchronized(this) {
                        this.notify();
                }
        }

        /**
         * Останавливаем поток
         */
        public void stopPainting() {
                this.run = false;
                synchronized(this) {
                        this.notify();
                }
        }

        
        /**Рисуем в потоке все наши рисунки*/
        public void run() {
                this.run = true;
                Canvas c = null;
                while (run) {
                        try {
                                c = this.surfaceHolder.lockCanvas(null);
                                synchronized (this.surfaceHolder) {
                                        Thread.sleep(50);
                                        bubble.add(new Bubble(this, bubbles));
                                        doDraw©;
                                }
                        } catch (InterruptedException e) {
                                                        e.printStackTrace();
                                                } finally {
                                if (c != null) {
                                        this.surfaceHolder.unlockCanvasAndPost©;
                                }
                        }
                        // pause if no need to animate
                        synchronized (this) {
                                if (wait) {
                                        try {
                                                wait();
                                        } catch (Exception e) {}
                                }
                        }
                }
        }

        /**
         * Растягиваем картинку под размер экрана
         */
        public void setSurfaceSize(int width, int height) {
                this.width = width;
                this.height = height;
                synchronized(this) {
                        this.notify();
                        bg = Bitmap.createScaledBitmap(bg, width, height, true);
                }
        }
        
        /**
         * Обрабатываем нажатия на экран
         */
        public boolean doTouchEvent(MotionEvent event) {
                posX = event.getX();
            posY = event.getY();
            synchronized (surfaceHolder) {
                   for (int i = bubble.size() - 1; i >= 0; i--) {
                          Bubble sprite = bubble.get(i);
                          if (sprite.isCollition(posX, posY)) {
                                    bubble.remove(sprite);
                                    score++;
                                //temps.add(new Boms(temps, this, blood, posX, posY));
                                break;
                          }
                   }
            }
            return true;
      }
        
        /**
         * Рисуем на сцене в потоке
         */
        private void doDraw(Canvas canvas) {
                        canvas.drawColor(Color.WHITE);
                        canvas.drawBitmap(bg, 0,0, null);
                        /*for (int i = temps.size() - 1; i >= 0; i--) {
                    temps.get(i).onDraw(canvas);
                        }*/
                        
                        for(Bubble bub: bubble) {
                                if(bub.y >= 0 || bub.x <= 0)
                                        bub.onDraw(canvas);
                                else
                                        bubble.remove(this);
                        }
                        
                    canvas.drawText("Score: " + score, 50, 70, mScorePaint);
        }        
        }


И так если Вы заметили то код мало отличается от кода по созданию игр, единственное отличие это то что мы рисуем сразу в потоке не раскошеливаясь на создание сцены с SurfaceView. 

Я забыл упомянуть что обоинау нас будет выглядеть вот так:


Теперь я думаю понятно что будет представлять из себя этот класс. В нем мы рисуем фон, рисуем бульбы и рисуем количество хлопнутых Вами бульбашек. 

Дальше создаем класс LiveWallpaperService, это наследник класса WallpaperService про которого я говорил в начале статьи. Он имеет следующий вид:

LiveWallpaperService.java
package live.wallpaper;
import java.util.ArrayList;
import java.util.List;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.service.wallpaper.WallpaperService;
import android.service.wallpaper.WallpaperService.Engine;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
public class LiveWallpaperService extends WallpaperService {

    public static final String PREFERENCES = "net.androgames.blog.sample.livewallpaper";
    public static final String PREFERENCE_RADIUS = "preference_radius";
    
    @Override
    public Engine onCreateEngine() {
            return new SampleEngine();
    }

    @Override
    public void onCreate() {
            super.onCreate();
    }

    @Override
    public void onDestroy() {
            super.onDestroy();
    }

    public class SampleEngine extends Engine implements SharedPreferences.OnSharedPreferenceChangeListener {

            private LiveWallpaperPainting painting;
            private SharedPreferences prefs;
            
            SampleEngine() {
                    SurfaceHolder holder = getSurfaceHolder();
                    prefs = LiveWallpaperService.this.getSharedPreferences(PREFERENCES, 0);
                    prefs.registerOnSharedPreferenceChangeListener(this);
                    painting = new LiveWallpaperPainting(holder, getApplicationContext(), 
                                    Integer.parseInt(prefs.getString(PREFERENCE_RADIUS, "10")));
            }

            public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
            }

            @Override
            public void onCreate(SurfaceHolder surfaceHolder) {
                    super.onCreate(surfaceHolder);
                    setTouchEventsEnabled(true);
            }

            @Override
            public void onDestroy() {
                    super.onDestroy();
                    // remove listeners and callbacks here
                    painting.stopPainting();
            }

            @Override
            public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                    super.onSurfaceChanged(holder, format, width, height);
                    painting.setSurfaceSize(width, height);
            }

            @Override
            public void onSurfaceCreated(SurfaceHolder holder) {
                    super.onSurfaceCreated(holder);
                    painting.start();
                    
            }


            @Override
            public void onVisibilityChanged(boolean visible) {
                    if (visible) {
                            painting.resumePainting();
                    } else {
                            // remove listeners and callbacks here
                            painting.pausePainting();
                    }
            }
            
            @Override
            public void onSurfaceDestroyed(SurfaceHolder holder) {
                    super.onSurfaceDestroyed(holder);
                    boolean retry = true;
                    painting.stopPainting();
                    while (retry) {
                            try {
                                    painting.join();
                                    retry = false;
                            } catch (InterruptedException e) {}
                    }
            }


            @Override
            public void onTouchEvent(MotionEvent event) {
                    super.onTouchEvent(event);
                    painting.doTouchEvent(event);
            }
            
    }
}


Методы OnCreate, OnDestroy, onVisibilityChanged, onSurfaceChanged, onSurfaceCreated и onSurfaceDestroyed вызываются, когда запускаем обои. Только с помощью этих методов, живые обои могут быть анимироваными.

На видео ниже показано чего мы добились, больбашки летят и при клике на них — убиваются, скоре записываются и если поставить их на рабочий стол то будет тот же эффект, если немного допилить со стороны дизайнера то вообще будет сказка. 

78 комментариев:

  1. Добавьте еще уроков по созданию Живых Обоев!? Поговорим в ЛС пожалуйста . Skype: stepup2221

    ОтветитьУдалить
  2. Какие изображения надо скинуть в res??

    ОтветитьУдалить
    Ответы
    1. Добавил, хостинг с предыдущими картинками умер, пришлось перезаливать.

      Удалить
  3. Вечер добрый, вопросец такого характера: а как описать видео файл (т.е. в живых обоях что бы проигрывался определенный видео фрагмент)что то не могу разобраться))

    ОтветитьУдалить
    Ответы
    1. Видео? Видео никак не вставить, только анимацию.

      Удалить
    2. Я сначала тоже так думал, пока не наткнулся на одну live wallpaper внутри которой лежит файл mp4, который воспроизводится,вот и хочется разобраться. Могу куда нибудь скинуть или ссылку или сам apk.

      Удалить
    3. Это уже Open GL 3.0. Там возможно встраивание видео.

      Удалить
  4. спасибо за уроки!
    только у меня рабочий стол состоит из нескольких экранов - центральный экран, три экрана влево и три вправо. Как можно развернуть обоину на всю их ширину, чтобы она прокручивалась вместе с ними?

    ОтветитьУдалить
  5. Вот тут лежит прога которая устанавливает в качестве обоев видеофайл
    http://www.osnaz.ru/load/android_interfejs/raznoe/video_live_wallpaper/70-1-0-3707
    Думаю если ее код упаковать сразу с видеофайлом получатся живые обои с видео.

    ОтветитьУдалить
  6. rnd.nextInt(1) постоянно выдает ноль. Зачем это выражение там?

    ОтветитьУдалить
    Ответы
    1. Выдает или ноль или один, так уже и не скажу. Не помню

      Удалить






  7. В этом блоке манифеста на активити и uses выдает ошибку... не могу понять в чем дело

    1) Exported activity does not require permission
    2)Multiple annotations found at this line:
    - tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be
    enabled) with android:targetSdkVersion="?"
    - tag appears after tag

    ОтветитьУдалить
    Ответы
    1. 1) Пропишите путь к активити так packadgename.MAINACTIVITY
      2) У вас не подходит версия сдк, повысьте её до версии 10 или выше.

      Удалить
  8. Спасибо!
    Опираясь на данный код, сделал чтото вроде падающих листьев сверху, а на фоне осенее дерево.
    есть 2 вопроса:
    1.Как сделать так, чтобы задний фон(картинка, которая шире, чем размер экрана)двигался следуя движению рабочего стола( влево вправо)? Нето выставив картинку шире, она стоит на месте.
    2.Как регулировать количество пузырьков, сделать их меньшее количество? (в моем случае листьев)Потому что через минут 20-30 анимация начинает ужасно притормаживать(как будто создалось такое количество что система не выдерживает)
    может их можно как то "заканчивать"?

    ОтветитьУдалить
    Ответы
    1. Подскажи пожалукйста когда урок делал не было проблем с манифестом??

      Удалить
    2. Кажется были, но в твоем случае, как я понял надо поменять версию SDK вручную,если не получается чтото поменять, то я методом тыка удалял строки х)

      Удалить
    3. Проблем было много, возможно вы забыли прописать все сервисы или активити. Возможно пакедж нейм неверный. Вариантов полно, используйте мой для примера, просто вставьте свои названия сервисов и активностей и пакедж нейм. Тогда сто процентов что будет все работать. Если не работает значит где то в коде в одном из классов неявная ошибка.

      Удалить
  9. Кто подскажет где почитать что то хорошее, что бы понять как задавать бвидение тех же бульбашек??? хочу что бы например в несколько полос идеально вверх поднимались, а как это реализовать не понимаю, вечно ошибочки в коде(((

    ОтветитьУдалить
    Ответы
    1. Что бы что задавать? Если вы хотите что бы бульбашки ровно летели, без x координаты, то есть все время вверх, удалите x в методе onDraw(), поставьте например 0 вот так: c.drawBitmap(bmp, 0, y, null);

      Удалить
  10. А не подскажешь где почитать про задачу такого движения на JAVA много что прочел но так и не погу дойти как задать например скорость движения взависимости от врмени на телефоне, или что бы они не сталкивались и все подобное...

    За блог спасибо. Во многом помог)))

    ОтветитьУдалить
    Ответы
    1. В зависимости от времени на телефоне? Ну для начала нужно понять зачем это, а дальше просто получаем текущую дату, переводим её в нужный нам вид и записываем в переменную, а переменную передаем в функцию рисования. Все это можно найти в интернете, в основном на stackoverflow.com, но я лучше бы гуглил. У меня в уроках по написанию игры так же можете посмотреть как я что делал, может это поможет.

      Удалить
  11. А как сделать так, чтобы пузырки не бесконечно рисовались, а раз в 3 или 5 секунд?

    ОтветитьУдалить
    Ответы
    1. В классе LiveWallpaperPainting.java в методе run() заменить Thread.sleep(50); на Thread.sleep(5000)l тогда будет появляться пузырьки каждые 5 сек.

      Удалить
    2. я так пробовал, но тогда скорость пузырьков становится тоже медленной(

      Удалить
    3. Скорость не может уменьшиться так как вы её прописываете в классе Bubble.java.

      Удалить
    4. Дело в том, что как я понял сам поток останавливается на 5 секунд, и происходит не плавное перемещение пузырька а просто рывок с определенной скоростью через 5 секунд. Все плавное перемещение пропадает. Я просто слабо понимаю в потоках, но думаю что не Thread.sleep() нужно менять, а в самом методе рисования объекта нужно ставить какую то паузу(наверно написал полную чушь) )

      Удалить
    5. Честно скажу что я этот урок писал давно и уже не помню всех тонкостей разработки. Но я уверен что все дело в треде.слееп, восстанавливать проект я не буду так как это займет время, а у меня его щас мало. Попробуйте поизменять параметры в отрисовке объектов (: может попадете куда нибудь куда нужно.

      Удалить
  12. Если кому то будет надо, чтобы сделать меньше пузырей, решил проблему таким способом:
    if(i%70==0){
    bubble.add(new Bubble(this, bubbles));
    i=0;
    }

    ОтветитьУдалить
    Ответы
    1. А куда конкретнее вписать надо?
      вместо Thread.sleep? или рядом дописать? Чтото не понимаю... и что значит i?

      Удалить
  13. очень хочу сделать обои такого рода http://www.youtube.com/watch?v=Tw4UwG4HsVo
    сама рисую, если подскажете как, то обойку вам сделаю бесплатно в благодарность)) скайп anastasia111128
    заранее спасибо

    ОтветитьУдалить
    Ответы
    1. Сделано примерно так же как и у меня, нужно только красивые арты и дополнительно падающее снежинки или что там. А клик переопределить на дракончика.

      Удалить
  14. Первый раз имею дело с Android, для начала решила на основе вашего кода сделать проект в intellij IDEA. Вроде бы всё в порядке, кроме mywallpaper.xml, где во вкладке Preview оно мне пишет: "Missing class wallpaper". При этом, билдится без ошибок, происходит запуск приложения и... ничего не происходит. Можете ли вы мне что-нибудь посоветовать по этому поводу? Спасибо!

    ОтветитьУдалить
    Ответы
    1. Проверьте точно android:settingsActivity="live.wallpaper.LiveWallpaperSettings" написано правельно имя класса и пакелдж нейма?

      Удалить
    2. Спасибо большое за пример обоев! Всё замечательно запустилось и работает собранное на intellij. Правда, от чего-то бывают проблемы при первом запуске после инсталляции. Иногда не сразу подгружается фон, а иногда бульбашки не летят долгое время время. Пока не поняла, почему.

      P.S. В коде надо добавить некоторые изменения во избежание кой-каких ошибок. С изменениями функция run() должна выглядеть так:
      public void run() {
      this.run = true;
      Canvas c = null;
      while (run) {
      try {
      c = this.surfaceHolder.lockCanvas(null);
      synchronized (this.surfaceHolder) {
      Thread.sleep(50);
      bubble.add(new Bubble(this, bubbles, this.height, this.width));

      if(c != null)
      doDraw(c);
      }
      } catch (InterruptedException e) {
      e.printStackTrace();
      } try {
      this.surfaceHolder.unlockCanvasAndPost(c);

      } catch (IllegalArgumentException exception) {
      exception.printStackTrace();
      }
      // pause if no need to animate
      synchronized (this) {
      if (wait) {
      try {
      wait();
      } catch (Exception e) {}
      }
      }
      }
      }

      может быть, это будет кому-нибудь полезно...

      Удалить
  15. Очень сильно тормозит через пару минут,они походу не удаляються ,как сделать чтобы бульбашки когда уходят за экран удалялись?Если не тяжело напишите пожалуйста где и что прописать.Заранее спасибо.)

    ОтветитьУдалить
    Ответы
    1. В LiveWallpaperPainting.java найдите строку
      for(Bubble bub: bubble) {
      if(bub.y >= 0 || bub.x <= 0)
      bub.onDraw(canvas);
      else
      bubble.remove(this);
      }

      и замените её на

      for(Bubble bub: bubble) {
      if(bub.x <= 1000)
      bubble.remove(this);
      else
      bub.onDraw(canvas);
      }

      Удалить
  16. Добрый вечер!Подскажите как сделать чтобы бульбы падали с верху вниз? .Как сделать так, чтобы задний фон(картинка, которая шире, чем размер экрана)двигался следуя движению рабочего стола( влево вправо)? Нето выставив картинку шире, она стоит на месте.

    ОтветитьУдалить
    Ответы
    1. В классе Bubble java в методе update пишешь вместо

      public void update() {
      y -= Math.abs(speed * Math.cos(angle));
      x -= Math.abs(speed * Math.sin(angle));
      }
      это

      public void update() {
      y += Math.abs(speed * Math.cos(angle));
      x -= Math.abs(speed * Math.sin(angle));
      }

      Для того что бы задний фон скролилась нужно это делать на опенГЛ, канвас на такое не канает

      Удалить
  17. После того как вставил этот код for(Bubble bub: bubble) {
    if(bub.x <= 1000)
    bubble.remove(this);
    else
    bub.onDraw(canvas);
    }. Вообже бульбашки изчезли(((Просто заставка.Что не так может быть.Спасибо

    ОтветитьУдалить
    Ответы
    1. Поменяйте цифры и больше меньше по комбинируйте, я писал это наугад, не угадал, готового проекта под рукой нет.

      Удалить
  18. Добрый день!Подскажите пожалуйста,в исходнике лежит не сколько фонов как сделать чтобы они менялись в настройках обоев или сами через определённое время.Спасибо.

    ОтветитьУдалить
    Ответы
    1. Для этого нужно создать файл настроек и в нем производить все манипуляции. Ну или второй вариант что бы менялось через определенное время устанавливаете таймер (примеров в интернете полно по использованию таймера) создаете массив обоев примерно так:

      String[] { r.drawable.img1, r.drawable.img2, r.drawable.img3 };

      Дальше создаете Random и в таймере пишете вывод картинки, все это делается в методе onDraw().

      Удалить
  19. Ещё забыл спросить можно ли вместо bubble.PNG использовать к примеру gif анимацию?

    ОтветитьУдалить
    Ответы
    1. Анимация проигрываться не будет, нужно или создавать спрайт с анимацией и потом проигрывать её или оставить как есть.

      Удалить
  20. Поменяйте цифры и больше меньше по комбинируйте, я писал это наугад, не угадал, готового проекта под рукой нет.

    if(bub.x <= 1000)
    Извеняюсь вот здесь менять?

    ОтветитьУдалить
  21. Дело в том что я ещё и изменил размер бульбашек ,саму картинку сделал чуть меняше!Не могут ли они из за этого не появляться?

    ОтветитьУдалить
    Ответы
    1. нет, причина не в этом, и да ответ на вопрос выше.

      Удалить
  22. Извеняюсь ,я наверно уже надоел,но если можно ещё один последний вопрос
    if(bub.x <= 1000)
    ставить например 900 или 1200 и т.п. на угад ,а на что ореинтироваться может смотреть где то, или просто на угад играться?) Спасибо.

    ОтветитьУдалить
  23. Извиняюсь-а почему Ваше приложение не видно у меня на смартфоне-скачал APK файл-а иконки нет !, хотя сам файл есть.

    ОтветитьУдалить
    Ответы
    1. Без понятия тестировал на своем Samsung galaxy ace и все работало.

      Удалить
  24. Спасибо.Получилось. Но мне кажется пузыри нужно сразу устанавливать-не надо размеры их менять. А то у меня через раз исключение выбивается.
    А как сделать чтобы пузыри сверху шли ?
    Вот тоже неплохой примерчик.
    https://github.com/kirillcool/android-live-wallpaper

    ОтветитьУдалить
    Ответы
    1. Для того что бы летели свеху вниз - в классе Bubble java в методе update пишешь вместо

      public void update() {
      y -= Math.abs(speed * Math.cos(angle));
      x -= Math.abs(speed * Math.sin(angle));
      }
      это

      public void update() {
      y += Math.abs(speed * Math.cos(angle));
      x -= Math.abs(speed * Math.sin(angle));
      }

      Для того что бы задний фон скролилась нужно это делать на опенГЛ, канвас на такое не канает

      Удалить
  25. Open GL и 3D особенно это вещь ! Спасибо за посказку огромное-а то я бы часа два причину искал. Щас елочку со снежком падющим сделаю.

    ОтветитьУдалить
  26. Что делать если не определяются ресурсы? тоесть файлы bubble.png и bg.png. к примеру в строке bubbles = BitmapFactory.decodeResource(context.getResources(), R.drawable.bubble); подчеркивается bubble, и пишет "bubble can not be resolved or is not a field"?

    ОтветитьУдалить
    Ответы
    1. Добавить эти файлы в папку res/drawable

      Удалить
    2. drawable-ldpi, drawable-hdpi, drawable-mdpi, drawable-xhdpi, drawable-xxhpi добавил во все эти папки, создал просто папку drawable и кинул туда, закомментил строчки и разкомментил все равно пишет эту ошибку.

      Удалить
  27. bubbles = BitmapFactory.decodeResource(context.getResources(), R.drawable.bubble);
    bg = BitmapFactory.decodeResource(context.getResources(), R.drawable.bg);

    у меня на R.drawable.bubble и R.drawable.bg ругается

    ОтветитьУдалить
  28. Добрый день! Возможно, вопрос не по теме, но все же задам, может кто подскажет! Как определить програмно есть ли в телефоне (лаунчере) поддержка прокручиваемых обоев???

    ОтветитьУдалить
  29. можете сделать урок по созданию живых обоев только чтоб менялась картинка на другую картинку что то вроде гифки!?

    ОтветитьУдалить
    Ответы
    1. Анимация это последовательность картинок, вам нужно просто создать спрайт и проигрывать его. Это можно просмотреть в уроках по созданию игры.

      Удалить
  30. Добрый день!
    А можно сделать рандомную смену цвета самих бульбашек?

    ОтветитьУдалить
    Ответы
    1. Можно создать массив с картинками и рандомно просто спаунить их.

      Удалить
  31. Подскажите,пожалуйста,делаю обои по вашему уроку.Суть в том,что когда меняю изображения на свои(и все,только изображения),подчеркивается желтым private height и width,а также context.
    А в WallpaperServiceEngine в библиотеках import android.service.wallpaper.WallpaperService.Engine;

    ОтветитьУдалить
    Ответы
    1. Ну возможно вам нужно импортировать библиотеки которые нужны для работы с контекстом. А private int height вы так напишите, тогда по идее не должно подчеркивать.

      Удалить
    2. import android.content.Context;
      import android.view.SurfaceHolder;
      import java.util.ArrayList;
      import java.util.List;

      public class GgalaxyAst extends Thread
      implements Runnable
      {
      private SurfaceHolder surfaceHolder;
      private Context context;
      private boolean wait;
      private boolean run;
      private int score = 0;

      Вроде бы все есть,а private context подчеркивается.С Width и height тоже самое.Даже не знаю,в чем проблема.
      import android.content.SharedPreferences;
      import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
      import android.service.wallpaper.WallpaperService;
      import android.service.wallpaper.WallpaperService.Engine;
      А тут 2 и 4 строки подчеркиваются как неиспользованные,хотя сам класс не отличается от вашего)

      Удалить
    3. Попробуйте сделать Project - clean возможно поможет. А так я не знаю что это, очередная магия от эклипса.

      Удалить
  32. Добавил рисунки во все папки drawable-ldpi, drawable-hdpi, drawable-mdpi, drawable-xhdpi, drawable-xxhpi, и даже создал drawable и туда тоже кинул, но при clean project все еще пишет ошибки типа "bubble can not be resolved or is not a field"

    ps в коде присутствует "import android.R;"

    ОтветитьУдалить
    Ответы
    1. Быстро сам решил с помощью замены строки
      import android.R;
      на
      import live.wallpaper.R;

      Удалить
  33. Доброго дня, підкажіть таку річ, чи можу я якось намалювати слід за кожною бульбашкою?

    ОтветитьУдалить
    Ответы
    1. Ну думаю можно, но я по правде сказать не знаю какой вы след хотите? Просто если прозрачный какой нить след то можно дорисовать его прям к бульбашке и будет смотреться норм, или например создать несколько спаун точек и выводить разные буольбашки с разными следами. Если что то по сложней то уточните я подумаю.

      Удалить
    2. Це я навів як приклад. А взагалі хочеться зробити таке, типу як падає зірка, то за нею слід таки лишається який потихеньку тухне. Завтра хочу пробувати зробити шейдерами(shader), але не знаю на стільки це буде красиво. Буду вдячний за ідею або куски коду!

      Удалить
    3. Хочеться щось таке - http://imageshack.us/photo/my-images/716/031er.png/

      Удалить
  34. Вопрос возник, написал всё как в выше приведённом примере, при предварительном просмотре обоев всё работает, как только хочу их установить на рабочий стол, выдаётся ошибка. В логе жалуется на метод run().

    ОтветитьУдалить