2009-10-27 14 views
167

Używam RotateAnimation do obracania obrazu, którego używam jako niestandardowej cyklicznej tarczy w systemie Android. Oto mój plik rotate_indefinitely.xml, które umieściłem w res/anim/:Jak uzyskać płynne obracanie obrazu w systemie Android?

<?xml version="1.0" encoding="UTF-8"?> 
<rotate 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:fromDegrees="0" 
    android:toDegrees="360" 
    android:pivotX="50%" 
    android:pivotY="50%" 
    android:repeatCount="infinite" 
    android:duration="1200" />  

Kiedy stosuje się to do mojego ImageView użyciu AndroidUtils.loadAnimation(), działa świetnie!

spinner.startAnimation( 
    AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely)); 

Problem polega na tym, że obrót obrazu wydaje się być zatrzymany u góry każdego cyklu.

Innymi słowy, obraz obraca się o 360 stopni, na krótko zatrzymuje, a następnie ponownie obraca się o 360 stopni, itp

Podejrzewam, że problemem jest to, że animacja jest za pomocą domyślnej interpolator jak android:iterpolator="@android:anim/accelerate_interpolator" (AccelerateInterpolator), ale Nie wiem, jak powiedzieć, żeby nie interpolować animacji.

Jak wyłączyć interpolację (jeśli to rzeczywiście jest problem), aby cykl animacji był płynny?

Odpowiedz

178

Masz rację co do AccelerateInterpolator; zamiast tego powinieneś użyć LinearInterpolator.

Możesz użyć wbudowanego android.R.anim.linear_interpolator z pliku XML animacji za pomocą android:interpolator="@android:anim/linear_interpolator".

Można również utworzyć własny plik interpolacji XML w projekcie, np. nazwać res/anim/linear_interpolator.xml:

<?xml version="1.0" encoding="utf-8"?> 
<linearInterpolator xmlns:android="http://schemas.android.com/apk/res/android" /> 

i dodać do swojej animacji XML:

android:interpolator="@anim/linear_interpolator" 

Uwaga specjalna: Jeśli obracania animacja jest wewnątrz zestawu, ustawianie interpolator nie wydaje się działać. Wykonując obrót górny element to naprawia. (to zaoszczędzi twój czas.)

+1

Dzieje się tak, ponieważ interpolator został napisany nieprawidłowo (bez "n"). Nie musisz tworzyć własnego – Kingpin

+22

Wypróbowałem każdy dostępny interpolator, w tym liniowy, i wciąż otrzymuję ten mały "zaczep" na początku każdego cyklu. –

+11

Jeśli animacja obracania znajduje się wewnątrz zestawu, ustawienie interpolatora nie działa. Wykonanie obrotu elementu górnego naprawia go – shalafi

1

Czy to możliwe, że ponieważ przechodzisz od 0 do 360, spędzasz trochę więcej czasu 0/360, niż się spodziewasz? Być może ustawisz na Rusztowania na 359 lub 358.

+1

Wielką teorię. Jestem pewien, że to nie dlatego, że przyspieszenie/spowolnienie jest dość płynne i celowe. Na wszelki wypadek próbowałem zmniejszyć stopnie do 358 i nie było zauważalnej zmiany w zachowaniu. – emmby

26

Spróbuj użyć toDegrees="359" od 360 ° i 0 ° są takie same.

3

Bez względu na to, co próbowałem, nie mogłem zmusić tego do poprawnego działania za pomocą kodu (i setRotation) dla płynnej animacji obrotu. To, co zrobiłem, spowodowało, że zmiany stopnia były tak małe, że małe pauzy są niezauważalne. Jeśli nie musisz wykonywać zbyt wielu obrotów, czas wykonania tej pętli jest znikomy. Efekt jest gładki obrót:

 float lastDegree = 0.0f; 
     float increment = 4.0f; 
     long moveDuration = 10; 
     for(int a = 0; a < 150; a++) 
     { 
      rAnim = new RotateAnimation(lastDegree, (increment * (float)a), Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); 
      rAnim.setDuration(moveDuration); 
      rAnim.setStartOffset(moveDuration * a); 
      lastDegree = (increment * (float)a); 
      ((AnimationSet) animation).addAnimation(rAnim); 
     } 
28

to działa prawidłowo

<?xml version="1.0" encoding="UTF-8"?> 
<rotate xmlns:android="http://schemas.android.com/apk/res/android" 
    android:duration="1600" 
    android:fromDegrees="0" 
    android:interpolator="@android:anim/linear_interpolator" 
    android:pivotX="50%" 
    android:pivotY="50%" 
    android:repeatCount="infinite" 
    android:toDegrees="358" /> 

Aby odwrócić Rotate:

<?xml version="1.0" encoding="UTF-8"?> 
<rotate xmlns:android="http://schemas.android.com/apk/res/android" 
    android:duration="1600" 
    android:fromDegrees="358" 
    android:interpolator="@android:anim/linear_interpolator" 
    android:pivotX="50%" 
    android:pivotY="50%" 
    android:repeatCount="infinite" 
    android:toDegrees="0" /> 
+4

Dla wersji odwrotnej wystarczy tylko powtórzenie 2 i ustaw android: repeatMode = "reverse" - nie ma potrzeby posiadania dwóch różnych plików XML. –

+0

dlaczego 358, a nie 359 lub 360? – behelit

+0

Gdzie można dodać ten plik do zasobów? Czy animacja jest osobnym pakietem do tego? – abggcv

5

przycinanie <set> -elementowe że owinął <rotate> -elementowe rozwiązuje problem!

Dzięki Shalafi!

Więc Rotation_ccw.xml powinny loook tak:

<?xml version="1.0" encoding="utf-8"?> 

<rotate 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:fromDegrees="0" 
    android:toDegrees="-360" 
    android:pivotX="50%" 
    android:pivotY="50%" 
    android:duration="2000" 
    android:fillAfter="false" 
    android:startOffset="0" 
    android:repeatCount="infinite" 
    android:interpolator="@android:anim/linear_interpolator" 
    /> 
3

Jak hanry wspomniał powyżej oddanie iterpolator liniowej jest w porządku. Ale jeśli rotacja jest wewnątrz zestawu, musisz ustawić android: shareInterpolator = "false", aby było gładkie.

<?xml version="1.0" encoding="utf-8"?> 
<set xmlns:android="http://schemas.android.com/apk/res/android" 
**android:shareInterpolator="false"** 
> 
<rotate 
    android:interpolator="@android:anim/linear_interpolator" 
    android:duration="300" 
    android:fillAfter="true" 
    android:repeatCount="10" 
    android:repeatMode="restart" 
    android:fromDegrees="0" 
    android:toDegrees="360" 
    android:pivotX="50%" 
    android:pivotY="50%" /> 
<scale xmlns:android="http://schemas.android.com/apk/res/android" 
    android:interpolator="@android:anim/linear_interpolator" 
    android:duration="3000" 
    android:fillAfter="true" 
    android:pivotX="50%" 
    android:pivotY="50%" 
    android:fromXScale="1.0" 
    android:fromYScale="1.0" 
    android:toXScale="0" 
    android:toYScale="0" /> 
</set> 

Jeśli Sharedinterpolator nie jest fałszywy, powyższy kod powoduje usterki.

40

Miałem również ten problem i próbowałem ustawić interpolator liniowy w formacie xml bez powodzenia. Rozwiązaniem, które zadziałało dla mnie było stworzenie animacji jako RotateAnimation w kodzie.

RotateAnimation rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); 
rotate.setDuration(5000); 
rotate.setInterpolator(new LinearInterpolator()); 

ImageView image= (ImageView) findViewById(R.id.imageView); 

image.startAnimation(rotate); 
0

w Android jeśli chce animowania obiektu i uczynić go przenieść obiekt z Location1 do Location2 dane animacja API ustalenia miejsc pośrednich (tweening), a następnie w kolejce do głównego nici odpowiednie operacje ruch w odpowiednich godzinach przy użyciu licznika czasu. Działa to dobrze, z wyjątkiem tego, że główny wątek jest zwykle używany do wielu innych rzeczy - malowania, otwierania plików, reagowania na dane wprowadzane przez użytkownika itp. Czas oczekiwania w kolejce często może być opóźniony. Dobrze napisane programy będą zawsze starały się wykonywać jak najwięcej operacji w wątkach (poza głównym), jednak nie zawsze można uniknąć używania głównego wątku. Operacje, które wymagają działania na obiekcie interfejsu użytkownika, zawsze muszą być wykonywane w głównym wątku. Ponadto wiele interfejsów API przeniesie operacje z powrotem do głównego wątku jako formę bezpieczeństwa wątków.

Widoki są rysowane w tym samym wątku GUI, który jest również używany do wszystkich interakcji użytkownika.

Jeśli więc chcesz szybko zaktualizować GUI lub jeśli renderowanie zajmuje zbyt dużo czasu i wpływa na wygodę użytkownika, użyj SurfaceView.

Przykład rotacji obrazu:

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback { 
    private DrawThread drawThread; 

    public MySurfaceView(Context context) { 
     super(context); 
     getHolder().addCallback(this); 
    } 

    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int width, 
      int height) { 
    } 

    @Override 
    public void surfaceCreated(SurfaceHolder holder) { 
     drawThread = new DrawThread(getHolder(), getResources()); 
     drawThread.setRunning(true); 
     drawThread.start(); 
    } 

    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) { 
     boolean retry = true; 
     drawThread.setRunning(false); 
     while (retry) { 
      try { 
       drawThread.join(); 
       retry = false; 
      } catch (InterruptedException e) { 
      } 
     } 
    } 
} 


class DrawThread extends Thread{ 
    private boolean runFlag = false; 
    private SurfaceHolder surfaceHolder; 
    private Bitmap picture; 
    private Matrix matrix; 
    private long prevTime; 

    public DrawThread(SurfaceHolder surfaceHolder, Resources resources){ 
     this.surfaceHolder = surfaceHolder; 

     picture = BitmapFactory.decodeResource(resources, R.drawable.icon); 

     matrix = new Matrix(); 
     matrix.postScale(3.0f, 3.0f); 
     matrix.postTranslate(100.0f, 100.0f); 

     prevTime = System.currentTimeMillis(); 
    } 

    public void setRunning(boolean run) { 
     runFlag = run; 
    } 

    @Override 
    public void run() { 
     Canvas canvas; 
     while (runFlag) { 
      long now = System.currentTimeMillis(); 
      long elapsedTime = now - prevTime; 
      if (elapsedTime > 30){ 

       prevTime = now; 
       matrix.preRotate(2.0f, picture.getWidth()/2, picture.getHeight()/2); 
      } 
      canvas = null; 
      try { 
       canvas = surfaceHolder.lockCanvas(null); 
       synchronized (surfaceHolder) { 
        canvas.drawColor(Color.BLACK); 
        canvas.drawBitmap(picture, matrix, null); 
       } 
      } 
      finally { 
       if (canvas != null) { 
        surfaceHolder.unlockCanvasAndPost(canvas); 
       } 
      } 
     } 
    } 
} 

działalność:

public class SurfaceViewActivity extends Activity { 
    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(new MySurfaceView(this)); 
    } 
} 
1

Jeśli używasz zestaw animacji jak ja należy dodać interpolacji wewnątrz ustawionego tagu:

<set xmlns:android="http://schemas.android.com/apk/res/android" 
android:interpolator="@android:anim/linear_interpolator"> 

<rotate 
    android:duration="5000" 
    android:fromDegrees="0" 
    android:pivotX="50%" 
    android:pivotY="50%" 
    android:repeatCount="infinite" 
    android:startOffset="0" 
    android:toDegrees="360" /> 

<alpha 
    android:duration="200" 
    android:fromAlpha="0.7" 
    android:repeatCount="infinite" 
    android:repeatMode="reverse" 
    android:toAlpha="1.0" /> 

</set> 

To działało dla mnie.

15

Może coś w tym pomogą:

Runnable runnable = new Runnable() { 
    @Override 
    public void run() { 
     imageView.animate().rotationBy(360).withEndAction(this).setDuration(3000).setInterpolator(new LinearInterpolator()).start(); 
    } 
}; 

imageView.animate().rotationBy(360).withEndAction(runnable).setDuration(3000).setInterpolator(new LinearInterpolator()).start(); 

Nawiasem mówiąc, można obrócić o ponad 360 takich jak:

imageView.animate().rotationBy(10000)... 
+0

Działa perfekcyjnie, czy imageView.clearAnimation() czyści także działające? – XcodeNOOB

+0

Działa poprawnie, aby rozpocząć animację, ale po uruchomieniu jej nie jest czyścić przez imageView.clearAnimation(), użyłem w przypadku detektora dotyku –

3
ObjectAnimatior.ofFloat(view,"rotation",0,360).start(); 

Spróbuj tego.

0

Spróbuj użyć więcej niż 360, aby uniknąć ponownego uruchomienia.

używam 3600 insted 360 i to działa dobrze dla mnie:

<rotate xmlns:android="http://schemas.android.com/apk/res/android" 
    android:fromDegrees="0" 
    android:toDegrees="3600" 
    android:interpolator="@android:anim/linear_interpolator" 
    android:repeatCount="infinite" 
    android:duration="8000" 
    android:pivotX="50%" 
    android:pivotY="50%" /> 
Powiązane problemy