Android: анимация вращения ImageView - держите тип масштаба по центру

У меня есть ImageView, который имеет android:scaleType="fitCenter"

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="at.lukle.picturerotation.MainActivity">

    <ImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitCenter"
        android:src="@drawable/android"/>

    <Button
        android:id="@+id/btn_rotate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="rotate"/>

</RelativeLayout>

Это выглядит так:

Нормальный

Когда на кнопку нажимают, я применяю анимацию вращения:

public class MainActivity extends AppCompatActivity {

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

        Button btnRotate = (Button) findViewById(R.id.btn_rotate);
        final ImageView iv = (ImageView) findViewById(R.id.iv);

        btnRotate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                iv.animate().rotationBy(90f).start();
            }
        });
    }
}

Теперь это выглядит так: повернутого

Изображение обрезается сбоку. Я хочу, чтобы scaleType также применялся к повернутому изображению, чтобы ImageView не только поворачивался, но и масштабировался в соответствии с шириной. Думаю, мне нужна анимация масштабирования, но я не знаю, как это сделать.

Я также пытался просто использовать iv.setRotation(90), но у меня такая же проблема здесь...

4 ответа

Мое решение не элегантное (я новичок в разработке) Я надеюсь, что кто-то может предложить лучшее решение....

    int h,w;
    Boolean safe=true;

Получение параметров imageView невозможно при инициализации действия. Для этого, пожалуйста, обратитесь к этому решениюИЛИ установите размеры в onClick of a Button Вот так

    rotateButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if(imageView.getRotation()/90%2==0){
                h=imageView.getHeight();
                w=imageView.getWidth();

            }
        .
        .//Insert the code Snippet below here 
       }

И код для запуска, когда мы хотим повернуть ImageView

if(safe)     
imageView.animate().rotationBy(90).scaleX(imageView.getRotation()/90%2==0?(w*1.0f/h):1).scaleY(imageView.getRotation()/90%2==0?(w*1.0f/h):1).setDuration(2000).setInterpolator(new LinearInterpolator()).setListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                      safe=false;
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                      safe=true;

                }

                @Override
                public void onAnimationCancel(Animator animation) {

                }

                @Override
                public void onAnimationRepeat(Animator animation) {

                }
            }).start();
        }
    });

Этого решения достаточно для указанной выше Задачи. Хотя оно уменьшит изображение, даже если в этом нет необходимости (когда высота меньше, чем Ширина). Если это вас беспокоит, вы можете добавить еще один троичный оператор внутри scaleX/scaleY.

Использовал это

iv.setRotation(iv.getRotation() + 90);

Вместо

 iv.animate().rotationBy(90f).start();

еще два обновления взять android:configChanges как это

 <activity
        android:name=".youactivity"
        android:label="@string/app_name"

        android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
        >

Android: AdjustViewBounds верно для изображения

  <ImageView
    android:id="@+id/iv"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="fitCenter"
    android:layout_centerInParent="true"
    android:adjustViewBounds="true"
    android:src="@mipmap/android"/>

Для этого выполните следующие действия:

  1. задавать scaleType в матрицу:
 <ImageView ...
    android:scaleType="matrix"/>
  1. В onCreate загрузите изображение в imageview и установите кнопку поворота onClick listener:
imageView.post(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap= BitmapFactory.decodeResource(getResources(),R.drawable.dummy);


           imageView.setImageBitmap(bitmap);

            if (bitmap.getWidth() > 0) {

                float scale = ((float)imageView.getMeasuredWidth())/((float)imageView.getDrawable().getIntrinsicWidth());

                imageView.getLayoutParams().height = (int)(scale * imageView.getDrawable().getIntrinsicHeight());
                imageView.setImageMatrix(scaleMatrix(scale, scale));
            }
        }
    });

А также

btnRotate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
              animateImageHeight(imageView,btnRotate);
            }
        });
  1. Добавьте в действие метод animateImageHeight:

void animateImageHeight(final ImageView imageView, final Button btnRotate){

    final float drawableWidth = imageView.getDrawable().getIntrinsicWidth();
    final float drawableHeight = imageView.getDrawable().getIntrinsicHeight();
    float viewWidth = imageView.getMeasuredWidth();
    final float viewHeight = imageView.getMeasuredHeight();
    final int rotation = imageRotation % 360;
    final int newRotation = (rotation + 90);

    final int newViewHeight;
    final float imageScale;
    final float newImageScale;

    if (rotation==0 || rotation==180)
    {
        imageScale = viewWidth / drawableWidth;
        newImageScale = viewWidth / drawableHeight;
        newViewHeight = (int)(drawableWidth * newImageScale);
    }
    else if (rotation==90 || rotation==270){
        imageScale = viewWidth / drawableHeight;
        newImageScale = viewWidth / drawableWidth;
        newViewHeight = (int)(drawableHeight * newImageScale);
    }
    else{
        throw new UnsupportedOperationException("rotation can 0, 90, 180 or 270. ${rotation} is unsupported");
    }


    ValueAnimator animator= ValueAnimator.ofFloat(0f,1f) .setDuration(1000L);



    animator.setInterpolator(new AccelerateDecelerateInterpolator());

    animator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
            btnRotate.setEnabled(false);
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            imageRotation = newRotation % 360;
            btnRotate.setEnabled(true);
        }

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float animVal = (float)animation.getAnimatedValue();
            float complementaryAnimVal = 1 - animVal;

            int animatedHeight =
                    (int)(complementaryAnimVal * viewHeight + animVal * newViewHeight);
            float animatedScale =
                    (complementaryAnimVal * imageScale + animVal * newImageScale);
            float animatedRotation =
                    (complementaryAnimVal * rotation + animVal * newRotation);


            imageView.getLayoutParams().height=animatedHeight;

            Matrix matrix=
                    rotationMatrix(
                    animatedRotation,
                    drawableWidth / 2,
                    drawableHeight / 2
            );

            matrix.postScale(
                    animatedScale,
                    animatedScale,
                    drawableWidth / 2,
                    drawableHeight / 2);
            matrix.postTranslate(-(drawableWidth - imageView.getMeasuredWidth())/2, -(drawableHeight - imageView.getMeasuredHeight())/2);

            imageView.setImageMatrix(matrix);

            imageView.requestLayout();
        }
    });

    animator.start();
}

Вы можете найти версию kotlin и полную демонстрацию здесь.

пытаться:

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

    <androidx.constraintlayout.widget.ConstraintLayout 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"
        tools:context=".MainActivity">

        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/pic"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
          private lateinit var binding: ActivityMainBinding
    private var scale = 1F
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        //Change to your own calculation logic to calculate the zoom factor
        val outMetrics = DisplayMetrics()
        windowManager.getDefaultDisplay().getRealMetrics(outMetrics)
        val w = outMetrics.widthPixels
        val h = outMetrics.heightPixels
        scale = if (w > h) w.toFloat() / h.toFloat() else h.toFloat() / w.toFloat()
        binding.image.scaleX = scale
        binding.image.scaleY = scale
    }

    fun set() {
        sfRotation = (sfRotation + 90) % 360
        Log.d(">>>sfRotation", sfRotation.toString())
        Log.d(">>>hwrotation", hwRotation.toString())
        binding.image.rotation = sfRotation.toFloat()
        binding.image.scaleX = scale
        binding.image.scaleY = scale
    }
Другие вопросы по тегам