Android 大转盘

商品角度朝中心位置偏转。

 // 外部调用方法   
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); }

你可能感兴趣的:(Android 大转盘)