前言
在项目中经常会看到TitleBar是一个返回按钮+标题居中显示的设计页面,通常的做法如下:
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/white"
app:navigationIcon="@drawable/ic_svg_back_ptc_24"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:singleLine="true"
android:ellipsize="end"
android:text="Documents"
android:textSize="18sp"
android:textColor="#ff333333"
/>
</androidx.appcompat.widget.Toolbar>
其中返回按钮ic_svg_back_ptc_24对应的文件为:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/dp_24"
android:height="@dimen/dp_24"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/black"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
</vector>
</vector>
这样做是可以实现效果,但是如果app中有大量这样的页面,我们就要在不同页面写很多这样重复的代码,有没有更简洁的方法呢?让我想想啊,Toolbar本身是带标题栏的,但标题栏不是居中显示的,原生Toolbar生成显示标题栏对应的代码如下:
/**
* Set the title of this toolbar.
*
* <p>A title should be used as the anchor for a section of content. It should
* describe or name the content being viewed.</p>
*
* @param title Title to set
*/
public void setTitle(CharSequence title) {
if (!TextUtils.isEmpty(title)) {
if (mTitleTextView == null) {
final Context context = getContext();
mTitleTextView = new AppCompatTextView(context);
mTitleTextView.setSingleLine();
mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
if (mTitleTextAppearance != 0) {
mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
}
if (mTitleTextColor != null) {
mTitleTextView.setTextColor(mTitleTextColor);
}
}
if (!isChildOrHidden(mTitleTextView)) {
addSystemView(mTitleTextView, true);
}
} else if (mTitleTextView != null && isChildOrHidden(mTitleTextView)) {
removeView(mTitleTextView);
mHiddenViews.remove(mTitleTextView);
}
if (mTitleTextView != null) {
mTitleTextView.setText(title);
}
mTitleText = title;
}
既然原生Toolbar不符合我们的要求,那我们就自定义一个Toolbar覆盖setTitle方法并在此方法里设置Title居中显示就好了,于是参考Toolbar源码,封装了一个Title居中显示的Toolbar ,完整代码如下:
import android.content.Context
import android.content.res.ColorStateList
import android.text.TextUtils
import android.util.AttributeSet
import android.view.Gravity
import android.view.ViewGroup
import android.widget.TextView
import androidx.annotation.NonNull
import androidx.annotation.StyleRes
import androidx.appcompat.R
import androidx.appcompat.widget.AppCompatTextView
import androidx.appcompat.widget.Toolbar
import androidx.core.widget.TextViewCompat
/**
* Title居中的Toolbar
*/
class CenterTitleToolbar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : Toolbar(context, attrs, defStyleAttr) {
private var mTitleTextAppearance = 0
private var mTitleTextColor: ColorStateList? = null
private var mTitleTextView: TextView? = null
private var mTitleText: CharSequence? = null
init {
val ta = getContext().obtainStyledAttributes(attrs, R.styleable.Toolbar, defStyleAttr, 0)
mTitleTextAppearance = ta.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0)
mTitleText = ta.getText(R.styleable.Toolbar_title)
if (!TextUtils.isEmpty(mTitleText)) {
title = mTitleText
}
if (ta.hasValue(R.styleable.Toolbar_titleTextColor)) {
setTitleTextColor(ta.getColorStateList(R.styleable.Toolbar_titleTextColor)!!)
}
ta.recycle()
}
override fun setTitle(title: CharSequence?) {
if (!TextUtils.isEmpty(title)) {
if (mTitleTextView == null) {
mTitleTextView = AppCompatTextView(context).apply {
isSingleLine = true
ellipsize = TextUtils.TruncateAt.END
addView(this, LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER))
}
}
if (mTitleTextAppearance != 0) {
mTitleTextView?.apply {
TextViewCompat.setTextAppearance(this,mTitleTextAppearance)
}
}
if (mTitleTextColor != null) {
mTitleTextView?.setTextColor(mTitleTextColor)
}
}
else if(mTitleTextView!=null){
removeView(mTitleTextView)
}
mTitleTextView?.text = title
mTitleText = title
}
override fun getTitle(): CharSequence? {
return mTitleText
}
override fun setTitleTextColor(@NonNull color: ColorStateList) {
mTitleTextColor = color
mTitleTextView?.setTextColor(color)
}
override fun setTitleTextAppearance(context: Context, @StyleRes resId: Int) {
mTitleTextAppearance = resId
mTitleTextView?.apply {
TextViewCompat.setTextAppearance(this,resId)
}
}
}
使用
- 定义Toolbar Style
<style name="TKToolbarStyle" parent="@style/Widget.AppCompat.Toolbar">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">?attr/actionBarSize</item>
<item name="android:background">@android:color/white</item>
<item name="android:clickable">true</item>
<item name="navigationIcon">@drawable/ic_svg_back_ptc_24</item>
<item name="titleTextAppearance">@style/TKToolbarTitleAppearance</item>
</style>
<style name="TKToolbarTitleAppearance" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">18sp</item>
<item name="android:textColor">#ff333333</item>
</style>
- 在layout文件中引用CenterTitleToolbar
<CenterTitleToolbar
style="@style/TKToolbarStyle"
app:title="Documents"
/>
显示效果如下:
Toolbar.png
总结
我们通过继承原生Toolbar,并覆盖setTitle方法,优雅简单的实现了一个Title居中显示的CenterTitleToolbar ,该类可复用简化大量模版代码,同时又继承了原生Toolbar所有的特性。