Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

RotateAnimation и <rotate>

Rotate

<rotate> (XML)
Метроном
Аттракцион
Бутылочка

<rotate>

Используйте атрибуты fromDegrees и toDegrees внутри тега <rotate>, чтобы задать начальный и конечный углы поворота вокруг опорной точки. Опорная точка указывается с помощью атрибутов pivotX и pivotY, которые содержат процентные значения относительно ширины и высоты объекта Drawable соответственно.

Рассмотрим примеры вращения объекта. Создадим проект, в котором ImageView будет постоянно вращаться либо относительно своего центра, либо вокруг своего угла. В папке res/anim создаем два файла:

rotate_center.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator" >

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

</set>

rotate_corner.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator" >

    <rotate
        android:duration="2000"
        android:fromDegrees="0"
        android:pivotX="0%"
        android:pivotY="0%"
        android:startOffset="0"
        android:toDegrees="360" />

</set>

Разместим на форме две кнопки и ImageView:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/rotatecenter"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Вращение относительно центра" />

    <Button
        android:id="@+id/rotatecorner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Вращение относительно угла картинки" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="120dp"
        android:src="@drawable/ic_launcher" />

</LinearLayout>

Теперь код для анимации:


package ru.alexanderklimov.rotate;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;

public class MainAppActivity extends Activity {

	ImageView myImageView;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		myImageView = findViewById(R.id.imageView);

		Button buttonRotateCenter = findViewById(R.id.rotatecenter);
		Button buttonRotateCorner = findViewById(R.id.rotatecorner);

		final Animation animationRotateCenter = AnimationUtils.loadAnimation(
				this, R.anim.rotate_center);
		final Animation animationRotateCorner = AnimationUtils.loadAnimation(
				this, R.anim.rotate_corner);

		buttonRotateCenter.setOnClickListener(new Button.OnClickListener() {
			@Override
			public void onClick(View view) {
				myImageView.startAnimation(animationRotateCenter);
			}
		});

		buttonRotateCorner.setOnClickListener(new Button.OnClickListener() {
			@Override
			public void onClick(View view) {
				myImageView.startAnimation(animationRotateCorner);
			}
		});
	}
}

Запускайте проект и вращайте картинку.

Rotate

Класс RotateAnimation наследуется от класса Animation и отвечает за анимацию вращения.

Метроном

Добавим на экран ImageButton, который будет объектом вращения.


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity" >

    <ImageButton
        android:id="@+id/imagebutton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:layout_gravity="center_vertical|center_horizontal"
        android:src="@drawable/pinkhellokitty" />

</LinearLayout>

Напишем обработчик щелчка кнопки:


public void onClick(View view) {
    RotateAnimation animation = new RotateAnimation(-30, 60, 100, 180);
    animation.setRepeatMode(Animation.REVERSE);
    animation.setRepeatCount(Animation.INFINITE);
    animation.setInterpolator(new LinearInterpolator());
    animation.setDuration(1000L);

    view.startAnimation(animation);
}

Числа для вращения я подбирал наугад, можете использовать свои значения, зависящие от размеров экрана и т.п. В данном примере движение картинки напоминает работу метронома.

Аттракцион

Следующая анимация напомнила мне аттракцион в парке развлечений. За основу взять пример из урока, где мы учились выводить текст вдоль окружности.


package ru.alexanderklimov.rotateanimation;

import ...

public class MainActivity extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		setContentView(new GraphicsView(this));
	}

	static public class GraphicsView extends View {
        private RotateAnimation animation;
        
        public GraphicsView(Context context) {
        	super(context);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            // creates the animation the first time
            if (animation == null) {
                createAnimation(canvas);
            }
			
            // Настраиваем окружность
            Paint cPaint = new Paint();
            cPaint.setColor(Color.BLUE); // цвет круга
            cPaint.setStyle(Style.STROKE); // делаем окантовку вместо круга
            cPaint.setStrokeWidth(2.0f); // толщина окантовки
            cPaint.setAntiAlias(true); // сглаживание
            
            // Работаем с текстом
            Paint tPaint = new Paint();
            tPaint.setTextSize(36); // размер текста
            tPaint.setAntiAlias(true); // сглаживание
            tPaint.setColor(Color.BLUE); // цвет текста
            
            Path circle = new Path();
            circle.addCircle(240, 240, 115, Direction.CW);
            
            canvas.drawPath(circle, cPaint);
            canvas.drawTextOnPath("Коты и кошки всех стран, соединяйтесь! * ",
    				circle, 0, 32, tPaint);
        }
		
        private void createAnimation(Canvas canvas) {
            animation = new RotateAnimation(0, 360, canvas.getWidth() / 2, canvas.getHeight() / 2);
            animation.setRepeatMode(Animation.REVERSE);
            animation.setRepeatCount(Animation.INFINITE);
            animation.setDuration(10000L);
            animation.setInterpolator(new AccelerateDecelerateInterpolator());
 
            startAnimation(rotate);
        }
    }
}

Если нужно, чтобы был эффект вращения текста внутри окружности, то следует поколдовать с координатами, например, у меня вышло так:


protected void onDraw(Canvas canvas) {
    // creates the animation the first time
    if (rotate == null) {
        createAnimation(canvas);
    }
    
    // Настраиваем окружность
    Paint cPaint = new Paint();
    cPaint.setColor(Color.BLUE); // цвет круга
    cPaint.setStyle(Style.STROKE); // делаем окантовку вместо круга
    cPaint.setStrokeWidth(2.0f); // толщина окантовки
    cPaint.setAntiAlias(true); // сглаживание
    
    // Работаем с текстом
    Paint tPaint = new Paint();
    tPaint.setTextSize(36); // размер текста
    tPaint.setAntiAlias(true); // сглаживание
    tPaint.setColor(Color.BLUE); // цвет текста
    
    Path circle = new Path();
    
    int centerX = canvas.getWidth() / 2;
    int centerY = canvas.getHeight() / 2;
    circle.addCircle(centerX, centerY, 115, Direction.CW);
    
    canvas.drawPath(circle, cPaint);
    canvas.drawTextOnPath("Коты и кошки всех стран, соединяйтесь! * ",
            circle, 0, 32, tPaint);
}

Бутылочка

Можно написать даже простую игру "Бутылочка". Понадобится изображение бутылки с молоком, которую потом отдадим котёнку.


<?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"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageview_bottle"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        app:srcCompat="@drawable/bottle"/>

</RelativeLayout>

При нажатии на бутылку запускается вращение.


package ru.alexanderklimov.rotation;

import ...

public class MainActivity extends AppCompatActivity {

    public static final Random sRandom = new Random();
    private ImageView mBottleImageView;
    private int lastAngle = -1;

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

        mBottleImageView = (ImageView) findViewById(R.id.imageview_bottle);

        mBottleImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                spinBottle();
            }
        });
    }

    private void spinBottle() {
        int angle = sRandom.nextInt(3600 - 360) + 360;
        // Центр вращения
        float pivotX = mBottleImageView.getWidth() / 2;
        float pivotY = mBottleImageView.getHeight() / 2;

        final Animation animation = new RotateAnimation(lastAngle == -1 ? 0 : lastAngle, angle, pivotX, pivotY);
        lastAngle = angle;
        animation.setDuration(2500);
        animation.setFillAfter(true);

        mBottleImageView.startAnimation(animation);
    }

    // В примере не используется
    private void resetBottle() {
        float pivotX = mBottleImageView.getWidth() / 2;
        float pivotY = mBottleImageView.getHeight() / 2;

        final Animation animation = new RotateAnimation(lastAngle == -1 ? 0 : lastAngle, 0, pivotX, pivotY);
        lastAngle = -1;
        animation.setDuration(2000);
        animation.setFillAfter(true);

        mBottleImageView.startAnimation(animation);
    }
}

Не забыли про мою просьбу отдать бутылку котёнку?

Спиннер (Fidget Spinner)

Крутить можно не только бутылку, но и спиннер. На скорую руку написал приложение.

Картинку рисовал в векторе, а также прикрутил к веб-странице.

Реклама