zoukankan      html  css  js  c++  java
  • NewBuiltBottomSheetDialog【新建底部对话框】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处!

    前言

    演示在底部选项卡上方弹出底部对话框效果。

    效果图

    代码分析

    NewBuiltBottomSheetDialog继承BottomSheetDialog;

    适配华为手机手动隐藏虚拟导航栏,监听屏幕高度变化;

    使用步骤

    一、项目组织结构图

    注意事项:

    1、  导入类文件后需要change包名以及重新import R文件路径

    2、  Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖

    二、导入步骤

    在APP中的bundle.gradle文件中添加以下代码,引入design【版本号跟appcompat-v7的保持一致】

    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,用于在监听屏幕高度变化中获取屏幕的实际高度值;

    需要制定底部选项卡区域高度值:@dimen/tab_bottom_height,用于在监听屏幕高度变化中获取屏幕的实际高度值;

    <?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中。

    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 APP适配全面屏手机的技术要点

    android检测导航栏是否存在的方法

    Android判断手机时候有导航栏的方法

    Android ContextThemeWrapper cannot be cast to android.app.Activity

    Android View坐标系详解(getTop()、getX、getTranslationX...)

    项目demo下载地址

    https://github.com/haiyuKing/NewBuiltBottomSheetDialogDemo

  • 相关阅读:
    运行缓慢的查询语句
    EditPlus 替换所有文件夹下的文件包含子文件夹
    PRM–pageLoaded事件
    DataSet接收XML数据并按条件搜索
    复杂数据类型使用基础
    WebService客户端调用错误处理
    仿GOOGLE个性首页可移动图层效果
    asp.net 2.0中md5密码加密
    页面上的可鼠标移动内嵌页面层,并有关闭按钮,背景不可点击
    最好的。NET反编译工具
  • 原文地址:https://www.cnblogs.com/whycxb/p/9156799.html
Copyright © 2011-2022 走看看