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;
    /**
     * <pre>
     *  (v1 + 0) * (v1+1) / 2 = target ;
     *  v1*v1 + v1 - 2target = 0 ;
     *  v1=-1+(1*1 + 8 *1 * target)/2;
     * </pre>
     */
    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);
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • 作者 银灯鸳帏 她,美得历代文人都为之赞叹,为之惋惜,为之悲痛。 她的美成全了自己,也埋葬了自己,她就是中国历史...
    银灯鸳帏阅读 2,001评论 0 3
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,376评论 25 707
  • 内容:在设计中要警惕复杂的解决方案。场景:适用于任何项目,而且应在所有大型或复杂系统或项目的设计过程中使用。用法:...
    c84f3109853b阅读 228评论 1 0
  • 1 前天还是大前天的晚上,口语群的一个大姐姐特地给我发来一条微信消息。原文如下: “你好,在群里待了几天,孩子说这...
    彳亍尔尔阅读 325评论 1 5
  • 1.查看所有的库. mysql> show databases; 2.终止命令,一定要打在;号前面. mysql>...
    mychineseheart阅读 223评论 0 0