【问题标题】:Android How to implement Bottom Sheet from Material Design docsAndroid 如何从 Material Design 文档中实现 Bottom Sheet
【发布时间】:2014-12-30 12:46:03
【问题描述】:

你如何实现底层规范? http://www.google.com/design/spec/components/bottom-sheets.html

Google Drive 的新更新通过按下浮动操作按钮显示了这一点 ->

尽管规范从未提及圆角,但无论是否可行,只是不确定如何去做。当前使用 AppCompat 库,目标设置为 21。

谢谢

【问题讨论】:

标签: android android-5.0-lollipop material-design floating-action-button


【解决方案1】:

按照指南中的规定,我会选择直角。至于实现 - 也许最好使用这个项目的想法:https://github.com/umano/AndroidSlidingUpPanel

我认为您可以按原样使用它,也可以将这个想法用于实施。 关于如何实现类似滑动面板的另一篇很棒的文章可以在这里找到: http://blog.neteril.org/blog/2013/10/10/framelayout-your-best-ui-friend/

【讨论】:

    【解决方案2】:

    编辑

    BottomSheet 现在是android-support-library 的一部分。见John Shelleys' answer


    不幸的是,目前没有关于如何做到这一点的“官方”方式(至少据我所知没有)。
    幸运的是,有一个名为 "BottomSheet" (click) 的库模仿了 BottomSheet 的外观和感觉,并支持 Android 2.1 及更高版本。

    如果是 Drive 应用,下面是使用这个库的代码:

        new BottomSheet.Builder(this, R.style.BottomSheet_Dialog)
                .title("New")
                .grid() // <-- important part
                .sheet(R.menu.menu_bottom_sheet)
                .listener(new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO
            }
        }).show();
    

    menu_bottom_sheet(基本上是标准的 /res/menu/*.xml 资源)

    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:id="@+id/folder"
            android:title="Folder"
            android:icon="@drawable/ic_action_folder" />
        <item
            android:id="@+id/upload"
            android:title="Upload"
            android:icon="@drawable/ic_action_file_upload" />
        <item
            android:id="@+id/scan"
            android:title="Scan"
            android:icon="@drawable/ic_action_camera_alt" />
    </menu>
    

    输出如下所示:

    我认为这与原作非常接近。如果您对颜色不满意,您可以自定义它 - see this (click)

    【讨论】:

    • 轰隆隆。这正是我正在寻找的!今晚我试试看!
    • 这很棒,谢谢分享!如果您想显示共享意图中显示的所有选项怎么办?它们将根据设备上安装的应用程序动态生成。有什么想法可以实现吗?
    • @Cat 好吧,基本上您只需要获取可以处理意图的所有应用程序的列表(例如,请参阅this StackO-Question)并将它们添加到“馈送”BottomSheet 的适配器。您还可以查看 lib 的官方示例,该示例完全符合您的要求。 (Click - Line 153 - 179)。
    • 成功了,谢谢@reVerse!我最终重用了getShareActions 方法中的代码来获得所有可能的共享选项。顺便说一句,在构建器上调用 .limit(R.integer.num_share_actions) 也很有用,除非您希望工作表覆盖整个屏幕
    • @JohnShelley 没关系,绝对。我还建议选择“官方解决方案”,虽然目前它真的很有限,但至少谷歌最终奠定了基础。 ;)
    【解决方案3】:

    以下是其他一些选项:

    • Flipboard 提供了一个,但是嵌入活动 需要修改底页才能工作。
    • tutti-ch 的 bottomsheet :这是从 Android Repo 的 ResolverActivity 中提取的,不需要修改启动 Activity。

    【讨论】:

      【解决方案4】:

      回答我自己的问题,以便开发人员知道新的支持库终于提供了这个!向强大的 Google 致敬!

      来自Android Developer's Blog的一个例子:

      // The View with the BottomSheetBehavior
      View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);  
      BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);  
      behavior.setBottomSheetCallback(new BottomSheetCallback() {  
         @Override  
         public void onStateChanged(@NonNull View bottomSheet, int newState) {  
           // React to state change  
         }  
      
        @Override  
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {  
           // React to dragging events  
        }  
      });
      

      @reVerse 的 answer above 仍然是一个有效的选项,但很高兴知道 Google 也支持一个标准。

      【讨论】:

      • 感谢发帖。我认为这现在应该是“正确”的答案:)
      • @androiddeveloper 完成。考虑到它现在是特定于支持的东西,我觉得用户在寻找这些示例时会有更好的运气。当我最初问这个问题时,支持库中没有提供它。如果问题是“如何实现支持库底部工作表视图”,那么我也会提供更深入的答案:)
      • @JohnShelley 你不需要使用一些 XML 的东西吗?是否可以将底部表格 peek/collapsed(或在底部时所称的任何内容)设置为具有 wrap-content view ,并且在展开时为全屏?
      • 我想有很多人想知道如何使用 Google 新的支持 API,而 Vipul Shah 有一个很棒的 YouTube 链接可以帮助您了解如何使用它,请参考@987654323 中的 YouTube 链接@
      • 问题依然存在:如何实现底部工作表菜单。上例中的菜单项在哪里??????你知道,你点击的菜单项...
      【解决方案5】:

      Google 最近发布了Android Support Library 23.2,正式将Bottom sheets 引入了Android 设计支持库。

      【讨论】:

      • 是否有任何示例/教程?
      【解决方案6】:

      您现在可以使用来自 android 支持库 23.2 的官方 BottomSheetBehavior API。

      下面是示例代码sn -p

      bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottomSheet));
      
      case R.id.expandBottomSheetButton:
       bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
       break;
      case R.id.collapseBottomSheetButton:
       bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
       break;
      case R.id.hideBottomSheetButton:
       bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
       break;
      case R.id.showBottomSheetDialogButton:
       new MyBottomSheetDialogFragment().show(getSupportFragmentManager(), "sample");
      

      请参考Android BottomSheet youtube tutorial了解。

      【讨论】:

      • 嗨 Vipul,感谢您的链接。我有一个问题。是否可以为底页设置锚点?
      • 很棒的 youtube 链接。你在某个地方的公共仓库中有源代码吗?
      • 太棒了,你拯救了我的一天,我认为这应该被接受的答案
      【解决方案7】:

      关注博文:http://android-developers.blogspot.com/2016/02/android-support-library-232.html

      我的 xml 最终看起来像这样:

      <android.support.design.widget.CoordinatorLayout
          xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:id="@+id/coordinator_layout"
          xmlns:app="http://schemas.android.com/apk/res-auto">
          <LinearLayout
              android:id="@+id/bottom_sheet"
              android:layout_width="match_parent"
              android:layout_height="100dp"
              android:orientation="horizontal"
              app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
              <ImageView
                  android:src="@android:drawable/ic_input_add"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content" />
          </LinearLayout>
      </android.support.design.widget.CoordinatorLayout>
      

      在我的片段的 onCreateView 中:

          coordinatorLayout = (CoordinatorLayout)v.findViewById(R.id.coordinator_layout);
          View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
          BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
          behavior.setPeekHeight(100);
          behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
              @Override
              public void onStateChanged(@NonNull View bottomSheet, int newState) {
                  // React to state change
              }
      
              @Override
              public void onSlide(@NonNull View bottomSheet, float slideOffset) {
                  // React to dragging events
              }
          });
      

      setPeekHeight 的默认值为 0,所以如果你不设置它,你将无法看到你的视图。

      【讨论】:

      • 是的,xml 正是我的片段的外观。它不适合你吗?
      • 默认窥视高度为 -1(自动!),而不是 0。
      • 如果您将使用 AndroidX,请记住包名更改:com.google.android.material.bottomsheet.BottomSheetBehavior
      • 菜单项在哪里?????????????????? (底部菜单的全部要点)
      【解决方案8】:

      如果你想实现这样的底部工作表,请遵循此设计模式 几个简单的步骤

      1. 创建bottom_sheet_layout.xml布局文件
      2. 创建bottom_sheet_background.xml可绘制文件

      像这样设置bottom_sheet_background.xml 可绘制文件

          <?xml version="1.0" encoding="utf-8"?>
          <shape xmlns:android="http://schemas.android.com/apk/res/android"
              android:shape="rectangle">
          
              <solid android:color="@color/bottom_sheet_background"/>
              <corners
                  android:topRightRadius="20dp"
                  android:topLeftRadius="20dp"/>
          
          </shape>
          
          
      

      你的bottom_sheet_layout.xml

          <?xml version="1.0" encoding="utf-8"?>
          <LinearLayout
              android:id="@+id/bottom_Sheet"
              android:background="@drawable/bottom_sheet_background"
              xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:paddingStart="24dp"
              android:paddingEnd="24dp"
              android:paddingTop="16dp"
              android:paddingBottom="42dp"
              android:orientation="vertical"
              xmlns:app="http://schemas.android.com/apk/res-auto">
          
              <ImageView
                  android:id="@+id/rectangle_3"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_gravity="center_horizontal"
                  android:background="@drawable/rectangle_39"
                  />
          
          
              //add your design code here
          
          </LinearLayout>
          
          
      

      还有你的activity_main.xml 或片段

          <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent"
              android:layout_height="match_parent"
              xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto">
          
          
             //design your code here
          
          
              //this is your bottom sheet layout included here
              <include
                  android:id="@+id/bottom_sheet_layout"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  app:layout_behavior="com.google.android.material.
                  bottomsheet.BottomSheetBehavior"
                  layout="@layout/bottom_sheet_layout"/>
          
          </androidx.coordinatorlayout.widget.CoordinatorLayout>
      

      最后,在 MainActivityFragment 类中添加代码。在这里,我在您的 onCreateonCreateView 中添加 Kotlin 代码

      BottomSheetBehavior.from(binding.bottomSheetLayout.bottomSheet).apply {
          //peek height is default visible height
          peekHeight = 200
          this.state = BottomSheetBehavior.STATE_COLLAPSED
      }
      

      就是这样!

      【讨论】:

        【解决方案9】:

        现在随着 Android Jetpack Compose 的发布,它是 Android 的现代 UI 工具包,无需使用任何 xml 代码即可更轻松地制作底部表格:-

        1.创建持久的底部表,用户可以访问底部表范围之外的内容:-

        val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
            bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
        )
        val coroutineScope = rememberCoroutineScope()
        MaterialTheme {
            Column {
                BottomSheetScaffold(
                    modifier = Modifier.fillMaxSize(),
                    topBar = { TopAppBar(viewModel, onNavigateToRecipeListScreen, hideKeyBoard) },
                    content = {
                        CreateRecipeContent(
                            viewModel,
                            context,
                            readExternalStorage,
                            bottomSheetScaffoldState,
                            coroutineScope
                        )
                    },
                    scaffoldState = bottomSheetScaffoldState,
                    sheetContent = {
                        Column(
                            Modifier
                                .fillMaxWidth()
                                .height(200.dp)
                                .background(color = colorResource(id = R.color.colorPrimaryLight))
                        )
                        {
                            Text(
                                text = "SELECT PICTURE",
                                style = TextStyle(fontSize = 26.sp),
                                fontWeight = FontWeight.Bold,
                                modifier = Modifier
                                    .padding(8.dp)
                                    .align(Alignment.Start),
                                color = Color.Black
                            )
                            Spacer(modifier = Modifier.height(16.dp))
                            IconButton(onClick = {
                                when {
                                    context.let { it1 ->
                                        ContextCompat.checkSelfPermission(
                                            it1,
                                            Manifest.permission.READ_EXTERNAL_STORAGE
                                        )
                                    } == PackageManager.PERMISSION_GRANTED -> {
                                        val takePictureIntent =
                                            Intent(MediaStore.ACTION_IMAGE_CAPTURE)
                                        launchCamera(takePictureIntent)
                                        coroutineScope.launch {
                                            bottomSheetScaffoldState.bottomSheetState.collapse()
                                        }
                                    }
                                    else -> {
                                        // You can directly ask for the permission.
                                        // The registered ActivityResultCallback gets the result of this request.
                                        viewModel.isCameraPermissionAsked = true
                                        readExternalStorage()
                                        coroutineScope.launch {
                                            bottomSheetScaffoldState.bottomSheetState.collapse()
                                        }
                                    }
                                }
        
                            }, modifier = Modifier.fillMaxWidth()) {
                                Text(
                                    text = "TAKE PHOTO",
                                    style = TextStyle(fontSize = 20.sp),
                                    fontWeight = FontWeight.Bold,
                                    modifier = Modifier
                                        .padding(8.dp)
                                        .align(Alignment.Start),
                                    textAlign = TextAlign.Left,
                                    color = Color.Black
                                )
                            }
                            Spacer(modifier = Modifier.height(16.dp))
                            IconButton(onClick = {
                                when {
                                    context.let { it1 ->
                                        ContextCompat.checkSelfPermission(
                                            it1,
                                            Manifest.permission.READ_EXTERNAL_STORAGE
                                        )
                                    } == PackageManager.PERMISSION_GRANTED -> {
                                        val galleryIntent = Intent(
                                            Intent.ACTION_PICK,
                                            MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                        )
                                        galleryIntent.type = "image/*"
                                        launchGalley(galleryIntent)
                                        coroutineScope.launch {
                                            bottomSheetScaffoldState.bottomSheetState.collapse()
                                        }
                                    }
                                    else -> {
                                        // You can directly ask for the permission.
                                        // The registered ActivityResultCallback gets the result of this request.
                                        viewModel.isCameraPermissionAsked = false
                                        readExternalStorage()
                                        coroutineScope.launch {
                                            bottomSheetScaffoldState.bottomSheetState.collapse()
                                        }
                                    }
                                }
        
                            }, modifier = Modifier.fillMaxWidth()) {
                                Text(
                                    text = "CHOOSE FROM GALLERY",
                                    style = TextStyle(fontSize = 20.sp),
                                    fontWeight = FontWeight.Bold,
                                    modifier = Modifier
                                        .padding(8.dp)
                                        .align(Alignment.Start),
                                    textAlign = TextAlign.Left,
                                    color = Color.Black
                                )
                            }
        
                        }
                    }, sheetPeekHeight = 0.dp
                )
        
        
            }
        }
        

        以上代码截取,截图来自应用程序:-

        https://play.google.com/store/apps/details?id=com.bhuvnesh.diary

        完全由我使用 Jetpack Compose 创建

        1. 要创建用户无法访问底部表格范围之外的内容的模态底部表格:-

           ModalBottomSheetLayout(
               sheetState = modalBottomSheetState,
               sheetElevation = 8.dp,
               sheetContent = {
                   //sheet content
               }
           ) {
               ...
               //main content
           }
          

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-11-29
          • 2017-06-29
          • 1970-01-01
          • 2015-08-26
          • 2015-02-21
          • 1970-01-01
          • 2015-04-07
          相关资源
          最近更新 更多