Android实现可点击的幸运大转盘

之前的项目有一个幸运大转盘的功能,在网上找了很久,都没有合适的方法。

这是效果图,实现目标:十二星座的图片可点击切换选中效果,根据选择不同的星座,实现不同的 方法。之前网上的都是带有指针的,或者可点击改变效果,但是并不知道选择的到底是哪个,即虚拟选择。

实现该功能的主要代码如下:

1、自定义一个布局,存放图片,实现圆形布局。

/**
 *
 *
 * CircleMenuLayout.java
 *
 * @author wuxiaosu
 *
 */
public class CircleMenuLayout extends ViewGroup {
  /**
   * 布局的半径
   */
  private int mRadius;
  /**
   * 该容器内child item的默认尺寸
   */
  private static final float RADIO_DEFAULT_CHILD_DIMENSION = 1f;
  /**
   * 菜单的中心child的默认尺寸
   */
  private float RADIO_DEFAULT_CENTERITEM_DIMENSION = 1 / 3f;
  /**
   * 该容器的内边距,无视padding属性,如需边距请用该变量
   */
  private static final float RADIO_PADDING_LAYOUT = 1 / 12f;
  /**
   * 该容器的内边距,无视padding属性,如需边距请用该变量
   */
  private float mPadding;
  /**
   * 布局时的开始角度
   */
  private double mStartAngle = 0;
  /**
   * 菜单项的文本
   */
  private String[] mItemTexts;
  /**
   * 菜单项的图标
   */
  private int[] mItemImgs;

  /**
   * 菜单的个数
   */
  private int mMenuItemCount;

  private int mMenuItemLayoutId = R.layout.circle_menu_item;

  public CircleMenuLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    // 无视padding
    setPadding(0, 0, 0, 0);
  }

  /**
   * 设置布局的宽高,并策略menu item宽高
   */
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    /**
     * 整个圆盘布局 的宽高
     */
    int resWidth = 0;
    int resHeight = 0;

    /**
     * 根据传入的参数,分别获取测量模式和测量值ֵ
     */
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);

    int height = MeasureSpec.getSize(heightMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    /**
     * 如果宽或者高的测量模式非精确值ֵ
     */
    if (widthMode != MeasureSpec.EXACTLY
        || heightMode != MeasureSpec.EXACTLY) {
      // 主要设置为背景图的高度
      resWidth = getSuggestedMinimumWidth();
      // 如果未设置背景图片,则设置为屏幕宽高的默认值ֵ
      resWidth = resWidth == 0 ? getDefaultWidth() : resWidth;

      resHeight = getSuggestedMinimumHeight();
      // 如果未设置背景图片,则设置为屏幕宽高的默认值ֵ
      resHeight = resHeight == 0 ? getDefaultWidth() : resHeight;
    } else {
      // 如果都设置为精确值,则直接取小值;
      resWidth = resHeight = Math.min(width, height);
    }

    Log.e("TAG", "resWidth = " + resWidth + ", resHeight = " + resHeight);

    setMeasuredDimension(resWidth, resHeight);

    // 获得半径
    mRadius = Math.max(getMeasuredWidth(), getMeasuredHeight());

    // menu item数量
    final int count = getChildCount();
    // menu item尺寸
    int childSize = (int) (mRadius * RADIO_DEFAULT_CHILD_DIMENSION);
    Log.e("TAG", "childSize = " + childSize);
    // menu item测量模式
    int childMode = MeasureSpec.EXACTLY;

    // 迭代测量
    for (int i = 0; i < count; i++) {
      final View child = getChildAt(i);

      if (child.getVisibility() == GONE) {
        continue;
      }

      // 计算menu item的尺寸;以及和设置好的模式,去对item进行测量
      int makeMeasureSpec = -1;

      if (child.getId() == R.id.id_circle_menu_item_center) {
        makeMeasureSpec = MeasureSpec.makeMeasureSpec(
            (int) (mRadius * RADIO_DEFAULT_CENTERITEM_DIMENSION),
            childMode);
      } else {
        makeMeasureSpec = MeasureSpec.makeMeasureSpec(childSize,
            childMode);
      }
      child.measure(makeMeasureSpec, makeMeasureSpec);
    }

    mPadding = RADIO_PADDING_LAYOUT * mRadius;

  }

  /**
   * MenuItem的点击事件接口
   *
   *
   */
  public interface OnMenuItemClickListener {
    void itemClick(View view, int pos);

    void itemCenterClick(View view);
  }

  /**
   * MenuItem的点击事件接口
   */
  private OnMenuItemClickListener mOnMenuItemClickListener;

  /**
   * 设置MenuItem的点击事件接口
   *
   * @param mOnMenuItemClickListener
   */
  public void setOnMenuItemClickListener(
      OnMenuItemClickListener mOnMenuItemClickListener) {
    this.mOnMenuItemClickListener = mOnMenuItemClickListener;
  }

  /**
   * 设置menu item的位置
   */
  @SuppressLint("NewApi")
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int layoutRadius = mRadius;
    Log.e("TAG", "layoutRadius = " + layoutRadius);

    // Laying out the child views
    final int childCount = getChildCount();

    int left, top;
    // menu item 的尺寸
    int cWidth = (int) (layoutRadius * RADIO_DEFAULT_CHILD_DIMENSION);

    // 根据menu item的个数,计算角度
    float angleDelay = 0;
    if ((getChildCount() - 1) != 0) {
      angleDelay = 360 / (getChildCount() - 1);
    }
    angleDelay = 30f;
    Log.e("TAG", "单个角度 angleDelay = " + angleDelay);

    // 遍历去设置menuitem的位置
    for (int i = 0; i < childCount; i++) {
      final View child = getChildAt(i);

      if (child.getId() == R.id.id_circle_menu_item_center)
        continue;

      if (child.getVisibility() == GONE) {
        continue;
      }

      mStartAngle %= 360;

      // 计算,中心点到menu item中心的距离--即图片中心位置到圆心的距离
      float tmp = layoutRadius / 2f - cWidth / 2 - mPadding;
      // 根据屏幕密度计算,基数为60(暂定60)
      tmp = LuckyUtil.getDensity() * 57;
      Log.e("TAG", "tmp = " + tmp);

      // tmp cosa 即menu item中心点的横坐标
      left = layoutRadius
          / 2
          + (int) Math.round(tmp
              * Math.cos(Math.toRadians(mStartAngle)) - 1 / 2f
              * cWidth);
      // tmp sina 即menu item的纵坐标
      top = layoutRadius
          / 2
          + (int) Math.round(tmp
              * Math.sin(Math.toRadians(mStartAngle)) - 1 / 2f
              * cWidth);

      child.layout(left, top, left + cWidth, top + cWidth);
      // 叠加尺寸
      mStartAngle += angleDelay;
    }

    // 找到中心的view,如果存在设置onclick事件
    View cView = findViewById(R.id.id_circle_menu_item_center);
    if (cView != null) {
      cView.setOnClickListener(new OnClickListener() {
        @SuppressLint("DrawAllocation")
        @Override
        public void onClick(View v) {

          if (mOnMenuItemClickListener != null) {
            mOnMenuItemClickListener.itemCenterClick(v);
          }
        }
      });
      // 设置center item位置
      int cl = layoutRadius / 2 - cView.getMeasuredWidth() / 2;
      int cr = cl + cView.getMeasuredWidth();
      cView.layout(cl, cl, cr, cr);
    }

  }

  /**
   * 主要为了action_down时,返回true
   */
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    return true;
  }

  /**
   * 设置菜单条目的图标和文本
   *
   * @param resIds
   */
  public void setMenuItemIconsAndTexts(int[] resIds, String[] texts) {
    mItemImgs = resIds;
    mItemTexts = texts;

    // 参数检查
    if (resIds == null && texts == null) {
      throw new IllegalArgumentException("菜单项文本和图片至少设置其一");
    }

    // 初始化mMenuCount
    mMenuItemCount = resIds == null ? texts.length : resIds.length;

    if (resIds != null && texts != null) {
      mMenuItemCount = Math.min(resIds.length, texts.length);
    }

    addMenuItems();

  }

  /**
   * 设置MenuItem的布局文件,必须在setMenuItemIconsAndTexts之前调用
   *
   * @param mMenuItemLayoutId
   */
  public void setMenuItemLayoutId(int mMenuItemLayoutId) {
    this.mMenuItemLayoutId = mMenuItemLayoutId;
  }

  /**
   * 添加菜单项
   */
  @SuppressLint("NewApi")
  private void addMenuItems() {
    LayoutInflater mInflater = LayoutInflater.from(getContext());

    /**
     * 根据用户设置的参数,初始化view
     */
    for (int i = 0; i < mMenuItemCount; i++) {
      final int j = i;
      View view = mInflater.inflate(mMenuItemLayoutId, this, false);
      ImageView iv = (ImageView) view
          .findViewById(R.id.id_circle_menu_item_image);

      if (iv != null) {
        iv.setVisibility(View.VISIBLE);
        iv.setImageResource(mItemImgs[i]);
        iv.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View v) {

            if (mOnMenuItemClickListener != null) {
              mOnMenuItemClickListener.itemClick(v, j);
            }
          }
        });
      }

      // 设置角度
      view.setRotation(90 + i * (360 / (mMenuItemCount)));

      Log.e("TAG", "旋转角度 = " + (i * (360 / mMenuItemCount)));

      // 添加view到容器中
      addView(view);
    }
  }

  /**
   * 获得默认该layout的尺寸
   *
   * @return
   */
  private int getDefaultWidth() {
    WindowManager wm = (WindowManager) getContext().getSystemService(
        Context.WINDOW_SERVICE);
    DisplayMetrics outMetrics = new DisplayMetrics();
    wm.getDefaultDisplay().getMetrics(outMetrics);
    return Math.min(outMetrics.widthPixels, outMetrics.heightPixels);
  }

}

(该自定义布局参考了网上一个建行demo的布局,具体的忘记了,请见谅。)

2、xml布局

 <com.zhcl.yqwan.lucky.rotation.circle.CircleMenuLayout
            android:id="@+id/id_menulayout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="@drawable/ratote_bg_two" >

            <RelativeLayout
              android:id="@+id/id_circle_menu_item_center"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:visibility="gone" >
            </RelativeLayout>
          </com.zhcl.yqwan.lucky.rotation.circle.CircleMenuLayout>

该处主要是自定义控件使用位置的布局。

3、activity中调用

/**
   * 设置属性或者星座的图片,并绘制图形
   *
   * @param def
   * @param img
   * @param selectedImg
   * @param str
   * @param type
   *      :区分星座和生肖,1--星座,2--生肖。
   */
  private void setRotateSelectedImg(final int[] def, final int[] img,
      final int[] selectedImg, String[] str, final int type) {
    mCircleMenuLayout.setMenuItemIconsAndTexts(def, str);

    mCircleMenuLayout
        .setOnMenuItemClickListener(new OnMenuItemClickListener() {

          @Override
          public void itemClick(View view, int pos) {
            // 如果是第一次进来,将之前默认选中的item改变为原来的颜色
            // 如果选中的index和默认的index不同,则将默认的设置为原来的颜色
            // Toast.makeText(LotteryRotationActivity.this,
            // "pos = " + pos, Toast.LENGTH_SHORT).show();

            // 选中的index
            selectedIndex = pos;

            if (pos != 0 && type == 1) {
              img[0] = mConstellationImgs[0];
            } else if (pos != 0 && type == 2) {
              img[0] = mAnimalImgs[0];
            }

            mCircleMenuLayout.removeAllViews();
            // mCircleMenuLayout = (CircleMenuLayout)
            // findViewById(R.id.id_menulayout);
            // 替换选中的图片
            replaceImg = img[pos];
            img[pos] = selectedImg[pos];
            // 设置图片
            mCircleMenuLayout.setMenuItemIconsAndTexts(img,
                mItemTexts);
            // 还原图片,方便下一次点击替换
            img[pos] = replaceImg;
          }

          @Override
          public void itemCenterClick(View view) {

          }
        });

  }

// 星座:最开始默认选择一个
    setRotateSelectedImg(mConstellationImgsDefult, mConstellationImgs,
        mConstellationImgsSelected, mAnimalStr, 1);

这里定义了一个setRotateSelectedImg的方法,方便使用,其中final int[] def, final int[] img,final int[] selectedImg, 是三个图片数组,分别是初始化时默认的选中状态的图片数组(有一个被选中)、全部未选中的图片数组、选中后的图片数组(不同于默认的图片数组),String[] str是字符串数组,由于文字已经在切图中给出,此处可忽略。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android实现抽奖转盘实例代码

    本文详述了android抽奖程序的实现方法,程序为一个抽奖大转盘代码,里面定义了很多图形方法和动画. 实现主要功能的SlyderView.java源代码如下: import android.app.Activity; import android.content.Context; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Color; import

  • Android自定义View实现QQ运动积分转盘抽奖功能

    因为偶尔关注QQ运动, 看到QQ运动的积分抽奖界面比较有意思,所以就尝试用自定义View实现了下,原本想通过开发者选项查看下界面的一些信息,后来发现积分抽奖界面是在WebView中展示的,应该是在H5页面中用js代码实现的,暂时不去管它了. 这里的自定义View针对的是继承自View的情况,你可以将Canvas想象为画板, Paint为画笔,自定义View的过程和在画板上用画笔作画其实类似,想象在画板上作画的过程,你要画一个多大图形(对应View的测量 onMeasure方法),你要画什么样的图

  • Android自定义view制作抽奖转盘

    本文实例为大家分享了Android自定义view制作抽奖转盘的具体代码,供大家参考,具体内容如下 效果图 TurntableActivity package com.bawei.myapplication.turntable; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; im

  • Android中利用SurfaceView制作抽奖转盘的全流程攻略

    一.概述 今天给大家带来SurfaceView的一个实战案例,话说自定义View也是各种写,一直没有写过SurfaceView,这个玩意是什么东西?什么时候用比较好呢? 可以看到SurfaceView也是继承了View,但是我们并不需要去实现它的draw方法来绘制自己,为什么呢? 因为它和View有一个很大的区别,View在UI线程去更新自己:而SurfaceView则在一个子线程中去更新自己:这也显示出了它的优势,当制作游戏等需要不断刷新View时,因为是在子线程,避免了对UI线程的阻塞. 知

  • 基于Android实现转盘按钮代码

    先给大家展示下效果图: package com.lixu.circlemenu; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.lixu.circlemenu.view.CircleImageView; import com.lixu.ci

  • Android使用surfaceView自定义抽奖大转盘

    使用surfaceView自定义抽奖大转盘 话不多说,先上效果图 完整代码地址欢迎start 实现思路以及过程 1.首先了解SurfaceView的基本用法,它跟一般的View不太一样,采用的双缓存机制,可以在子线程中绘制View,不会因为绘制耗时而失去流畅性,这也是选择使用SurfaceView去自定义这个抽奖大转盘的原因,毕竟绘制这个转盘的盘块,奖项的图片和文字以及转动都是靠绘制出来的,是一个比较耗时的绘制过程. 2.使用SurfaceView的一般模板样式 一般会用到的成员变量 priva

  • Android自定义View实现抽奖转盘

    本文实例为大家分享了Android自定义View实现抽奖转盘的具体代码,供大家参考,具体内容如下 public class LuckCircle extends SurfaceView implements SurfaceHolder.Callback,Runnable { private SurfaceHolder mHolder; private Canvas mCanvas; //用于绘制的线程 private Thread mThread; //线程开关的控制 private boole

  • Android实现可点击的幸运大转盘

    之前的项目有一个幸运大转盘的功能,在网上找了很久,都没有合适的方法. 这是效果图,实现目标:十二星座的图片可点击切换选中效果,根据选择不同的星座,实现不同的 方法.之前网上的都是带有指针的,或者可点击改变效果,但是并不知道选择的到底是哪个,即虚拟选择. 实现该功能的主要代码如下: 1.自定义一个布局,存放图片,实现圆形布局. /** * * * CircleMenuLayout.java * * @author wuxiaosu * */ public class CircleMenuLayou

  • 微信小程序利用canvas 绘制幸运大转盘功能

    小程序对 canvas api 跟h5的不太一致 ,所以这个搞的比较久,不多说,先贴代码 Page({ /** * 页面的初始数据 */ data: { awardsConfig: {}, restaraunts: [], //大转盘奖品信息 }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { var self = this; wx.getSystemInfo({ //获取系统信息成功,将系统窗口的宽高赋给页面的宽高 success:

  • 微信小程序实现幸运大转盘功能的示例代码

    目录 一.项目展示 二.抽奖页 三.领奖页 一.项目展示 幸运大转盘是一个简单的抽奖小程序 参与用户点击抽奖便可抽取轮盘的奖品 二.抽奖页 抽奖页是一个大轮盘和活动规则 页面形式简单 主要核心在于轮盘 核心代码[轮盘旋转]如下: getLottery: function () { var that = this var awardIndex = Math.random() * 6 >>> 0; // 获取奖品配置 var awardsConfig = app.awardsConfig,

  • Vue 幸运大转盘实现思路详解

    转盘抽奖主要有两种,指针转动和转盘转动,个人觉得转盘转动比较好看点,指针转动看着头晕,转盘转动时指针是在转盘的中间位置,这里要用到css的transform属性和transition属性,这两个因为不常用最好是上网查查,用法和功能.   在html部分 <div id="wheel_surf"> <div class="wheel_surf_title">幸运大转盘</div> <div class="lucky-

  • pygame可视化幸运大转盘实现

    继续分享pygame有趣的技术知识,欢迎往下看. 一.先搭个架子 (一)黏贴背景图: 实现代码如下: import pygame pygame.init() # 初始化pygame类 screen = pygame.display.set_mode((600, 600)) # 设置窗口大小 pygame.display.set_caption('幸运大转盘') # 设置窗口标题 tick = pygame.time.Clock() fps = 10 # 设置刷新率,数字越大刷新率越高 # 方法一

  • Android Studio EditText点击图标清除文本内容的实例解析

    这篇文章是继自定义EditText样式之后的功能强化,对于实际应用项目有很大的参考意见,感兴趣的朋友可以移步上一篇,"Android Studion自定义EditText样式".具体清除EditText文本内容功能代码如下: package com.liheng; import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; import

  • Android悬浮按钮点击返回顶部FloatingActionButton

    先看一下Android悬浮按钮点击回到顶部的效果: FloatingActionButton是Design Support库中提供的一个控件,这个控件可以轻松实现悬浮按钮的效果 首先,要在项目中使用这个悬浮按钮就要先把design这个包导入项目 gradle中加入依赖 compile 'com.android.support:design:25.0.0' 接下来就是在xml中使用: 我这里是放置一个listView模拟返回顶部 <?xml version="1.0" encodi

  • Android 美食大转盘详解流程

    目录 效果视频 前言 美食大转盘 初始化SurfaceView 测量 绘制 绘制盘块 开始旋转转盘 停止旋转转盘 自定义转盘等份 控件引用 沉浸式体验 效果图 Reveal Animator 效果视频 自定义转盘代码 XML布局代码 Activity代码 代码下载地址 效果视频 前言 你还在为明天吃什么而烦恼嘛 美食大赏帮你解决选择困难症 帮你做出最佳的选择 做吃货,我们是认真的 美食大转盘 本示例使用SurfaceView绘制而成,接下来逐步分析, 文末会贴出全部代码``文末会贴出全部代码``

  • 手机移动端实现 jquery和HTML5 Canvas的幸运大奖盘特效

    HTML5 Canvas的幸运大奖盘特效 现在好的微信微商或者微信公众号都有这种大转盘的项目,这里就整理一个,可以参考下. 这是一款基于jquery和HTML5 Canvas的幸运大奖盘特效.该幸运大奖品特效支持移动端,它通过动态构造Canvas元素来生成大奖盘,并通过jquery代码来随机获取奖品. 使用方法 HTML结构 抽奖用的大转盘使用图片来制作,开始时它们被隐藏.整个抽奖大奖盘放置在一个容器中,控制容器的大小即可以控制奖盘的大小. XML/HTML代码 <div class="c

  • Android开发新手必须知道的10大严重错误

    本文总结了Android开发新手必须知道的10大严重错误.分享给大家供大家参考,具体如下: 作为过去10年广泛涉猎各热门平台的经验丰富手机应用开发商,我们觉得Android平台对新手开发者来说是最通俗易懂的平台.平价的工具.友好的开发社区.及众所周知的编程语言(Java),可以说开发Android应用不是难事.但尽管如此,我们依然发现有些错误反复出现在刚涉足Android平台的新手开发者身上,下面是他们常犯的10大严重错误.希望新手朋友们能够借鉴,避免误区. 1. 未阅读Android文件 An

随机推荐