美文网首页Android进阶之路Android开发经验谈Android技术知识
自定义Toolbar,解决你所有的适配苦恼!

自定义Toolbar,解决你所有的适配苦恼!

作者: Mr萧阳 | 来源:发表于2019-07-05 17:28 被阅读259次

    About Toolbar

    Toolbar是一个官方ToolBar的扩展工具类,省去了对不同版本适配的复杂方案,它可以帮助你轻松实现NavigationBarStatusBar的样式管理,最最重要的是它的使用方式及其接近原生,大家快来试用吧!

    起因

    按照官方的方式去管理NavigationBarStatusBar显示对各个系统版本的兼容性是很麻烦的。尤其在一些应用中可能不同的页面对应了不同显示状况,比如A页面NavigationBar需要显示成蓝色StatusBar需要显示成深蓝,而到了B页面NavigationBarStatusBar却需要显示成白色。这时候你需要在不同的页面通过Code调过来调过去麻烦得很。那么我们为什么不做一个款仅需要在xml文件中设置几个属性就能完成各种样式适配的Toolbar呢?OK,那我们来定下目标吧!
    1. 适配各API版本但不需要那么麻烦的去编写个API对应的styles文件。
    2. 使用方式简单,接近原生。
    3. 仅需要布局文件,不需要在个页面维护代码逻辑。
    针对这写要求,下面我们来实现一款自定义Toolbar.

    原理

    Toolbar的原理很简单,既然个系统版本需要兼容Statusbar等才能做到效果一致,那我们就不要Statusbar好了!
    首先,将StatusBar设置成透明,并且让页面布局可以延伸到StatusBar下。这可以通过全局style实现:

    <style name="AppTheme" parent="Theme.MaterialComponents.NoActionBar">
            <item name="android:windowTranslucentStatus">true</item>
            <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
    

    现在我们的布局已经延伸到(0,0)的起点了,接下来怎么办呢?我们肯定不能在每个页面布局里写一个适配StatusBar的布局吧,那我们可以考虑把它放到自定义的view控件中。接下来我们可以自定义一个Toolbar的控件继承自androidx.appcompat.widget.Toolbar。那么接下来的问题就转化成了:
    1. 保持Toolbar的原有特性和使用方法,因为我们view是集成来的,所以这点肯定是满足的;
    2. 该自定义Toolbar如何适配StatusBar部分;
    3. 系统Toolbar中各个内置控件的布局是通过私有方法计算之后显示出来的,我们如何调整到跟原来的显示一模一样。
    下面我们来将这些问题一一解决掉(第一条略)。
    Toolbar适配系统栏部分,我们可以考虑重写onDraw()方法,绘制一个可自定义颜色的矩形区域,并且保证该区域的高度等于系统栏高度就可以了。请看代码实现:

    //在构造函数中使用
    private void init(Context context, AttributeSet attrs) {
            if (attrs != null) {
                TypedArray typedArray = 
                    context.obtainStyledAttributes(attrs, R.styleable.Toolbar);
                color = typedArray.getColor(
                    R.styleable.Toolbar_statusBarColor,
                    getResources().getColor(R.color.colorPrimaryDark));
            }
            int id = context.getResources()
                .getIdentifier("status_bar_height","dimen","android");
            statusBarHeight = context.getResources()
                .getDimensionPixelOffset(id);
            paint = new Paint();
            paint.setColor(color);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        Rect rect = 
          new Rect(0, 0, getMeasuredWidth(), (int) statusBarHeight);
        canvas.drawRect(rect, paint);
        super.onDraw(canvas);
    }
    

    这个时候系统栏的背景就搞定了,运行下看下什么效果吧。
    运行完毕后,你会发现虽然系统栏背景颜色变了,但是我们的Toolbar就这么高,整个title都移到上面去了,怎么办呢?重写onMeasure()方法,让我们的Toolbar的高度变成:原高度+系统栏高度。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //为了兼容6.0及以前版本多次measure、layout问题
        measuredHeight = 
          measuredHeight == 0? getMinimumHeight() : measuredHeight;
        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), 
          measuredHeight +statusBarHeight);
    }
    

    再次运行,不错高度变了,但是为什么Title、icon之类的控件都快顶到系统栏了!看来我们还需要修改下onLayout()方法了。为什么是onLayout方法呢?是因为我们这些操作对控件的大小不会产生影响,只会对这些控件在Toolbar上的布局位置产生影响,所以我们需要重写下这个方法来调整内部控件的位置。

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        setPadding(getPaddingLeft(), 
                   statusBarHeight, 
                   getPaddingRight(), 
                   getPaddingBottom());
        super.onLayout(changed, l, t, r, b);
    }
    

    通过查看源码可以了解到,Toolbar是由ViewGroup实现的,其中各个控件的位置是通过私有方法计算得到的,而在这个方法中影响垂直位置计算的就是padding值,所以设置padding值将状态栏的那块高度空出来就OK了。
    至此我们这个控件就搞定了,赶快去试用吧!源码链接

    相关文章

      网友评论

        本文标题:自定义Toolbar,解决你所有的适配苦恼!

        本文链接:https://www.haomeiwen.com/subject/vgoehctx.html