Android 圆盘旋转/飞转菜单(高度定制化)

2023-10-22 09:59

本文主要是介绍Android 圆盘旋转/飞转菜单(高度定制化),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


看了建行的圆盘菜单,效果还不错,于是也动手试试做一个,目标——高度定制化,数量、样式及动画。

为什么要用适配器做成定制化?你知道的,UI那边总是动不动就改的,加点什么啊,删点什么啊,而且有多态机器需要适配,底色不一样就算了,数量和Item也不一样,怎么搞啊?我们总不能每一次都改一大串吧,改一大串和重复类似工作对我们来说简直就是折磨,所以,需要定制化。当然,如果第二次就直接淘汰圆盘了,那另当别论,

工程代码:https://github.com/aknew123/CircleMenu  点击打开链接

效果如下:

                             


程序架构的UML图,如下:



一、圆盘菜单自定义控件的使用

网上查看了一下,看一下他们的实现方式千篇一律,功能都写在一个文件里,阅读难度稍大,于是采用适配器模式做一个,把view和实现逻辑分离,就是简单的MVC,代码结构如下图:


当然,这只是一个Library,把DefaultMenuAdapter删了,重新编译就可以直接用jar包了,测试模块写在另一个工程,使用示例如下:


package com.example.circlemenutest;import java.util.ArrayList;
import java.util.List;import android.animation.ObjectAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.Toast;import com.pan.tanglang.circlemenu.model.CircleMenuStatus;
import com.pan.tanglang.circlemenu.view.CircleMenu;
import com.pan.tanglang.circlemenu.view.CircleMenu.OnMenuItemClickListener;
import com.pan.tanglang.circlemenu.view.CircleMenu.OnMenuStatusChangedListener;public class MainActivity extends Activity {public static final String TAG = "MainActivity";private String[] mItemTexts = new String[] { "安全中心", "特殊服务", "投资理财", "转账汇款", "我的账户", "信用卡", "腾讯", "阿里", "百度" };private int[] mItemImgs = new int[] { R.drawable.foreign01, R.drawable.foreign02, R.drawable.foreign03,R.drawable.foreign04, R.drawable.foreign05, R.drawable.foreign06, R.drawable.foreign07,R.drawable.foreign08, R.drawable.foreign09 };private CircleMenu mCircleMenu;private ImageView ivCenter;private float startRotate;private float startFling;ObjectAnimator animRotate = null;ObjectAnimator animFling = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mCircleMenu = (CircleMenu) findViewById(R.id.cm_main);ivCenter = (ImageView) findViewById(R.id.iv_center_main);ivCenter.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(MainActivity.this, "圆盘中心", Toast.LENGTH_SHORT).show();}});mCircleMenu.setOnItemClickListener(new OnMenuItemClickListener() {@Overridepublic void onClick(View view, int position) {Toast.makeText(MainActivity.this, mItemTexts[position], Toast.LENGTH_SHORT).show();}});mCircleMenu.setOnStatusChangedListener(new OnMenuStatusChangedListener() {@Overridepublic void onStatusChanged(CircleMenuStatus status, double rotateAngle) {// TODO 可在此处定制各种动画odAnimation(status, (float)rotateAngle);}});List<ItemInfo> data = new ArrayList<>();ItemInfo item = null;for (int i = 0; i < mItemTexts.length; i++) {item = new ItemInfo(mItemImgs[i], mItemTexts[i]);data.add(item);}mCircleMenu.setAdapter(new CircleMenuAdapter(data));}private void odAnimation(CircleMenuStatus status, float rotateAngle) {switch (status) {case IDLE:Log.i(TAG, "--- -IDLE-----");animRotate.cancel();animRotate.cancel();break;case START_ROTATING:Log.i(TAG, "--- -START_ROTATING-----");break;case ROTATING:animRotate = ObjectAnimator.ofFloat(ivCenter, "rotation", startRotate, startRotate + rotateAngle);animRotate.setDuration(200).start();startRotate += rotateAngle;// Log.i(TAG, "--- -ROTATING-----");break;case STOP_ROTATING:Log.i(TAG, "--- -STOP_ROTATING-----");break;case START_FLING:Log.i(TAG, "--- -START_FLING-----");break;case FLING:// Log.i(TAG, "--- -FLING-----");animFling = ObjectAnimator.ofFloat(ivCenter, "rotation", startFling, startFling + rotateAngle);animFling.setDuration(200).start();startFling += rotateAngle;break;case STOP_FLING:Log.i(TAG, "--- -STOP_FLING-----");break;default:break;}}
}

后面的是用户定制化动画实现(提供诸多状态,爱怎么折腾怎么折腾)。好了,我们来看看实现原理。

二、实现原理

1.先看需要做什么

(1).圆盘菜单CircleMenu是一个转盘,装有各种Item,是一个容器,那理所当然是继承ViewGroup,为了方便实现飞转,所以监听用户手势OnGestureListener;

(2).菜单项CircleItemView,虽是一个item,但为了可定制化,当然也是一个容器,也是为了方便实现飞转,重写onFling方法,所以这里继承LinearLayout,纯属为了方便布局,若有特殊需求,可在布局的时候,在外层添加一个FrameLayout容器,爱怎么搞怎么搞;

(3).Adapter和Model,既然有Item那肯定也有Adapter和数据model,Adapter继承BaseAdapter即可,就跟ListView一样。

好了,就这3个玩意儿,另外几个都是从上面这两个抽出来的。

2.旋转原理

来看一下转动分析图,


图中圆心的坐标应为( mRadius, mRadius),则圆盘半径为mRadius,按照大多数人的习惯右手向下滑动,圆盘也就跟着顺时针转动(或者说滚动),当然不是圆盘在转,是菜单项在滚动,滚动了弧度为a,那途中Item的x、y坐标应为

x = tmp *cos a;

y = tmp*sin a;

这是相对于圆心的坐标,再加上圆盘的半径,就是圆盘中的坐标了,子View只管在父容器中的坐标,父容器布局时会加上自身的left和top,这样逐层往上推就是在屏幕中的坐标了)。

我们将Item强制为正方形,itemWidth为Item的宽度,当 tmp  = mRaiuds - itemWidth / Math.sqrt(2) 时,那Item的外直角就走在圆盘圆周上,再大则要出边界了,所以,Item的中心点位于mRaiuds/2和mRaiuds - itemWidth / Math.sqrt(2)之间最为合理,那么,item的x、y为

		final int childCount = getChildCount();int left, top, halfDiagonal;// 限制Item的宽高int itemWidth = (int) (mRadius * RADIO_DEFAULT_CHILD_DIMENSION);float angleDelay = 360 / childCount;for (int i = 0; i < childCount; i++) {final View child = getChildAt(i);if (child.getVisibility() == View.GONE) {continue;}mStartAngle %= 360;// 取Item对角线的一半为Item中心到圆盘圆周的距离halfDiagonal = (int) (itemWidth / Math.sqrt(2));float distanceFromCenter = mRadius - halfDiagonal - mPadding;left = mRadius + (int) Math.round(distanceFromCenter * Math.cos(Math.toRadians(mStartAngle)) - 1 / 2f * itemWidth);top = mRadius + (int) Math.round(distanceFromCenter * Math.sin(Math.toRadians(mStartAngle)) - 1 / 2f * itemWidth);// 重新Layoutchild.layout(left, top, left +

这篇关于Android 圆盘旋转/飞转菜单(高度定制化)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/260715

相关文章

vue基于ElementUI动态设置表格高度的3种方法

《vue基于ElementUI动态设置表格高度的3种方法》ElementUI+vue动态设置表格高度的几种方法,抛砖引玉,还有其它方法动态设置表格高度,大家可以开动脑筋... 方法一、css + js的形式这个方法需要在表格外层设置一个div,原理是将表格的高度设置成外层div的高度,所以外层的div需要

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript

禁止平板,iPad长按弹出默认菜单事件

通过监控按下抬起时间差来禁止弹出事件,把以下代码写在要禁止的页面的页面加载事件里面即可     var date;document.addEventListener('touchstart', event => {date = new Date().getTime();});document.addEventListener('touchend', event => {if (new

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

poj 2187 凸包or旋转qia壳法

题意: 给n(50000)个点,求这些点与点之间距离最大的距离。 解析: 先求凸包然后暴力。 或者旋转卡壳大法。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <s

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动