Android实现可点击的幸运大转盘
程序员文章站
2023-12-29 13:59:58
之前的项目有一个幸运大转盘的功能,在网上找了很久,都没有合适的方法。
这是效果图,实现目标:十二星座的图片可点击切换选中效果,根据选择不同的星座,实现不同的...
之前的项目有一个幸运大转盘的功能,在网上找了很久,都没有合适的方法。
这是效果图,实现目标:十二星座的图片可点击切换选中效果,根据选择不同的星座,实现不同的 方法。之前网上的都是带有指针的,或者可点击改变效果,但是并不知道选择的到底是哪个,即虚拟选择。
实现该功能的主要代码如下:
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是字符串数组,由于文字已经在切图中给出,此处可忽略。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。