问题描述
我们直接来看以下代码:
<com.google.android.material.appbar.CollapsingToolbarLayout
app:collapsedTitleGravity="center_horizontal"/>
在上边的代码中,我将collapsedTitleGravity的值设置为center_horizontal,意思是想让标题文字在折叠后能够居中显示,但是呢,差强人意,来看看这狗屎效果(见下边会动的图),没错,标题往右边偏移的很厉害:
解决办法
- 由于我引入的是Android X版本,因此呢,以下代码中,对于反射用到的变量可能与大家真实SDK环境不相同,这个请自行到你们使用中的那个support包中查看源码,找到正确的变量名引用。
- 看解决问题的代码:
collapsingToolbarLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener()
{
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)
{
if (oldBottom == bottom)
{
collapsingToolbarLayout.removeOnLayoutChangeListener(this);
try
{
// 请注意参数字符串‘collapsingTextHelper’,这里对应都是Android X版本中CollapsingToolbarLayout类中声明的CollapsingTextHelper类的变量名
// 由于collapsingTextHelper是不对外提供获取的,因此,这里需要用到反射来获取这个对象
Field field = collapsingToolbarLayout.getClass().getDeclaredField("collapsingTextHelper");
field.setAccessible(true);
CollapsingTextHelper collapsingTextHelper = (CollapsingTextHelper) field.get(collapsingToolbarLayout);
field.setAccessible(false);
// 请注意‘collapsedBounds’是CollapsingTextHelper类中声明的Rect的变量名
// 这里是要获取原collapsedBounds的边界数值
Field collapsedBoundsField = field.getType().getDeclaredField("collapsedBounds");
collapsedBoundsField.setAccessible(true);
// 获取到原collapsedBounds的边界数值
Rect oldRect = (Rect) collapsedBoundsField.get(collapsingTextHelper);
collapsedBoundsField.setAccessible(false);
// 设置新的边界数值
collapsingTextHelper.setCollapsedBounds(0, oldRect.top, right, oldRect.bottom);
collapsingTextHelper.recalculate();
} catch (NoSuchFieldException | IllegalAccessException e)
{
e.printStackTrace();
}
}
}
});
来,再看看新的效果,如愿以偿:
问题分析
有心的同学肯定会问了,为什么呢? 我们来一步步分析下
- 首先查看并分析下
CollapsingToolbarLayout的部分源码:
if (this.collapsingTitleEnabled && this.dummyView != null) {
this.drawCollapsingTitle = ViewCompat.isAttachedToWindow(this.dummyView) && this.dummyView.getVisibility() == 0;
if (this.drawCollapsingTitle) {
boolean isRtl = ViewCompat.getLayoutDirection(this) == 1;
z = this.getMaxOffsetForPinChild((View)(this.toolbarDirectChild != null ? this.toolbarDirectChild : this.toolbar));
DescendantOffsetUtils.getDescendantRect(this, this.dummyView, this.tmpRect);
this.collapsingTextHelper.setCollapsedBounds(this.tmpRect.left + (isRtl ? this.toolbar.getTitleMarginEnd() : this.toolbar.getTitleMarginStart()), this.tmpRect.top + z + this.toolbar.getTitleMarginTop(), this.tmpRect.right + (isRtl ? this.toolbar.getTitleMarginStart() : this.toolbar.getTitleMarginEnd()), this.tmpRect.bottom + z - this.toolbar.getTitleMarginBottom());
this.collapsingTextHelper.setExpandedBounds(isRtl ? this.expandedMarginEnd : this.expandedMarginStart, this.tmpRect.top + this.expandedMarginTop, right - left - (isRtl ? this.expandedMarginStart : this.expandedMarginEnd), bottom - top - this.expandedMarginBottom);
this.collapsingTextHelper.recalculate();
}
}
重点看这一行:
this.collapsingTextHelper.setCollapsedBounds(this.tmpRect.left + (isRtl ? this.toolbar.getTitleMarginEnd() : this.toolbar.getTitleMarginStart()), this.tmpRect.top + z + this.toolbar.getTitleMarginTop(), this.tmpRect.right + (isRtl ? this.toolbar.getTitleMarginStart() : this.toolbar.getTitleMarginEnd()), this.tmpRect.bottom + z - this.toolbar.getTitleMarginBottom());
从字面意思来看,这是设置一个折叠后的边界范围,那么,是什么边界范围呢?
如果全面查看了CollapsingToolbarLayout的源码,我们会发现,CollapsingToolbarLayout的标题实际上是用画布来绘制出来的,因此我认为,这个边界就是这块画布的尺寸。那么到现在为止呢,我们可以认定,标题一定是是通过这块画布画出来的,而这块画布是有边界的,它的边界就是通过上边这行代码进行计算得来的。
那...我是不是可以重新设置一下画布的尺寸,就可以随意更改标题被绘制的位置了?答案已经很清楚,那么接下来,我要想办法去修改这块画布的尺寸。
- 分析这块画布的计算过程,以及如何才能修改它。
未完待续 ...