企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 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)