Android标题居中的Toolbar

2017年特意写第一篇博客开个头。之前一直不写博客的原因,一个是懒,另一个是因为觉得自己没那个能力,会误导别人。但是最近特别想听听别人的意见,所以就下定决心开始写博客。废话不说,赶快开波。

前言

用过Toolbar的都知道标题是在左边的,但通常UI都要求我们把标题居中。没用过的童鞋可以看看泡网的一篇文章android:ToolBar详解(手把手教程)。我并不想自己写一个布局来充当Toolbar,因为那样会增加不少工作量。但怎样才能把Toolbar的标题居中?
有问题先google。如果你还在百度的话,我也没什么办法了。谷歌后找到这样一种方法。在原来的Toolbar里面加上一个TextView,标题居中的关键是第12行,将layout_gravity设置成center。

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary">

        <TextView
            android:id="@+id/toolbar_title"
            style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"/>
    </android.support.v7.widget.Toolbar>

接着再来个helper类。很简单的一个类,看看init方法就可以过了。

public class ToolbarHelper {
    private TextView mTitleTextView;
    private Toolbar mToolbar;

    public void init(AppCompatActivity activity, Toolbar toolbar, TextView titleTextView) {
        if (activity == null) {
            return;
        }
        mTitleTextView = titleTextView;
        mToolbar = toolbar;
        activity.setSupportActionBar(toolbar);
        // titleTextView不为null才设置actionbar不显示Title
        if (titleTextView != null) {
            ActionBar actionBar = activity.getSupportActionBar();
            if (actionBar != null) {
                // 不显示title和subTitle
                actionBar.setDisplayShowTitleEnabled(false);
            }
        }
    }

    public void setTitle(@StringRes int resid) {
        if (mTitleTextView != null) {
            mTitleTextView.setText(resid);
        }
    }

    public void setTitle(CharSequence title) {
        if (mTitleTextView != null) {
            mTitleTextView.setText(title);
        }
    }

    public void setTitleTextColor(@ColorInt int color) {
        if (mTitleTextView != null) {
            mTitleTextView.setTextColor(color);
        }
    }

    public Toolbar getToolbar() {
        return mToolbar;
    }

    public TextView getTitleTextView() {
        return mTitleTextView;
    }
}

然后在Activity中,最好是BaseActivity,创建一个ToolbarHelper对象。在初始化View的时候,调用toolbarHelper的init()方法。

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
TextView titleView = (TextView) findViewById(R.id.toolbar_title);
mToolbarHelper.init(this, toolbar, titleView);

接下来才是重点,重写onTitleChanged方法

    @Override
    protected void onTitleChanged(CharSequence title, int color) {
        super.onTitleChanged(title, color);
        if (mToolbarHelper != null) {
            mToolbarHelper.setTitle(title);
        }
    }

这样标题居中的Toolbar就搞定了。但有一点值得注意的是,不要调用toolbar自身的setTitle()方法。否则的话Toolbar自身的标题也会显示。

重点来了,CenterTitleToolbar

这样就满足了吗?不,上面这种方法太麻烦了。又要在xml里面加东西,又要改activity。能不能只改动Toolbar里面的代码,不需要动Toolbar以外的任何代码就能实现标题居中?
基于这个思路,我们要创建一个Toolbar的子类。使Toolbar的title居中有两种做法。

  • 通过反射获取其mTitleTextView属性,并修改其LayoutParams的gravity属性
  • 重写toolbar的setTitle方法,自己维护一个titleTextView,并将其居中显示。

这里我选择第二种。因为第一种使用了反射,且mTitleTextView是通过懒加载形式创建的,要考虑好反射获取的时机。OK,想好了就开工了。
先来看看Toolbar源码里的setTitle()方法

    public void setTitle(CharSequence title) {
        if (!TextUtils.isEmpty(title)) {
            if (mTitleTextView == null) {
                final Context context = getContext();
                mTitleTextView = new TextView(context);
                mTitleTextView.setSingleLine();
                mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
                if (mTitleTextAppearance != 0) {
                    mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
                }
                if (mTitleTextColor != 0) {
                    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;
    }

看到第3-14行就知道我刚刚为什么说mTitleTextView是通过懒加载来创建了吧。其他都看懂了,isChildOrHidden,addSystemView这两个方法做了什么?继续看源码。

    // 判断该view的父View是否为Toolbar
    private boolean isChildOrHidden(View child) {
        return child.getParent() == this || mHiddenViews.contains(child);
    }

    // 创建LayoutParams对象,并添加到Toolbar
    private void addSystemView(View v, boolean allowHide) {
        final ViewGroup.LayoutParams vlp = v.getLayoutParams();
        final LayoutParams lp;
        if (vlp == null) {
            lp = generateDefaultLayoutParams();
        } else if (!checkLayoutParams(vlp)) {
            lp = generateLayoutParams(vlp);
        } else {
            lp = (LayoutParams) vlp;
        }
        lp.mViewType = LayoutParams.SYSTEM;

        if (allowHide && mExpandedActionView != null) {
            v.setLayoutParams(lp);
            mHiddenViews.add(v);
        } else {
            addView(v, lp);
        }
    }

这两个方法中都有mHiddenViews,这个对于我们的需求没什么用,忽略就好了。不必每句都看懂,我们知道它大概做了什么即可。
OK,开始写我们的CenterTitleToolbar。先继承Toolbar,声明和Title相关的属性

    private TextView mTitleTextView;
    private CharSequence mTitleText;
    private int mTitleTextColor;
    private int mTitleTextAppearance;

重写setTitle()方法。

    @Override
    public void setTitle(CharSequence title) {
        if (!TextUtils.isEmpty(title)) {
            if (mTitleTextView == null) { // 懒加载
                final Context context = getContext();
                mTitleTextView = new TextView(context);
                mTitleTextView.setSingleLine();
                mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
                if (mTitleTextAppearance != 0) {
                    mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
                }
                if (mTitleTextColor != 0) {
                    mTitleTextView.setTextColor(mTitleTextColor);
                }
            }
            if (mTitleTextView.getParent() != this) {
                // 添加到Toolbar并居中显示
                addCenterView(mTitleTextView);
            }
        } else if (mTitleTextView != null && mTitleTextView.getParent() == this) {
            // 当title为空时,remove
            removeView(mTitleTextView);
        }
        if (mTitleTextView != null) {
            mTitleTextView.setText(title);
        }
        mTitleText = title;
    }

依照原来的思路稍微的修改。代码和原来的雷同,改动的地方就只有16,18,20和22行。关键是addCenterView这个方法。标题居中就看这里了。

    private void addCenterView(View v) {
        final ViewGroup.LayoutParams vlp = v.getLayoutParams();
        final LayoutParams lp;
        if (vlp == null) {
            lp = generateDefaultLayoutParams();
        } else if (!checkLayoutParams(vlp)) {
            lp = generateLayoutParams(vlp);
        } else {
            lp = (LayoutParams) vlp;
        }

        lp.gravity = Gravity.CENTER;
        addView(v, lp);
    }

该方法与Toolbar的addSystemView()方法雷同,我基本都是直接拷过来改要改的地方,去掉了一些无用的代码。看到第12行,没错,我们前面做了那么多,就为了将gravity设置为center。

细心的童鞋会发现,setTitle()方法里还有mTitleTextAppearance 和 mTitleTextColor需要获取。没错,要想获取这两个值,我们需要重写setTitleTextAppearance()和setTitleTextColor()方法,只不过是将原来Toolbar的代码拷过来,这里就不说了。除此之外,还需要在构建方法中自己获取一次。

        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
                R.styleable.Toolbar, defStyleAttr, 0);
        try {
            final int titleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
            if (titleTextAppearance != 0) {
                setTitleTextAppearance(context, titleTextAppearance);
            }
            if (a.hasValue(R.styleable.Toolbar_titleTextColor)) {
                setTitleTextColor(a.getColor(R.styleable.Toolbar_titleTextColor, 0xffffffff));
            }
        } finally {
            a.recycle();
        }

这里解释下为什么已经重写了setTitleTextAppearance()和setTitleTextColor()方法还需要在构造方法中获取这两个值。因为Toolbar的源码获取TextAppearance并没有调用setTitleTextAppearance()方法,导致我们自己的mTitleTextAppearance和Toolbar的不一致。还要注意下调用那两个方法的顺序。

最后

有什么错漏的麻烦指出。感谢大家。

源码地址,喜欢的Star一下:
https://github.com/benioZhang/CenterTitleToolbar

参考:
http://blog.hwangjr.com/2015/07/10/Android-ToolBar-Widget-Usage/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值