# 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"
>
~~~
效果如下圖所示:

# 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`來進行實例化,進而設置。
效果:

## 3.1 響應事件
~~~
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// item.itemId
}
~~~
# 4. 更多操作
## 4.1 設置大圖顯示問題
通過設置:
~~~
toolbar.navigationIcon = getDrawable(R.drawable.icon)
~~~
運行效果:

填充了整個`toolbar`,但是我們所希望的是在左邊顯示一個圖片,而不是這樣,比如下面的效果:

且在之前的一個小圖(`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 使用圓形圖片

雖然上面的效果可以將一個大圖縮放為一個小圖,進而達到顯示的目的。但是,卻還是太丑。所以這里需要將其整為一個圓形圖片。由于返回的是一個`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)
}
~~~
效果:

但是,這里有兩個缺點:
- 鋸齒太多;
- 布局沒有居中;
考慮到前面我們使用過第三方`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)
~~~
效果:

然后,我們添加更多的按鈕`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)
}
~~~
效果:

點擊圖片后:

當然,可以再修改一下狀態欄的顏色:
~~~
// 設置狀態欄顏色
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">
~~~
效果:

# 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)
}
~~~
效果:

- 介紹
- 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特效