• 【安卓】Material Design


    ❤️ Author: 老九
    ☕️ 个人博客:老九的CSDN博客
    🙏 个人名言:不可控之事 乐观面对
    😍 系列专栏:

    Material Design

    Toolbar

    • ActionBar是之前提到的自定义标题栏,被限定只能位于Activity的顶部,Toolbar不仅继承了ActionBar的所有功能,配合其它控件完成Material Design的效果。
    • 以喜爱是ActionBar 的来源:
    <application
            android:allowBackup="true"
            android:dataExtractionRules="@xml/data_extraction_rules"
            android:fullBackupContent="@xml/backup_rules"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            <!--使用android:theme属性置顶了一个Theme.Test的主题,那么这个Theme.test的主题在哪呢-->
            android:theme="@style/Theme.Test"
            tools:targetApi="31">
            <activity
                android:name=".MainActivity"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 使用android:theme属性置顶了一个Theme.Test的主题,那么这个Theme.test的主题在哪呢?
    • 打开res/value/styles.xml文件
    <resources xmlns:tools="http://schemas.android.com/tools">
        <!-- Base application theme. -->
        <style name="Theme.Test" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
            <!-- Primary brand color. -->
            <item name="colorPrimary">@color/purple_500</item>
            <item name="colorPrimaryVariant">@color/purple_700</item>
            <item name="colorOnPrimary">@color/white</item>
            <!-- Secondary brand color. -->
            <item name="colorSecondary">@color/teal_200</item>
            <item name="colorSecondaryVariant">@color/teal_700</item>
            <item name="colorOnSecondary">@color/black</item>
            <!-- Status bar color. -->
            <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
            <!-- Customize your theme here. -->
        </style>
    </resources>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 这里有一个叫Theme.test的主题,然后指定它的parent主题是Theme.MaterialComponents.DayNight.DarkActionBar。这个DarkActionBar是一个深色的ActionBar主题
    • 我们现在要使用Toolbar来代替ActionBar,因此需要指定一个不带ActionBar的主题,通常有Theme.AppCompat.NoActionBar和Theme.AppCompat.Light.NoActionBar这两种主题可以选择
     <style name="Theme.Test" parent="Theme.AppCompat.Light.NoActionBar">
    
    • 1

    在这里插入图片描述

    • 设置toolbar的xml代码,style属性使用attr?进行引用
    <?xml version="1.0" encoding="utf-8"?>
    <!--xmlns.app:置顶了一个新的命名空间,正是由于每个布局文件都会使用xmlns:android,我们才能用 android:id
    等等,现在我们使用了xmlns.app,我们现在可以使用app:attribute这样的写法,由于Material属性是在新系统
    中新增的,老系统中不存在,为了能够兼容老系统给,我们应该使用app:attribute这样的写法-->
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 修改MainActivity中的代码
    package com.example.test
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    
    
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val toolbar : androidx.appcompat.widget.Toolbar = findViewById(R.id.toolbar)
            setSupportActionBar(toolbar)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 这里关键的代码,setSupportActionBar()方法将Toolbar的实例拆纳入,这样我们就既做到了使用Toolbar,又让它的外观和ActionBar一致
    • AndroidManifest.xml中使用label属性,指定Toolabr中显示文字的内容
    • 接着我们在res->new->Directory中创建一个menu文件夹。然后右击menu,创建一个toolbar.xml文件
    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app = "http://schemas.android.com/apk/res-auto">
        <!--always若屏幕空间不够不显示,ifRoom不够的话显示于菜单中,never永远显示菜单中。-->
        <item
            android:id= "@+id/backup"
            android:icon="@drawable/ic_launcher_background"
            android:title="Backup"
            app:showAsAction="always"/>
    </menu>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 然后在MainActivity中修改代码,重写两个方法,onCreateOptionsMenu()方法中加载了toolbar.xml这个彩蛋文件,然后在另一个方法处理各种按钮的点击事件。
    package com.example.test
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.view.Menu
    import android.view.MenuItem
    import android.widget.Toast
    
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val toolbar : androidx.appcompat.widget.Toolbar = findViewById(R.id.toolbar)
            setSupportActionBar(toolbar)
        }
    
        override fun onCreateOptionsMenu(menu: Menu?): Boolean {
            menuInflater.inflate(R.menu.toolbar,menu)
            return true
        }
    
        override fun onOptionsItemSelected(item: MenuItem): Boolean {
            when(item.itemId){
                R.id.backup ->Toast.makeText(this,"you click me",Toast.LENGTH_SHORT).show()
            }
            return true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    滑动菜单

    DrawerLayout

    • 滑动菜单就是将一些菜单选项隐藏起来,不是放在主屏幕上,通过滑动的方式将菜单显示出来。第一个空间是Framelayout,用于主屏幕显示内容,第二个TextView,作为滑动窗口显示的内容。
    • 这里要强调一下,必须要指定android:layout_gravity = “start”
    <?xml version="1.0" encoding="utf-8"?>
    <!--xmlns.app:置顶了一个新的命名空间,正是由于每个布局文件都会使用xmlns:android,我们才能用 android:id
    等等,现在我们使用了xmlns.app,我们现在可以使用app:attribute这样的写法,由于Material属性是在新系统
    中新增的,老系统中不存在,为了能够兼容老系统给,我们应该使用app:attribute这样的写法-->
    <androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/drawer_layout"
        android:orientation="vertical">
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    </FrameLayout>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="This is an menu"
            android:layout_gravity = "start"
            android:textSize="30sp"
            android:background="#FFF"/>
    
    </androidx.drawerlayout.widget.DrawerLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 有的时候用户不知道有这个功能,那么我么在Toolbar的最左面增加一个导航按钮,点击按钮也会将滑动窗口的内容弹出来。在Mainactivity中修改代码
    package com.example.test
    
    import android.os.Bundle
    import android.util.Log
    import android.view.Menu
    import android.view.MenuItem
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.appcompat.widget.Toolbar
    import androidx.core.view.GravityCompat
    import androidx.drawerlayout.widget.DrawerLayout
    
    
    class MainActivity : AppCompatActivity() {
        //findviewbyid不能在生命周期外面,view还没创建呢!!!!
        lateinit var drawerLayout: DrawerLayout
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val toolbar: Toolbar = findViewById(R.id.toolbar)
            setSupportActionBar(toolbar)
    
            drawerLayout = findViewById<DrawerLayout>(R.id.drawer_layout)
            Log.i("lmp", drawerLayout.toString())
            supportActionBar?.let {
                it.setDisplayHomeAsUpEnabled(true)
                it.setHomeAsUpIndicator(R.drawable.ic_launcher_foreground)
            }
        }
    
        override fun onCreateOptionsMenu(menu: Menu?): Boolean {
            menuInflater.inflate(R.menu.toolbar, menu)
            return true
        }
    
        override fun onOptionsItemSelected(item: MenuItem): Boolean {
            when (item.itemId) {
                R.id.backup -> Toast.makeText(this, "you click me", Toast.LENGTH_SHORT).show()
                android.R.id.home -> drawerLayout.openDrawer(GravityCompat.START)
            }
            return true
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 先获取DrawerLayout的实例,setDisplayHomeAsUpEnabled是让导航按钮显示出来,setHomeAsUpIndicator方法是设置一个导航按钮的图标。
    • 实际上Toolbar最左侧的这个按钮就叫做home按钮,它默认的图标是一个返回箭头,含义是返回上一个Activity
    • 接下来在onOptionsItemSelected()方法中对home按钮进行点击事件处理,Home按钮的id永远都是android.R.id.home,然后调用DrawerLayout的openDrawer()方法将滑动菜单显示出来,openDrawer()方法要求传入一个Gravity参数,为了保证这里的行为和xml里定义的一直,传入start。

    NavigationView

    • 只有一个TextView 的滑动窗口略显单调,优化滑动窗口界面,通过NavigationView
    • 首先准备menu菜单,建立nav_menu.xml.
    <?xml version="1.0" encoding="utf-8"?>
    
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <group android:checkableBehavior="single">
            <item
                android:id="@+id/nav_call"
                android:icon="@drawable/ic_launcher_background"
                android:title="Call" />
    
            <item
                android:id="@+id/nav_friends"
                android:icon="@drawable/ic_launcher_background"
                android:title="Friends" />
        </group>
    </menu>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • menu中嵌套了一个group标签,然后将group的checkableBehavior属性指定为single。group表示一个组,checkableBehavior指定为single表示组中的所有菜单项职能单选。
    • icon属性指定菜单项的图标,title指定菜单项显示的文字。
    • 下面开始准备HeaderLayout。
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:background="?attr/colorPrimary"
        android:padding="10dp">
    
        <ImageView
            android:id="@+id/icon_image"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:src = "@drawable/ic_launcher_background"
            android:layout_centerInParent="true"/>
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id = "@+id/username"
            android:layout_alignParentBottom="true"
            android:text="Tony green"
            android:textColor="#FFF"
            android:textSize="14sp"/>
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/mail"
            android:layout_above="@id/username"
            android:text="tonygreendev@gmail.com"
            android:textColor="#FFF"
            android:textSize="14sp"/>
    
    </RelativeLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 创建一个nav_header.xml文件,布局最外层是一个RelativeLayout,高度设置层180dp,这是一个NavigationView比较合适的高度
    • 最后修改activity_main中的代码
    <?xml version="1.0" encoding="utf-8"?><!--xmlns.app:置顶了一个新的命名空间,正是由于每个布局文件都会使用xmlns:android,我们才能用 android:id
    等等,现在我们使用了xmlns.app,我们现在可以使用app:attribute这样的写法,由于Material属性是在新系统
    中新增的,老系统中不存在,为了能够兼容老系统给,我们应该使用app:attribute这样的写法-->
    <androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
        </FrameLayout>
    
    
        <com.google.android.material.navigation.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/nav_header"
            app:menu="@menu/nav_menu" />
    
    
    </androidx.drawerlayout.widget.DrawerLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 将之前的TextView改为NavigationView布局。这里通过app:menu和app:headerLayout 属性将我们刚才准备好的menu和headerLayout设置进去,这样NavigationView就定义完成了。
    • 最后处理菜单项的点击事件
     val navView = findViewById<NavigationView>(R.id.nav_view)
            navView.setCheckedItem(R.id.nav_call)
            navView.setNavigationItemSelectedListener {
                drawerLayout.closeDrawers()
                true
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 这里首先调用了NavigationView的setCheckedItem()方法将Call菜单项设置为默认选中。接着调用setNavigationItemSelectedListnener()方法来设置一个菜单项中事件的监听器每当用户点击了任意菜单项时,就会回调传入Lambda表达式当中吗我们可以在这里编写具体的逻辑处理。这里调用了DrawerLayout的closeDrawers()方法,将滑动菜单关闭,并返回true表示此事件已被处理。

    悬浮按钮和可交互提示

    • 立面是Material Design中的一条设计思想,最具代表的就是悬浮按钮了。另外会在这里面有一种可交互式的提示工具,相比于之前的Toast,其会做出一定的相应。

    FloatingActionButton

    • 修改activity_main.xml的代码,在Framelayout中增加FloatingActionButton。其中layout _gravity是将该控件放置在屏幕的右下角,src属性给FloatingActionButton设置了一个图标。app:elevation用于显示该控件的高度值,若高,则投影范围大,否则小。
    <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/fab"
                android:layout_gravity="bottom|end"
                android:layout_margin="16dp"
                android:src="@drawable/ic_launcher_background"
                android:elevation="8dp"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 点击事件和button一模一样,这里点击事件用了扩展函数
    
    fun String.showToast(context:Context){
            Toast.makeText(context,this,Toast.LENGTH_SHORT).show()
        }
        
     val fab : FloatingActionButton = findViewById(R.id.fab)
            fab.setOnClickListener{
                "floatingActionButton".showToast(this)
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    SnackBar

    • 处理点击事件时仍然用的是Toast,现在学习一种更为先进的提示工具SnackBar,这个控件比如用户删了重要的东西,你只是用Toast提示了一下,用户狠抓狂,但此时若你提供了Undo按钮,降低了事故的发生率
    • SnackBar提供了make方法去创建了SnackBar对象,马戏发给你发提供的参数有,view布局,SnackBar会自动查找最外层的布局。第二个参数是显示内容,第三个是时常,这时候会使用setAction设置一个动作,从而使其不仅仅是提示,更重要的是交互,最后使用show展示出来。
    • 这时候会发现,SnackBar将悬浮按钮遮住了。
     val fab : FloatingActionButton = findViewById(R.id.fab)
            fab.setOnClickListener{
                view -> Snackbar.make(view,"Datadeleted",Snackbar.LENGTH_SHORT)
                .setAction("Undo"){
                    Toast.makeText(this,"Data restored",Toast.LENGTH_SHORT).show()
                }.show()
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    CoordinatorLayout

    • coordinatorLayout是加强版的FrameLayout,coordinatorLayout可以监听所有子控件的各种事件,并自动帮助我们做出最为合适的相应。刚才弹出的Snackbar提示将悬浮按钮遮挡住了,而如果我们能让CoordinatorLayout间听到Snackbar的弹出世间,自动将内部的FloatingActionButton向上偏移,从而不会被Snackbar遮挡。
    <?xml version="1.0" encoding="utf-8"?><!--xmlns.app:置顶了一个新的命名空间,正是由于每个布局文件都会使用xmlns:android,我们才能用 android:id
    等等,现在我们使用了xmlns.app,我们现在可以使用app:attribute这样的写法,由于Material属性是在新系统
    中新增的,老系统中不存在,为了能够兼容老系统给,我们应该使用app:attribute这样的写法-->
    <androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <androidx.coordinatorlayout.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    
            <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/fab"
                android:layout_gravity="bottom|end"
                android:layout_margin="16dp"
                android:src="@drawable/ic_launcher_background"
                android:elevation="8dp"/>
    
           <androidx.recyclerview.widget.RecyclerView
              android:id="@+id/recyclerview"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
              />
    
        </androidx.coordinatorlayout.widget.CoordinatorLayout>
    
    
        <com.google.android.material.navigation.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/nav_header"
            app:menu="@menu/nav_menu" />
    
    
    </androidx.drawerlayout.widget.DrawerLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    卡片式布局

    • MaterialCardView是用于实现卡片式布局的重要控件,实际上,MaterialCardVIew也是一个FrameLayout,只是提供了圆角和阴影等效果,看上去会有立体的感觉。
    • 首先在activity_main中添加recyclerview的代码
      <androidx.recyclerview.widget.RecyclerView
              android:id="@+id/recyclerview"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
              />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 然后定制RecyclerVIew的子项做一个自定义布局,使用MaterialCardVIew
    <?xml version="1.0" encoding="utf-8"?>
    <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_margin="5dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardCornerRadius="4dp"
        tools:ignore="MissingDefaultResource">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <ImageView
                android:id="@+id/fruit_ImageView"
                android:scaleType="centerCrop"
                android:layout_width="match_parent"
                android:layout_height="100dp" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/fruit_name"
                android:layout_gravity="center_horizontal"
                android:layout_margin="5dp"
                android:textSize="16sp"/>
        </LinearLayout>
    </com.google.android.material.card.MaterialCardView>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 这里使用MaterialCardView来作为子项的最外层布局,从而使得RecyclerView中的每个元素都是放在卡片当中。由于MaterialCardView是一个FrameLayout,因此它没有什么方便的定位方式,这里只好在MaterialCardView中再嵌套一个LinearLayout,然后再LinearLayout中放置具体的内容。
    • 然后我们需要为RecyclerView准备一个适配器
    package com.example.test
    
    import android.R
    import android.R.*
    import android.content.Context
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.ImageView
    import android.widget.TextView
    import androidx.recyclerview.widget.RecyclerView
    
    class FruitAdapter(val context : Context, val fruitList: List<Fruit>) :
        RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
    
        class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
            var fruitimage: ImageView = view.findViewById(com.example.test.R.id.fruit_ImageView)
            var fruitname: TextView = view.findViewById(com.example.test.R.id.fruit_name)
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view: View = LayoutInflater.from(context)
                .inflate(com.example.test.R.layout.fruit_item, parent, false)
            return ViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            val fruit = fruitList[position]
            holder.fruitname.text = fruit.name
            holder.fruitimage.setImageResource(fruit.imageId)
        }
    
        override fun getItemCount(): Int {
            return fruitList.size
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 最后修改MainActivity中的代码
    public class MainActivity extends AppCompatActivity {
     
        .....
        private Fruit[] fruits = {new Fruit("Apple", R.drawable.fruit1), new Fruit("Banana", R.drawable.fruit2),
                new Fruit("Orange", R.drawable.fruit3), new Fruit("Watermelon", R.drawable.fruit4),
                new Fruit("Pear", R.drawable.fruit5), new Fruit("Grape", R.drawable.fruit6),
                new Fruit("Pineapple", R.drawable.fruit7), new Fruit("Strawberry", R.drawable.fruit8)};
        private List<Fruit> fruitList = new ArrayList<>();
     
        private FruitAdapter adapter;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        ....
        initFruits();
        RecyclerView recyclerview = (RecyclerView) findViewById(R.id.recyclerview_fruits);
        GridLayoutManager layoutmanager = new GridLayoutManager(this,2);
        recyclerview.setLayoutManager(layoutmanager);
        adapter = new FruitAdapter(fruitList);
        recyclerview.setAdapter(adapter);
        ....
        }
       private void initFruits() {
            fruitList.clear();
            for (int i = 0; i < 50; i++) {
                Random random = new Random();
                int index = random.nextInt(fruits.length);
                fruitList.add(fruits[index]);
            }
        }
    }}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 这里我们出现了一个问题,就是我们的Toolbar被RecyclerVIew给挡住了,这时我们需要借助另外一个工具了,AppBarLayout

    AppBarLayout

    • 首先我们分析一下为什么RecyclerView会把Toolbar给遮挡住。因为RecyclerView和Toolbar都是放置在CoordinatorLayout中的,而CoordinatorLayout就是一个加强版的FrameLayout,因此所有控件都会摆放在布局的左上角,从而产生了遮挡的现象。
    • 如何解决呢,首先将Toolbar嵌套到AppBarLayout中,第二部给RecyclerView指定一个布局行为。
    <?xml version="1.0" encoding="utf-8"?><!--xmlns.app:置顶了一个新的命名空间,正是由于每个布局文件都会使用xmlns:android,我们才能用 android:id
    等等,现在我们使用了xmlns.app,我们现在可以使用app:attribute这样的写法,由于Material属性是在新系统
    中新增的,老系统中不存在,为了能够兼容老系统给,我们应该使用app:attribute这样的写法-->
    <androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <androidx.coordinatorlayout.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <com.google.android.material.appbar.AppBarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
    
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    
            </com.google.android.material.appbar.AppBarLayout>
    
            <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/fab"
                android:layout_gravity="bottom|end"
                android:layout_margin="16dp"
                android:src="@drawable/ic_launcher_background"
                android:elevation="8dp"/>
    
           <androidx.recyclerview.widget.RecyclerView
              android:id="@+id/recyclerview"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               app:layout_behavior="@string/appbar_scrolling_view_behavior"
              />
    
        </androidx.coordinatorlayout.widget.CoordinatorLayout>
    
    
        <com.google.android.material.navigation.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/nav_header"
            app:menu="@menu/nav_menu" />
    
    
    </androidx.drawerlayout.widget.DrawerLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 此时,我们想在滚动的时候把Toolbar隐藏,当AppBarLayout接收到滚动事件的时候,它的内部的字控件其实是可以置顶如何去相应这些事件的,通过app:layout_scrollFlags属性就能实现。
    <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_scrollFlags="scroll|enterAlways|snap"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 这里app:layout_scrollFlags属性中,scroll表示RecyclerView向上滚动的时候,Toolbar会跟着一起向上滚动进行隐藏;enterAlways表示RecyclerView想下滚动的时候,Toolabr会跟着一起向下滚动,Toolbar会跟着一起向下滚动并重新显示;snap表示当Toolbar还没有完全隐藏或显示的时候,会根据当前滚动的距离,自动选择隐藏还是显示

    下拉刷新

    • SwipeRefreshLayout是下拉刷新功能时的核心类,配合RecyclerView进行显示。
    • 首先要在app/build.gradle文件中添加如下依赖
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
    
    • 1
    • 接着修改activity_main的代码
      <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/swipeRefresh"
                app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
           <androidx.recyclerview.widget.RecyclerView
              android:id="@+id/recyclerview"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               app:layout_behavior="@string/appbar_scrolling_view_behavior"
              />
                
            </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 可以看到,这里我们在RecyclerView的外面又嵌套了一层SwipeRefreshLayout,这样RecyclerView就自动拥有了下拉刷新的功能。
    • 另外需要注意,由于RecyclerVIew现在变成了SwipeRefreshLayout的子控件,因此之前使用的声明布局,也熬一到SwipeRefreshLayout中才行。
    • 此时还没有结束,虽然RecyclerView已经支持下拉刷新功能,但是我们还要在代码中处理具体的刷新逻辑才行,修改MainActivity中的代码。
    ......
     initFruits()
            val layoutManager = GridLayoutManager(this,2)
            val recyclerview : RecyclerView = findViewById(R.id.recyclerview)
            recyclerview.layoutManager = layoutManager
            val adapter = FruitAdapter(this,fruitList)
            recyclerview.adapter = adapter
            
            swipeResfresh = findViewById(R.id.swipeRefresh)
            swipeResfresh.setColorSchemeResources(com.google.android.material.R.color.design_default_color_primary)
            swipeResfresh.setOnRefreshListener {
                refreshFruits(adapter)
            }
    ......
     private fun refreshFruits(adapter : FruitAdapter){
            thread {
                Thread.sleep(2000)
                runOnUiThread{
                    initFruits()
                    adapter.notifyDataSetChanged()
                    swipeResfresh.isRefreshing = false
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 首先调用SwipeRefreshLayout的seColorSchemeResources()方法来设置下拉刷新进度条的颜色,接着调用setOnRefreshListener()方法来设置一个下拉刷新的监听器,当用户进行了下拉刷新的操作,就会回调到Lambda表达式中,然后我们在这里去处理具体的刷新逻辑就行了。
    • 通常情况下,当出发了下拉刷新事件,应该失去网络上请求最新的数据,然后再将这些数据展示出来,这里我们带哦用了一个refreshFruits()方法,济宁本地刷新操作。现在refreshFruits()方法中开启了一个线程,然后将线程沉睡了两秒钟。因为如果不沉睡,本地刷新速度非常快,看不出来。之后使用了runOnUiThread()方法将线程切换回住线程,然后调用initFruits()方法重新生成数据,在调用Adapter的notify方法通知数据发生了变化,最后调用SwipeRefreshLayout的setRefreshing()方法传入false,表示刷新事件结束,并隐藏刷新进度条。

    可折叠式标题栏

    • CollapsingToolbarLayout是一个作用于Toolbar基础之上的布局,能够让Toolbar的效果更丰富,不仅仅是展示一个标题栏。
    • 但是CollapsingToolbarLayout是不能独立存在的,只能作为AppBarLayout的直接子布局来使用,而AppBarLayout必须是CoordinatorLayout的子布局。
    • 首先我们需要一个额外的Activity作为水果的详情展示界面。
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appBar"
            android:layout_width="match_parent"
            android:layout_height="250dp">
    
            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolabar"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:contentScrim="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">
    
                <ImageView
                    android:id="@+id/fruit_image"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:scaleType="centerCrop"
                    app:layout_collapseMode="parallax" />
    
                <androidx.appcompat.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:layout_collapseMode="pin">
    
                </androidx.appcompat.widget.Toolbar>
            </com.google.android.material.appbar.CollapsingToolbarLayout>
        </com.google.android.material.appbar.AppBarLayout>
    
        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
    
                <com.google.android.material.card.MaterialCardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="15dp"
                    android:layout_marginLeft="15dp"
                    android:layout_marginRight="15dp"
                    android:layout_marginTop="35dp"
                    app:cardCornerRadius="4dp">
    
                    <TextView
                        android:id="@+id/fruit_content_text"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_margin="10dp" />
    
                </com.google.android.material.card.MaterialCardView>
            </LinearLayout>
        </androidx.core.widget.NestedScrollView>
    
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_anchor="@id/appBar"
            app:layout_anchorGravity="bottom|end"
            android:layout_margin="16dp"
            android:src="@drawable/ic_launcher_background" />
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 首先实现标题栏的部分,使用CoordinatorLayout作为最外层的布局
    • 接着我们在CoordinatorLayout中嵌套一个 AppBarLayout
    • 在AppBarLayout中再嵌套一个CollapsingToolbarLayout,其中android:theme的属性置顶了一个主题,因为之前在activity_main.xml中给Toolabr置顶的也是这个主题,祝不过这里要实现更高级的Toolbar效果,因此需要将这个主题的置顶提到上一层。app:contentScrim属性用于置顶CollapsingToolabrLayout在区域折叠状态以及折叠之后的背景色,exitUntilCollapsed表示当CollapsingToolbarlayout随着滚动完成折叠之后就保留在界面上,不再移出屏幕
    • 之后在CollapsingToolbarLayout中定义一个ImageView和一个Toolbar,其中app:layout_collapseMode属性用于指定当前控件在CollapsingToolabrLayout折叠过程中的折叠模式,指定为pin表示折叠的过程中位置始终保持不变,ImageView指定成parallax,表示会在折叠的过程中产生一定的错位偏移。
    • 之后开始编写水果内容详情部分
    • 我们先在最外层布局使用了一个NestedScrollVIew,和AppBarLayout是平级的,NestedScrollVIew增加了嵌套响应滚动事件的功能。并且指定了一个布局行为,用于处理父布局滑动手势的机会,NestedScrollView中允许存在一个直接子布局。在子布局中加上看片布局和TextVIew
    • 最后加入了一个FloatingActionButton,它和AppBarLayout以及NestedScrollVIew是平级的。FloatingActionButton中使用app:layout_anchor属性指定了一个锚点,将锚点设置为AppBarLayout,这样悬浮按钮就会出现在水果标题栏的区域内。
    • 接下来我们开始编写功能逻辑,修改FruitActivity中的代码。
    package com.example.test
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.view.MenuItem
    import android.widget.ImageView
    import android.widget.TextView
    import com.google.android.material.appbar.CollapsingToolbarLayout
    
    
    class FruitActivity : AppCompatActivity() {
    
        companion object{
            const val FRUIT_NAME = "fruit_name"
            const val FRUIT_IMAGE_ID = "fruit_image_id"
        }
    
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_fruit)
    
            val fruitName = intent.getStringExtra(FRUIT_NAME) ?: ""
            val fruitImageId = intent.getIntExtra(FRUIT_IMAGE_ID,0)
    
            val toolbar : androidx.appcompat.widget.Toolbar = findViewById(R.id.toolbar)
            val fruit_imageview : ImageView = findViewById(R.id.fruit_image)
            val collapsingToolbarLayout :CollapsingToolbarLayout =
                findViewById(R.id.collapsing_toolabar)
            val fruit_textview : TextView = findViewById(R.id.fruit_content_text)
            setSupportActionBar(toolbar)
            supportActionBar?.setDisplayHomeAsUpEnabled(true)
            collapsingToolbarLayout.title = fruitName
            fruit_imageview.setImageResource(fruitImageId)
            fruit_textview.text = fruitName.repeat(5000)
        }
    
        override fun onOptionsItemSelected(item: MenuItem): Boolean {
            when(item.itemId){
                android.R.id.home ->{
                    finish()
                    return true
                }
            }
            return super.onOptionsItemSelected(item)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 首先通过Intent获取了传入的水果名和水果图片的资源id,接着把Toolbar作为ActionBar显示,并启动Home按钮
    • 接下来开始填充界面上的内容给,调用CollapsingToolbarLayout的settile方法,将水果名设置成标题,然后传入水果图片,并设置到标题栏的ImageView上面,最后处理home的点击事件。
    • 最后处理RecyclerVIe的点击事件,实现跳转获得intent数据。
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view: View = LayoutInflater.from(context)
                .inflate(com.example.test.R.layout.fruit_item, parent, false)
            val holder  = ViewHolder(view)
            holder.itemView.setOnClickListener{
                val position = holder.adapterPosition
                val fruit = fruitList[position]
                val intent = Intent(context,FruitActivity :: class.java).apply {
                    putExtra(FruitActivity.FRUIT_NAME,fruit.name)
                    putExtra(FruitActivity.FRUIT_IMAGE_ID,fruit.imageId)
                }
                context.startActivity(intent)
            }
            return holder
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ————————————————————————
    ♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
    版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章

  • 相关阅读:
    chisel入门初步2_2——-1/2次方生成器
    Android-Studio与Python环境配置
    机器学习简介
    关于 Redis 中集群
    02-`Linux`的基本命令
    深入解析Spring Boot中最常用注解的使用方式(上篇)
    Snap内部备忘录:制定60亿美元营收目标,未放弃AR长期愿景
    【XSS跨站脚本】存储型XSS(持久型)
    Guava Preconditions类的各种用法
    记一次U8登录异常问题
  • 原文地址:https://blog.csdn.net/partworld/article/details/126296982