Fix a flicker problem on top app bar

The height of top app bar area is dependent on the line count of title.
This mechanism is always triggered when entering either subsetting page
or rotating the screen, regardless the state of collapsing toolbar. That
is saying this mechanism caused a flicker problem or a title in wrong
color. The mechanism will be updated to check the state of collapsing
toolbar to make sure that it is only triggered if the collapsing toolbar
is in expanded state.

This changes also added null checker to app bar since there's no
AppBarLayout existing in the older version of Android.

Fix: 186419089
Fix: 187709056
Fix: 191746368
Test: visual verified
1) Navigate to a subsetting page with multiline title
2) Rotate the screen to landscape mode and collapse the title
3) Rotate the screen to portrait mode and see if the title is in
collapsing state

Change-Id: Ica868245e0b0a799f76b7f953c606c8e543375f6
This commit is contained in:
Mill Chen
2021-06-23 12:16:28 +08:00
parent 144bb33a6a
commit b18068a0b9
4 changed files with 176 additions and 84 deletions

View File

@@ -33,7 +33,7 @@
android:background="?android:attr/colorPrimary"
android:theme="@style/Theme.CollapsingToolbar.Settings">
<com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_one_line_height"
@@ -59,7 +59,7 @@
android:transitionName="shared_element_view"
app:layout_collapseMode="pin"/>
</com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout

View File

@@ -1,80 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.collapsingtoolbar;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.appbar.CollapsingToolbarLayout;
/**
* A customized version of CollapsingToolbarLayout that can apply different font size based on the
* line count of its title.
*/
public class AdjustableToolbarLayout extends CollapsingToolbarLayout {
private static final int TOOLBAR_MAX_LINE_NUMBER = 2;
public AdjustableToolbarLayout(@NonNull Context context) {
this(context, null);
}
public AdjustableToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AdjustableToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
initCollapsingToolbar();
}
@SuppressWarnings("RestrictTo")
private void initCollapsingToolbar() {
this.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) {
v.removeOnLayoutChangeListener(this);
final int count = getLineCount();
if (count > TOOLBAR_MAX_LINE_NUMBER) {
final ViewGroup.LayoutParams lp = getLayoutParams();
lp.height = getResources()
.getDimensionPixelSize(R.dimen.toolbar_three_lines_height);
setScrimVisibleHeightTrigger(
getResources().getDimensionPixelSize(
R.dimen.scrim_visible_height_trigger_three_lines));
setLayoutParams(lp);
} else if (count == TOOLBAR_MAX_LINE_NUMBER) {
final ViewGroup.LayoutParams lp = getLayoutParams();
lp.height = getResources()
.getDimensionPixelSize(R.dimen.toolbar_two_lines_height);
setScrimVisibleHeightTrigger(
getResources().getDimensionPixelSize(
R.dimen.scrim_visible_height_trigger_two_lines));
setLayoutParams(lp);
}
}
});
}
}

View File

@@ -35,10 +35,18 @@ import com.google.android.material.resources.TextAppearanceConfig;
* A base Activity that has a collapsing toolbar layout is used for the activities intending to
* enable the collapsing toolbar function.
*/
public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity {
public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity implements
AppBarLayout.OnOffsetChangedListener {
private static final int TOOLBAR_MAX_LINE_NUMBER = 2;
private static final int FULLY_EXPANDED_OFFSET = 0;
private static final String KEY_IS_TOOLBAR_COLLAPSED = "is_toolbar_collapsed";
@Nullable
private CollapsingToolbarLayout mCollapsingToolbarLayout;
@Nullable
private AppBarLayout mAppBarLayout;
private boolean mIsToolbarCollapsed;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -48,6 +56,12 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity {
super.setContentView(R.layout.collapsing_toolbar_base_layout);
mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
mAppBarLayout = findViewById(R.id.app_bar);
mAppBarLayout.addOnOffsetChangedListener(this);
if (savedInstanceState != null) {
mIsToolbarCollapsed = savedInstanceState.getBoolean(KEY_IS_TOOLBAR_COLLAPSED);
}
initCollapsingToolbar();
disableCollapsingToolbarLayoutScrollingBehavior();
final Toolbar toolbar = findViewById(R.id.action_bar);
@@ -107,14 +121,43 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity {
return true;
}
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int offset) {
if (offset == FULLY_EXPANDED_OFFSET) {
mIsToolbarCollapsed = false;
} else {
mIsToolbarCollapsed = true;
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (isChangingConfigurations()) {
outState.putBoolean(KEY_IS_TOOLBAR_COLLAPSED, mIsToolbarCollapsed);
}
}
/**
* Returns an instance of collapsing toolbar.
*/
@Nullable
public CollapsingToolbarLayout getCollapsingToolbarLayout() {
return mCollapsingToolbarLayout;
}
/**
* Return an instance of app bar.
*/
@Nullable
public AppBarLayout getAppBarLayout() {
return mAppBarLayout;
}
private void disableCollapsingToolbarLayoutScrollingBehavior() {
if (mAppBarLayout == null) {
return;
}
final CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
@@ -127,4 +170,39 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity {
});
params.setBehavior(behavior);
}
@SuppressWarnings("RestrictTo")
private void initCollapsingToolbar() {
if (mCollapsingToolbarLayout == null || mAppBarLayout == null) {
return;
}
mCollapsingToolbarLayout.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) {
v.removeOnLayoutChangeListener(this);
if (mIsToolbarCollapsed) {
return;
}
final int count = mCollapsingToolbarLayout.getLineCount();
if (count > TOOLBAR_MAX_LINE_NUMBER) {
final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
lp.height = getResources()
.getDimensionPixelSize(R.dimen.toolbar_three_lines_height);
mCollapsingToolbarLayout.setScrimVisibleHeightTrigger(
getResources().getDimensionPixelSize(
R.dimen.scrim_visible_height_trigger_three_lines));
mCollapsingToolbarLayout.setLayoutParams(lp);
} else if (count == TOOLBAR_MAX_LINE_NUMBER) {
final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
lp.height = getResources()
.getDimensionPixelSize(R.dimen.toolbar_two_lines_height);
mCollapsingToolbarLayout.setScrimVisibleHeightTrigger(
getResources().getDimensionPixelSize(
R.dimen.scrim_visible_height_trigger_two_lines));
mCollapsingToolbarLayout.setLayoutParams(lp);
}
}
});
}
}

View File

@@ -25,21 +25,31 @@ import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
/**
* A base fragment that has a collapsing toolbar layout for enabling the collapsing toolbar design.
*/
public abstract class CollapsingToolbarBaseFragment extends Fragment {
public abstract class CollapsingToolbarBaseFragment extends Fragment implements
AppBarLayout.OnOffsetChangedListener {
private static final int TOOLBAR_MAX_LINE_NUMBER = 2;
private static final int FULLY_EXPANDED_OFFSET = 0;
private static final String KEY_IS_TOOLBAR_COLLAPSED = "is_toolbar_collapsed";
@Nullable
private CollapsingToolbarLayout mCollapsingToolbarLayout;
@Nullable
private AppBarLayout mAppBarLayout;
@NonNull
private Toolbar mToolbar;
@NonNull
private FrameLayout mContentFrameLayout;
private boolean mIsToolbarCollapsed;
@Nullable
@Override
@@ -48,6 +58,13 @@ public abstract class CollapsingToolbarBaseFragment extends Fragment {
final View view = inflater.inflate(R.layout.collapsing_toolbar_base_layout, container,
false);
mCollapsingToolbarLayout = view.findViewById(R.id.collapsing_toolbar);
mAppBarLayout = view.findViewById(R.id.app_bar);
mAppBarLayout.addOnOffsetChangedListener(this);
if (savedInstanceState != null) {
mIsToolbarCollapsed = savedInstanceState.getBoolean(KEY_IS_TOOLBAR_COLLAPSED);
}
initCollapsingToolbar();
disableCollapsingToolbarLayoutScrollingBehavior();
mToolbar = view.findViewById(R.id.action_bar);
mContentFrameLayout = view.findViewById(R.id.content_frame);
return view;
@@ -60,6 +77,31 @@ public abstract class CollapsingToolbarBaseFragment extends Fragment {
requireActivity().setActionBar(mToolbar);
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (getActivity().isChangingConfigurations()) {
outState.putBoolean(KEY_IS_TOOLBAR_COLLAPSED, mIsToolbarCollapsed);
}
}
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int offset) {
if (offset == FULLY_EXPANDED_OFFSET) {
mIsToolbarCollapsed = false;
} else {
mIsToolbarCollapsed = true;
}
}
/**
* Return an instance of app bar.
*/
@Nullable
public AppBarLayout getAppBarLayout() {
return mAppBarLayout;
}
/**
* Return the collapsing toolbar layout.
*/
@@ -75,4 +117,56 @@ public abstract class CollapsingToolbarBaseFragment extends Fragment {
public FrameLayout getContentFrameLayout() {
return mContentFrameLayout;
}
private void disableCollapsingToolbarLayoutScrollingBehavior() {
if (mAppBarLayout == null) {
return;
}
final CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
behavior.setDragCallback(
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
return false;
}
});
params.setBehavior(behavior);
}
@SuppressWarnings("RestrictTo")
private void initCollapsingToolbar() {
if (mCollapsingToolbarLayout == null || mAppBarLayout == null) {
return;
}
mCollapsingToolbarLayout.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) {
v.removeOnLayoutChangeListener(this);
if (mIsToolbarCollapsed) {
return;
}
final int count = mCollapsingToolbarLayout.getLineCount();
if (count > TOOLBAR_MAX_LINE_NUMBER) {
final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
lp.height = getResources()
.getDimensionPixelSize(R.dimen.toolbar_three_lines_height);
mCollapsingToolbarLayout.setScrimVisibleHeightTrigger(
getResources().getDimensionPixelSize(
R.dimen.scrim_visible_height_trigger_three_lines));
mCollapsingToolbarLayout.setLayoutParams(lp);
} else if (count == TOOLBAR_MAX_LINE_NUMBER) {
final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
lp.height = getResources()
.getDimensionPixelSize(R.dimen.toolbar_two_lines_height);
mCollapsingToolbarLayout.setScrimVisibleHeightTrigger(
getResources().getDimensionPixelSize(
R.dimen.scrim_visible_height_trigger_two_lines));
mCollapsingToolbarLayout.setLayoutParams(lp);
}
}
});
}
}