赞
踩
转载请标明出处:
https://blog.csdn.net/m0_38074457/article/details/86433483
,本文出自【陈少华的博客】
- <declare-styleable name="TurntableView">
- <!--转盘的item数量-->
- <attr name="pannum" format="integer" />
- <!--转盘的item的文本-->
- <attr name="names" format="reference" />
- <!--转盘的本地图像地址-->
- <attr name="icons" format="reference" />
- <!--转盘背景的颜色-->
- <attr name="colors" format="reference" />
- <!--控件宽占屏幕宽的比例-->
- <attr name="percentage" format="float" />
- </declare-styleable>
- public TurntableView(Context context) {
- this(context, null);
- }
-
- public TurntableView(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TurntableView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- initView(context, attrs);
- }
initView方法中初始化控件的值
- /**
- * 初始化view
- *
- * @param context
- * @param attrs
- */
- private void initView(Context context, AttributeSet attrs) {
- //手势识别对象
- mDetector = new GestureDetectorCompat(context, new TurntableGestureListener());
- //抗锯齿
- mPaint.setAntiAlias(true);
-
- mScreenHeight = getResources().getDisplayMetrics().heightPixels;
- mScreenWidth = getResources().getDisplayMetrics().widthPixels;
-
- TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TurntableView);
- if (array != null) {
- mPanNum = array.getInteger(R.styleable.TurntableView_pannum, 8);
- int colorsId = array.getResourceId(R.styleable.TurntableView_colors, R.array.colors);
- int namesArray = array.getResourceId(R.styleable.TurntableView_names, R.array.names);
- int iconsArray = array.getResourceId(R.styleable.TurntableView_icons, R.array.icons);
- mPercentage = array.getFloat(R.styleable.TurntableView_percentage, (float) 0.75);
- int[] colors = context.getResources().getIntArray(colorsId);
- String[] namesStrs = context.getResources().getStringArray(namesArray);
- String[] iconsStrs = context.getResources().getStringArray(iconsArray);
-
- for (int i = 0; i < colors.length; i++) {
- mColors.add(colors[i]);
- }
-
- for (int i = 0; i < namesStrs.length; i++) {
- mNamesStrs.add(namesStrs[i]);
- }
- List<Integer> iconLists = new ArrayList<>();
- for (int i = 0; i < iconsStrs.length; i++) {
- iconLists.add(context.getResources().getIdentifier(iconsStrs[i], "mipmap", context.getPackageName()));
- }
- for (int i = 0; i < iconLists.size(); i++) {
- Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), iconLists.get(i));
- mBitmaps.add(bitmap);
- }
- array.recycle();
- }
- }

- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- //控制view的宽为屏幕宽的mPercentage
- int viewWid = (int) ((float) mScreenWidth * mPercentage);
- setMeasuredDimension(viewWid, viewWid);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mWid = w;
- mHei = h;
-
- //让转盘的宽为view的宽,半径为view的宽的一半
- mCenterX = mWid / 2;
- mCenterY = mCenterX;
- mRadius = mWid / 2;
-
- mOffsetAngle = (float) 360 / (float) mPanNum;
- }

- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- //画圆的背景颜色
- drawBackground(canvas);
- //画图片
- drawImage(canvas);
- //画文本
- drawText(canvas);
- }
- /**
- * 画圆的整体背景
- *
- * @param canvas
- */
- private void drawBackground(Canvas canvas) {
- mPaint.setStyle(Paint.Style.FILL);
- RectF rectF = new RectF(0, 0, mWid, mHei);
-
- float angle = mCurrentAngle;
- for (int i = 0; i < mPanNum; i++) {
- int yushu = i % mColors.size();
- mPaint.setColor(mColors.get(yushu));
- canvas.drawArc(rectF, angle, mOffsetAngle, true, mPaint);
- angle = angle + mOffsetAngle;
- }
- }
-
- /**
- * 画图像
- *
- * @param canvas
- */
- private void drawImage(Canvas canvas) {
- //绘制图片开始的角度位置
- float radian = mCurrentAngle + mOffsetAngle / (float) 2;
- //使图像的宽度的一半为半径的1/7
- float imageOffset = (float) mRadius / (float) 7;
- for (int i = 0; i < mBitmaps.size(); i++) {
- //计算图片中心位置的坐标
- //Math.toRadians 是为了提高计算精度
- float x = (float) (mCenterX + (float) mRadius * (float) 0.6 * Math.cos(Math.toRadians(radian)));
- float y = (float) (mCenterY + (float) mRadius * (float) 0.6 * Math.sin(Math.toRadians(radian)));
-
- RectF rectF = new RectF(x - imageOffset, y - imageOffset, x + imageOffset, y + imageOffset);
- canvas.drawBitmap(mBitmaps.get(i), null, rectF, mPaint);
- radian = radian + mOffsetAngle;
- }
- }
-
- /**
- * 画文本
- *
- * @param canvas
- */
- private void drawText(Canvas canvas) {
- mPaint.setColor(Color.WHITE);
- mPaint.setTextAlign(Paint.Align.CENTER);
- mPaint.setTextSize(30);
- RectF rectF = new RectF(0, 0, mWid, mHei);
-
- //计算text文本的高度
- Paint.FontMetrics fm = mPaint.getFontMetrics();
- float textHeight = fm.bottom - fm.top;
-
- float startAngle = mCurrentAngle;
- for (int i = 0; i < mNamesStrs.size(); i++) {
- //使文本根据,每个item的圆弧路径绘制
- Path path = new Path();
- path.addArc(rectF, startAngle, mOffsetAngle);
- canvas.drawTextOnPath(mNamesStrs.get(i), path, 0, textHeight + 10, mPaint);
- startAngle = startAngle + mOffsetAngle;
- }
- }

- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return mDetector.onTouchEvent(event);
- }
- private class TurntableGestureListener extends GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onDown(MotionEvent e) {
- //如果正在抽奖,则不可以手势滑动
- if (isDrawingLottery) {
- return false;
- }
- if (mOnFlingAnimator != null) {
- mOnFlingAnimator.cancel();
- }
- mStartX = e.getX();
- mStartY = e.getY();
- return true;
- }
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- LoggerUtil.i(TAG, "onScroll");
- float e2X = e2.getX();
- float e2Y = e2.getY();
-
- float xStart = mStartX - mCenterX;
- float yStart = mCenterY - mStartY;
- double distancestart = Math.sqrt(xStart * xStart + yStart * yStart);
- //计算移动点到圆心的距离
- float xMove = e2X - mCenterX;
- float yMove = mCenterY - e2Y;
- double distanceMove = Math.sqrt(xMove * xMove + yMove * yMove);
- double distanceMoveDz = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
-
- double cosValue = (distancestart * distancestart + distanceMove * distanceMove - distanceMoveDz * distanceMoveDz) / (2 * distancestart * distanceMove);
- //多指触控的时候会造成cosValue 大于1,这里控制一下
- if (cosValue > 1) {
- LoggerUtil.i(TAG + "大于1", cosValue);
- cosValue = 1;
- } else if (cosValue < -1) {
- LoggerUtil.i(TAG + "小于1", cosValue);
- cosValue = -1;
- }
- double acos = Math.acos(cosValue);
- double changeAngleDz = Math.toDegrees(acos);
-
- //大于0 顺时针,小于0 逆时针
- float value = (mStartX - mCenterX) * (e2Y - mCenterY) - (mStartY - mCenterY) * (e2X - mCenterX);
- if (value >= 0) {
- mCurrentAngle = (float) (mCurrentAngle + changeAngleDz);
- isClockwise = true;
- } else {
- mCurrentAngle = (float) (mCurrentAngle - changeAngleDz);
- isClockwise = false;
- }
- setRotate(mCurrentAngle);
-
- mStartX = e2X;
- mStartY = e2Y;
- return true;
- }
-
- @Override
- public boolean onFling(MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) {
- float e2X = e2.getX();
- float e2Y = e2.getY();
-
- float dx = velocityX * (float) 0.005;
- float dy = velocityY * (float) 0.005;
- float xStart = mStartX - mCenterX;
- float yStart = mCenterY - mStartY;
- double distancestart = Math.sqrt(xStart * xStart + yStart * yStart);
- //计算移动点到圆心的距离
- float xMove = e2X - mCenterX;
- float yMove = mCenterY - e2Y;
- double distanceMove = Math.sqrt(xMove * xMove + yMove * yMove);
- double distanceMoveDz = Math.sqrt(dx * dx + dy * dy);
-
- double cosValue = (distancestart * distancestart + distanceMove * distanceMove - distanceMoveDz * distanceMoveDz) / (2 * distancestart * distanceMove);
- //多指触控的时候会造成cosValue 大于1,这里控制一下
- if (cosValue > 1) {
- cosValue = 1;
- } else if (cosValue < -1) {
- cosValue = -1;
- }
- double acos = Math.acos(cosValue);
- double changeAngleDz = Math.toDegrees(acos);
-
- if (isClockwise) {
-
- } else {
- //逆时针
- changeAngleDz = changeAngleDz * -1;
- }
-
- mOnFlingAnimator = ValueAnimator.ofFloat((float) changeAngleDz, 0);
- mOnFlingAnimator.setDuration(1000);
- mOnFlingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float animatedX = (float) animation.getAnimatedValue();
- mCurrentAngle = mCurrentAngle + animatedX;
- setRotate(mCurrentAngle);
- }
- });
-
- mOnFlingAnimator.start();
- return true;
- }
- }

onScroll方法中计算手指滑动时候转盘转动的角度原理:
步骤1.将JitPack存储库添加到构建文件中
项目的根build.gradle中添加以下代码:
- allprojects {
- repositories {
- ...
- maven { url 'https://jitpack.io' }
- }
- }
步骤2.build.gradle添加依赖项
- dependencies {
- implementation 'com.github.hnsycsxhzcsh:TurntableView:v1.1'
- }
步骤3. 布局中引用控件(控件需要放在一个父布局中,父布局中放一个图片按钮)
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <com.turntableview.TurntableView
- android:id="@+id/turntable"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true" />
-
- <ImageView
- android:id="@+id/iv_node"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:background="@mipmap/node" />
-
- </RelativeLayout>

步骤4. activity中添加监听
- mIvGo.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mTurntable.startRotate(new ITurntableListener() {
- @Override
- public void onStart() {
- Toast.makeText(MainActivity.this, "开始抽奖", Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onEnd(int position, String name) {
- mTvResult.setText("抽奖结束抽中第" + (position + 1) + "位置的奖品:" + name);
- }
- });
- }
- });

可以在github上下载我的项目:https://github.com/hnsycsxhzcsh/TurntableView,如果我的博客对你有帮助的话,欢迎博客点赞支持,并在github右上角star支持!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。