自定义组件之规则分布按钮的菜单

北城以北 提交于 2020-03-16 14:21:26

某厂面试归来,发现自己落伍了!>>>

废话不多说,先上效果图(下了几个屏幕录制软件,效果不是很好,只能截取部分图片了,各位有什么好用的推荐下):

  


  

      图中间的圆形布局菜单是一个继承ViewGroup的组件,可以动态添加外围菜单按钮个数,会自动围绕中间按钮形成一个圆圈。不过外围的按钮不要太多,不然会重叠,而且太多也不好看。

      主要思路就是重写ViewGroup的onMeasure()和onLayout()这两个方法。其中onMeasure()用来计算组件所需的区域大小,可以根据实际需要进行计算。onLayout()方法中根据需求摆放你的childView。

      接下来,重点讲下如何计算组件的大小。首先,组件区域肯定是一块矩形区域,本组件刚好是一个正方形。所有外围childView围成的圆是这个正方形区域的内接圆。假设这个正方形区域的宽和高分别为Width和Height,中心圆的半径为centerR,外围圆的半径为outsideR,中心圆和外围圆之间间隔Dur,以外围childView围成的圆半径为R。那么

      Width = Height = (centerR/2 + outsideR + Dur)*2; R = centerR/2 + outsideR/2 + Dur;

正方形区域的算法就这么简单。但是,在onMeasure()方法中,有一个东东一定要会用,那就是MeasureSpec。关于MeasureSpec的资料已经很多了,大家可以自己去查阅资料(这里推荐一个http://blog.csdn.net/failure01/article/details/8577013)。它可以理解为用来合成或者分解组件的大小Size和模式Mode的一个工具类。另外,大家在获取childView大小的时候,一定要先用measureChild方法测量下它们的大小。

      测量组件大小讲完了,再来讲讲childView的摆放问题。onLayout(l,t,r,b)有4个参数,分别表示该组件在屏幕上的左,上,右,下四个位置。而方法中的childView摆放要调用的layout(l,t,r,b)这4个参数,分别表示该childView在组件中的左,上,右,下四个位置。对于本组件,中心圆的位置当然是处于正中间。圆心位置为centerX = centerY = Width/2; 那么

l = centerX - centerR, t = centerY - centerR,  r = centerX + centerR, b = centerY + centerR;

再来说说外围childView的位置摆放。这是一个本组件最麻烦的环节了。因为要根据childView的个数来确定每一个childView的位置。假设两个childView与中心圆的夹角为θ,我们将其中一个childView作为基准(选取顶部的childView),其余childView依次根据偏移角度来计算位置。顶部childView的圆心位置为(centerX - R , centerY - R)。那么,顺时针第一个childView圆心位置为(centerX + R*sinθ,centerY - R*sinθ),第二个childView的圆心位置为(centerX + R*sinλθ,centerY - R*sinλθ)...,有了圆心位置,那左,上,右,下的位置都是顺理成章了,哈哈。

说了一大堆有点抽象,贴下代码:

@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);
		
		int max_outsideWidth = 0;
		int max_outsideHeight = 0;
		
		System.out.println("widthSize: " + widthSize + ", heightSize: " + heightSize + ", widthMode: " + widthMode + ", heightMode: " + heightMode);
		
		for(int i=0; i<getChildCount(); i++){
			View child = getChildAt(i);
			Object tag = child.getTag();
			if(tag != null && centerTag.equals(tag)){
				View home = getChildAt(0);
				measureChild(home, widthMeasureSpec, heightMeasureSpec);
				centerWidth = home.getMeasuredWidth();
				centerHeight = home.getMeasuredHeight();
				//System.out.println("centerWidth: " + centerWidth + ", centerHeight: " + centerHeight);
			}else {
				measureChild(child, widthMeasureSpec, heightMeasureSpec);
				outsideWidth = child.getMeasuredWidth();
				outsideHeight = child.getMeasuredHeight();
				
				max_outsideWidth = Math.max(max_outsideWidth, outsideWidth);
				max_outsideHeight = Math.max(max_outsideHeight, outsideHeight);
				
				//System.out.println("outsideWidth"+i+": " + max_outsideWidth + ", outsideHeight"+i+": " + max_outsideWidth);
			}
		}
		
		Width = (centerWidth/2 + max_outsideWidth + dis_bt)*2 + space;
		Height = (centerHeight/2 + max_outsideHeight + dis_bt)*2 + space;
		Radius = centerWidth/2 + max_outsideWidth/2 + dis_bt;
		centerX = Width/2;
		centerY = Height/2;
		System.out.println("width: " + Width + " , height: " + Height + " , centerX: " + centerX + " , centerY: " + centerY);
		super.onMeasure(MeasureSpec.makeMeasureSpec(Width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(Height, MeasureSpec.EXACTLY));
		//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}
@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		if(changed){
			System.out.println("position is l: " + l + ", t: " + t + ", r: " + r + ", b: " + b);
			int centerW = 0,centerH = 0;
			int count = getChildCount();
			int outsideCount = count - 1;
			double ang = 2*Math.PI/outsideCount;
			int outsideIndex = 0;
			for(int i=0; i<count; i++){
				View child = getChildAt(i);
				Object tag = child.getTag();
				if(tag != null && tag.equals(centerTag)){
					centerW = child.getMeasuredWidth();
					centerH = child.getMeasuredHeight();
					System.out.println("center mw: " + centerW + ", mh: " + centerH);
					int cl = centerX - centerW/2;
					int ct = centerY - centerH/2;
					int cr = centerX + centerW/2;
					int cb = centerY + centerH/2;
					child.layout(cl, ct, cr, cb);
				}else {
					int submw = child.getMeasuredWidth();
					int submh = child.getMeasuredHeight();
					double sin = Math.sin(outsideIndex*ang);
					double cos = Math.cos(outsideIndex*ang);
					System.out.println(outsideIndex + " ang: " + ang + " , sin: " + sin + ", cos: " + cos);
					int x = (int) (centerX + Radius*sin);
					int y = (int) (centerY - Radius*cos);
					System.out.println(outsideIndex + "  x: " + x + ", y: " + y);
					outsideIndex++;
					child.layout(x-submw/2, y-submh/2, x+submw/2, y+submh/2);
				}
			}
		}
	}
最后再奉上源码,第一次写博文,写的不好,条理有些乱,还请大神们指点一二。
http://download.csdn.net/detail/hatah0126/6533225
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!