Android slide to respond as ImageView animation

I need to implement Animation on an ImageView that looks like a slide in order to respond to animations that exist on many Android devices. Demand:

  • Support API level> = 8 (if this is not possible, then 9), so a convenient drag and drop listener is out of the question
  • move the ImageView to the right or left when dragging, only horizontal drag is required. First, the image is centered horizontally.
  • Scale ImageView while dragging - as you approach the end of the screen, the smaller the image becomes.
  • When releasing the image, the image must be animated to the middle of the screen and scaled to its original size (also animated).

I found many code examples and tried to implement it myself, but the combination of all the requirements made it very difficult, and I could not get a decent result, so please do not put links to the material from the first page of Google search, since I spent many days trying to implement these examples, I would appreciate an example of working code + xml layout (if necessary)

+5
source share
2 answers

You can do this without too much difficulty by combining:

First, take an ImageView object and attach View.OnTouchListener to it.

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... mImage = findViewById(...); mImage.setOnTouchListener(mTouchListener); } 

Second, program OnTouchListener to capture the ACTION_DOWN event and save the X coordinate of the initial touch position. Then for each ACTION_MOVE calculate the delta (for translation and scaling) and for ACTION_UP return the ImageView to its original state.

 private View.OnTouchListener mTouchListener = new View.OnTouchListener() { private float mStartX; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN : mStartX = event.getRawX(); return true; case MotionEvent.ACTION_MOVE : float currentX = event.getRawX(); animateTo(currentX - mStartX, true); // Snap to drag return true; case MotionEvent.ACTION_UP : case MotionEvent.ACTION_CANCEL : animateTo(0, false); // Ease back return true; } return false; } }; 

animateTo() will be implemented as follows. The immediate flag indicates whether translation and scaling should be, well, immediate (this is true when responding to every move event and false when returning to its original position and scale).

 private void animateTo(float displacement, boolean immediate) { final int EASE_BACK_DURATION = 300; // ms int duration = (immediate ? 0 : EASE_BACK_DURATION); Display display = getWindowManager().getDefaultDisplay(); int totalWidth = display.getWidth(); float scale = 1.0f - Math.abs(displacement / totalWidth); ViewPropertyAnimator.animate(mImage) .translationX(displacement) .scaleX(scale) .scaleY(scale) .setDuration(duration) .start(); } 

You might want to work with scaling. As written, this means that when you drag to the edge of the screen, the image will be 50% of its original size.

This solution should work without problems at API level 8 (although I have not tested it). Full text is available here if you want to.

+6
source

The first thing that came to mind was the LayoutParams animation, through Handler . Not sure if it will meet your requirements and this will probably require a few more tests.

In any case, it was pretty funny to memorize the math ^^ So, here I am on it, using only my own tools for Android:

the code:

 package com.example.simon.draggableimageview; import android.os.Handler; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; /** * Created by Simon on 2014 Nov 18. */ public class MainActivity extends ActionBarActivity { private static final String TAG = "MainActivity"; // Avoid small values for the following two or setSize will start lagging behind // The maximum time, animation (from smallest) to default size will take private static final int MAX_DURATION = 500; // Minimum delay (ms) for each loop private static final int MIN_DELAY = 20; // By how many px (at least) each (animation back to default state) loop will shift the image private static final float MIN_X_SHIFT = 3; private ImageView mImage; private int mInitialW, mInitialH, mCenterX; private int mMaxMargin; private AnimateBack mAnimateBack; private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler = new Handler(); mImage = (ImageView) findViewById(R.id.imageView); final RelativeLayout imageHolder = (RelativeLayout) findViewById(R.id.imageHolder); mImage.post(new Runnable() { @Override public void run() { // Image ready, measure it mInitialH = mImage.getHeight(); mInitialW = mImage.getWidth(); imageHolder.post(new Runnable() { @Override public void run() { // Calc other measurements int containerWidth = imageHolder.getWidth(); mCenterX = containerWidth / 2; mMaxMargin = containerWidth - mInitialW; } }); } }); imageHolder.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mAnimateBack = new AnimateBack(); mAnimateBack.run(); break; case MotionEvent.ACTION_MOVE: mHandler.removeCallbacks(mAnimateBack); if (motionEvent.getX() > mMaxMargin + mInitialW || motionEvent.getX() < 0) { // Fake Action_Up if out of container bounds motionEvent.setAction(MotionEvent.ACTION_UP); onTouch(view, motionEvent); return true; } setSize(motionEvent.getX() - mCenterX); break; } return true; } }); } private void setSize(float offsetFromCenter) { // Calculate new left margin RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mImage.getLayoutParams(); params.leftMargin = (int) (mMaxMargin * offsetFromCenter / (mCenterX - mInitialW / 2.0)); // Calculate dimensions float ratio = 1 - (Math.abs(offsetFromCenter) / mCenterX); params.width = (int) (mInitialW * ratio); params.height = (int) (mInitialH * ratio); mImage.setLayoutParams(params); // Log.e(TAG, String.format("leftMargin: %d, W: %d, H: %d", // params.leftMargin, params.width, params.height)); } private class AnimateBack implements Runnable { private int loopCount, loopDelay; private float loopBy; public AnimateBack() { RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mImage.getLayoutParams(); float offsetFromCenter = (float) params.leftMargin / mMaxMargin * (mCenterX - mInitialW / 2.0f); float totalDuration = (Math.abs(offsetFromCenter) * MAX_DURATION / mCenterX); loopBy = MIN_X_SHIFT; loopCount = (int) Math.abs(offsetFromCenter / loopBy); loopDelay = (int) (totalDuration / loopCount); if (loopDelay < MIN_DELAY) { // Use the minimum delay loopDelay = MIN_DELAY; // Minimum loop count is 1 loopCount = (int) Math.max(totalDuration / loopDelay, 1); // Calculate by how much each loop will inc/dec the margin loopBy = Math.round(Math.abs(offsetFromCenter / loopCount)); } Log.d(TAG, String.format("Animate back will take: %fms. Will start from offset %d. " + "It will advance by %dpx every %dms", totalDuration, (int) offsetFromCenter, (int) loopBy, loopDelay)); } @Override public void run() { --loopCount; RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mImage.getLayoutParams(); // Calculate offsetFromCenter float offsetFromCenter = (float) ((float) params.leftMargin / mMaxMargin * (mCenterX - mInitialW / 2.0)); // Don't pass 0 when looping if (params.leftMargin > 0) { offsetFromCenter = Math.max(offsetFromCenter - loopBy, 0); } else { offsetFromCenter = Math.min(offsetFromCenter + loopBy, 0); } setSize(offsetFromCenter); if (loopCount == 0) { mHandler.removeCallbacks(this); } else { mHandler.postDelayed(this, loopDelay); } } } } 

Markup:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:id="@+id/imageHolder" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <ImageView android:id="@+id/imageView" android:layout_width="200dp" android:layout_height="200dp" android:src="@drawable/ic_launcher"/> </RelativeLayout> </RelativeLayout> 

Preview:

enter image description here

+4
source

Source: https://habr.com/ru/post/1206923/


All Articles