Pierwsza rzecz, która przyszła na myśl, to animacja LayoutParams
, poprzez Handler
. Nie jestem pewien, czy spełni on twoje wymagania, a to prawdopodobnie wymaga więcej testów.
W każdym razie to było całkiem zabawne pamiętając matematyki ^^ Więc oto moja go na to, używając tylko rodzimych android narzędzia:
Kod:
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);
}
}
}
}
Układ:
<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>
Podgląd:
działa jak urok :) dzięki – Orr