【android笔记】Material Design

Material Design

界面设计语言,包括视觉、运功、互动效果等特性

Toolbar

继承了ActionBar的所有功能,而且灵活性更高,可以配合其他控件完成一些Material Design的效果

1.取消ActionBar

通常我们的ActionBar是在res/values/themes文件下
在这里插入图片描述
在这里插入图片描述

通过AppTheme来指定它的parent主题是什么,这里我们准备使用Toolbar来代替ActionBar,因此需要指定一个不带ActionBar的主题

Light.NoActionBar表示不带ActionBar的浅色主题

<style name="Theme.MaterialTest" parent="Theme.MaterialComponents.Light.NoActionBar">

2使用Toolbar

(1)修改布局文件

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@color/design_default_color_primary"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    />
        <!-- 高度为 actionBar的高度-->
        <!-- theme指定Toolbar为深色主题-->
        <!--popupTheme弹出菜单项指定成浅色-->

添加了一个Toolbar的控件

(2)修改Acitivity.kt文件

setSupportActionBar(toolbar) //将会toolbar实例传入,让它的外观和功能和ActionBar一致

(3)添加菜单

在res文件下新建menu文件夹,然后在菜单文件夹下创建一个ToolBar的菜单布局

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/backup"
        android:icon="@mipmap/ic_backup"
        android:title="Backup"
        app:showAsAction="always"
        />
    <item
        android:id="@+id/delete"
        android:icon="@mipmap/ic_delete"
        android:title="Delete"
        app:showAsAction="ifRoom"
        />
    <item
        android:id="@+id/settings"
        android:icon="@mipmap/ic_settings"
        android:title="Settings"
        app:showAsAction="never"
        />

    <!-- app:showAsAction指定显示位置,always表示永远在Toolbar中;ifRoom表示如果屏幕
     空间足够显示在Toolbar中,否则显示在菜单栏中,never表示永远显示在菜单栏中-->


</menu>

修改Activity.kt中的代码

//初始化菜单,加载菜单文件
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 backup",Toast.LENGTH_SHORT).show()
        R.id.delete->Toast.makeText(this,"you click delete",Toast.LENGTH_SHORT).show()
        R.id.settings->Toast.makeText(this,"you click settings",Toast.LENGTH_SHORT).show()

    }
    return true
}

DrawerLayout(滑动菜单)

DrawerLayout是一个布局,在布局中允许放入两个直接子控件,第一个子控件是在主屏幕中显示的内容,第二个子控件是滑动菜单中显示的内容。

修改布局文件

第一个子控件为FrameLayout,用于中间屏幕的显示内容,第二个TextView作为滑动菜单中显示的内容

<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/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </FrameLayout>


    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#FFF"
        android:text="This is menu"
        android:textSize="30sp"
        />
    <!--layout_gravity指定滑动菜单是向左滑还是向右划,start会根据系统语言进行判断,英语汉语就在左边,阿拉伯语就在右边-->
</androidx.drawerla
yout.widget.DrawerLayout

这样就可以达到滑动的效果了

添加导航按钮

点击导航按钮也可以打开滑动菜单

修改Activity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar) 
    
    supportActionBar?.let {
        it.setDisplayHomeAsUpEnabled(true) //让导航按钮显示出来
        it.setHomeAsUpIndicator(R.mipmap.ic_menu) //设置导航图标
    }
}
....
//给菜单设置点击事件
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when(item.itemId){
  
        android.R.id.home->drawerLayout.openDrawer(GravityCompat.START)
        //对home的点击事件进行处理,将滑动菜单显示出来,GravityCompat用来指定这里的行为和我们xml中指定的一致
        .....

    }
    return true
}

NavigationView(实现滑动菜单的内容优化)

一个控件,实现滑动菜单内容展示的优化

由Material Desig提供,所以我们需要引入这个库

implementation 'com.google.android.material:material:1.1.0'
implementation 'de.hdodenhof:circleimageview:3.0.1'

第一个是Material库,第二个是Circleimageview可以实现图片的圆形化功能

这个控件需要两个部分,一个是头部分headerLayout和菜单部分menu

nav_menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/navCall"
            android:icon="@mipmap/nav_call"
            android:title="Call"
            />
     ....
    </group>
</menu>

所有的子菜单都放在一个组里面,指定checkableBehavior=“single”,表示这个组里面所有的菜单只能单选

nav_header.xml

最外层是一个RelativeLayout,设置一个合适的高度,然后里面放了三个控件,一个头像图片采用de.hdodenhof.circleimageview.CircleImageView使它圆形化,两个TextView分别显示邮箱和用户名。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="180dp"
    android:padding="10dp"
    android:background="@color/design_default_color_primary"
    >

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/iconImage"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:src="@mipmap/nav_icon"
        android:layout_centerInParent="true"
        />

    <TextView
        android:id="@+id/mailText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="rookieWai@gmail.com"
        android:textSize="14sp"/>

    <TextView
        android:id="@+id/userText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/mailText"
        android:text="rookieWai"
        android:textSize="14sp"/>


</RelativeLayout>

使用NavigationView

修改之前的代码,将layout中的TextView滑动菜单内容修改为NavigationView

<com.google.android.material.navigation.NavigationView
    android:id="@+id/navView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:menu="@menu/nav_menu"
    app:headerLayout="@layout/nav_header"
    />

修改activity

navView.setCheckedItem(R.id.navCall)  //设置为默认选择
navView.setNavigationItemSelectedListener {
    drawerLayout.closeDrawers()
    true   //返回true表示事件已被处理
}

FloatingActionButton(悬浮按钮)

Material库中提供的一个控件,可以轻松实现悬浮按钮的效果

<com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_margin="16dp"
    android:src="@mipmap/ic_done"
    app:elevation="8dp"
    />

end表示如果系统语言是从左往右,那么end就在右,从右往左就在左

app:elevation=“8dp” 设置悬浮高度,高度越大投影范围也就越大,投影效果越淡

fab.setOnClickListener{
    Toast.makeText(this,"Fab clicked",Toast.LENGTH_SHORT).show()
}

设置点击事件,实际用法和Button一样


Snackbar(更先进的提示工具)

Material库提供的更加先进的提示工具

Snackbar在Tost的提示基础上,允许在提示中加入一个可交互按钮,点击按钮可以执行额外的逻辑操作。

//view,当前页面的任意View,会使用它自动查找最外层的布局
Snackbar.make(view,"Data deleted",Snackbar.LENGTH_SHORT)
//设置一个动作,添加点击后的逻辑
    .setAction("Undo"){
        Toast.makeText(this,"Data restored",Toast.LENGTH_SHORT).show()

    }
    .show()

CoordinatorLayout(加强版的FramLayout)

由Android库提供

可以监听所有子控件的所有事件,并自动帮助我们做出最合理的响应。

使用方式就是将FramLayout标签换成CoordinatorLayout

上述Snackbar弹出的提示会将悬浮按钮遮挡住,而如果我们使用CoordinatorLayout就能监听到Snackbar的弹出时间,那么它就会自动将内部的FloatingActionButton向上偏移,从而保证不会被Snackbar遮挡。


MaterialCardView(卡片布局)

MaterialCardView本质上是一个FrameLayout,只是额外提供了圆角和阴影等效果。

添加依赖,第一个是recyclerview循环控件,用来放卡片内容;第二个是Glide库,它是一个开源的图片加载库,可以用于加载本地图片,网络图片、Gif。

implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.github.bumptech.glide:glide:4.9.0'

首先在主界面添加一个RecyclerView

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

然后为RecyclerView的子项创建子布局

这个子布局的最外层布局就是MaterialCardView

<com.google.android.material.card.MaterialCardView
    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="wrap_content"
    android:layout_margin="5dp"
    app:cardCornerRadius="4dp"
    >
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/fruitImage"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:scaleType="centerCrop"
            />
        <TextView
            android:id="@+id/fruitName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_margin="5dp"
            android:text="16sp"/>
    </LinearLayout>

  <!--app:cardCornerRadius="4dp"指定卡片圆角的弧度  app:elevation可指定高度-->
    <!--android:scaleType="centerCrop"指定图片的缩放模式,这里是让图片保存原有比例
    填充满整个ImageView并将超出屏幕的部分裁剪掉-->
  

</com.google.android.material.card.MaterialCardView>

这里的子布局有ImageView和TextView分别用来存放水果图片和名字,所以我们需要一个水果类

class Fruit (val name:String,val imageId:Int)

为RecyclerView创建适配器

class FruitAdapter(val context: Context,val fruitList: List<Fruit>): RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
	
	//ViewHolder类
    inner class ViewHolder(view : View):RecyclerView.ViewHolder(view){
        val fruitImage:ImageView=view.findViewById(R.id.fruitImage)
        val fruitName: TextView =view.findViewById(R.id.fruitName)
    }
	
	
	//创建viewHolder实例
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

        val view=LayoutInflater.from(context).inflate(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
        Glide.with(context).load(fruit.imageId).into(holder.fruitImage)
        //load方法记载图片,传入的可以是URL地址,也可以是一个本地路径或者是一个资源id,最后调用into()方法将图片设置到某一个ImageView中
    }

    override fun getItemCount(): Int {
        return fruitList.size
    }
}

修改activit.kt文件展示RecyclerView

val fruits= mutableListOf(Fruit("Apple",R.mipmap.apple),Fruit("Banana",R.mipmap.banana),
    Fruit("Orange",R.mipmap.orange),Fruit("Watermelon",R.mipmap.watermelon),Fruit("Pear",R.mipmap.pear),
    Fruit("Grape",R.mipmap.grape),Fruit("Pineapple",R.mipmap.pineapple),Fruit("Strawberry",R.mipmap.strawberry),
    Fruit("Cherry",R.mipmap.cherry),Fruit("Mango",R.mipmap.mango))

val fruitList=ArrayList<Fruit>()


override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        .......
        initFruits()
        //GridLayoutManager布局方式,参数:context、列数
        val layoutManager=GridLayoutManager(this,2)
        //将recyclerView布局方式制定为GridLayoutManager
        recyclerView.layoutManager=layoutManager
        val adapter=FruitAdapter(this,fruitList)
        recyclerView.adapter=adapter
    }

    private fun initFruits(){
        fruitList.clear()
        repeat(50){
            val index=(0 until fruits.size).random()
            fruitList.add(fruits[index])
        }
    }


AppBarLayout(解决控件遮挡)

AppBarLayout是一个垂直方向的LinearLayout,它内部做了很多滚动事件封装

下面使用AppBarLayout解决上面RecyclerView遮挡Toolbar的问题

将Toolbar嵌套到AppBarLayout里

<com.google.android.material.appbar.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <androidx.appcompat.widget.Toolbar
		...
		app:layout_scrollFlags="scroll|enterAlways|snap"
        />

</com.google.android.material.appbar.AppBarLayout>

app:layout_scrollFlags=“scroll|enterAlways|snap”,其中scroll表示当RecyclerView向上滚动的时候,Toolbar会跟着一起向上滚动并实现隐藏;enterAlways表示当Recycler向下滚动的时候,Toolbar会跟着一起向下滚动并重新显示;snap表示当Toolbar还没有完全隐藏或显示的时候,会根据滚动的距离,自动选择是隐藏还是显示。

给RecyclerView制定一个布局行为

app:layout_behavior="@string/appbar_scrolling_view_behavior"

SwipeRefreshLayout(实现下拉刷新)

添加依赖

implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'

在RecyclerView外面嵌套一层SwipeRefreshLayout

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:id="@+id/swipeRefresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    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"
        />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

处理刷新逻辑

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    .....
    //设置刷新进度条的颜色
    swipeRefresh.setColorSchemeResources(R.color.design_default_color_primary)
    //设置下拉刷新的监听器
    swipeRefresh.setOnRefreshListener {
        refreshFruit(adapter)
    }
}

//通过本地刷新模拟
private fun refreshFruit(adapter: FruitAdapter){
    thread {
        Thread.sleep(2000)
        runOnUiThread{
            initFruits()
            adapter.notifyDataSetChanged() //通知数据发送改变
            swipeRefresh.isRefreshing=false //表示刷新时间结束,并隐藏进度条
        }

    }
}

可折叠式标题栏

CollapsingToolbarLayout

作用于Toolbar基础上的控件,只能作为AppBarLayout的直接子布局来使用,AppBarLayout必须作为CoordinatorLayout的子布局

新建一个布局

包括标题栏和内容详情

标题拉满使用CoordinatorLayout作为最外层,里面嵌套一个AppBarLayout,然后在AppBarLayout里面再嵌套一个CollapsingToolbarLayout,CollapsingToolbarLayout中放入标题栏中的内容,这里我们放入了一个ImageView展示水果图片和一个Toolbar,表示这是高级版的标题栏是由普通的标题加上图片组合而成。

内容详情由一个NestedScrollView嵌套一个LinearLayout,LinearLayout里面放一个卡片布局,布局中有一个TextView展示内容。

<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/collapsingToolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:contentScrim="@color/design_default_color_primary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">`
            <!--contentScrim趋于折叠状态以及折叠之后的背景颜色-->
            <!--scroll表示会随着内容详情的滚动一起滚动-->
            <!--exitUntilCollapsed表示随着滚动完成折叠之后就保留在界面上,不再移出屏幕-->

            <ImageView
                android:id="@+id/fruitImageView"
                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"/>
            <!--layout_collapseMode指定折叠模式,parallax折叠过程中产生一定的偏移,pin位置不变-->

        </com.google.android.material.appbar.CollapsingToolbarLayout>


    </com.google.android.material.appbar.AppBarLayout>


    <!--NestedScrollView在ScrollView的基础上增加了嵌套响应滚动时间,内部只允许存在一个直接子布局-->
    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <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/fruitContentText"
                    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"
        android:layout_margin="16dp"
        android:src="@mipmap/ic_comment"
        app:layout_anchor="@id/appBar"
        app:layout_anchorGravity="bottom|end"/>
    <!--layout_anchor指定一个锚点-->


</androidx.coordinatorlayout.widget.CoordinatorLayout>

创建fruit的Activity

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)
        setSupportActionBar(toolbar)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        collapsingToolbar.title=fruitName
        Glide.with(this).load(fruitImageId).into(fruitImageView)
        fruitContentText.text=fruitName.repeat(500)

    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            android.R.id.home->{
                finish()
                return true
            }
        }

        return super.onOptionsItemSelected(item)
    }
}

在适配器的onCreateViewHolder方法中实现界面跳转的逻辑,并传递数据

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val view=LayoutInflater.from(context).inflate(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
}

fitsSystemWindows(让标题栏背景与系统状态栏融合)

1.CollapsingToolbarLayout标题栏中的ImageView设置属性android:fitsSystemWindows=“true”,其外层的所有控件都需设置

2.在程序的主题中将状态栏颜色设置为透明

添加主题

<style name="FruitActivityTheme" parent="Theme.MaterialTest">
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

应用主题

<activity 
android:name=".FruitActivity"
android:theme="@style/FruitActivityTheme">
</activity>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值