package cn.androidy.thinking.views;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.animation.LinearInterpolator;
import java.util.List;
import cn.androidy.thinking.R;
/**
* 仿IOS底部滚轮选择器
* Created by Rick Meng on 2015/6/23.
*/
public class ScrollPicker extends View {
private List<Object> mDataList;
private DisplayMetrics dm;
private PickerLayerManager plm;
private Paint mPaint;
private int mColorText = 0xff333333;
private boolean isDoingAnimation = false;
private float lastDownY;
private int[] SHADOWS_COLORS = new int[]{0xFF111111,
0x00AAAAAA, 0x00AAAAAA};
// Center Line
private Drawable centerDrawable;
// Wheel drawables
private int wheelBackground = R.drawable.wheel_bg;
private int wheelForeground = R.drawable.wheel_val;
// Shadows drawables
private GradientDrawable topShadow;
private GradientDrawable bottomShadow;
private float motionTransY;
private float autoTransY;
private VelocityTracker mVelocityTracker;
private int mMaxVelocity;
private static final int FLING_SPEED = 1000;
public ScrollPicker(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ScrollPicker(Context context) {
super(context);
init();
}
public ScrollPicker(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
dm = getResources().getDisplayMetrics();
plm = new PickerLayerManager(dm.density);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mColorText);
mPaint.setTextSize(20 * dm.density);
mVelocityTracker = VelocityTracker.obtain();
mMaxVelocity = ViewConfiguration.get(getContext()).getScaledMaximumFlingVelocity();
}
/**
* @param event 向VelocityTracker添加MotionEvent
* @see android.view.VelocityTracker#obtain()
* @see android.view.VelocityTracker#addMovement(MotionEvent)
*/
private void acquireVelocityTracker(final MotionEvent event) {
if (null == mVelocityTracker) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
/**
* 释放VelocityTracker
*
* @see android.view.VelocityTracker#clear()
* @see android.view.VelocityTracker#recycle()
*/
private void releaseVelocityTracker() {
if (null != mVelocityTracker) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
public void bindData(List<Object> dataList) {
mDataList = dataList;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
initResourcesIfNecessary();
int h = getHeight();
int w = getWidth();
plm.initCanvasTotalArea(w, h);
plm.bindData(mDataList);
plm.initLayerParams(mPaint, 0);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
plm.doDraw(canvas, mPaint);
drawCenterRect(canvas);
drawShadows(canvas);
}
/**
* Set the shadow gradient color
*
* @param start
* @param middle
* @param end
*/
public void setShadowColor(int start, int middle, int end) {
SHADOWS_COLORS = new int[]{start, middle, end};
}
/**
* Sets the drawable for the wheel background
*
* @param resource
*/
public void setWheelBackground(int resource) {
wheelBackground = resource;
setBackgroundResource(wheelBackground);
}
/**
* Sets the drawable for the wheel foreground
*
* @param resource
*/
public void setWheelForeground(int resource) {
wheelForeground = resource;
centerDrawable = getContext().getResources().getDrawable(wheelForeground);
}
public boolean show() {
//正在执行动画时不响应点击操作
if (isDoingAnimation) {
return false;
}
ObjectAnimator objectAnimator =
ObjectAnimator.ofInt(this, "translationY", 480, 0).setDuration(300);
objectAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
isDoingAnimation = true;
if (View.VISIBLE != getVisibility()) {
setVisibility(View.VISIBLE);
}
}
@Override
public void onAnimationEnd(Animator animation) {
isDoingAnimation = false;
}
@Override
public void onAnimationCancel(Animator animation) {
isDoingAnimation = false;
}
@Override
public void onAnimationRepeat(Animator animation) {
isDoingAnimation = true;
}
});
objectAnimator.start();
return true;
}
public boolean close() {
//正在执行动画时不响应点击操作
if (isDoingAnimation) {
return false;
}
ObjectAnimator objectAnimator =
ObjectAnimator.ofInt(this, "translationY", 0, 480).setDuration(300);
objectAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
isDoingAnimation = true;
if (View.VISIBLE != getVisibility()) {
setVisibility(View.VISIBLE);
}
}
@Override
public void onAnimationEnd(Animator animation) {
isDoingAnimation = false;
}
@Override
public void onAnimationCancel(Animator animation) {
isDoingAnimation = false;
}
@Override
public void onAnimationRepeat(Animator animation) {
isDoingAnimation = true;
}
});
objectAnimator.start();
return true;
}
/**
* Initializes resources
*/
private void initResourcesIfNecessary() {
if (centerDrawable == null) {
centerDrawable = getContext().getResources().getDrawable(wheelForeground);
}
if (topShadow == null) {
topShadow = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, SHADOWS_COLORS);
}
if (bottomShadow == null) {
bottomShadow = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, SHADOWS_COLORS);
}
setBackgroundResource(wheelBackground);
}
/**
* Draws rect for current value
*
* @param canvas the canvas for drawing
*/
private void drawCenterRect(Canvas canvas) {
int center = getHeight() / 2;
int offset = getItemHeight();
centerDrawable.setBounds(0, center - offset, getWidth(), center + offset);
centerDrawable.draw(canvas);
}
/**
* Draws shadows on top and bottom of control
*
* @param canvas the canvas for drawing
*/
private void drawShadows(Canvas canvas) {
int height = getItemHeight();
topShadow.setBounds(0, 0, getWidth(), height);
topShadow.draw(canvas);
bottomShadow.setBounds(0, getHeight() - height, getWidth(), getHeight());
bottomShadow.draw(canvas);
}
private int getItemHeight() {
return plm.getItemHeight();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
acquireVelocityTracker(event);
final VelocityTracker verTracker = mVelocityTracker;
switch (action) {
case MotionEvent.ACTION_DOWN:
dispatchActionDownEvent(event);
return true;
case MotionEvent.ACTION_MOVE:
verTracker.computeCurrentVelocity(1000, mMaxVelocity);
dispatchActionMoveEvent(event);
return true;
case MotionEvent.ACTION_UP:
dispatchActionUpEvent(event);
releaseVelocityTracker();
return true;
case MotionEvent.ACTION_CANCEL:
releaseVelocityTracker();
break;
}
return super.onTouchEvent(event);
}
/**
* 记录当前速度
*
* @param velocityX x轴速度
* @param velocityY y轴速度
*/
private static final String sFormatStr = "velocityX=%f velocityY=%f";
private String recodeInfo(final float velocityX, final float velocityY) {
final String info = String.format(sFormatStr, velocityX, velocityY);
return info;
}
private void dispatchActionDownEvent(MotionEvent event) {
lastDownY = event.getY();
}
private void dispatchActionMoveEvent(MotionEvent event) {
if (autoTransY != 0) {
motionTransY = autoTransY;
autoTransY = 0;
} else {
motionTransY += (event.getY() - lastDownY);
}
plm.initLayerParams(mPaint, motionTransY);
invalidate();
lastDownY = event.getY();
}
private void dispatchActionUpEvent(MotionEvent event) {
float yspeed = mVelocityTracker.getYVelocity();
if (Math.abs(yspeed) > FLING_SPEED) {
onFling(mVelocityTracker.getYVelocity());
} else {
fitSelection();
}
}
private void fitSelection() {
float targetTransY = -plm.getSelectedIndex() * plm.getItemHeight() * PickerLayer.ITEM_SCALE;
ValueAnimator animator = ValueAnimator.ofFloat(motionTransY, targetTransY).setDuration(300);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
autoTransY = (Float) animation.getAnimatedValue();
plm.initLayerParams(mPaint, autoTransY);
invalidate();
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
motionTransY = autoTransY;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
private void onFling(float yVelocity) {
float distance = Math.min(100 * dm.density, Math.abs(yVelocity * 0.3f));
if (yVelocity < 0) {
distance = -distance;
}
float targetTransY = motionTransY + distance;
ValueAnimator animator = ValueAnimator.ofFloat(motionTransY, targetTransY).setDuration((long) (Math.abs(distance / yVelocity) * 1000));
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
autoTransY = (Float) animation.getAnimatedValue();
plm.initLayerParams(mPaint, autoTransY);
invalidate();
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
motionTransY = autoTransY;
fitSelection();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
public void selectIndex(int index) {
float targetTransY = -index * plm.getItemHeight() * PickerLayer.ITEM_SCALE;
ValueAnimator animator = ValueAnimator.ofFloat(motionTransY, targetTransY).setDuration(300);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
autoTransY = (Float) animation.getAnimatedValue();
plm.initLayerParams(mPaint, autoTransY);
invalidate();
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
motionTransY = autoTransY;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
}