商品角度朝中心位置偏转。
// 外部调用方法
private void getImg(String logoURL) {
ImageView imageView1 = new ImageView(this);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(214, 308);
imageView1.setLayoutParams(params);
Picasso.with(this).load(logoURL).into(imageView1);
mLuckyPanView.setChildView(imageView1);
}
public class TurntableView extends ViewGroup {
InterfaceBackToast interfaceBackToast = null;
/**
* 自定义控件的自定义事件
* @para m iBack 接口类型
*/
public void setonClick(InterfaceBackToast iBack) {
interfaceBackToast = iBack;
}
public void onDestory() {
}
public interface InterfaceBackToast {
public void oninterfaceback();
}
/**
* 1.绘制背景圆圈
* 2.添加 addImageView 形成一个圈。
*/
private int mWidth; //ViewGroup 宽度
private PointF mCenterPoint;
private int LayoutRadius; //直径
private int childCount = 0;
private volatile float mStartAngle = 270;
/**
* 滚动的速度
*/
private float mSpeed = 0;
/**
* 是否滑动
*/
private boolean isFling = false;
/**
* 绘制盘快的画笔
*/
private Paint mArcPaint;
/**
* 绘制盘块的范围
*/
private RectF mRange = new RectF();
public TurntableView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d("onlayout", "onlayout 进来");
if(!isFling){
if (getChildCount() != 0){
mStartAngle = 270 - 360 / getChildCount() /2;
}
}
setChildLayout(l, t, r, b);
}
private void init() {
mCenterPoint = new PointF();
mArcPaint = new Paint();
mArcPaint.setAntiAlias(true);
mArcPaint.setDither(true);
//调用这句话,要不然不能绘制onDraw方法
setWillNotDraw(false);
}
//父类控件的可用大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = measureHanlder(widthMeasureSpec);
int width = mWidth;
/**
* 宽和高 选择其中一个最小的来设置 view宽高
*/
mCenterPoint.x = mWidth / 2;
mCenterPoint.y = mWidth / 2;
LayoutRadius = width - getPaddingLeft() - getPaddingRight();
mRange = new RectF(getPaddingLeft(), getPaddingLeft(), LayoutRadius + getPaddingLeft()
, LayoutRadius + getPaddingLeft());
/**
* 测绘子view的大小
*/
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
measureChild(view, MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST));
}
setMeasuredDimension(width, width);
Log.d("onDraw", "onMeasure");
}
@Override
protected void onDraw(Canvas canvas) {
Log.d("onDraw", "onDraw");
float tmpAngle = mStartAngle;
float sweepAngle;
if (childCount != 0) {
sweepAngle = 360 / childCount;
} else {
//当什么奖品没有的时候,设置的颜色。
sweepAngle = 360;
mArcPaint.setColor(Color.argb(0, 0, 0, 0));
canvas.drawArc(mRange, tmpAngle, sweepAngle, true, mArcPaint);
}
/**
* 绘制转盘的背景
*/
for (int i = 0; i < childCount; i++) {
if (i % 2 == 0) {
mArcPaint.setColor(Color.rgb(254, 227, 76));
} else {
mArcPaint.setColor(Color.rgb(1, 136, 200));
}
canvas.drawArc(mRange, tmpAngle, sweepAngle, true, mArcPaint);
tmpAngle += sweepAngle;
}
if (isFling) {
setChildLayout(0, 0, 0, 0);
}
}
//设置view的位置
private void setChildLayout(int l, int t, int r, int b) {
childCount = getChildCount();
if (childCount == 0) return;
float childAngle = (float) (360 / childCount);
Log.d("childAngle", "childAngle=" + childAngle);
float tmpAngle = mStartAngle;
for (int i = 0; i < childCount; i++) {
//开始绘制自布局的时候,就已经得到该布局的大小了
View view = getChildAt(i);
int childHeight = view.getMeasuredHeight();
int childWidth = view.getMeasuredWidth();
// Log.d("onlayout", "childHeight=" + childHeight + " childWidth=" + childWidth);
drawIcon(tmpAngle, view, childWidth, childHeight, i);
Log.d("tmpAngle", "tmpAngle=" + tmpAngle + " i=" + (i + 1));
tmpAngle += childAngle;
}
}
/**
* android 30° 是顺时针方向.
*
* @param startAngle 起始角度
*/
public void drawIcon(float startAngle, View view, int width, int height, int i) {
/**
* 每个View 角度 α/2
*/
float ItemCenterPoint = (360 / childCount / 2);
float angle = (float) ((ItemCenterPoint + startAngle) * (Math.PI / 180));
/**
* 解释一下:
* (0,0) 当count=4时候
* 第一个角度是0°。 但是我们需要偏移到自己象限的中间。
* 0°+itemCenterPoint itemCenterPoint等于=45° 记住是顺时针旋转。
*
* ---------------
* | \
* | \
* | \
*
* 为什么ImageView 选择角度是这样算的。这里主要说明90°的原因
*
* *
* * View 指向上
* *
* * * * * *
* * 这个角度是我们View偏移的角度,但是我们View是向上的 需要多加一个90° 让它回归到 水平位置
* *
* *
*/
float n = startAngle % 360 + ItemCenterPoint + 90;
Log.d("n-drawIcon", "startAngle%360=" + (startAngle % 360) + " ItemCenterPoint=" + ItemCenterPoint + " n=" + n + " startAngle=" + startAngle);
int x = (int) (mCenterPoint.x + (LayoutRadius / 3) * Math.cos(angle));
int y = (int) (mCenterPoint.y + (LayoutRadius / 3) * Math.sin(angle));
Log.d("drawIcon", "postion=" + " angle=" + angle + " x=" + x + " y=" + y);
view.layout(x - width / 2, y - width / 2, x + width / 2, y + width / 2);
setViewAngle(n, view);
}
/**
* 根据角度来设置View角度
*/
public void setViewAngle(float mStartAngle, View view) {
Log.d("setViewAngle", "setViewAngle=" + mStartAngle);
view.setRotation(mStartAngle);
}
private int measureHanlder(int measureSpec) {
int result;
//获得当前view 的显示模式
int specMode = MeasureSpec.getMode(measureSpec);
//获得当前view 的大小
int specSize = MeasureSpec.getSize(measureSpec);
//如果为测量时的大小
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(100, specSize);
} else {
result = 100;
}
return result;
}
public void setChildView(View view) {
addView(view);
}
/**
* 点击开始旋转
* @param luckyIndex
*/
public void luckyStart(int luckyIndex) {
// 每项角度大小
float angle = (float) (360 / getChildCount());
// 中奖角度范围(因为指针向上,所以水平第一项旋转到指针指向,需要旋转210-270;)
float from = 270 - (luckyIndex + 1) * angle + angle/2;
float to = from + angle ;
// 停下来时旋转的距离
float targetFrom = 4 * 360 + from;
/**
*
* (v1 + 0) * (v1+1) / 2 = target ;
* v1*v1 + v1 - 2target = 0 ;
* v1=-1+(1*1 + 8 *1 * target)/2;
*
*/
float v1 = (float) (Math.sqrt(1 * 1 + 8 * 1 * targetFrom) - 1) / 2;
float targetTo = 4 * 360 + to;
float v2 = (float) (Math.sqrt(1 * 1 + 8 * 1 * targetTo) - 1) / 2;
mSpeed = v1;
isFling = true;
mStartAngle = 0;
post(mFlingRunnable = new AutoFlingRunnable(mSpeed));
}
public boolean isFling() {
return isFling;
}
public void setFling(boolean fling) {
isFling = fling;
}
/**
* 自动滚动的Runnable
*/
private AutoFlingRunnable mFlingRunnable;
/**
* 自动滚动的任务
*/
private class AutoFlingRunnable implements Runnable {
private float angelPerSecond;
public AutoFlingRunnable(float velocity) {
this.angelPerSecond = velocity;
}
public void run() {
if ((int) Math.abs(angelPerSecond) <= 0) {
isFling = false;
mSpeed = 0;
interfaceBackToast.oninterfaceback();
return;
}
isFling = true;
mStartAngle += angelPerSecond;
// 逐渐减小这个值
angelPerSecond --;
// 重新布局
invalidate();
//50 毫秒之后再次开启这个线程
postDelayed(this, 50);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
//必须要在MOVE中return才有效果,在这里return后UP事件也会被拦截
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return super.onInterceptTouchEvent(e);
}