[Android] UI进阶笔记:从 Toolbar 到可折叠标题栏的完整实战
学习 Android 开发的过程中,UI 控件往往是最直观也最容易踩坑的部分。本文整理了我在学习《第一行代码》后的实践笔记,涵盖 Toolbar
、自定义标题栏、菜单、Snackbar
、CoordinatorLayout
、可折叠标题栏、SwipeRefreshLayout
下拉刷新、FloatingActionButton
悬浮按钮 等常见控件的使用方法和心得体会。通过这些总结,希望能帮助你快速掌握 Android 界面开发的核心知识点,让界面更灵活、更具交互感eve~
Toolbar
Toolbar是什么?
Toolbar
是 Android 5.0)后引入的新控件,用来替代以前的ActionBar
,可以自由控制位置、样式- 因为是可自定义的,所以比 ActionBar 灵活得多
Toolbar 的常见功能
- 应用标题
默认显示 App 名称,你可以改成自定义标题 - 导航按钮(返回键 / 菜单按钮)
可以添加返回箭头、菜单按钮,或者换成你想要的图标 - 菜单
可以在右上角加“搜索”“更多”等按钮 - 完全自定义布局
Toolbar 内部可以放 TextView、ImageView,甚至自定义控件
Toolbar的使用(配合菜单)
- 可以直接在layout布局文件中使用:
<Toolbarandroid:layout_width="match_parent"android:layout_height="40dp"android:id = "@+id/toolbar"android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"app:popupTheme = "@style/ThemeOverlay.AppCompat.Dark"app:layout_constraintTop_toTopOf="parent"android:title="你好"></Toolbar>
以上代码中有两个较为陌生的属性:
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar
"代表顶部导航栏的主题颜色
@style/ThemeOverlay.AppCompat.Dark.ActionBar
代表白色字体图标;@style/ThemeOverlay.AppCompat.Light
表示深色字体图标
app:popupTheme = "@style/ThemeOverlay.AppCompat.Dark"
表示菜单的主体颜色
@style/ThemeOverlay.AppCompat.Light
→ 白色弹窗
@style/ThemeOverlay.AppCompat.Dark
→ 深色弹窗
app:navigationIcon
设置左上角的导航图标(常见是返回箭头)
菜单项这样来写:
<itemandroid:id="@+id/it1"android:icon="@drawable/ic_launcher_background"android:title="TODO"app:showAsAction = "always"></item>
<itemandroid:id="@+id/it2"android:icon="@drawable/ic_launcher_foreground"android:title="TOD1"app:showAsAction = "ifRoom"></item>
<itemandroid:id="@+id/it3"android:title="TOD2"android:icon="@drawable/ic_launcher_foreground"app:showAsAction = "never"></item>
app:showAsAction = "always"
代表表现的方式:
always
代表一定要显示在顶部导航栏,否则不显示
ifRoom
代表如果能显示显示,不能显示就去菜单项
never
一定在菜单项
最后把菜单项膨胀到视图,一定要使用这个方法,因为有菜单时,系统会回调这个方法;
public boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.toolbai,menu);return true;
}
public boolean onOptionsItemSelected(MenuItem item) {if (item.getItemId() == R.id.action_search) {Toast.makeText(this, "点击了搜索", Toast.LENGTH_SHORT).show();return true;}return super.onOptionsItemSelected(item);
}
设置toolbar
Toolbar toolbars = (Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbars);
设置导航按钮(返回箭头)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbars.setNavigationOnClickListener(v -> finish());
注意:toolbar最好使用androidx
androidx.appcompat.widget.Toolbar
使用Androidx的toolbar时需要添加依赖,依赖如下:
implementation 'androidx.appcompat:appcompat:1.6.1'
当然我们也可以自定义toolbar,在toolbar中自定义控件:
Toolbar toolbar = findViewById(R.id.custom_toolbar);
setSupportActionBar(toolbar);
// 去掉系统默认标题,使用我们自己的 TextView
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false)
;核心就是设置后setSupportActionBar的这一句代码,我们就可以使用自定义控件了,那么具体怎么自定义,那就看你想要什么样子的效果啦;
不过需要注意一点,这里只是去掉了默认的标题,所以系统默认的标题不显示了,但是我们仍然可以使用其他方法,比如设置返回图标;
好了,Toolbar的简单学习先到这里;
FloatingActionButton
这是悬浮按钮,我们可以做点击事件
在xml文件中简单使用如下
<com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom|right"android:layout_margin="20dp"android:id = "@+id/fa"android:elevation="2dp"android:src = "@drawable/ic_launcher_foreground"/>
属性解释:
android:elevation="2dp"
数值越大,阴影面积大,但浅,反之则相反;
android:src = "@drawable/ic_launcher_foreground"
可用来添加图片
Snackbar
一个功能类似toast提示框,与toast不同的是,可以和用户交互
floatingActionButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Snackbar.make(view,"嘿嘿",Snackbar.LENGTH_LONG).setAction("确定", new View.OnClickListener() {@Overridepublic void onClick(View view) {Toast.makeText(MainActivity.this,"雪雪学安卓",Toast.LENGTH_SHORT).show();}}).show();}
});
Snackbar.make(view,"嘿嘿",Snackbar.LENGTH_LONG);
-
make方法是确认提示主体,第一个参数是界面任意一个view,snackbar会自动选择最上层那一个,第二个参数是提示语
-
setAction方法
通过setaction方法和用户交互,第一个参数是按钮效果,第二个参数做监听
最后别忘记show()!
CoordinatorLayout布局
这个布局和帧布局差不多
但是它可以监听所有的子控件,从而做出最佳响应:比如悬浮按钮和提示框重合问题
需要注意的是:
那前提得是子控件,所以提示框传入的view参数得是coordinatorlayout的子控件了;
glide依赖如下
implementation 'com.github.bumptech.glide:glide:4.16.0'
Recycleview进阶版
可能照片资源过大,所以这个时候就需要它了;
依赖如下:
implementation 'com.github.bumptech.glide:glide:4.16.0'
Glide.with(mcontext).load(fruit.getImageid()).into(holder.imageView);
第一个参数是context,第二个是把这个id加载到第三个参数的布局中;
GridLayoutManager layoutManager = new GridLayoutManager(this,2);
代表两列
<androidx.coordinatorlayout.widget.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><com.google.android.material.appbar.AppBarLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><androidx.appcompat.widget.Toolbarandroid:layout_width="match_parent"android:layout_height="40dp"android:id = "@+id/toolbar"android:theme="@style/Widget.AppCompat.Light.ActionBar"app:popupTheme = "@style/ThemeOverlay.AppCompat.Light"app:layout_scrollFlags="scroll|enterAlways|snap"app:layout_constraintTop_toTopOf="parent"android:title="你好"></androidx.appcompat.widget.Toolbar></com.google.android.material.appbar.AppBarLayout><androidx.recyclerview.widget.RecyclerViewandroid:layout_width="match_parent"android:layout_height="match_parent"app:layout_behavior="@string/appbar_scrolling_view_behavior"android:id="@+id/recy"></androidx.recyclerview.widget.RecyclerView>
陌生属性介绍:
-
com.google.android.material.appbar.AppBarLayout
这个布局为了顶部导航栏的显示; -
app:layout_behavior="@string/appbar_scrolling_view_behavior"
为了顶部导航栏的显示,否则可能遮挡顶部导航栏; -
app:layout_scrollFlags="scroll|enterAlways|snap"
scroll
作用:视图会随着内容滚动而一起滚动(向上滑时隐藏,向下滑时显示)
enterAlways
作用:
向下滚动时立即显示视图(不需要滚动到顶部)
snap
作用: 滚动停止时自动"吸附"到最近的边界(要么完全显示,要么完全隐藏, 提供更流畅的用户体验)
exitUntilCollapsed
作用:
- 视图会滚动退出,但不会完全消失,而是停留在折叠状态
- 必须与
scroll
标志一起使用 - 需要定义
minHeight
属性指定折叠后的高度
我们通常是scroll
和exitUntilCollapsed
搭配使用!
SwipeRefreshLayout下拉刷新
依赖:
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
xml文件:用法包在recyview外面表示刷新
<androidx.swiperefreshlayout.widget.SwipeRefreshLayoutandroid:layout_width="match_parent"app:layout_behavior="@string/appbar_scrolling_view_behavior"android:id = "@+id/swip"android:layout_height="match_parent"><androidx.recyclerview.widget.RecyclerViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/recy"></androidx.recyclerview.widget.RecyclerView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
在mainactivity
我把过程中难以理解的代码都标注出来啦
SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swip);
swipeRefreshLayout.setColorSchemeColors(R.color.black);//刷新的颜色
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
//每次刷新都会回调@Overridepublic void onRefresh() {//经常得到数据之后,开始刷新,所以要在子线程中new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}//回到主线程中runOnUiThread(new Runnable() {@Overridepublic void run() {//适配器刷新Notifyfruitadapots.notifyDataSetChanged();//表示刷新事件结束,隐藏刷新进度条swipeRefreshLayout.setRefreshing(false);}});}}).start();}
});
可折叠式表示栏
CoordinatorLayout
这是最外层的布局
然后你需要把你的用于折叠的照片,还有顶部导航栏,都设置在**appbarlayout
**布局中
<com.google.android.material.appbar.AppBarLayoutandroid:layout_height="250dp"android:layout_width="match_parent"android:id = "@+id/appbar"><com.google.android.material.appbar.CollapsingToolbarLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:id = "@+id/coll"android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"app:contentScrim="?attr/colorPrimary"app:layout_scrollFlags="snap|scroll|exitUntilCollapsed"><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:id = "@+id/iv"app:layout_collapseMode="parallax"android:scaleType="centerCrop"></ImageView><androidx.appcompat.widget.Toolbarandroid:layout_width="match_parent"android:layout_height="50dp"android:id = "@+id/toolbar"app:layout_collapseMode="pin"></androidx.appcompat.widget.Toolbar></com.google.android.material.appbar.CollapsingToolbarLayout></com.google.android.material.appbar.AppBarLayout>
定义在CollapsingToolbarLayout
-
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
代表设置一个主题 -
app:contentScrim="?attr/colorPrimary"
代表这个布局在趋于折叠和折叠后的背景色 -
app:layout_scrollFlags="snap|scroll|exitUntilCollapsed"
是appbarlayout
中的属性
app:layout_collapseMode="parallax"
表示折叠模式
有三种折叠模式:
Pin
代表在折叠时永不消失,这里代表顶部导航栏,在折叠会回固定在顶部,通常和toolbar一起联合使用;
parallax
模式(视差模式)
效果:控件以较慢速度滚动,产生景深效果,我们还可以设置它的滚动的速度,更有感觉。
滚动的速度通过此来控制,数值越小,速度越慢:
app:layout_collapseParallaxMultiplier="0.7"
默认
模式(无模式)
效果:控件会随滚动完全消失,一般不设置,无折叠效果。
中间内容:经常是recycleview等等
<androidx.core.widget.NestedScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"app:layout_behavior="@string/appbar_scrolling_view_behavior"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><androidx.cardview.widget.CardViewandroid:layout_width="match_parent"android:id = "@+id/card"app:cardCornerRadius="4dp"android:layout_margin="10dp"android:layout_height="wrap_content"><TextViewandroid:layout_width="match_parent"android:id = "@+id/tv"android:layout_height="wrap_content"></TextView></androidx.cardview.widget.CardView></LinearLayout></androidx.core.widget.NestedScrollView>
app:layout_behavior="@string/appbar_scrolling_view_behavior"
这个属性搭配折叠效果;
NestedScrollView**
只能有一个直接子项布局**
体会
编者对于在运用折叠式标题栏时有了一些新的体会:
- 当你在某控件中设置
parallax
,即使折叠后仍然显示当前折叠时的背景,它不会因为折叠就直接消失显示pin模式下的控件,假如parallax
设置的是背景图,那么背景图会和pin
模式下的视图重叠,直接你看不到是因为通常toobar
设置的是pin
,toolbar
的会挡住背景图,当你尝试把toolbar
设置为透明时,你会发现有重合的问题qvq; - 折叠后的高度由谁决定?
AI中会解释道:由toolbar
的高度决定,但你仔细看,你就会发现是由使用pin模式的控件决定的,因为toolbar
设置了pin
,所以AI才会这样解释,当我们toolbar
中的设置为100dp,那么折叠后的高度就是100dp了;此时又发现一个属性minheigh
(控制CollapsingToolbarLayout
的最小可见高度),它也会影响高度,当它的高度大于PIN
模式下控件的高度时,那么此时折叠后的高度就是minheight
了,所以如果是minHeight
比toolbar
的高度要高,最后的结果就是上面是toolbar
,折叠后仍然留有一部分高度是minheight
的剩余高度; - 可折叠模式写在
coll
布局的直接子view中; - 当有很多空间都设置了
pin
模式,按照在xml文件中的排布在顶部依次显示; - 可折叠式标题栏的核心:
最外层是coor
的一个布局
中间套appbarlayout
负责响应滑动:android:fitsSystemWindows="true"
这里这个属性true
代表toolbar
在状态栏下方,避免系统的窗口区域和布局重叠;
里面是coll
的一个布局:里面写两个重要属性:app:layout_scrollFlags
,控制滚动的效果,app:contentScrim
折叠后显示的背景色(不写会出现透明/白色突兀问题);
在未折叠的布局中写属性控制折叠后的效果;
在内容区例如recycleview
或者scroll
中写app:layout_behavior="@string/appbar_scrolling_view_behavior"
不然滑动不会带动 AppBar
折叠;
希望编者的理解对你有帮助哦!
悬浮按钮升级版
<com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="16dp"app:layout_anchor="@id/appbar"app:layout_anchorGravity="bottom|end"></com.google.android.material.floatingactionbutton.FloatingActionButton>
app:layout_anchor="@id/appbar"
锚定谁
app:layout_anchorGravity="bottom|end"
锚定谁的哪里
anchor
锚点:
这么设置就会出现在标题栏的区域内
另外:
collapsingToolbarLayout.setTitle("i like");
这里也可以设置折叠后导航栏的标题,搭配顶部导航栏的使用;
本次分享到这里结束!