废话不多说,先上效果图(下了几个屏幕录制软件,效果不是很好,只能截取部分图片了,各位有什么好用的推荐下):
图中间的圆形布局菜单是一个继承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
来源:oschina
链接:https://my.oschina.net/u/990810/blog/175118