Skocz do zawartości
Lucass

Animowana tapeta - problem z prędkością działania

    Rekomendowane odpowiedzi

    Lucass

    Witam,

    próbuje stworzyć swoją pierwszą animowaną tapetę - a przede wszystkim rozgryźć jak to powinno się poprawnie robić.

    Założenie jest takie - w tle bitmapa jpg - rozmiar 1150x800px ok 70 kb jako przewijane tło.

    Na pierwszym planie jest obiekt wyspa czyli przezroczyste png ok 350x450 px ok 100 kb - który scrolluje się z większą prędkością (pseudo efekt paralaksy).

    do tego na pierwszym planie kilka małych png ( po 10 - 30 kb) jako animowane światła i przelatujący statek.

    Wszystko działa jak należy, ALE... niestety szybkość działania pozostawia wiele do życzenia.

    Nawet jeżeli wyrzucę wszystko i pozostawię tylko tło, wszystko chodzi b.wolno (testuję to na Samsungu Note więc coś jest nie tak z kodem). Nie chce mi się wierzyć, że Android nie może udźwignąć przewijania 100 kb jpga. Najbardziej widoczne jest to na widgetach i ikonkach które wyraźnie zaczynają skakać i szarpać po uruchomieniu tapety.

    Pytanie więc - co robię źle? Nie wykluczam, że po prostu źle do tego podchodzę. Poniżej mój kod:

    public class DemoWallpaperService extends WallpaperService {
    
    
    @Override
    public Engine onCreateEngine() {
       return new DemoWallpaperEngine();
    
    }
    
    private class DemoWallpaperEngine extends Engine {
       private boolean mVisible = false;
       private final Handler mHandler = new Handler();
    
        int x=0,y=0,a=255,i=-1, a1=255, i1=-1;
        float r=0,rs=1;
        float rx1=10, rxs=-1;
    
        private Matrix mMatrix = new Matrix();
        private Matrix mMatrix1 = new Matrix();
        private Matrix mMatrixRotate1 = new Matrix();
        private Matrix mMatrixRotate2 = new Matrix();
    
        public Bitmap spaceShip = BitmapFactory.decodeResource(getResources(), R.drawable.spaceship);
        public Bitmap background= BitmapFactory.decodeResource(getResources(), R.drawable.back2short2j);
        public Bitmap wyspa= BitmapFactory.decodeResource(getResources(), R.drawable.wyspa22g);
        public Bitmap ksiezyc = BitmapFactory.decodeResource(getResources(), R.drawable.ksiezyc);
        public Bitmap reflektorfront= BitmapFactory.decodeResource(getResources(), R.drawable.reflektorwyspa);
    
        private float mPixels;
        private float mPixels1;
    
    
    
       private final Runnable mUpdateDisplay = new Runnable() {
       @Override
       public void run() {
           draw();
       }};
    
    
    
       private void draw() {
          SurfaceHolder holder = getSurfaceHolder();
          Canvas c = null;
    
          Display d = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
          int wx= d.getWidth();
          int wy= d.getHeight();
    
    
          try {
              Runtime.getRuntime().gc();
             c = holder.lockCanvas();
             c.save();
    
             if (c != null) {
    
                 Paint paintMoon = new Paint();
    
                 if(a1<=15){
                     i1=1;
                 }
                 else if(a1>=255){
                     i1=-1;
                 }
                 a1+=5*i1;
                 paintMoon.setAlpha(a1);
    
                 c.translate((float)mPixels, 0f);
                 c.drawBitmap(background, mMatrix, null);
                 c.drawBitmap(ksiezyc, 1027*wx/480,15*wy/800, paintMoon);
    
                 if(rx1<=-15){
                     rxs=1;
                 }
                 else if(rx1>=15){
                     rxs=-1;
                 }
                 rx1+=rxs*0.7;
    
                 c.translate((float)mPixels1, 0f);
                 //reflektor wyspa back
                 mMatrixRotate2.setTranslate(340*wx/480,300*wy/800);
                 mMatrixRotate2.preRotate(rx1,reflektorfront.getWidth()/2,20);
                 c.drawBitmap(reflektorfront, mMatrixRotate2, null);
    
    
                 c.drawBitmap(wyspa, mMatrix1, null);
    
                 if(r<=-15){
                     rs=1;
                 }
                 else if(r>=15){
                     rs=-1;
                 }
                 r+=rs*0.5;
    
    
                 mMatrixRotate1.setTranslate(160*wx/480,380*wy/800);
    
                 mMatrixRotate1.preRotate(r,reflektorfront.getWidth()/2,20);
    
                 c.drawBitmap(reflektorfront, mMatrixRotate1, null);
    
    
                 if(x<c.getWidth()){
                 x+=3;}
                 else{x=0;}
                 if(y<c.getHeight()){
                 y+=3;}
                 else{y=0;}
                 Paint paint = new Paint();
    
    
                 if(a<=5){
                     i=1;
                 }
                 else if(a>=255){
                     i=-1;
                 }
                 a+=10*i;
                 paint.setAlpha(a);
    
                 c.drawBitmap(spaceShip,x,y,paint);
    
    
                    c.restore();
    
             }
          } finally {
             if (c != null)
                holder.unlockCanvasAndPost(c);
          }
          mHandler.removeCallbacks(mUpdateDisplay);
          if (mVisible) {
              mHandler.postDelayed(mUpdateDisplay, 10);
          }
       }
       @Override
       public void onOffsetsChanged(float xOffset, float yOffset,
                float xStep, float yStep, int xPixels, int yPixels){
    
           super.onOffsetsChanged(xOffset, yOffset, xStep, yStep, xPixels, yPixels);                   
            mPixels = xPixels*7/4;
    
            mPixels1 = 500+xPixels;
    
               draw();
       }
    
    
    
       @Override
       public void onVisibilityChanged(boolean visible) {
           mVisible = visible;
           if (visible) {
               draw();
           } else {
               mHandler.removeCallbacks(mUpdateDisplay);
           }
       }
    
        @Override
         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    
            super.onSurfaceChanged(holder, format, width, height);
    
            float w = background.getWidth();
            float h = background.getHeight();
            float s = height / (float)h;
            float z = height / (float)h;
            mMatrix.reset();
            mMatrix1.reset();
            mMatrixRotate1.reset();
            mMatrixRotate2.reset();
    
            mMatrix.setScale(s, s);
            mMatrix1.setScale(z, z);
            mMatrixRotate1.setScale(s, s);
    
            draw();
         }
    
       @Override
       public void onSurfaceDestroyed(SurfaceHolder holder) {
           super.onSurfaceDestroyed(holder);
           mVisible = false;
           mHandler.removeCallbacks(mUpdateDisplay);
       }
    
    
       @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            super.onCreate(surfaceHolder);
    
            setTouchEventsEnabled(false);
        }
    
       @Override
       public void onDestroy() {
            super.onDestroy();
            mVisible = false;
            mHandler.removeCallbacks(mUpdateDisplay);
       }
    }    

    Przeglądałem kilkanaście tutoriali i przykładów i na ich podstawie skleciłem powyższe, ale mam wrażenie że mogę źle podchodzić do problemu, także za wszelkie wskazówki będę bardzo wdzięczny :)

    Udostępnij tę odpowiedź


    Odnośnik do odpowiedzi
    Udostępnij na innych stronach

    Też się dopiero staram nauczyć jak się piszę te animowane tapety, ale widzę, że robimy to podobnie... Możesz spróbować rozbić to tak jakby na warstwy tzn. zamiast

             if (c != null) {
    
                 Paint paintMoon = new Paint();
    
                 if(a1<=15){
                     i1=1;
                 }
                 else if(a1>=255){
                     i1=-1;
                 }
                 a1+=5*i1;
                 paintMoon.setAlpha(a1);
    
                 c.translate((float)mPixels, 0f);
                 c.drawBitmap(background, mMatrix, null);
                 c.drawBitmap(ksiezyc, 1027*wx/480,15*wy/800, paintMoon);
    
                 if(rx1<=-15){
                     rxs=1;
                 }
                 else if(rx1>=15){
                     rxs=-1;
                 }
                 rx1+=rxs*0.7;
    
                 c.translate((float)mPixels1, 0f);
                 //reflektor wyspa back
                 mMatrixRotate2.setTranslate(340*wx/480,300*wy/800);
                 mMatrixRotate2.preRotate(rx1,reflektorfront.getWidth()/2,20);
                 c.drawBitmap(reflektorfront, mMatrixRotate2, null);
    
    
                 c.drawBitmap(wyspa, mMatrix1, null);
    
                 if(r<=-15){
                     rs=1;
                 }
                 else if(r>=15){
                     rs=-1;
                 }
                 r+=rs*0.5;
    
    
                 mMatrixRotate1.setTranslate(160*wx/480,380*wy/800);
    
                 mMatrixRotate1.preRotate(r,reflektorfront.getWidth()/2,20);
    
                 c.drawBitmap(reflektorfront, mMatrixRotate1, null);
    
    
                 if(x<c.getWidth()){
                 x+=3;}
                 else{x=0;}
                 if(y<c.getHeight()){
                 y+=3;}
                 else{y=0;}
                 Paint paint = new Paint();
    
    
                 if(a<=5){
                     i=1;
                 }
                 else if(a>=255){
                     i=-1;
                 }
                 a+=10*i;
                 paint.setAlpha(a);
    
                 c.drawBitmap(spaceShip,x,y,paint);
    
    
                    c.restore();
    
             }
          } finally {
             if (c != null)
                holder.unlockCanvasAndPost(c);
          }

    Możesz spróbować zrobić coś takiego:

    if (c != null) {
    warstwa1(c);
    warstwa2(c);
    warstwa3(c)
    }finally {
             if (c != null)
                holder.unlockCanvasAndPost(c);
          }

    I w wartwa1(Canvas c) rysować to co jest na samym tle, a w kolejnych warstwa2(Canvas c) itd. rysować to co jest dalej dzięki temu będzie to trochę bardziej dla Ciebie czytelne.

    Możesz zmienić z

       if (mVisible) {
              mHandler.postDelayed(mUpdateDisplay, 10);
          }

    na

       if (mVisible) {
              mHandler.post(mUpadateDisplay);
          }

    chociaż 10 milisekund to niby nie jest duża różnica, ale u mnie jak robiłem tapetę z przesuwającymi się obiektami najlepiej to wyglądało bez żadnego opóźnienia.

    Nie ogarniam tego Matrixa jeszcze do końca, więc może gdzieś tam jest przyczyna spowolnienia... Spróbuj na razie rysować na samych współrzędnych i nie wiem czy przez c.save(); i c.restore(); nie ma spowolnienia bo to ma na celu o ile dobrze rozumiem idee tej funkcji przywracać ekran do poprzedniego stanu (osobiście jakoś mi to nigdy nie działało więc robiłem bez tego).

    Dziwi mnie to, że nawet samo tło chodzi powoli.. Też nie jestem specem w tej dziedzinie bo sam próbuje się tego nauczyć.

    Udostępnij tę odpowiedź


    Odnośnik do odpowiedzi
    Udostępnij na innych stronach
    Lucass

    @Aftermatch

    Dzięki za odpowiedź, dobrze wiedzieć że ktoś też dłubie w tym temacie.

    Rzeczywiście canvas save & restore nic nie daje. Wyrzuciłem to ale tez specjalnie nie wpłynęlo na zmianę prędkości.

    Zmieniłem też na mHandler.post(mUpadateDisplay); - teraz tapeta chodzi superpłynnie ale ikony i widgety skaczą...

    Może mam za duże rozmiary bitmapy - jaki rozmiar u Ciebie chodzi płynnie?

    No nic kombinuję dalej jak coś wymyślę to napiszę. W każdym razie dobrze wiedzieć że ogólnie zamysł nie jest zły :)

    Udostępnij tę odpowiedź


    Odnośnik do odpowiedzi
    Udostępnij na innych stronach

    Rozmiar chyba w tym wypadku nie ma znaczenia bo ja zwykle używałem 720x1280 i nie było problemów. Może to jest właśnie kwestia rysowania przez Matrix, ja chwile temu wymyśliłem coś takiego do rysowania uniwersalnego rozmiaru:

    						tlo = tlo.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.tlo2), (int)szerokoscEkranu, (int)wysokoscEkranu, false);
    					c.drawBitmap(tlo, 0, 0, null);	
    

    I szerokoscEkranu i wysokoscEkranu mam w onSurfaceChagned().

    I jeszcze dam Ci taką radę, bo przez nią nie byłem w stanie dokończyć mojej tapety, żebyś używał jak najmniej zmiennych typu Bitmap gdyż przy 14 obrazkach 720x1280 działa, a przy próbie załadowania 15 wywalało aplikacje na starcie...

    Dokładniej działało takie coś:

     
    tlo = BitmapFactory.decodeResource(getResources(), R.drawable.tlo);
    tlo2 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo2);
    tlo3 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo3);
    tlo4 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo4);
    tlo5 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo5);
    tlo6 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo6);
    tlo7 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo7);
    tlo8 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo8);
    tlo9 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo9);
    tlo10 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo10);
    tlo11 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo11);
    tlo12 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo12);
    tlo13 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo13);
    tlo14 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo14);
    
    

    A gdy dodałem

    tlo15 = BitmapFactory.decodeResource(getResources(), R.drawable.tlo15);
    

    aplikacja wywalało przy starcie... Więc staraj się mieć ich jak najmniej

    Udostępnij tę odpowiedź


    Odnośnik do odpowiedzi
    Udostępnij na innych stronach
    Lucass

    Metodą prób i błędów udało mi się uzyskać zadowalającą prędkość, ale musiałem jednak ustawić postDelayed na 25 i usunąć w funkcji draw() zaraz po try{ wiersz:

    Runtime.getRuntime().gc();

    dalej nie mam jednak pojęcia jak u Ciebie chodzi to płynnie przy 14 bitmapach 720x1280? Ja mam tylko 5 bitmap z których największa ma 720x600

    Być może to przez png i przezroczystości w moich bitmapach.

    Zaraz jeszcze się pobawię z zamianą Matryc na skalowanie tak jak podałeś, może to coś da, w każdym razie dzięki za podpowiedź:)

    Udostępnij tę odpowiedź


    Odnośnik do odpowiedzi
    Udostępnij na innych stronach

    U mnie dużo przyspieszyło ładowanie Bitmapy bezpośrednio przed tym kiedy jest użyta, bo bez tego też się lagowało... Ale jeżeli masz kilka obrazków na raz to niestety to Ci nie pomoże... Ale też używałem PNG z przezroczystością, ale o mniejszych rozmiarach i chodziło płynnie.

    Udostępnij tę odpowiedź


    Odnośnik do odpowiedzi
    Udostępnij na innych stronach

    Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

    Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

    Zarejestruj nowe konto

    Załóż nowe konto. To bardzo proste!

    Zarejestruj się

    Zaloguj się

    Posiadasz już konto? Zaloguj się poniżej.

    Zaloguj się

    • Ostatnio przeglądający   0 użytkowników

      Brak zarejestrowanych użytkowników przeglądających tę stronę.

    x