可旋转的圆形菜单栏
一个圆形的可旋转的菜单栏。功能:可动态添加菜单view,菜单view随着手势的旋转而旋转,菜单view可点击。
自定义一个RelativeLayout,添加的菜单view被放置在一个圆形轨迹中,随着手势旋转而旋转。

代码:动态添加菜单view
private List<View> list = new ArrayList<>();
public void initView() {
int size = 5;
for (int i=0;i<size;i++){
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.test,null);
TextView textView = (TextView) view.findViewById(R.id.textview);
textView.setText(i+"");
list.add(view);
}
myView.updateView(list);
}
更新view并把view放在一个圆形轨迹中,通过三角函数公式,计算出x,y的坐标,通过属性动画放置菜单view
public void updateView(List<View> list){
if(list==null||list.size()==0){
return;
}
this.list = list;
int i = 0;
for(View childView : list){
addView(childView);
childView.setTag(i++);
childView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext,v.getTag()+"...",Toast.LENGTH_SHORT).show();
}
});
}
requestLayout();
instance.post(new Runnable() {
@Override
public void run() {
animation(instance.list);
}
});
}
public void animation(List<View> views) {
for (int i = 0; i < views.size(); i++) {
int angle = (wAngle / (views.size()+1)) * (i + 1);
if(wAngle == 180){
angle = (wAngle / (views.size()+1)) * (i + 1);
}else if(wAngle == 360){
angle = (wAngle / (views.size())) * (i + 1);
}
Point point = caclulatePoint(angle);
ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(views.get(i), "translationX", 0.0f, point.x).setDuration(0);
ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(views.get(i), "translationY", 0.0f, -point.y).setDuration(0);
AnimatorSet set = new AnimatorSet();
//set.setInterpolator(new OvershootInterpolator());
set.playTogether(objectAnimatorX, objectAnimatorY);
set.start();
}
}
public Point caclulatePoint(int angle) {
Point point = new Point();
double b = Math.toRadians(angle);
double cos = Math.cos(b);
double sin = Math.sin(b);
double x = raduis * sin;
double y = raduis * cos;
point.set((int) x, (int) y);
//Log.e("values", "values:" + x + "---" + y + "---" + angle);
return point;
}
初始菜单view的位置
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
for (int i = 0;i<getChildCount();i++){
View view = instance.getChildAt(i);
int cLeft = getMeasuredWidth()/2-view.getMeasuredWidth()/2;
int cTop = getMeasuredHeight()/2-view.getMeasuredHeight()/2;
int cRight = cLeft+view.getMeasuredWidth();
int cBottom = cTop+view.getMeasuredHeight();
Log.e("cLeft","cLeft:"+cLeft+"........."+cTop+"........."+cRight+"........"+cBottom);
view.layout(cLeft,cTop,cRight,cBottom);
}
}
根据菜单view的大小和旋转的角度(360/菜单view数量)计算出最小旋转半径和最小父view的宽度和高度,这样的好处是当菜单view多的时候,旋转半径会加大,保证菜单view不会重叠在一起;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = this.getChildCount();
if(count==0){
return;
}
Log.e("count","count:"+count);
for (int i = 0; i < count; i++) {
View view = getChildAt(i);
measureChild(view, widthMeasureSpec, heightMeasureSpec);
}
View childView = null;
int childH = 0;
int childW = 0;
if(count>0){
childView = getChildAt(0);
childH = childView.getMeasuredHeight();
childW = childView.getMeasuredWidth();
}
//最小弦长
double minL = Math.sqrt(childW*childW+childH*childH);
int angle = wAngle /count;
//最小半径 目的是动态设置布局的宽高,以防止子view重叠在一起
double minR = minL/(2*Math.sin(Math.toRadians(angle)))+minL/2;
raduis = minR;
Log.e("minR","minR:"+minR);
//最小宽高;
int parentW = (int)(minR+childH/2)*2;
if(appWidthHeight<parentW){
appWidthHeight = parentW;
}
setMeasuredDimension((int)appWidthHeight,(int)appWidthHeight);
}
根据手势计算出旋转的角度(余弦定理)通过setRotation方法可实现旋转,当父view旋转的时候,子view在线程里自转达到菜单view的旋转
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_DOWN) {
oriLeft = v.getLeft();
oriRight = v.getRight();
oriTop = v.getTop();
oriBottom = v.getBottom();
lastY = (int) event.getRawY();
lastX = (int) event.getRawX();
oriRotation = v.getRotation();
Log.d(TAG, "ACTION_DOWN: " + oriRotation);
}
delDrag(v, event, action);
invalidate();
return false;
}
/**
* 处理拖动事件
*
* @param v
* @param event
* @param action
*/
protected void delDrag(View v, MotionEvent event, int action) {
switch (action) {
case MotionEvent.ACTION_MOVE:
int dx = (int) event.getRawX() - lastX;
int dy = (int) event.getRawY() - lastY;
Point center = new Point(oriLeft + (oriRight - oriLeft) / 2, oriTop + (oriBottom - oriTop) / 2);
Point first = new Point(lastX, lastY);
Point second = new Point((int) event.getRawX(), (int) event.getRawY());
oriRotation += angle(center, first, second);
Log.e("oriRotation","oriRotation:"+oriRotation);
if(Float.isNaN(oriRotation)){
return;
}
v.setRotation(oriRotation);
//View view = instance.getChildAt(getChildCount()-1);
//view.setRotation(-oriRotation);
instance.post(new ViewRunnables());
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
/*float endRotation = instance.getRotation();
int angle = (wAngle / (getChildCount()+1));
if(endRotation<(angle-wAngle)||endRotation>(wAngle-angle)){
//oriRotation = 0;
ObjectAnimator animator = ObjectAnimator.ofFloat(instance,"rotation",endRotation,endRotation,0);
animator.setDuration(300).start();
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
for (int i = 0;i<getChildCount();i++){
if(i!=(getChildCount()-1)){
}
View view = instance.getChildAt(i);
view.setRotation(0);
}
}
});
}*/
//instance.post(new ViewRunnables());
break;
}
}
public class ViewRunnables implements Runnable {
@Override
public void run() {
for (int i = 0;i<getChildCount();i++){
if(i!=(getChildCount()-1)){
}
View view = instance.getChildAt(i);
view.setRotation(-oriRotation);
}
}
}
滑动时菜单view不可点击,静止时可点击
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
return true;
case MotionEvent.ACTION_DOWN:
oriLeft = instance.getLeft();
oriRight = instance.getRight();
oriTop = instance.getTop();
oriBottom = instance.getBottom();
lastY = (int) ev.getRawY();
lastX = (int) ev.getRawX();
oriRotation = instance.getRotation();
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev);
}
本文介绍了一个可旋转的圆形菜单栏设计,菜单项能动态添加,并且随着手势旋转。菜单视图在圆形轨迹中分布,利用三角函数计算位置,并通过属性动画实现旋转效果。在旋转过程中,菜单项不可点击,静止时则可点击。同时提供了示例代码和demo地址。
332

被折叠的 条评论
为什么被折叠?



