這個按鈕的大致需求是在屏幕右上角新增一個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個積分才能下載- - 我最近也是木有積分下載資源啦,走過路過的好心人打賞兩個積分唄!!!如果也有像樓主這樣的積分窮人,如果需要資源的話可以留下你的郵箱,我看到回覆會第一時間發送附件給你的- -