android自定义ViewGroup卫星导航菜单
来源:互联网 发布:ubuntu u盘 编辑:程序博客网 时间:2024/04/30 12:29
卫星导航菜单看起来还是很酷的,实现起来也不是很难,网上也已经有很多这样的demo,而且封装的也特别好。作为一个技术gou,不能只是一味的用现成的轮子,向大神学习,自己造轮子。今天菜鸟也来尝试一下,从头到尾来实现一吧这高端的ui效果。。
老规矩,上图上代码:
效果还可以吧,也许有的人会认为有点low,但是我想说的是晚上关上灯效果是一样的,重点是内涵。。。
<?xml version="1.0" encoding="utf-8"?><resources> <attr name="radius" format="dimension" /> <attr name="CustomLayout"> <enum name="left_top" value="0" /> <enum name="right_top" value="1" /> <enum name="right_bottom" value="2" /> <enum name="left_bottom" value="3" /> <enum name="bottom_center" value="4" /> </attr> <attr name="MenuStats"> <enum name="open" value="0" /> <enum name="close" value="1" /> </attr> <declare-styleable name="PlanetViewGroup"> <attr name="radius" /> <attr name="CustomLayout" /> <attr name="MenuStats" /> </declare-styleable></resources>
用到了enum,主要就是指定用户选择的范围,不让用户乱搞。。。
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.apple.Custom.PlanetViewGroup android:id="@+id/planet_top" android:layout_width="match_parent" android:layout_height="match_parent" app:CustomLayout="bottom_center" app:MenuStats="open" app:radius="130dp"> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_red_light" android:src="@mipmap/ic_launcher" android:text="1" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_blue_dark" android:src="@mipmap/ic_launcher" android:text="2" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_green_light" android:src="@mipmap/ic_launcher" android:text="3" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_orange_dark" android:src="@mipmap/ic_launcher" android:text="4" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_blue_light" android:src="@mipmap/ic_launcher" android:text="5" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_orange_dark" android:src="@mipmap/ic_launcher" android:text="6" /> <TextView android:layout_width="30dp" android:layout_height="30dp" android:background="@android:color/holo_red_dark" android:src="@mipmap/ic_launcher" android:text="C" /> </com.example.apple.Custom.PlanetViewGroup> <com.example.apple.Custom.PlanetViewGroup android:id="@+id/planet_bottem" android:layout_width="match_parent" android:layout_height="match_parent" app:CustomLayout="left_top" app:MenuStats="open" app:radius="130dp"> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_red_light" android:src="@mipmap/ic_launcher" android:text="1" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_blue_dark" android:src="@mipmap/ic_launcher" android:text="2" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_green_light" android:src="@mipmap/ic_launcher" android:text="3" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_orange_dark" android:src="@mipmap/ic_launcher" android:text="4" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_blue_light" android:src="@mipmap/ic_launcher" android:text="5" /> <TextView android:layout_width="20dp" android:layout_height="20dp" android:background="@android:color/holo_orange_dark" android:src="@mipmap/ic_launcher" android:text="6" /> <TextView android:layout_width="30dp" android:layout_height="30dp" android:background="@android:color/holo_red_dark" android:src="@mipmap/ic_launcher" android:text="C" /> </com.example.apple.Custom.PlanetViewGroup></RelativeLayout>
使用起来也是比较简单的,可以在布局里面指定菜单的位置,一般就四个角,和底部的中间位置,这里都是支持的 。还可以指定,子菜单是关闭还是显示,open close.
package com.example.apple.Custom;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;import android.view.animation.OvershootInterpolator;import com.example.apple.pullzoom.R;import com.nineoldandroids.animation.Animator;import com.nineoldandroids.animation.AnimatorSet;import com.nineoldandroids.animation.ObjectAnimator;/** * Created by apple on 17/9/21. */public class PlanetViewGroup extends ViewGroup implements View.OnClickListener { final String TAG = this.getClass().getSimpleName(); CustomLayout mlayout = CustomLayout.left_top; MenuStats menuStats = MenuStats.open; private int count = 0; View centerView; MenuOnClickListener lis; int cl, ct, cr, cb, childWidth, childHeight; /** * 半径 */ private int radius = 100; private double angle; public enum CustomLayout { left_top, right_top, right_bottom, left_bottom, bottom_center } public enum MenuStats { open, close; } int measuredHeight, measuredWidth; public PlanetViewGroup(Context context) { this(context, null); } public PlanetViewGroup(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PlanetViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(),attrs); } private void init(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PlanetViewGroup); int indexCount = typedArray.getIndexCount(); for (int i = 0; i < indexCount; i++) { int index = typedArray.getIndex(i); switch (index) { case R.styleable.PlanetViewGroup_radius: radius = typedArray.getDimensionPixelSize(index, 50); break; case R.styleable.PlanetViewGroup_MenuStats: int anInt = typedArray.getInt(index, 0); switch (anInt) { case 0: menuStats = MenuStats.open; break; case 1: menuStats = MenuStats.close; break; } break; case R.styleable.PlanetViewGroup_CustomLayout: anInt = typedArray.getInt(index, 0); switch (anInt) { case 0: mlayout = CustomLayout.left_top; break; case 1: mlayout = CustomLayout.right_top; break; case 2: mlayout = CustomLayout.right_bottom; break; case 3: mlayout = CustomLayout.left_bottom; break; case 4: mlayout = CustomLayout.bottom_center; break; } break; } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec); } measuredHeight = getMeasuredHeight(); measuredWidth = getMeasuredWidth(); angle = Math.PI / (2 * (count - 2)); radius = Math.min(radius, measuredWidth / 2); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (!changed) return; if (count < 3) return;//里面控件太少,菜单显示没有意思 //最后一个view作为主控件view centerView = getChildAt(count - 1); controllerView(); subViewLayout(false); } /** * 计算子view坐标 * * @param showAnim */ private void subViewLayout(boolean showAnim) { View child; for (int i = 0; i < count - 1; i++) { child = getChildAt(i); if (!showAnim) { if (menuStats == MenuStats.close) { child.setVisibility(GONE); } else { child.setVisibility(VISIBLE); } } final int index = i + 1; if (lis != null) { child.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (lis != null) { lis.menuItem(index); } menuStats = MenuStats.close; viewVisibleStats(GONE); alphaAndScale(getChildAt(index - 1)); } }); } childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); if (mlayout == CustomLayout.left_top) { cl = (int) (radius * Math.sin((i) * angle)); ct = (int) (radius * Math.cos((i) * angle)); } else if (mlayout == CustomLayout.right_top) { cl = measuredWidth - childWidth - (int) (radius * Math.sin((i) * angle)); ct = (int) (radius * Math.cos((i) * angle)); } else if (mlayout == CustomLayout.right_bottom) { cl = measuredWidth - childWidth - (int) (radius * Math.sin((i) * angle)); ct = measuredHeight - childHeight - (int) (radius * Math.cos((i) * angle)); } else if (mlayout == CustomLayout.left_bottom) { cl = (int) (radius * Math.sin((i) * angle)); ct = measuredHeight - childHeight - (int) (radius * Math.cos((i) * angle)); } else if (mlayout == CustomLayout.bottom_center) { cl = (measuredWidth - childWidth) / 2 + (int) (radius * Math.cos((i) * (Math.PI / (count - 2)))); ct = measuredHeight - childHeight - (int) (radius * Math.sin((i) * (Math.PI / (count - 2)))); } if (showAnim) { if (menuStats == MenuStats.close) { hidePlanetMenu(child, index); } else { showPlanetMenu(child, index); } } cr = cl + childWidth; cb = ct + childHeight; child.layout(cl, ct, cr, cb); } } /** * 对view缩放和透明度变化 * * @param child */ private void alphaAndScale(final View child) { child.setVisibility(VISIBLE); ObjectAnimator alpha = ObjectAnimator.ofFloat(child, "alpha", 1, 0); ObjectAnimator scaleX = ObjectAnimator.ofFloat(child, "scaleX", 1, 2); ObjectAnimator scaleY = ObjectAnimator.ofFloat(child, "scaleY", 1, 2); AnimatorSet set = new AnimatorSet(); set.setDuration(500); set.play(alpha).with(scaleX).with(scaleY); set.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { child.setScaleX(1); child.setScaleY(1); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); set.start(); } /*** * 子菜单动画 */ private void showPlanetMenu(View child, int index) { ObjectAnimator translationY = ObjectAnimator.ofFloat(child, "y", centerView.getTop(), ct); ObjectAnimator translationX = ObjectAnimator.ofFloat(child, "x", centerView.getLeft(), cl); ObjectAnimator rotation = ObjectAnimator.ofFloat(child, "rotation", 0, 360); translationY.setInterpolator(new OvershootInterpolator()); translationX.setInterpolator(new OvershootInterpolator()); AnimatorSet set = new AnimatorSet(); set.playTogether(translationY, translationX, rotation); set.setDuration(300 + index * 100); set.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { viewVisibleStats(VISIBLE); } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); set.start(); } /*** * 子菜单动画 */ private void hidePlanetMenu(View child, int index) { ObjectAnimator translationY = ObjectAnimator.ofFloat(child, "y", ct, centerView.getTop()); ObjectAnimator translationX = ObjectAnimator.ofFloat(child, "x", cl, centerView.getLeft()); ObjectAnimator rotation = ObjectAnimator.ofFloat(child, "rotation", 0, 360); AnimatorSet set = new AnimatorSet(); set.setDuration(300 + index * 100); set.setInterpolator(new OvershootInterpolator()); set.playTogether(translationY, translationX, rotation); set.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { viewVisibleStats(GONE); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); set.start(); } /** * 主菜单控制按钮 */ private void controllerView() { MarginLayoutParams marginLayoutParams = null; LayoutParams layoutParams = centerView.getLayoutParams(); if(layoutParams instanceof MarginLayoutParams){ marginLayoutParams = (MarginLayoutParams)layoutParams; } childWidth = centerView.getMeasuredWidth(); childHeight = centerView.getMeasuredHeight(); if (mlayout == CustomLayout.left_top) { cl = 0; ct = 0; cr = childWidth; cb = childHeight; } else if (mlayout == CustomLayout.right_top) { cl = measuredWidth - childWidth; ct = 0; cr = measuredWidth; cb = childHeight; } else if (mlayout == CustomLayout.right_bottom) { cl = measuredWidth - childWidth; ct = measuredHeight - childHeight; cr = measuredWidth; cb = measuredHeight; } else if (mlayout == CustomLayout.left_bottom) { cl = 0; ct = measuredHeight - childHeight; cr = childWidth; cb = measuredHeight; } else if (mlayout == CustomLayout.bottom_center) { cl = (measuredWidth - childWidth) / 2; ct = measuredHeight - childHeight; cr = cl + childWidth; cb = measuredHeight; } if(marginLayoutParams == null){ centerView.layout(cl, ct , cr , cb ); }else{ centerView.layout(cl + marginLayoutParams.leftMargin, ct + marginLayoutParams.topMargin, cr + marginLayoutParams.rightMargin, cb + marginLayoutParams.bottomMargin); } centerView.setOnClickListener(this); } @Override public void onClick(View v) { ObjectAnimator rotation = ObjectAnimator.ofFloat(v, "rotation", 0, 360).setDuration(500); rotation.setDuration(500); rotation.start(); if (menuStats == MenuStats.open) { menuStats = MenuStats.close; subViewLayout(true); } else { menuStats = MenuStats.open; subViewLayout(true); } } /** * 子view是否可见 * * @param visible */ private void viewVisibleStats(int visible) { for (int i = 0; i < count - 1; i++) { getChildAt(i).setVisibility(visible); getChildAt(i).setAlpha(1); } } public void setMenuOnClickListener(MenuOnClickListener lis) { this.lis = lis; } public interface MenuOnClickListener { void menuItem(int pos); }}
全部实现代码就一个类,而且也不多,说明还是简单呀。坐标计算采用的是三角函数,计算子view的x,y。动画用的属性动画,大伙儿都知道属性动画最好用,因为它可以直接修改view的属性,影响最深远的就是他的点击事件,会随着view的位置而改变。这里MarginLayoutParams进行了处理,之view要使用MarginLayoutParams,就必须在父控件里面重写generateLayoutParams
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(),attrs);
}
不然会报错。。
((PlanetViewGroup) findViewById(R.id.planet_bottem)).setMenuOnClickListener(new PlanetViewGroup.MenuOnClickListener() { @Override public void menuItem(int pos) { Toast.makeText(getApplication(), "click" + pos, Toast.LENGTH_SHORT).show(); } });
actvivity粘贴上面这段,ok,就完了,是不是很简单呀,粘贴复制就能出效果,如果跑不起来可以来打我。。。。
- android自定义ViewGroup卫星导航菜单
- 自定义ViewGroup之卫星菜单
- Android自定义卫星弧度菜单
- 自定义ViewGroup——卫星式菜单的实现
- android之类似卫星菜单,来自定义ViewGroup。。。。。
- Android 自定义卫星式弧形菜单
- Android 实现自定义的卫星式菜单
- Android 自定义卫星式弧形菜单
- android自定义viewgroup初步之一----抽屉菜单
- android自定义ViewGroup(侧滑菜单)
- 自定义旋转卫星菜单
- 自定义卫星菜单CustomArcMenu
- Android 卫星弹出式界面(ViewGroup)
- Android 实现卫星导航
- Android自定义VIew之实现卫星菜单效果
- Android卫星菜单
- Android自定义控件6----继承ViewGroup自定义侧滑菜单
- android 自定义viewGroup实现网易左侧菜单效果
- Java 9 新特性之模块化和进程API
- 智慧城市建设概述
- [设计模式]职责链模式
- Redis系列-7.有序集合(zset)结构
- X86 指令速查
- android自定义ViewGroup卫星导航菜单
- dp
- .NET Conf 2017后初尝Xamarin Forms 3.0@Linux
- 列表组件的运用
- 9-24NOIP模拟赛总结
- RMI不支持远程注册(绑定)
- 如何屏蔽RecyclerView单边滑动到头阴影(fadingEdge)
- 反向传播手记
- 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 M. Frequent Subsets Problem