Androidで画像をスムーズに回転させる方法は?


217

私が使用しているRotateAnimation私は、Androidでカスタム循環的スピナーとして使用していますというイメージを回転させます。rotate_indefinitely.xmlこれが私が置いた私のファイルですres/anim/

<?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" />    

これを私のImageViewusing に適用すると、AndroidUtils.loadAnimation()うまく機能します!

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

1つの問題は、画像の回転が各サイクルの上部で一時停止しているように見えることです。

つまり、画像は360度回転し、一時停止してから、再び360度回転します。

問題は、アニメーションがandroid:iterpolator="@android:anim/accelerate_interpolator"AccelerateInterpolator)のようなデフォルトの補間を使用していることだと思いますが、アニメーションを補間しないように指示する方法がわかりません。

アニメーションのサイクルをスムーズにするには、どうすれば補間をオフにできますか(実際に問題がある場合)。

回答:


199

あなたはAccelerateInterpolatorについて正しいです。代わりにLinearInterpolatorを使用してください。

を使用しandroid.R.anim.linear_interpolatorて、アニメーションXMLファイルの組み込みを使用できますandroid:interpolator="@android:anim/linear_interpolator"

または、プロジェクトに独自のXML補間ファイルを作成できます。たとえば、次のように名前を付けますres/anim/linear_interpolator.xml

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

アニメーションXMLに追加します。

android:interpolator="@anim/linear_interpolator"

特記事項:回転アニメーションがセット内にある場合、補間器の設定は機能していないようです。一番上の要素を回転させると修正されます。(これにより時間を節約できます。)


1
これは、interpolatorのスペルが間違っていたためです( "n"なし)。自分で作る必要はありません
キングピン、2011

25
私は線形を含む利用可能なすべての補間器を試してみました、そして私はまだすべてのサイクルの初めにこの小さな「ヒッチ」を得ます。
Adam Rabung、2011

11
回転アニメーションがセット内にある場合、補間器の設定が機能していないようです。回転を最上位の要素にすると修正されます
shalafi '16年

ねえ、すべてのアニメーション間の小さな「一時停止」なしで実際にaccelerator_decelerate_interpolatorを使用したい場合はどうでしょうか?
アゴニスト_2016年

90

私もこの問題を抱えていて、線形補間をxmlで設定しようとしましたが、成功しませんでした。私にとってうまくいった解決策は、アニメーションをコードでRotateAnimationとして作成することでした。

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);

9
アニメーションを最後の向きのままにしたい場合は、次を追加しますrotate.setFillAfter(true);
Fonix

4
アニメーションを終了せずに永続的に保持したい場合は、次を追加しますrotate.setRepeatCount(Animation.INFINITE);
ahmednabil88

38

これはうまくいきます

<?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" />

逆回転するには:

<?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" />

5
逆の場合は、repeatcount 2を設定し、android:repeatMode = "reverse"を設定するだけです。2つの異なるXMLファイルを用意する必要はありません。
RoundSparrow hilltx 2014年

3
なぜ359や360ではなく358なのですか?
ベヘリット2017年

このファイルをリソースのどこに追加しますか?アニメーションはこのための別のパッケージですか?
abggcv

30

多分、このようなものが役立つでしょう:

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();

ちなみに、あなたは次のように360以上回転することができます:

imageView.animate().rotationBy(10000)...

imageView.clearAnimation()はランナブルもクリーンアップしますか?
XcodeNOOB 2016

開始アニメーションは正常に機能しますが、開始後は実行可能で、imageView.clearAnimation()によってクリーンアップされません。タッチリスナーイベントで使用しました
Abhijit Jagtap

これらのランナブルを個別に停止することはできません
パンツ

@パンツは、ある条件でwithEndAction(this)を呼び出すことができます。条件がfalseの場合、withEndAction(this)は呼び出されず、アニメーションは停止します
Vitaly Zinchenko



8

プログラムによる回転オブジェクト。

//時計回りの回転:

    public void rotate_Clockwise(View view) {
        ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 180f, 0f);
//        rotate.setRepeatCount(10);
        rotate.setDuration(500);
        rotate.start();
    }

//反時計回りの回転:

 public void rotate_AntiClockwise(View view) {
        ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 180f);
//        rotate.setRepeatCount(10);
        rotate.setDuration(500);
        rotate.start();
    } 

ビューは、ImageViewまたは他のウィジェットのオブジェクトです。

rotate.setRepeatCount(10); 回転を繰り返すために使用します。

500はアニメーションの継続時間です。


6

剪定<set>包ま-element <rotate>-element解きに問題が!

シャラフィに感謝!

したがって、Rotation_ccw.xmlは次のようになります。

<?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

何を試しても、スムーズな回転アニメーションのコード(およびsetRotation)を使用してこれを正しく機能させることはできませんでした。私がやったことは、程度の変化を非常に小さくして、小さな一時停止が気付かれないようにすることでした。ローテーションをあまり必要としない場合、このループを実行する時間はごくわずかです。効果はスムーズな回転です。

        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);
        }

「アニメーション」変数宣言はどこにありますか?
Rishabh Saxena

3

hanryが前述したように、ライナーiterpolatorを配置することは問題ありません。ただし、回転がセット内にある場合は、android:shareInterpolator = "false"を設定して円滑にする必要があります。

<?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>

Sharedinterpolatorがfalseでない場合、上記のコードはグリッチを与えます。


3

私のようなセットアニメーションを使用している場合は、セットタグ内に補間を追加する必要があります。

<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>

それは私のために働いた。


3

コトリンでは:

 ivBall.setOnClickListener(View.OnClickListener {

            //Animate using XML
            // val rotateAnimation = AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely)

            //OR using Code
            val rotateAnimation = RotateAnimation(
                    0f, 359f,
                    Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f

            )
            rotateAnimation.duration = 300
            rotateAnimation.repeatCount = 2

            //Either way you can add Listener like this
            rotateAnimation.setAnimationListener(object : Animation.AnimationListener {

                override fun onAnimationStart(animation: Animation?) {
                }

                override fun onAnimationRepeat(animation: Animation?) {
                }

                override fun onAnimationEnd(animation: Animation?) {

                    val rand = Random()
                    val ballHit = rand.nextInt(50) + 1
                    Toast.makeText(context, "ballHit : " + ballHit, Toast.LENGTH_SHORT).show()
                }
            })

            ivBall.startAnimation(rotateAnimation)
        })

1

0から360に移動したため、予想よりも0/360で少し多くの時間を費やしている可能性はありますか?おそらく、Degreesを359または358に設定します。


1
素晴らしい理論。スピードアップ/スローダウンは非常にスムーズで意図的に見えるので、それはそうではないと確信しています。念のため、度を358に下げてみましたが、動作に識別できる変化はありませんでした。
emmby 2009年

1

再起動を避けるために、360以上を使用するようにしてください。

私は360の代わりに3600を使用していますが、これは私にとってはうまくいきます:

<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%" />

0

Androidでは、オブジェクトをアニメーション化してオブジェクトをlocation1からlocation2に移動させる場合、アニメーションAPIが中間位置(トゥイーン)を計算し、タイマーを使用して適切な時間に適切な移動操作をメインスレッドにキューイングします。 。これは正常に機能しますが、メインスレッドは通常、ペイント、ファイルのオープン、ユーザー入力への応答など、他の多くの目的で使用されます。キューに入れられたタイマーは、しばしば遅延する可能性があります。よく書かれたプログラムは常にバックグラウンド(メインではない)スレッドで可能な限り多くの操作を実行しようとしますが、メインスレッドの使用を常に回避できるわけではありません。UIオブジェクトを操作する必要がある操作は、常にメインスレッドで実行する必要があります。また、多くのAPIは、操作をスレッドセーフの形式としてメインスレッドに戻します。

ビューはすべて同じGUIスレッド上に描画され、すべてのユーザーインタラクションにも使用されます。

したがって、GUIをすばやく更新する必要がある場合、またはレンダリングに時間がかかりすぎてユーザーエクスペリエンスに影響する場合は、SurfaceViewを使用してください。

回転画像の例:

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);
                }
            }
        }
    }
}

アクティビティ:

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));
    }
}

0
private fun rotateTheView(view: View?, startAngle: Float, endAngle: Float) {
    val rotate = ObjectAnimator.ofFloat(view, "rotation", startAngle, endAngle)
    //rotate.setRepeatCount(10);
    rotate.duration = 400
    rotate.start()
}

1
このコードは質問に対する解決策を提供するかもしれませんが、それがなぜ/どのように機能するかに関してコンテキストを追加する方が良いです。これは、将来のユーザーが学習し、その知識を自分のコードに適用するのに役立ちます。また、コードが説明されている場合は、賛成票の形でユーザーから肯定的なフィードバックを受ける可能性があります。
borchvm
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.