版權聲明:本文爲HaiyuKing原創文章,轉載請註明出處!java
演示在底部選項卡上方彈出底部對話框效果。android
NewBuiltBottomSheetDialog繼承BottomSheetDialog;git
適配華爲手機手動隱藏虛擬導航欄,監聽屏幕高度變化;github
注意事項:app
一、 導入類文件後須要change包名以及從新import R文件路徑ide
二、 Values目錄下的文件(strings.xml、dimens.xml、colors.xml等),若是項目中存在,則複製裏面的內容,不要整個覆蓋佈局
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.why.project.newbuiltbottomsheetdialogdemo"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//BottomSheetDialog compile "com.android.support:design:27.1.1"
}
須要指定底部選項卡區域的id值:@+id/bottom_layout,用於在監聽屏幕高度變化中獲取屏幕的實際高度值;gradle
須要制定底部選項卡區域高度值:@dimen/tab_bottom_height,用於在監聽屏幕高度變化中獲取屏幕的實際高度值;ui
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- 中間fragment切換區域 --> <FrameLayout android:id="@+id/id_floating_dragger_center_layout" android:layout_width="match_parent" android:layout_height="0.0dp" android:layout_weight="1"></FrameLayout> <!-- 陰影部分 --> <View android:layout_width="match_parent" android:layout_height="3dp" android:background="@drawable/home_tab_bottom_line"/> <!-- 底部選項卡區域 --> <LinearLayout android:id="@+id/bottom_layout" android:layout_width="match_parent" android:layout_height="@dimen/tab_bottom_height" android:orientation="horizontal" android:gravity="center" android:background="#ffffff"> <!-- 添加 --> <Button android:id="@+id/btn_add" android:layout_width="46dp" android:layout_height="46dp" android:background="@drawable/home_tab_add" android:gravity="center" android:layout_gravity="center" /> </LinearLayout> </LinearLayout>
聲明一個變量,存儲屏幕的實際高度值,並傳入NewBuiltBottomSheetDialog中。this
package com.why.project.newbuiltbottomsheetdialogdemo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.ViewTreeObserver; import android.widget.Button; import android.widget.Toast; import com.why.project.newbuiltbottomsheetdialogdemo.dialog.NewBuiltBottomSheetDialog; public class MainActivity extends AppCompatActivity { private Button btn_add; private int displayHeight = 0;//屏幕顯示的高度值(不包括虛擬導航欄的高度) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); initEvents(); } private void initViews() { btn_add = findViewById(R.id.btn_add); } private void initEvents() { //監聽屏幕高度變化 View rootView = this.getWindow().getDecorView(); rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //https://blog.csdn.net/u013872857/article/details/53750682 int[] loc = new int[2]; findViewById(R.id.bottom_layout).getLocationOnScreen(loc); displayHeight = loc[1] + getResources().getDimensionPixelSize(R.dimen.tab_bottom_height);//底部區域+底部的高度值=顯示區域的高度值 } }); btn_add.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { NewBuiltBottomSheetDialog bottomSheetDialog = new NewBuiltBottomSheetDialog(MainActivity.this,displayHeight); bottomSheetDialog.setOnCustomButtonClickListener(new NewBuiltBottomSheetDialog.OnCustomButtonClickListener() { @Override public void onAddNoteButtonClick() { //打開新建筆記界面 Toast.makeText(MainActivity.this,"新建筆記",Toast.LENGTH_SHORT).show(); } @Override public void onAddFileButtonClick() { //打開新建文件界面 Toast.makeText(MainActivity.this,"新建文件",Toast.LENGTH_SHORT).show(); } @Override public void onAddPhotoButtonClick() { //打開新建圖片界面 Toast.makeText(MainActivity.this,"新建圖片",Toast.LENGTH_SHORT).show(); } @Override public void onAddVideoButtonClick() { //打開新建視頻界面 Toast.makeText(MainActivity.this,"新建視頻",Toast.LENGTH_SHORT).show(); } }); bottomSheetDialog.show(); } }); } }
關鍵在於須要指定內邊距的下方值android:paddingBottom="@dimen/tab_bottom_height",高度值就是首頁的底部選項卡區域的高度值。
<?xml version="1.0" encoding="utf-8"?> <!-- 首頁底部的添加按鈕打開的底部對話框 --> <!-- android:paddingBottom="@dimen/tab_bottom_height"是關鍵 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rootlayout" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/tab_bottom_height" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:background="@drawable/home_popwidow_bg" android:gravity="center" android:paddingBottom="5dp" > <!-- 新建筆記 --> <LinearLayout android:layout_width="0.0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/tv_addNote" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="新建筆記" android:textSize="14sp" android:textColor="#555556" android:gravity="center" android:layout_gravity="center" android:drawablePadding="5dp"/> </LinearLayout> <!-- 新建文件 --> <LinearLayout android:layout_width="0.0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/tv_addFile" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="新建文件" android:textSize="14sp" android:textColor="#555556" android:gravity="center" android:layout_gravity="center" android:drawablePadding="5dp"/> </LinearLayout> <!-- 新建圖片 --> <LinearLayout android:layout_width="0.0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/tv_addPhoto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="新建圖片" android:textSize="14sp" android:textColor="#555556" android:gravity="center" android:layout_gravity="center" android:drawablePadding="5dp"/> </LinearLayout> <!-- 新建視頻 --> <LinearLayout android:layout_width="0.0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/tv_addVideo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="新建視頻" android:textSize="14sp" android:textColor="#555556" android:gravity="center" android:layout_gravity="center" android:drawablePadding="5dp"/> </LinearLayout> </LinearLayout> </LinearLayout>
package com.why.project.newbuiltbottomsheetdialogdemo.dialog; import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.design.widget.BottomSheetDialog; import android.util.DisplayMetrics; import android.view.Display; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.TextView; import com.why.project.newbuiltbottomsheetdialogdemo.R; import java.lang.reflect.Method; /** * Created by HaiyuKing * Used */ public class NewBuiltBottomSheetDialog extends BottomSheetDialog { private static final String TAG = NewBuiltBottomSheetDialog.class.getSimpleName(); private Context mContext; private int displayHeight_build;//屏幕顯示的高度值,從activity中傳入,用於判斷是否存在虛擬導航欄 private TextView tv_addNote; private TextView tv_addFile; private TextView tv_addPhoto; private TextView tv_addVideo; public NewBuiltBottomSheetDialog(@NonNull Context context, int displayHeight) { super(context); mContext = context; this.displayHeight_build = displayHeight; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.dialog_bottomsheet_new_built); //能夠變相實現底部外邊距效果 int screenHeight = getScreenHeight(scanForActivity(mContext)); int statusBarHeight = getStatusBarHeight(getContext()); int navigationBarHeight = getNavigationBarHeight(getContext());//底部虛擬導航高度 //若是傳入的displayHeight_build == 0,那麼就使用默認的方法(存在的問題是,顯示虛擬導航欄打開APP後,使用過程當中隱藏虛擬導航欄,再打開對話框的時候,顯示的位置不正確) int dialogHeight = screenHeight - navigationBarHeight - dip2px(mContext,0);//dip2px(mContext,0)預留在這裏,若是之後想要調整距離的話 if(displayHeight_build > 0){ dialogHeight = displayHeight_build - navigationBarHeight - dip2px(mContext,0);//dip2px(mContext,0)預留在這裏,若是之後想要調整距離的話 } // getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, dialogHeight == 0 ? ViewGroup.LayoutParams.MATCH_PARENT : dialogHeight); getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);//紅米6pro適配 //設置透明 getWindow().findViewById(R.id.design_bottom_sheet).setBackgroundResource(android.R.color.transparent); initViews(); initEvents(); } /**獲取實際屏幕高度,不包括虛擬功能高度*/ private int getScreenHeight(Activity activity) { DisplayMetrics displaymetrics = new DisplayMetrics(); Display d = activity.getWindowManager().getDefaultDisplay(); d.getMetrics(displaymetrics); return displaymetrics.heightPixels; } /**獲取狀態欄高度值*/ private int getStatusBarHeight(Context context) { int statusBarHeight = 0; Resources res = context.getResources(); int resourceId = res.getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { statusBarHeight = res.getDimensionPixelSize(resourceId); } return statusBarHeight; } /** * 獲取底部虛擬導航欄高度 */ public int getNavigationBarHeight(Context activity) { boolean hasNavigationBar = navigationBarExist(scanForActivity(activity)) && !vivoNavigationGestureEnabled(activity); if (!hasNavigationBar) {//若是不含有虛擬導航欄,則返回高度值0 return 0; } Resources resources = activity.getResources(); int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); //獲取NavigationBar的高度 int height = resources.getDimensionPixelSize(resourceId); return height; } /*========================================方法1======================================================*/ /** * 經過獲取不一樣狀態的屏幕高度對比判斷是否有NavigationBar * https://blog.csdn.net/u010042660/article/details/51491572 * https://blog.csdn.net/android_zhengyongbo/article/details/68941464*/ public boolean navigationBarExist(Activity activity) { WindowManager windowManager = activity.getWindowManager(); Display d = windowManager.getDefaultDisplay(); DisplayMetrics realDisplayMetrics = new DisplayMetrics(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { d.getRealMetrics(realDisplayMetrics); } int realHeight = realDisplayMetrics.heightPixels; int realWidth = realDisplayMetrics.widthPixels; DisplayMetrics displayMetrics = new DisplayMetrics(); d.getMetrics(displayMetrics); int displayHeight = displayMetrics.heightPixels; int displayWidth = displayMetrics.widthPixels; if(this.displayHeight_build > 0){ displayHeight = displayHeight_build; } return (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0; } /*========================================方法2======================================================*/ /** * 檢測是否有底部虛擬導航欄【有點兒問題,當隱藏虛擬導航欄後,打開APP,仍然判斷顯示了虛擬導航欄】 * @param context * @return */ public boolean checkDeviceHasNavigationBar(Context context) { boolean hasNavigationBar = false; Resources rs = context.getResources(); int id = rs.getIdentifier("config_showNavigationBar", "bool", "android"); if (id > 0) { hasNavigationBar = rs.getBoolean(id); } try { Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); Method m = systemPropertiesClass.getMethod("get", String.class); String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys"); if ("1".equals(navBarOverride)) { hasNavigationBar = false; } else if ("0".equals(navBarOverride)) { hasNavigationBar = true; } } catch (Exception e) { } return hasNavigationBar; } /** * 獲取vivo手機設置中的"navigation_gesture_on"值,判斷當前系統是使用導航鍵仍是手勢導航操做 * @param context app Context * @return false 表示使用的是虛擬導航鍵(NavigationBar), true 表示使用的是手勢, 默認是false * https://blog.csdn.net/weelyy/article/details/79284332#更換部分被拉伸的圖片資源文件 * 因爲全面屏手機都沒有底部的Home,Back等實體按鍵,所以,大多數全面屏手機都是支持虛擬導航鍵,即經過上面的方法checkDeviceHasNavigationBar獲取的返回值都是true。 */ public boolean vivoNavigationGestureEnabled(Context context) { int val = Settings.Secure.getInt(context.getContentResolver(), "navigation_gesture_on", 0); return val != 0; } /**解決java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.Activity問題 * https://blog.csdn.net/yaphetzhao/article/details/49639097*/ private Activity scanForActivity(Context cont) { if (cont == null) return null; else if (cont instanceof Activity) return (Activity)cont; else if (cont instanceof ContextWrapper) return scanForActivity(((ContextWrapper)cont).getBaseContext()); return null; } /** * 獲取dp的px值*/ public int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } private void initViews() { tv_addNote = (TextView) findViewById(R.id.tv_addNote); tv_addFile = (TextView) findViewById(R.id.tv_addFile); tv_addPhoto = (TextView) findViewById(R.id.tv_addPhoto); tv_addVideo = (TextView) findViewById(R.id.tv_addVideo); } private void initEvents() { tv_addNote.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mOnCustomButtonClickListener != null){ mOnCustomButtonClickListener.onAddNoteButtonClick(); } dismiss(); } }); tv_addFile.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mOnCustomButtonClickListener != null){ mOnCustomButtonClickListener.onAddFileButtonClick(); } dismiss(); } }); tv_addPhoto.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mOnCustomButtonClickListener != null){ mOnCustomButtonClickListener.onAddPhotoButtonClick(); } dismiss(); } }); tv_addVideo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mOnCustomButtonClickListener != null){ mOnCustomButtonClickListener.onAddVideoButtonClick(); } dismiss(); } }); } public static abstract interface OnCustomButtonClickListener { //新建筆記按鈕的點擊事件接口 public abstract void onAddNoteButtonClick(); //新建文件按鈕的點擊事件接口 public abstract void onAddFileButtonClick(); //新建圖集按鈕的點擊事件接口 public abstract void onAddPhotoButtonClick(); //新建視頻按鈕的點擊事件接口 public abstract void onAddVideoButtonClick(); } private OnCustomButtonClickListener mOnCustomButtonClickListener; public void setOnCustomButtonClickListener(OnCustomButtonClickListener mOnCustomButtonClickListener) { this.mOnCustomButtonClickListener = mOnCustomButtonClickListener; } }
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- 底部選項卡高度值 --> <dimen name="tab_bottom_height">52dp</dimen> </resources>
package com.why.project.newbuiltbottomsheetdialogdemo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.ViewTreeObserver; import android.widget.Button; import android.widget.Toast; import com.why.project.newbuiltbottomsheetdialogdemo.dialog.NewBuiltBottomSheetDialog; public class MainActivity extends AppCompatActivity { private Button btn_add; private int displayHeight = 0;//屏幕顯示的高度值(不包括虛擬導航欄的高度) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); initEvents(); } private void initViews() { btn_add = findViewById(R.id.btn_add); } private void initEvents() { //監聽屏幕高度變化 View rootView = this.getWindow().getDecorView(); rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //https://blog.csdn.net/u013872857/article/details/53750682 int[] loc = new int[2]; findViewById(R.id.bottom_layout).getLocationOnScreen(loc); displayHeight = loc[1] + getResources().getDimensionPixelSize(R.dimen.tab_bottom_height);//底部區域+底部的高度值=顯示區域的高度值 } }); btn_add.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { NewBuiltBottomSheetDialog bottomSheetDialog = new NewBuiltBottomSheetDialog(MainActivity.this,displayHeight); bottomSheetDialog.setOnCustomButtonClickListener(new NewBuiltBottomSheetDialog.OnCustomButtonClickListener() { @Override public void onAddNoteButtonClick() { //打開新建筆記界面 Toast.makeText(MainActivity.this,"新建筆記",Toast.LENGTH_SHORT).show(); } @Override public void onAddFileButtonClick() { //打開新建文件界面 Toast.makeText(MainActivity.this,"新建文件",Toast.LENGTH_SHORT).show(); } @Override public void onAddPhotoButtonClick() { //打開新建圖片界面 Toast.makeText(MainActivity.this,"新建圖片",Toast.LENGTH_SHORT).show(); } @Override public void onAddVideoButtonClick() { //打開新建視頻界面 Toast.makeText(MainActivity.this,"新建視頻",Toast.LENGTH_SHORT).show(); } }); bottomSheetDialog.show(); } }); } }
無
Android ContextThemeWrapper cannot be cast to android.app.Activity
Android View座標系詳解(getTop()、getX、getTranslationX...)