欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-12-29 13:59:58
之前的项目有一个幸运大转盘的功能,在网上找了很久,都没有合适的方法。 这是效果图,实现目标:十二星座的图片可点击切换选中效果,根据选择不同的星座,实现不同的...

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

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

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是字符串数组,由于文字已经在切图中给出,此处可忽略。

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

上一篇:

下一篇: