Làm thế nào để tạo một vòng quay hình ảnh mượt mà trong Android?


217

Tôi đang sử dụng RotateAnimationđể xoay hình ảnh mà tôi đang sử dụng như một công cụ quay vòng theo chu kỳ tùy chỉnh trong Android. Đây là rotate_indefinitely.xmltập tin của tôi , mà tôi đã đặt 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" />    

Khi tôi áp dụng điều này vào ImageViewviệc sử dụng của tôi AndroidUtils.loadAnimation(), nó hoạt động rất tốt!

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

Một vấn đề là vòng quay hình ảnh dường như tạm dừng ở đầu mỗi chu kỳ.

Nói cách khác, hình ảnh xoay 360 độ, tạm dừng nhanh, sau đó xoay 360 độ một lần nữa, v.v.

Tôi nghi ngờ rằng vấn đề là hoạt hình đang sử dụng bộ nội suy mặc định như android:iterpolator="@android:anim/accelerate_interpolator"( AccelerateInterpolator), nhưng tôi không biết làm thế nào để bảo nó không nội suy hoạt hình.

Làm cách nào để tắt nội suy (nếu đó thực sự là vấn đề) để làm cho chu kỳ hoạt hình của tôi diễn ra suôn sẻ?

Câu trả lời:


199

Bạn nói đúng về AccelerateInterpolator; thay vào đó, bạn nên sử dụng linearInterpolator.

Bạn có thể sử dụng tích hợp android.R.anim.linear_interpolatortừ tệp XML hoạt hình của mình với android:interpolator="@android:anim/linear_interpolator".

Hoặc bạn có thể tạo tệp nội suy XML của riêng bạn trong dự án của bạn, ví dụ: đặt tên cho nó res/anim/linear_interpolator.xml:

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

Và thêm vào XML hoạt hình của bạn:

android:interpolator="@anim/linear_interpolator"

Lưu ý đặc biệt: Nếu hoạt hình xoay của bạn nằm trong một tập hợp, cài đặt bộ nội suy dường như không hoạt động. Làm cho phần tử xoay phần trên sửa nó. (điều này sẽ tiết kiệm thời gian của bạn.)


1
Điều này là do nội suy đã viết sai chính tả (không có "n"). Bạn không cần phải làm cho riêng mình
Kingpin

25
Tôi đã thử mọi bộ nội suy có sẵn, bao gồm cả tuyến tính, và tôi vẫn nhận được "cú huých" nhỏ này vào đầu mỗi chu kỳ.
Adam Rabung

11
Nếu hoạt hình xoay của bạn nằm trong một tập hợp, cài đặt bộ nội suy dường như không hoạt động. Làm cho phần tử xoay xoay sửa nó
shalafi

Này, điều gì sẽ xảy ra nếu bạn thực sự muốn sử dụng speedate_decelerate_interpolator mà không có "tạm dừng" nhỏ giữa mỗi hình ảnh động?
agonist_

90

Tôi cũng gặp vấn đề này và đã cố gắng đặt bộ nội suy tuyến tính trong xml nhưng không thành công. Giải pháp hiệu quả với tôi là tạo ra hình ảnh động dưới dạng RotateAnimation trong mã.

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
nếu bạn muốn hoạt hình giữ nguyên định hướng ở cuối, hãy thêmrotate.setFillAfter(true);
Fonix

4
nếu bạn muốn hoạt hình tồn tại vĩnh viễn không có hồi kết, hãy thêmrotate.setRepeatCount(Animation.INFINITE);
ahmednabil88

38

Cái này hoạt động tốt

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

Để đảo ngược:

<?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
Để đảo ngược, chỉ cần lặp lại 2 và đặt android: repeatMode = "Reverse" - không cần phải có hai tệp XML khác nhau.
RoundSparrow hilltx

3
Tại sao lại là 358 mà không phải là 359 hay 360?
xử

Nơi để thêm tập tin này vào tài nguyên? Là hoạt hình một gói riêng cho việc này?
abggcv

30

Có lẽ, một cái gì đó như thế này sẽ giúp:

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

Nhân tiện, bạn có thể xoay hơn 360 như:

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

Làm việc hoàn hảo, liệu imageView.clearAnimation () cũng sẽ dọn sạch runnable?
XcodeNOOB

Nó hoạt động tốt để bắt đầu hoạt hình, nhưng sau khi bắt đầu chạy, nó không được dọn sạch bởi imageView.clearAnimation (), tôi đã sử dụng trong sự kiện người nghe cảm ứng
Abhijit Jagtap

Không thể dừng các runnables này một cách độc lập
Quần

@Pants bạn có thể gọi withEndAction (cái này) với một số điều kiện. Nếu điều kiện là sai, withEndAction (điều này) sẽ không được gọi và hoạt hình sẽ dừng lại
Vitaly Zinchenko


19
ObjectAnimator.ofFloat(view, View.ROTATION, 0f, 360f).setDuration(300).start();

Thử cái này.


8

Đối tượng quay theo chương trình.

// xoay chiều kim đồng hồ :

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

// Xoay ngược chiều kim đồng hồ:

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

Chế độ xem là đối tượng của ImageView hoặc các tiện ích khác.

xoay.setRepeatCount (10); sử dụng để lặp lại vòng quay của bạn.

500 là thời lượng hoạt hình của bạn.


6

Cắt tỉa <set> -Euity mà bọc <rotate>-Euity giải quyết vấn đề!

Cảm ơn Shalafi!

Vì vậy, Rotation_ccw.xml của bạn sẽ loook như thế này:

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

Bất kể tôi đã cố gắng gì, tôi không thể làm cho nó hoạt động tốt bằng cách sử dụng mã (và setRotation) để có hiệu ứng xoay vòng mượt mà. Điều cuối cùng tôi làm là làm cho mức độ thay đổi quá nhỏ, đến mức tạm dừng nhỏ là không thể nhận thấy. Nếu bạn không cần thực hiện quá nhiều phép quay, thời gian để thực hiện vòng lặp này là không đáng kể. Hiệu quả là một vòng quay trơn tru:

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

bạn "khai báo" biến khai báo ở đâu?
Rishabh Saxena

3

Như hanry đã đề cập ở trên đặt iterpolator lót là tốt. Nhưng nếu xoay vòng trong một tập hợp, bạn phải đặt android: shareInterpolator = "false" để làm cho nó trơn tru.

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

Nếu Sharedinterpolator không sai, đoạn mã trên sẽ bị trục trặc.


3

Nếu bạn đang sử dụng một bộ Animation như tôi, bạn nên thêm phép nội suy bên trong thẻ set:

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

Điều đó đã làm việc cho tôi.


3

Trong Kotlin:

 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

Có thể bởi vì bạn đi từ 0 đến 360, bạn dành nhiều thời gian hơn một chút ở mức 0/360 so với bạn mong đợi? Có lẽ đặt thànhDegrees thành 359 hoặc 358.


1
Lý thuyết tuyệt vời. Tôi khá chắc chắn rằng đó không phải là vì việc tăng tốc / giảm tốc khá trơn tru và có chủ ý. Chỉ trong trường hợp tôi đã cố gắng giảm độ xuống còn 358 và không có thay đổi rõ rệt trong hành vi.
emmby

1

Cố gắng sử dụng nhiều hơn 360 để tránh khởi động lại.

Tôi sử dụng 3600 insted của 360 và điều này hoạt động tốt với tôi:

<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

Trong Android, nếu bạn muốn làm động một đối tượng và làm cho nó di chuyển một đối tượng từ location1 sang location2, API hoạt hình sẽ tìm ra các vị trí trung gian (tweening) và sau đó xếp hàng vào luồng chính các thao tác di chuyển thích hợp vào thời điểm thích hợp bằng cách sử dụng bộ hẹn giờ . Điều này hoạt động tốt ngoại trừ luồng chính thường được sử dụng cho nhiều thứ khác - vẽ, mở tệp, trả lời đầu vào của người dùng, v.v. Một bộ đếm thời gian xếp hàng thường có thể bị trì hoãn. Các chương trình được viết tốt sẽ luôn cố gắng thực hiện càng nhiều thao tác càng tốt trong các luồng nền (không phải chính) tuy nhiên bạn không thể luôn luôn tránh sử dụng luồng chính. Các hoạt động yêu cầu bạn hoạt động trên một đối tượng UI luôn phải được thực hiện trên luồng chính. Ngoài ra, nhiều API sẽ đưa các hoạt động trở lại luồng chính như một hình thức an toàn của luồng.

Tất cả các khung nhìn được vẽ trên cùng một luồng GUI cũng được sử dụng cho tất cả các tương tác của người dùng.

Vì vậy, nếu bạn cần cập nhật GUI nhanh chóng hoặc nếu việc kết xuất mất quá nhiều thời gian và ảnh hưởng đến trải nghiệm người dùng thì hãy sử dụng SurfaceView.

Ví dụ về hình ảnh xoay:

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

Hoạt động:

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
Mặc dù mã này có thể cung cấp một giải pháp cho câu hỏi, tốt hơn hết là thêm ngữ cảnh vào lý do tại sao / cách thức hoạt động của nó. Điều này có thể giúp người dùng trong tương lai tìm hiểu và áp dụng kiến ​​thức đó vào mã của riêng họ. Bạn cũng có thể có phản hồi tích cực từ người dùng dưới dạng upvote, khi mã được giải thích.
borchvm
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.