代碼如下:
public class MovingImageView extends ImageView {
private float canvasWidth, canvasHeight;
private float imageWidth, imageHeight;
private float offsetWidth, offsetHeight;
/**
* 移動類型
*/
private int movementType;
/**
* 限定最大比值
* canvasHeight/drawableHeight 或者 canvasWidth/drawableWidth
*/
private float maxRelativeSize;
/**
* 最小相對偏移值,圖片最起碼可以位移圖*0.2的距離
*/
private float minRelativeOffset;
private int mSpeed;
private long startDelay;
private int mRepetitions;
private boolean loadOnCreate;//load完畢后是否移動
private MovingViewAnimator mAnimator;
public MovingImageView(Context context) {
this(context, null);
}
public MovingImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MovingImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray ta = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.MovingImageView, defStyle, 0);
try {
maxRelativeSize = ta.getFloat(R.styleable.MovingImageView_miv_max_relative_size, 3.0f);
minRelativeOffset = ta.getFloat(R.styleable.MovingImageView_miv_min_relative_offset,
0.2f);
mSpeed = ta.getInt(R.styleable.MovingImageView_miv_speed, 50);
mRepetitions = ta.getInt(R.styleable.MovingImageView_miv_repetitions, -1);
startDelay = ta.getInt(R.styleable.MovingImageView_miv_start_delay, 0);
loadOnCreate = ta.getBoolean(R.styleable.MovingImageView_miv_load_on_create, true);
} finally {
ta.recycle();
}
init();
}
private void init() {
super.setScaleType(ScaleType.MATRIX);
mAnimator = new MovingViewAnimator(this);
}
/**
* 更新canvas size
*
* @param w new width.
* @param h new height.
* @param oldW old width.
* @param oldH old height.
*/
@Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
super.onSizeChanged(w, h, oldW, oldH);
canvasWidth = (float) w - (float) (getPaddingLeft() + getPaddingRight());
canvasHeight = (float) h - (float) (getPaddingTop() + getPaddingBottom());
updateAll();
}
private void updateAll() {
if (getDrawable() != null) {
updateImageSize();
updateOffsets();
updateAnimatorValues();
}
}
/**
* 更新圖片Size
*/
private void updateImageSize() {
imageWidth = getDrawable().getIntrinsicWidth();//獲取圖片高度
imageHeight = getDrawable().getIntrinsicHeight();//獲取圖片寬度
}
/**
* 更新偏移量,確定動畫范圍
*/
private void updateOffsets() {
float minSizeX = imageWidth * minRelativeOffset;
float minSizeY = imageHeight * minRelativeOffset;
offsetWidth = (imageWidth - canvasWidth - minSizeX) > 0 ? imageWidth - canvasWidth : 0;
offsetHeight = (imageHeight - canvasHeight - minSizeY) > 0 ? imageHeight - canvasHeight : 0;
}
/**
* 更新動畫基本數據
*/
private void updateAnimatorValues() {
if (canvasHeight == 0 && canvasWidth == 0)
return;
float scale = calculateTypeAndScale();
if (scale == 0)
return;
float w = (imageWidth * scale) - canvasWidth;
float h = (imageHeight * scale) - canvasHeight;
mAnimator.updateValues(movementType, w, h);
mAnimator.setStartDelay(startDelay);
mAnimator.setSpeed(mSpeed);
mAnimator.setRepetition(mRepetitions);
if (loadOnCreate) {
startMoving();
}
}
/**
* 設置最佳的運動類型
* 計算縮放比例
*
* @return image scale.
*/
private float calculateTypeAndScale() {
movementType = MovingViewAnimator.AUTO_MOVE;
float scale = 1f;
float scaleByImage = Math.max(imageWidth / canvasWidth, imageHeight / canvasHeight);
Matrix matrix = new Matrix();
if (offsetWidth == 0 && offsetHeight == 0) {//圖片太小,無法動畫,需要放大
//畫布寬度/圖片寬度
float sW = canvasWidth / imageWidth;
//畫布高度/圖片高度
float sH = canvasHeight / imageHeight;
if (sW > sH) {
scale = Math.min(sW, maxRelativeSize);//限定最大縮放值
matrix.setTranslate((canvasWidth - imageWidth * scale) / 2f, 0);
movementType = MovingViewAnimator.VERTICAL_MOVE;//垂直移動
} else if (sW < sH) {
scale = Math.min(sH, maxRelativeSize);//限定最大縮放值
matrix.setTranslate(0, (canvasHeight - imageHeight * scale) / 2f);
movementType = MovingViewAnimator.HORIZONTAL_MOVE;//水平移動
} else {
scale = Math.max(sW, maxRelativeSize);//限定最大縮放值
movementType = (scale == sW) ? MovingViewAnimator.NONE_MOVE :
MovingViewAnimator.DIAGONAL_MOVE;//對角線移動
}
} else if (offsetWidth == 0) {//寬度太小,無法執行水平動畫,放大寬度
scale = canvasWidth / imageWidth;
movementType = MovingViewAnimator.VERTICAL_MOVE;
} else if (offsetHeight == 0) {//高度太小,無法執行垂直動畫,放大高度
scale = canvasHeight / imageHeight;//求出畫布高度和圖片高度的比值用于確定畫布起始坐標
movementType = MovingViewAnimator.HORIZONTAL_MOVE;
} else if (scaleByImage > maxRelativeSize) {//圖片太大,根據最大比值設定圖片縮放值
scale = maxRelativeSize / scaleByImage;
if (imageWidth * scale < canvasWidth || imageHeight * scale < canvasHeight) {
scale = Math.max(canvasWidth / imageWidth, canvasHeight / imageHeight);
}
}
matrix.preScale(scale, scale);
setImageMatrix(matrix);
return scale;
}
/**
* 禁止設置ScaleType
*
* @param scaleType
*/
@Override
@Deprecated
public void setScaleType(ScaleType scaleType) {
//super.setScaleType(scaleType);
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
updateAll();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
updateAll();
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
updateAll();
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
updateAll();
}
/**
* 獲取animator
*
* @return
*/
public MovingViewAnimator getMovingAnimator() {
return mAnimator;
}
public float getMaxRelativeSize() {
return maxRelativeSize;
}
public void setMaxRelativeSize(float max) {
maxRelativeSize = max;
updateAnimatorValues();
}
public float getMinRelativeOffset() {
return minRelativeOffset;
}
public void setMinRelativeOffset(float min) {
minRelativeOffset = min;
updateAnimatorValues();
}
public boolean isLoadOnCreate() {
return loadOnCreate;
}
public void setLoadOnCreate(boolean loadOnCreate) {
this.loadOnCreate = loadOnCreate;
}
/**
* 開始移動
* 默認不停的移動
*/
public void startMoving() {
startMoving(-1);
}
/**
* 開始移動
*
* @param repetition 循環模式
*/
public void startMoving(int repetition) {
mAnimator.setRepetition(repetition);
mAnimator.start();
}
/**
* 恢復移動
*/
public void resumeMoving() {
mAnimator.resume();
}
/**
* 暫停移動
*/
public void pauseMoving() {
mAnimator.pause();
}
/**
* 停止移動
*/
public void stopMoving() {
mAnimator.stop();
}
/**
* 獲取當前狀態
*
* @return
*/
public MovingViewAnimator.MovingState getMovingState() {
return mAnimator.getMovingState();
}
}
public class MovingViewAnimator {
/**
* 水平移動
*/
public static final int HORIZONTAL_MOVE = 1;
/**
* 垂直移動
*/
public static final int VERTICAL_MOVE = 2;
/**
* 對角線移動
*/
public static final int DIAGONAL_MOVE = 3;
/**
* 自動移動
*/
public static final int AUTO_MOVE = 0;
/**
* 不移動
*/
public static final int NONE_MOVE = -1;
private AnimatorSet mAnimatorSet;
private View mView;
private boolean isRunning;
private int currentLoop;
private boolean infiniteRepetition = true;
private ArrayList<Float> pathDistances;
private int loopCount = -1;
private int movementType;
private float offsetWidth, offsetHeight;
private int mSpeed = 50;
private long mDelay = 0;
private Interpolator mInterpolator;
private MovingState currentState = MovingState.stop;
public enum MovingState {
stop,
moving,
pause
}
private Animator.AnimatorListener animatorListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(final Animator animation) {
//super.onAnimationEnd(animation);
//運行在主線程
mView.post((new Runnable() {
public void run() {
if (isRunning) {
if (infiniteRepetition) {
mAnimatorSet.start();
} else {
currentLoop--;
if (currentLoop > 0) {
mAnimatorSet.start();
}
}
}
}
}));
}
};
public MovingViewAnimator(View imgView) {
mView = imgView;
isRunning = false;
mAnimatorSet = new AnimatorSet();
pathDistances = new ArrayList<>();
mInterpolator = new AccelerateDecelerateInterpolator();
}
public MovingViewAnimator(View imgView, int type, float width, float height) {
this(imgView);
updateValues(type, width, height);
}
private void init() {
setUpAnimator();
setUpValues();
}
/**
* 根據移動類型設置不同的動畫
*/
private void setUpAnimator() {
AnimatorSet animatorSet = new AnimatorSet();
pathDistances.clear();
switch (movementType) {
case HORIZONTAL_MOVE:
animatorSet.playSequentially(createHorizontalAnimator(0, offsetWidth),
createHorizontalAnimator(offsetWidth, 0));
break;
case VERTICAL_MOVE:
animatorSet.playSequentially(createVerticalAnimator(0, offsetHeight),
createVerticalAnimator(offsetHeight, 0));
break;
case DIAGONAL_MOVE:
animatorSet.playSequentially(createDiagonalAnimator(0, offsetWidth, 0,
offsetHeight),
createDiagonalAnimator(offsetWidth, 0, offsetHeight, 0));
break;
case AUTO_MOVE:
animatorSet.playSequentially(
createVerticalAnimator(0, offsetHeight),
createDiagonalAnimator(0, offsetWidth, offsetHeight, 0),
createHorizontalAnimator(offsetWidth, 0),
createDiagonalAnimator(0, offsetWidth, 0, offsetHeight),
createHorizontalAnimator(offsetWidth, 0),
createVerticalAnimator(offsetHeight, 0));
}
if (mAnimatorSet != null) {
mAnimatorSet.removeAllListeners();
stop();
}
mAnimatorSet = animatorSet;
}
/**
* 設置參數數據
*/
private void setUpValues() {
setSpeed(mSpeed);
setStartDelay(mDelay);
setRepetition(loopCount);
setInterpolator(mInterpolator);
}
private void setListener() {
mAnimatorSet.addListener(animatorListener);
}
/**
* 更新動畫值.
*
* @param type
* @param w
* @param h
*/
public void updateValues(int type, float w, float h) {
this.movementType = type;
this.offsetWidth = w;
this.offsetHeight = h;
init();
}
public void setMovementType(int type) {
updateValues(type, offsetWidth, offsetHeight);
}
public void setOffsets(float w, float h) {
updateValues(movementType, w, h);
}
public void start() {
//Log.e("tag", "start.");
if (movementType != NONE_MOVE) {
isRunning = true;
if (!infiniteRepetition)
currentLoop = loopCount;
setListener();
mAnimatorSet.start();
currentState = MovingState.moving;
}
}
public void cancel() {
if (isRunning) {
mAnimatorSet.removeListener(animatorListener);
mAnimatorSet.cancel();
currentState = MovingState.stop;
}
}
@TargetApi(19)
public void pause() {
//Log.e("tag", "pause.");
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
return;
if (mAnimatorSet.isStarted()) {
mAnimatorSet.pause();
currentState = MovingState.pause;
}
}
@TargetApi(19)
public void resume() {
//Log.e("tag", "resume.");
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
return;
if (mAnimatorSet.isPaused()) {
mAnimatorSet.resume();
currentState = MovingState.moving;
}
}
public void stop() {
//Log.e("tag", "stop.");
isRunning = false;
mAnimatorSet.removeListener(animatorListener);
mAnimatorSet.end();
mView.clearAnimation();
currentState = MovingState.stop;
}
/**
* 設置重復模式
*
* @param repetition repetition < 0 循環播放
* repetition > 0 循環repetition次
*/
public void setRepetition(int repetition) {
if (repetition < 0)
infiniteRepetition = true;
else {
loopCount = repetition;
currentLoop = loopCount;
infiniteRepetition = false;
}
}
public Builder addCustomMovement() {
return new Builder();
}
public void clearCustomMovement() {
init();
start();
}
public int getMovementType() {
return movementType;
}
public int getRemainingRepetitions() {
return (infiniteRepetition) ? -1 : currentLoop;
}
public void setInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
mAnimatorSet.setInterpolator(interpolator);
}
/**
* 設置動畫播放之前的延時時間
*
* @param time
*/
public void setStartDelay(long time) {
mDelay = time;
mAnimatorSet.setStartDelay(time);
}
/**
* 設置每個動畫對應的持續時間
*
* @param speed
*/
public void setSpeed(int speed) {
mSpeed = speed;
List<Animator> listAnimator = mAnimatorSet.getChildAnimations();
for (int i = 0; i < listAnimator.size(); i++) {
Animator a = listAnimator.get(i);
a.setDuration(parseSpeed(pathDistances.get(i)));
}
}
/**
* 將速度設置值轉換成秒
*
* @param distance
* @return
*/
private long parseSpeed(float distance) {
return (long) ((distance / (float) mSpeed) * 1000f);
}
/**
* 創建水平移動動畫
*
* @param startValue
* @param endValue
* @return
*/
private ObjectAnimator createHorizontalAnimator(float startValue, float endValue) {
pathDistances.add(Math.abs(startValue - endValue));
return createObjectAnimation("scrollX", startValue, endValue);
}
/**
* 創建垂直移動動畫
*
* @param startValue
* @param endValue
* @return
*/
private ObjectAnimator createVerticalAnimator(float startValue, float endValue) {
pathDistances.add(Math.abs(startValue - endValue));
return createObjectAnimation("scrollY", startValue, endValue);
}
/**
* 創建對角線移動動畫
*
* @param startW
* @param endW
* @param startH
* @param endH
* @return
*/
private ObjectAnimator createDiagonalAnimator(float startW, float endW, float startH, float
endH) {
float diagonal = Pythagoras(Math.abs(startW - endW), Math.abs(startH - endH));
pathDistances.add(diagonal);
PropertyValuesHolder pvhX = createPropertyValuesHolder("scrollX", startW, endW);
PropertyValuesHolder pvhY = createPropertyValuesHolder("scrollY", startH, endH);
return ObjectAnimator.ofPropertyValuesHolder(mView, pvhX, pvhY);
}
private ObjectAnimator createObjectAnimation(String prop, float startValue, float endValue) {
return ObjectAnimator.ofInt(mView, prop, (int) startValue, (int) endValue);
}
private PropertyValuesHolder createPropertyValuesHolder(String prop, float startValue, float
endValue) {
return PropertyValuesHolder.ofInt(prop, (int) startValue, (int) endValue);
}
private static float Pythagoras(float a, float b) {
return (float) Math.sqrt((a * a) + (b * b));
}
/**
* 自定義自動移動方式
*/
public class Builder {
private ArrayList<Animator> mList;
private Builder() {
mList = new ArrayList<>();
pathDistances.clear();
}
public Builder addHorizontalMoveToRight() {
mList.add(createHorizontalAnimator(0, offsetWidth));
return this;
}
public Builder addHorizontalMoveToLeft() {
mList.add(createHorizontalAnimator(offsetWidth, 0));
return this;
}
public Builder addVerticalMoveToDown() {
mList.add(createVerticalAnimator(0, offsetHeight));
return this;
}
public Builder addVerticalMoveToUp() {
mList.add(createVerticalAnimator(offsetHeight, 0));
return this;
}
public Builder addDiagonalMoveToDownRight() {
mList.add(createDiagonalAnimator(0, offsetWidth, 0, offsetHeight));
return this;
}
public Builder addDiagonalMoveToDownLeft() {
mList.add(createDiagonalAnimator(offsetWidth, 0, 0, offsetHeight));
return this;
}
public Builder addDiagonalMoveToUpRight() {
mList.add(createDiagonalAnimator(0, offsetWidth, offsetHeight, 0));
return this;
}
public Builder addDiagonalMoveToUpLeft() {
mList.add(createDiagonalAnimator(offsetWidth, 0, offsetHeight, 0));
return this;
}
public void start() {
mAnimatorSet.removeAllListeners();
stop();
mAnimatorSet = new AnimatorSet();
mAnimatorSet.playSequentially(mList);
setListener();
setUpValues();
MovingViewAnimator.this.start();
}
}
/**
* 獲取當前狀態
*
* @return
*/
public MovingState getMovingState() {
return currentState;
}
}
最后記得在res--values--attrs.xml文件中,寫入如下代碼:
<declare-styleable name="MovingImageView">
<attr name="miv_speed" format="integer"/>
<attr name="miv_repetitions" format="integer"/>
<attr name="miv_max_relative_size" format="float"/>
<attr name="miv_min_relative_offset" format="float"/>
<attr name="miv_start_delay" format="integer"/>
<attr name="miv_load_on_create" format="boolean"/>
</declare-styleable>