Android给所有Activity添加全局自定义菜单
来源:互联网 发布:独家算法必中六红 编辑:程序博客网 时间:2024/06/03 18:01
这个按钮的大致需求是在屏幕右上角新增一个Button,点击之后在屏幕顶部与右侧召唤一系列的功能按钮,
我把这部分代码抽出来随便写了一个demo,需求实现上图:
(在点击按钮2的时候如果已经处于登录界面就提示用户不需要再次跳转,下面会有说明)
需求拿到手,心想:还好之前title是用的一个自定义View,随便在右边加个按钮不就妥了!!结果还是太天真啊,首先如果要实现如上的效果,这个布局肯定不好写,其次这尼玛有得页面根本没有title啊,然后联想到之前做过一个全局的悬浮按钮,但是这货需要悬浮窗的权限,如果用户没有开启就特么显示不出来~~~
然后第一想法就是各种度- -,结果发现网上根本没有这样的需求,莫非是我找的姿势不对?这就有点尴尬了,那就只能自己造轮子了...
额额,废话说了一大堆,开始切入正题吧- -
要在所有Activity添加统一的全局menu,那么很容易想到的就是在BaseActivity中做操作,既然是要添加统一的视图,那么就要在BaseActivity的setContentView()方法中添加一个menu视图了。我的做法如下:
@Override public void setContentView(int layoutResID) { super.setContentView(layoutResID); ((ViewGroup) getWindow().getDecorView()).addView(menu); }
这个getDecorView:这个方法是获取顶级视图
注意点1:addView添加入的视图应该是默认在左上角,和group里面原有的视图无关
注意点2:getDecorView既然是顶级视图,它包含整个屏幕,包括标题栏
注意点3:根据实际测试发现,标题栏的左上角位置的坐标才是坐标原点位置
此处引用了http://blog.csdn.net/rnZuoZuo/article/details/44959873的介绍,如果对这个方法有兴趣的童鞋可以自行查找资料哈,这里就不详细介绍了~~
我们这里menu是一个自定义View,继承了RelativeLayout,加载了一个很简单的RelativeLayout,该布局中将所有的按钮全部添加到屏幕右上角:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/menu_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingRight="10dp" android:paddingTop="45dp"> <Button android:id="@+id/btn_one" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:layout_marginTop="17dp" android:background="@null" android:drawableTop="@mipmap/assist_1" android:paddingBottom="5dp" android:text="按钮1" android:textSize="10sp" /> <Button android:id="@+id/btn_two" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:layout_marginTop="17dp" android:background="@null" android:drawableTop="@mipmap/assist_2" android:paddingBottom="5dp" android:text="按钮2" android:textSize="10sp" /> <Button android:id="@+id/btn_three" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:layout_marginTop="17dp" android:background="@null" android:drawableTop="@mipmap/assist_2" android:paddingBottom="5dp" android:text="按钮2" android:textSize="10sp" /> <Button android:id="@+id/btn_four" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:layout_marginTop="17dp" android:background="@null" android:drawableTop="@mipmap/assist_3" android:paddingBottom="5dp" android:text="按钮3" android:textSize="10sp" /> <Button android:id="@+id/btn_five" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:layout_marginTop="17dp" android:background="@null" android:drawableTop="@mipmap/assist_4" android:paddingBottom="5dp" android:text="按钮4" android:textSize="10sp" /> <Button android:id="@+id/btn_six" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:layout_marginTop="17dp" android:background="@null" android:drawableTop="@mipmap/assist_5" android:paddingBottom="5dp" android:text="按钮5" android:textSize="10sp" /> <Button android:id="@+id/btn_seven" android:layout_width="50dp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:layout_marginTop="17dp" android:background="@null" android:drawableTop="@mipmap/assist_6" android:paddingBottom="5dp" android:text="按钮7" android:textSize="10sp" /> <Button android:id="@+id/btn_main" android:layout_width="80dp" android:layout_height="80dp" android:layout_alignParentRight="true" android:layout_centerHorizontal="true" android:background="@mipmap/xhdpi" android:clickable="true" /></RelativeLayout>
当然你可以做成动态添加选项按钮,增加拓展性,因为我比较懒,这里就不做这一步了,就只分享我项目用到的,实际上在打算写博客的时候我考虑到这个需求完全可以做成
动态配置的,包括menu的形式与item的个数配置等等都能做成动态的,我后面会提到。
眼尖的童鞋肯定会发现我上面的menu有一个展开与收缩的动画效果,感觉这个menu关键的就是实现这个动画就行了,贴出show()与dismiss()动画实现代码:
show()
int n = btns.size(); for (int i = 0; i < n; i++) { float curTranslationX = btns.get(i).getTranslationX(); float curTranslationY = btns.get(i).getTranslationY(); PropertyValuesHolder valuesHolderX, valuesHolderY; if (i <= 1) {//打横的item valuesHolderX = PropertyValuesHolder.ofFloat("translationX", curTranslationX, ((i - n + 5) * w / 5));//X轴相对当前控件的位置 valuesHolderY = PropertyValuesHolder.ofFloat("translationY", curTranslationY, 0); } else {//打竖的item valuesHolderX = PropertyValuesHolder.ofFloat("translationX", curTranslationX, 0); valuesHolderY = PropertyValuesHolder.ofFloat("translationY", curTranslationY, (-(i - n) * h / 7));//Y轴相对当前控件的位置 } animatorX = ObjectAnimator.ofPropertyValuesHolder(btns.get(i), valuesHolderX, valuesHolderY); animatorX.setDuration(250); animatorX.setStartDelay(i * 50); animatorX.setInterpolator(new AnticipateOvershootInterpolator()); animatorX.start(); }
dismiss()
int n = btns.size(); for (int i = 0; i < n; i++) { //标记各按钮初始位置 float curTranslationX = btns.get(i).getTranslationX(); float curTranslationY = btns.get(i).getTranslationY(); PropertyValuesHolder valuesHolderX = PropertyValuesHolder.ofFloat("translationX", curTranslationX, initX); PropertyValuesHolder valuesHolderY = PropertyValuesHolder.ofFloat("translationY", curTranslationY, initY); animatorX = ObjectAnimator.ofPropertyValuesHolder(btns.get(i), valuesHolderX, valuesHolderY); animatorX.setDuration(250); animatorX.setStartDelay(i * 50); animatorX.setInterpolator(new AnticipateOvershootInterpolator()); animatorX.start(); }
btns是所有子item的集合,animator是动画类,如果对属性动画不熟悉的童鞋看此处的代码可能有点懵逼,建议多看看相关资料,我这里不做多介绍,valuesHolder是相对于menu主button的位置,w,h分别是屏幕的宽高。
menu的展示形式就是在这两个方法中定义的,如果你需要其他的展示动画,完全可以按照你自己的逻辑来进行修改,你可以做一个旋转围绕圆心的菜单,也能做成90度的卫星菜单等等,一切按需求来...
最后看一下这个menu的住button的点击事件:
btn_start.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (!isShowing) { if (mChangeListener != null) { mChangeListener.show();//添加菜单显示监听 } show(); rl_parent.setFocusable(true); rl_parent.setClickable(true); rl_parent.setBackgroundColor(getResources().getColor(R.color.menuShow)); isShowing = !isShowing; } else { if (mChangeListener != null) { mChangeListener.dismiss();//添加菜单隐藏监听 } dismiss(); rl_parent.setFocusable(false); rl_parent.setClickable(false); rl_parent.setBackgroundColor(getResources().getColor(R.color.menuDismiss)); isShowing = !isShowing; } } });
isShowing是全局的是否展示的状态,rl_parent是整个menu布局的跟布局,当menu展开的时候我们需要menu下一层的控件失去焦点,就类似于Dialog设置了setCancelable(false)的效果,所以就需要设置rl_parent在展开的时候获取焦点,在隐藏的时候取消焦点。实际上menu的展开与隐藏只是视觉上的效果,通过activity的setcontentview中addview的方式添加进去的menu是一直存在与activity的最上层的,这里只是在show()与dismiss()时分别给rl_parent设置了透明度不同的背景色而已
这里的mChangeListener是menu内部的接口,如果你的页面需要对menu的展示与隐藏进行监听,可以实现这个接口,就是一般的接口回调,这里没有什么好讲的了,包括menu的每个子item的点击事件也是通过这样的方法实现的,下面贴出两个接口:
public interface MenuChangeListener { public void show(); public void dismiss(); } public interface MenuItemClickListener { void menuOneItemClick(); void menuTwoItemClick(); void menuThreeItemClick(); void menuFourItemClick(); void menuFiveItemClick(); void menuSixItemClick(); void menuSevenItemClick(); }
实际上并不一定每个item的点击事件都要写一个回调,如果某一个item的事件是固定的,那么可以直接在menu类中实现该方法,而不用对外暴露。
因为我的项目中item的所有事件都是在baseActivity中实现的,但是如果说特定的item在具体的activity中需要响应不同的事件,
我的做法是在BaseActivity定义一个临时的Activity变量current_act,然后在每一个子Activity中都将当前的activity对象赋值给current_act,
demo中的BaseActivity代码如下
package com.aking.globalmenumaster.activity;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.ViewGroup;import android.widget.Toast;import com.aking.globalmenumaster.GlobalMenu;/** * Created by aking on 2017/4/6. */public class BaseActivity extends AppCompatActivity { private GlobalMenu menu; public Activity current_act; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); menu = new GlobalMenu(this); menu.setMenuItemClickListener(new GlobalMenu.MenuItemClickListener() { @Override public void menuOneItemClick() { startActivity(new Intent(BaseActivity.this, FullscreenActivity.class)); } @Override public void menuTwoItemClick() { if (current_act.getClass() == LoginActivity.class) { Toast.makeText(BaseActivity.this, "我已经在登录页面了", Toast.LENGTH_LONG).show(); } else { startActivity(new Intent(BaseActivity.this, LoginActivity.class)); } } @Override public void menuThreeItemClick() { startActivity(new Intent(BaseActivity.this, Main2Activity.class)); } @Override public void menuFourItemClick() { startActivity(new Intent(BaseActivity.this, Main22Activity.class)); } @Override public void menuFiveItemClick() { startActivity(new Intent(BaseActivity.this, Main23Activity.class)); } @Override public void menuSixItemClick() { startActivity(new Intent(BaseActivity.this, ScrollingActivity.class)); } @Override public void menuSevenItemClick() { startActivity(new Intent(BaseActivity.this, MainActivity.class)); } }); } @Override public void setContentView(int layoutResID) { super.setContentView(layoutResID); ((ViewGroup) getWindow().getDecorView()).addView(menu); } @Override protected void onPause() { super.onPause(); if (menu.isShowing()) { menu.dismiss(); } }}
在menuTwoItemClick()的事件中判断栈顶的activity是否是登录页面,如果是就不跳转,提示用户。如果说需要在某一个activity点击menu的item需要带一些参数到另外的页面,我的想法是也同样在BaseActivty中定义一个临时的变量用来存储这些参数,或者使用 SharedPreferences等等,因为我的项目中并没有这样的需求,所以我这里没有写,但我觉得很可能会有这样的需求....
需要说明的是new GlobalMenu(this); 这里初始化menu的时候传入的上下文对象一定要是Activity对象,因为我在menu类中需要通过这个上下文对象获取屏幕宽高,当然这里存在可优化的空间...
这里还存在一个已知的问题是,如果menu的主Button是一个有缝隙的矢量图的时候,在收缩的时候覆盖在下面的item会有一部分显示出来,影响界面,这里提供三种解决方案:
1>在dismiss()中为收缩动画注册一个监听器,当收缩动画完成之后,隐藏收起来的item,在show()中再显示item,代码实现如下:
animatorX.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { *//*此处添加一个动画监听,只有在动画结束时才隐藏其他按钮 * 但这样做会有一个bug,就是在用户点击菜单主按钮频率过快的时候会出现,menu处于show的状态,但是子item并没有展开或者展开的个数不对 * * *//* if (btns.get(finalI).getVisibility() == VISIBLE) { btns.get(finalI).setVisibility(INVISIBLE); } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } });
这样做的局限性我已经在注释中提到了,在我的源码中,这部分的代码是注释掉的。
2>不用在dismiss()注册动画监听,只需要在show()的时候显示,dismiss()的时候隐藏,这样做的后果就是看不到收缩的动画;
3>第三种方案是我个人觉得最完美的,那就是叫UI大大切一张完美的图,能完全覆盖掉后面的item,这样就不用控制他们的显示与隐藏了,哈哈哈...
时间有限,就介绍这么多了,感觉这里面内容也并不多,相信看了此篇文章的童鞋也能轻而易举的定制出属于自己的全局menu啦,最后附上代码下载连接:
http://download.csdn.net/detail/u011907407/9807354
这里需要1个积分才能下载- - 我最近也是木有积分下载资源啦,走过路过的好心人打赏两个积分呗!!!如果也有像楼主这样的积分穷人,如果需要资源的话可以留下你的邮箱,我看到回复会第一时间发送附件给你的- -
- Android给所有Activity添加全局自定义菜单
- 给表单添加自定义菜单
- Android 统一为项目中的所有 Activity 添加自定义TopBar
- WPF中给菜单添加自定义快捷键
- Android给自定义控件添加自定义属性
- Android给Activity状态栏设置自定义颜色
- vue---vue2.x自定义plugin,给vue添加全局方法,原型上增加全局方法
- 给win8、win10系统添加自定义右键菜单项目
- 给Eclipse的工程导航视图添加自定义右键菜单
- 添加自定义vue全局方法,同时给自定义的方法 传递component调用其方法
- 给菜单添加快捷键
- 【android】:android如何给每个activity添加背景
- 【Tech-Android-View】给自定义view添加自定义属性
- 【Tech-Android-View】给自定义view添加自定义属性
- Laravel 添加自定义全局函数
- 添加自定义系统菜单
- Lync添加自定义菜单
- 全局改变所有Activity的背景
- Oracle to_date() 用法细节
- NotImplementedException 错误提示
- Latex正文中插入罗马数字
- Ibatis 之 $ & #
- MyBatis懒加载
- Android给所有Activity添加全局自定义菜单
- 小鑫の日常系列故事(六)——奇遇记
- IM限制
- Java 之 goto
- sql数据类型
- ListView +HorizontalScrollView OnItem 冲突
- iBatis参数例子
- Git 常用命令
- python _winreg 读取 注册表 windows error 5