Android Scroll to Rotate Image (Step by step coding example)

Android Scroll to Rotate Image (Step by step coding example)

 

 

Hello,

 

In this example application we i will show you how to use scroller to rotate image.

 

I have created a simple Android app using Android studio. You can find the full source code in my github.

 

 

Extending ImageView

I created our own custom image view because by default image view doesn’t support rounded corner.  So the custom image view handle the rounded corner issue.

 

package com.appsgit.rotateimage;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.ImageView;

/**
 * Created on 9/11/17.
 */

public class MyImageView extends ImageView {
    private float radius = 58.0f;
    private Path path;
    private RectF rect;

    public MyImageView(Context context) {
        super(context);
        init();
    }

    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        path = new Path();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        rect = new RectF(0, 0, this.getWidth(), this.getHeight());
        path.addRoundRect(rect, radius, radius, Path.Direction.CW);
        canvas.clipPath(path);
        super.onDraw(canvas);
    }
}

 

I have copied an image to resource/drawable file, in order to show it in

 

In my activity_home.xml file i have used the custom image view.

 

Check below.

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">


    <com.appsgit.rotateimage.MyImageView
        android:id="@+id/imageview"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:src="@drawable/appleswift"
        android:scaleType="fitXY"
        >

    </com.appsgit.rotateimage.MyImageView>

</RelativeLayout>

 

The main player is  HomeActivity.java  class.

 

package com.appsgit.rotateimage;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.Scroller;

public class HomeActivity extends AppCompatActivity {

    private GestureDetector mDetector;

    private int mPieRotation;

    MyImageView mImageView;

    Scroller mScroller;

    private ObjectAnimator mAutoCenterAnimator;

    private ValueAnimator mScrollAnimator;

    /**
     * The initial fling velocity is divided by this amount.
     */
    public static final int FLING_VELOCITY_DOWNSCALE = 4;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);

        //we shoud implement gesture detector, otherwise we cannot get the user input.
        //we have created  GestureListener inner class.
        mDetector = new GestureDetector(HomeActivity.this, new GestureListener());

        //disable the long press gesture.
        mDetector.setIsLongpressEnabled(false);

        mImageView = (MyImageView) findViewById(R.id.imageview);

        mPieRotation = 0;

        // Create a Scroller to handle the fling gesture.
        if (Build.VERSION.SDK_INT < 11) {
            mScroller = new Scroller(this);
        } else {
            mScroller = new Scroller(this, null, true);
        }

        if (Build.VERSION.SDK_INT >= 11) {
            mAutoCenterAnimator = ObjectAnimator.ofInt(this, "imageRotation", 0);

            mAutoCenterAnimator.addListener(new Animator.AnimatorListener() {
                public void onAnimationStart(Animator animator) {
                }

                public void onAnimationEnd(Animator animator) {
                }

                public void onAnimationCancel(Animator animator) {
                }

                public void onAnimationRepeat(Animator animator) {
                }
            });

        }

        mScrollAnimator = ValueAnimator.ofFloat(0, 1);
        mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                tickScrollAnimation();
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        boolean result = mDetector.onTouchEvent(event);

        if (!result) {
            if (event.getAction() == MotionEvent.ACTION_UP) {
                stopScrolling();
                result = true;
            }
        }
        return result;
    }

    /**
     * Extends {@link GestureDetector.SimpleOnGestureListener} to provide custom gesture
     * processing.
     */
    private class GestureListener extends GestureDetector.SimpleOnGestureListener {

        //when you try to rotate the image. onscroll will be called behind the scene.
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            // Set the rotation directly.
            float scrolltorotte = vectorToScalarScroll(
                    distanceX,
                    distanceY,
                    e2.getX() - ((mImageView.getWidth()/2) + mImageView.getLeft()),
                    e2.getY() - ((mImageView.getHeight()/2) + mImageView.getTop()));

            setPieRotation(getPieRotation() - (int) scrolltorotte / FLING_VELOCITY_DOWNSCALE);
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

            float scrolltorotte = vectorToScalarScroll(
                    velocityX,
                    velocityY,
                    e2.getX() - ((mImageView.getWidth()/2) + mImageView.getLeft()),
                    e2.getY() - ((mImageView.getHeight()/2) + mImageView.getTop()));

            mScroller.fling(
                    0,
                    (int) getPieRotation(),
                    0,
                    (int) scrolltorotte / FLING_VELOCITY_DOWNSCALE,
                    0,
                    0,
                    Integer.MIN_VALUE,
                    Integer.MAX_VALUE);

            if (Build.VERSION.SDK_INT >= 11) {
                mScrollAnimator.setDuration(mScroller.getDuration());
                mScrollAnimator.start();
            }
            return true;
        }

        @Override
        public boolean onDown(MotionEvent e) {
            if (isAnimationRunning()) {
                stopScrolling();
            }
            return true;
        }
    }


    private boolean isAnimationRunning() {
        return !mScroller.isFinished() || (Build.VERSION.SDK_INT >= 11 && mAutoCenterAnimator.isRunning());
    }

    private static float vectorToScalarScroll(float dx, float dy, float x, float y) {
        // get the length of the vector
        float l = (float) Math.sqrt(dx * dx + dy * dy);

        // decide if the scalar should be negative or positive by finding
        // the dot product of the vector perpendicular to (x,y).
        float crossX = -y;
        float crossY = x;

        float dot = (crossX * dx + crossY * dy);
        float sign = Math.signum(dot);

        return l * sign;
    }


    public void setPieRotation(int rotation) {
        rotation = (rotation % 360 + 360) % 360;
        mPieRotation = rotation;
        mImageView.setRotation(rotation);
    }



    public int getPieRotation() {
        return mPieRotation;
    }

    private void tickScrollAnimation() {
        if (!mScroller.isFinished()) {
            mScroller.computeScrollOffset();
            setPieRotation(mScroller.getCurrY());
        } else {
            if (Build.VERSION.SDK_INT >= 11) {
                mScrollAnimator.cancel();
            }
            onScrollFinished();
        }
    }

    private void stopScrolling() {
        mScroller.forceFinished(true);
        if (Build.VERSION.SDK_INT >= 11) {
            mAutoCenterAnimator.cancel();
        }

        onScrollFinished();
    }


    /**
     * Called when the user finishes a scroll action.
     */
    private void onScrollFinished() {
    }

}

 

in the above logic GestureDetector playing very big role here. It calculates the rotating angle based on the user’s detection.

 

 

 

Copy my gradle file,

 

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.0"
    defaultConfig {
        applicationId "com.appsgit.rotateimage"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.android.support:design:26.+'
    testCompile 'junit:junit:4.12'
}

 

and my Manifest file is as follows.

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".HomeActivity"
            android:label="@string/title_activity_home"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

try to build and run the application in the device.  Move your finger across the image view in the home activity. image will be rotated to left or right.

 

Thanks,

If you have any problem with the example, let me know in the comment.

 

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.