# 1. 前言
每个`Activity`最顶部的那个标题栏其实就是`ActionBar`,不过`ActionBar`由于其设计的原因,被限定只能位于`Activity`的顶部,从而不能实现一些`Material Design`的效果,因此官方现在已经不再建议使用`ActionBar`了,更加推荐使用的`Toolbar`。
# 2. 简单使用
为了测试,这里新建一个`TestActivity`,对应的布局文件为`activity_test.xml`,那么在这个布局文件中首先需要指定`Toolbar`:
~~~
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/testToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/gray"
/>
</LinearLayout>
~~~
当然,我们需要将默认的`ActionBar`去除掉,也就是设置一下主题:
~~~
<style name="Theme.MyApplication" parent="Theme.MaterialComponents.Light.NoActionBar">
~~~
然后,在`TestActivity`文件中进行配置`Toolbar`:
~~~
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
val view = findViewById<Toolbar>(R.id.testToolbar)
setSupportActionBar(view)
}
}
~~~
关键代码也就是`setSupportActionBar(toolbarInstance)`。通过上述步骤,就可以达到预想结果。最终顶部`Toolbar`上左边显示的文本默认为应用名,我们可以对其进行修改,即在清单文件中进行指定`lable`属性:
~~~
<activity
android:name=".activitys.TestActivity"
android:label="测试Activity"
>
~~~
效果如下图所示:
![](https://img.kancloud.cn/37/05/3705944ee391366bc1e64ce41e856cf8_309x86.png)
# 3. 添加更多的按钮
在`res`目录下新建一个`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">
<item
android:title="Button1"
android:id="@+id/bt1"
android:icon="@drawable/ic_baseline_menu_24"
app:showAsAction="always"
/>
<item
android:id="@+id/bt2"
android:title="Button2"
android:icon="@drawable/ic_baseline_audiotrack_24"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/bt3"
android:title="Button3"
android:icon="@drawable/ic_baseline_bluetooth_24"
app:showAsAction="never"/>
</menu>
```
`android:id`用于指定按钮的`id`,`android:icon`用于指定按钮的图标,`android:title`用于指定按钮的文字。
在`item`中值得注意的是`app:showAsAction`部分,用来指定按钮显示的位置,主要可取值:
- `always`:表示永远显示在`toolbar`中,如果屏幕空间不够则不显示;
- `ifRoom`:表示如果屏幕空间足够的情况下,显示在`toolbar`中,否则就显示在`more`菜单中;
- `never`:表示永远显示在菜单中。
值得注意的是在`more`菜单中只会显示文本,不会显示图标。
然后我们需要在`Activity`的`onCreateOptionsMenu`方法中对这个`xml`文件进行关联:
```
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.toobar, menu);
return true;
}
```
在`onCreateOptionsMenu`方法中返回`true`表示显示这些控件,默认返回`false`。且对于`Menu`来说,由自己特定的`Inflater`即`MenuInflater`。
且观察上面代码我们也知道,`Menu`也不是自动关联,而是需要我们自己使用`MenuInflater`来进行实例化,进而设置。
效果:
![](https://img.kancloud.cn/56/d1/56d10648ebe2f1ab3ca33257c5e661cb_376x87.png)
## 3.1 响应事件
~~~
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// item.itemId
}
~~~
# 4. 更多操作
## 4.1 设置大图显示问题
通过设置:
~~~
toolbar.navigationIcon = getDrawable(R.drawable.icon)
~~~
运行效果:
![](https://img.kancloud.cn/ac/68/ac68b1c5bae5df5d04680a725102e3cd_400x98.png)
填充了整个`toolbar`,但是我们所希望的是在左边显示一个图片,而不是这样,比如下面的效果:
![](https://img.kancloud.cn/e9/39/e939d572091a199b118730ce04b7f75d_663x197.png)
且在之前的一个小图(`24x24`)中是可以正常显示的,所以这里猜测原因为图片过大(`700x700`)。那么就有两种解决方案:
- 使用`PS`处理图片为小图;
- 使用图片压缩;
这里就简单记录下如何进行图片压缩的,这里我定义了一个工具类来将资源图片中的文件返回一个压缩后的`Drawable`对象的方法:
~~~
fun zooImgToBitmap(ctx: Context, id: Int, newWidth: Int, newHeight: Int): Bitmap{
// 从资源id获取到图片的Bitmap对象
val imageBitmap = BitmapFactory.decodeResource(ctx.resources, id)
// 获取图片宽高
val width = imageBitmap.width
val height = imageBitmap.height
// 计算缩放比例
val scaleWidth = (newWidth * 1.0 / width).toFloat()
val scaleHeight = (newHeight * 1.0 / height).toFloat()
// 设置缩放
val matrix = Matrix()
matrix.postScale(scaleWidth, scaleHeight)
// 返回新的图片
return Bitmap.createBitmap(imageBitmap, 0, 0, width, height, matrix, true)
}
fun zooImgToDrawable(ctx: Context, id: Int, newWidth: Int, newHeight: Int): Drawable{
val bitmap = zooImgToBitmap(ctx, id, newWidth, newHeight)
val instance = BitmapDrawable(null, bitmap) as Drawable
return instance
}
~~~
那么我们只需要进行调整即可:
~~~
toolbar.navigationIcon = getUserDrawableIcon(R.drawable.icon)
// 加载一个图片
fun getUserDrawableIcon(id: Int): Drawable {
return zooImgToDrawable(this, id, 200, 200)
}
~~~
效果就是上面的结果。
## 4.2 使用圆形图片
![](https://img.kancloud.cn/e9/39/e939d572091a199b118730ce04b7f75d_663x197.png)
虽然上面的效果可以将一个大图缩放为一个小图,进而达到显示的目的。但是,却还是太丑。所以这里需要将其整为一个圆形图片。由于返回的是一个`Drawable`对象,而这个对象的`draw`方法已经确定,故而这里如果我们需要修改就需要自定义一个类,继承自`Drawable`:
~~~
class CircleImage : Drawable{
val paint by lazy { Paint() }
val path by lazy { Path() }
lateinit var bitmap: Bitmap
var radius: Float = 0f
var width: Float = 0f
var height: Float = 0f
constructor(bitmap: Bitmap){
this.bitmap = bitmap
width = bitmap.width.toFloat()
height = bitmap.height.toFloat()
if(width > height) {
radius = height * 1.0f / 2
} else{
radius = width * 1.0f / 2
}
path.addCircle(width * 1.0f / 2, height * 1.0f / 2, radius, Path.Direction.CCW)
}
override fun draw(canvas: Canvas) {
canvas.save()
canvas.clipPath(path)
canvas.drawBitmap(bitmap, 0f, 0f, paint)
canvas.restore()
}
override fun setAlpha(alpha: Int) {
}
override fun getOpacity(): Int {
return PixelFormat.UNKNOWN
}
override fun setColorFilter(colorFilter: ColorFilter?) {
}
}
~~~
然后调用:
~~~
fun zooImgToCircleDrawable(ctx: Context, id: Int, newWidth: Int, newHeight: Int): Drawable{
val bitmap = zooImgToBitmap(ctx, id, newWidth, newHeight)
return CircleImage(bitmap)
}
~~~
~~~
toolbar.navigationIcon = getUserDrawableIcon(R.drawable.icon)
// 加载一个图片
fun getUserDrawableIcon(id: Int): Drawable {
return zooImgToCircleDrawable(this, id, 80, 80)
}
~~~
效果:
![](https://img.kancloud.cn/03/e9/03e938c1baa46b0c72a8b084f7e7d333_275x193.png)
但是,这里有两个缺点:
- 锯齿太多;
- 布局没有居中;
考虑到前面我们使用过第三方`CircleImageView`,故而这里也还是考虑使用这个来实现。那么就需要自己直接定义需要的布局文件:
~~~
<!-- 顶部导航栏 -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_main"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetStartWithNavigation="0dp"
android:background="@color/white" >
<!--圆角图片组件-->
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerInParent="true"
android:layout_marginRight="20dp"
android:src="@drawable/icon"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/note_page_title"
android:textColor="@color/black"
android:textStyle="bold"
android:textSize="20sp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/note_page_summary"
android:textColor="@color/gray"
android:textSize="12sp"
android:layout_marginLeft="10dp"
/>
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
~~~
然后在代码中将标题和左侧`menu`均设置不显示:
~~~
// 设置Toolbar
setSupportActionBar(toolbar)
// 不要左侧menu
supportActionBar?.setDisplayHomeAsUpEnabled(false)
// 不显示标题
supportActionBar?.setDisplayShowTitleEnabled(false)
~~~
效果:
![](https://img.kancloud.cn/24/3b/243b4fd9e03a7f212219b9b59bbdc361_398x100.png)
然后,我们添加更多的按钮`res/menu/toolbar_item.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">
<item
android:id="@+id/bt2"
android:title="@string/note_page_description"
android:icon="@drawable/ic_baseline_search_24"
app:showAsAction="always"/>
<item
android:id="@+id/bt3"
android:title="@string/note_page_setting"
android:icon="@drawable/ic_baseline_settings_24"
app:showAsAction="ifRoom"/>
</menu>
~~~
在`Activity`中复写:
~~~
// 添加Toolbar菜单栏按钮
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.toolbar_item, menu)
return true
}
~~~
然后为圆角图片添加点击事件,以打开侧滑菜单栏:
~~~
note_page_toolbar_icon.setOnClickListener {
// 打开侧滑
drawerlayout.openDrawer(GravityCompat.START)
}
~~~
效果:
![](https://img.kancloud.cn/bf/7b/bf7bcffa7495af60f3db4c325e347ace_399x232.png)
点击图片后:
![](https://img.kancloud.cn/7f/61/7f615b0053878bf26973402e27c35531_407x321.png)
当然,可以再修改一下状态栏的颜色:
~~~
// 设置状态栏颜色
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(getResources().getColor(R.color.bottom_text_color_pressed, null));
~~~
# 5. 设置弹出菜单与ToolBar的位置
可以参考博客:[Toolbar中Menu的背景颜色、位置、同时显示文字等设置(亲测) - osc\_m1h580xl的个人空间 - OSCHINA - 中文开源技术交流社区](https://my.oschina.net/u/4413523/blog/3826279)
先定义一个样式:
~~~
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" >
<!-- 是否覆盖锚点,默认为true,即盖住Toolbar -->
<item name="overlapAnchor">false</item>
<!--距离右边间距-->
<item name="android:paddingRight">0dp</item>
<!--背景颜色-->
<item name="android:colorBackground">#000000</item>
<!--字体颜色-->
<item name="android:textColorPrimary">#ffffff</item>
</style>
~~~
然后进行设置`Toolbar`使用这个弹出主题:
~~~
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_main"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/bottom_text_color_pressed"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_collapseMode="pin">
~~~
效果:
![](https://img.kancloud.cn/20/a5/20a5472064e990b8aee81f8684e7b1cd_293x190.png)
# 6. 弹出optionMenu显示图标
首先确保弹出的菜单配置中有图片指定:
~~~
<?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">
<item
android:id="@+id/bt2"
android:icon="@drawable/ic_baseline_search_24"
android:title="@string/note_page_description"
app:actionViewClass="android.widget.SearchView"
app:showAsAction="ifRoom" />
<!--下面是两个用来在optionalMenu中显示的按钮-->
<item
android:id="@+id/bt3"
android:icon="@drawable/ic_baseline_home_normal_24"
android:title="@string/note_page_setting"
app:showAsAction="never" />
<item
android:id="@+id/bt4"
android:icon="@drawable/ic_baseline_check_circle_normal_24"
android:title="@string/note_page_setting"
app:showAsAction="never" />
</menu>
~~~
然后,通过反射来设置`Toolbar`弹出菜单有图标:
~~~
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
if (menu is MenuBuilder) {
val clazz = menu.javaClass
val declaredMethod =
clazz.getDeclaredMethod("setOptionalIconsVisible", Boolean::class.java)
declaredMethod.isAccessible = true
declaredMethod.invoke(menu, true)
}
return super.onPrepareOptionsMenu(menu)
}
~~~
效果:
![](https://img.kancloud.cn/60/0c/600c1dc4be8714c5854b5c375ef5c04e_385x210.png)
- 介绍
- UI
- MaterialButton
- MaterialButtonToggleGroup
- 字体相关设置
- Material Design
- Toolbar
- 下拉刷新
- 可折叠式标题栏
- 悬浮按钮
- 滑动菜单DrawerLayout
- NavigationView
- 可交互提示
- CoordinatorLayout
- 卡片式布局
- 搜索框SearchView
- 自定义View
- 简单封装单选
- RecyclerView
- xml设置点击样式
- adb
- 连接真机
- 小技巧
- 通过字符串ID获取资源
- 自定义View组件
- 使用系统控件重新组合
- 旋转菜单
- 轮播图
- 下拉输入框
- 自定义VIew
- 图片组合的开关按钮
- 自定义ViewPager
- 联系人快速索引案例
- 使用ListView定义侧滑菜单
- 下拉粘黏效果
- 滑动冲突
- 滑动冲突之非同向冲突
- onMeasure
- 绘制字体
- 设置画笔Paint
- 贝赛尔曲线
- Invalidate和PostInvalidate
- super.onTouchEvent(event)?
- setShadowLayer与阴影效果
- Shader
- ImageView的scaleType属性
- 渐变
- LinearGradient
- 图像混合模式
- PorterDuffXfermode
- 橡皮擦效果
- Matrix
- 离屏绘制
- Canvas和图层
- Canvas简介
- Canvas中常用操作总结
- Shape
- 圆角属性
- Android常见动画
- Android动画简介
- View动画
- 自定义View动画
- View动画的特殊使用场景
- LayoutAnimation
- Activity的切换转场效果
- 属性动画
- 帧动画
- 属性动画监听
- 插值器和估值器
- 工具
- dp和px的转换
- 获取屏幕宽高
- JNI
- javah命令
- C和Java相互调用
- WebView
- Android Studio快捷键
- Bitmap和Drawable图像
- Bitmap简要介绍
- 图片缩放和裁剪效果
- 创建指定颜色的Bitmap图像
- Gradle本地仓库
- Gradle小技巧
- RxJava+Okhttp+Retrofit构建网络模块
- 服务器相关配置
- node环境配置
- 3D特效