/*
* Copyright (C) 2014 Albert Grobas
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.qyh.joe.customview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.widget.ImageView;
import com.example.qyh.joe.R;
/**
* 运动图像在屏幕上的自定义图片,动画效果。
*/
public class MovingImageView extends ImageView {
//control vars
private float canvasWidth, canvasHeight;
private float imageWidth, imageHeight;
private float offsetWidth, offsetHeight;
private int movementType;
//user vars
private float maxRelativeSize, minRelativeOffset;
private int mSpeed;
private long startDelay;
private int mRepetitions;
private boolean loadOnCreate;
//Our custom animator
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 attributes = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.MovingImageView, defStyle, 0);
try {
maxRelativeSize = attributes.getFloat(R.styleable.MovingImageView_miv_max_relative_size, 3.0f);
minRelativeOffset = attributes.getFloat(R.styleable.MovingImageView_miv_min_relative_offset, 0.2f);
mSpeed = attributes.getInt(R.styleable.MovingImageView_miv_speed, 50);
mRepetitions = attributes.getInt(R.styleable.MovingImageView_miv_repetitions, -1);
startDelay = attributes.getInt(R.styleable.MovingImageView_miv_start_delay, 0);
loadOnCreate = attributes.getBoolean(R.styleable.MovingImageView_miv_load_on_create, true);
} finally {
attributes.recycle();
}
init();
}
private void init() {
//mandatory
super.setScaleType(ScaleType.MATRIX);
mAnimator = new MovingViewAnimator(this);
}
/**
* Updates canvas size, includes padding.
*
* @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);
//update canvas size
canvasWidth = (float) w - (float) (getPaddingLeft() + getPaddingRight());
canvasHeight = (float) h - (float) (getPaddingTop() + getPaddingBottom());
//after canvas changes need an update
updateAll();
}
private void updateAll() {
if (getDrawable() != null) {
updateImageSize();
updateOffsets();
updateAnimator();
}
}
private void updateImageSize() {
imageWidth = getDrawable().getIntrinsicWidth();
imageHeight = getDrawable().getIntrinsicHeight();
}
/**
* Offset is the difference between image and canvas including the min relative size.
* Determines the base path animation length.
*/
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;
}
/**
* Gets scale and sets the real length path on Animator.
*/
private void updateAnimator() {
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)
mAnimator.start();
}
/**
* Sets the best movement type and scale.
*
* @return image scale.
*/
private float calculateTypeAndScale() {
movementType = MovingViewAnimator.AUTO_MOVE;
float scale = 1f;
float scaleByImage = Math.max(imageWidth / canvasWidth, imageHeight / canvasHeight);
Matrix m = new Matrix();
//Image is too small to performs any animation, needs a scale
if (offsetWidth == 0 && offsetHeight == 0) {
float sW = canvasWidth / imageWidth;
float sH = canvasHeight / imageHeight;
if (sW > sH) {
scale = Math.min(sW, maxRelativeSize);
m.setTranslate((canvasWidth - imageWidth * scale) / 2f, 0);
movementType = MovingViewAnimator.VERTICAL_MOVE;
} else if (sW < sH) {
scale = Math.min(sH, maxRelativeSize);
m.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;
}
//Width too small to perform any horizontal animation, scale to width
} else if (offsetWidth == 0) {
scale = canvasWidth / imageWidth;
movementType = MovingViewAnimator.VERTICAL_MOVE;
//Height too small to perform any vertical animation, scale to height
} else if (offsetHeight == 0) {
scale = canvasHeight / imageHeight;
movementType = MovingViewAnimator.HORIZONTAL_MOVE;
//Enough size but too big, resize down
} else if (scaleByImage > maxRelativeSize) {
scale = maxRelativeSize / scaleByImage;
if(imageWidth * scale < canvasWidth || imageHeight * scale < canvasHeight) {
scale = Math.max(canvasWidth / imageWidth, canvasHeight / imageHeight);
}
}
m.preScale(scale, scale);
setImageMatrix(m);
return scale;
}
/**
* Don't touch this!
*
* @param scaleType deprecated for force setup to ScaleType.Matrix
*/
@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();
}
/**
* Returns the animator.
*
* @return Moving Animator.
*/
public MovingViewAnimator getMovingAnimator() {
return mAnimator;
}
public float getMaxRelativeSize() {
return maxRelativeSize;
}
public void setMaxRelativeSize(float max) {
maxRelativeSize = max;
updateAnimator();
}
public float getMinRelativeOffset() {
return minRelativeOffset;
}
public void setMinRelativeOffset(float min) {
minRelativeOffset = min;
updateAnimator();
}
public boolean isLoadOnCreate() {
return loadOnCreate;
}
public void setLoadOnCreate(boolean loadOnCreate) {
this.loadOnCreate = loadOnCreate;
}
}