简书链接:非持有snackbar实例的情况下关闭永久显示的snackbar
文章字数:456,阅读全文大约需要1分钟
1652091309(1).jpg
根据布局分析发现是添加到了根节点,也就是我传递的coordinatelayout最后一个子view.

布局源码为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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.
-->

<view
xmlns:android="http://schemas.android.com/apk/res/android"
class="com.google.android.material.snackbar.SnackbarContentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom">

<TextView
android:id="@+id/snackbar_text"
style="?attr/snackbarTextViewStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical|left|start"/>

<Button
android:id="@+id/snackbar_action"
style="?attr/snackbarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right|end"
android:minWidth="48dp"
android:visibility="gone"/>

</view>


文件为mtrl_layout_snackbar_include.xml
因此得到如下解决办法 ,

1
2
3
4
5
6
7
   public static void closeSnackBar(ViewGroup root) {
// class="com.google.android.material.snackbar.SnackbarContentLayout"
View snackBarButton = root.findViewById(R.id.snackbar_action);
if(snackBarButton!=null){
snackBarButton.callOnClick();
}
}

也可以尝试获取构建snackbar的root 布局最后一个view判断是否等于SnackbarContentLayout
另外我尝试分析com.google.android.material.snackbar.SnackbarManager实例发现是默认类,没权限访问而且也没有相关的关闭方法

如果没有actionview呢

image.png

进一步分析,com.google.android.material.snackbar.Snackbar$SnackbarLayout{c79a47b VFE…… …….. 22,2106-1058,2238}

下面是

com.google.android.material.snackbar.SnackbarContentLayout{2e6a9aa V.E…… …….. 22,0-1014,132}

SnackBarLayout源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
*/
@RestrictTo(LIBRARY_GROUP)
public static final class SnackbarLayout extends BaseTransientBottomBar.SnackbarBaseLayout {
public SnackbarLayout(Context context) {
super(context);
}

public SnackbarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Work around our backwards-compatible refactoring of Snackbar and inner content
// being inflated against snackbar's parent (instead of against the snackbar itself).
// Every child that is width=MATCH_PARENT is remeasured again and given the full width
// minus the paddings.
int childCount = getChildCount();
int availableWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT) {
child.measure(
MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), MeasureSpec.EXACTLY));
}
}
}
}

private TextView getMessageView() {
return getContentLayout().getMessageView();
}

private Button getActionView() {
return getContentLayout().getActionView();
}

private SnackbarContentLayout getContentLayout() {
return (SnackbarContentLayout) view.getChildAt(0);
}
}



最后的调整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*
要关闭源码首先需要SnackbarManager 虽然是静态,但是不是公开的,需要反射
private static SnackbarManager snackbarManager;

static SnackbarManager getInstance() {
if (snackbarManager == null) {
snackbarManager = new SnackbarManager();
}
return snackbarManager;
}

@NonNull private final Object lock;
@NonNull private final Handler handler;

@Nullable private SnackbarRecord currentSnackbar;
@Nullable private SnackbarRecord nextSnackbar;

拿到 snackbarrecord,

在snackabar的父类 BaseTransientBottomBar
void onViewHidden(int event) {
// First tell the SnackbarManager that it has been dismissed
SnackbarManager.getInstance().onDismissed(managerCallback);
if (callbacks != null) {
// Notify the callbacks. Do that from the end of the list so that if a callback
// removes itself as the result of being called, it won't mess up with our iteration
int callbackCount = callbacks.size();
for (int i = callbackCount - 1; i >= 0; i--) {
callbacks.get(i).onDismissed((B) this, event);
}
}
// Lastly, hide and remove the view from the parent (if attached)
ViewParent parent = view.getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(view);
}
}
方法中,参数为3, ,可移除handler.


*/
public static void closeSnackBar(ViewGroup root) {
// class="com.google.android.material.snackbar.SnackbarContentLayout"
// root= (ViewGroup) root.getChildAt(root.getChildCount()-1);//可以优化效率,可我总觉得这样不太靠谱
View snackBarButton = root.findViewById(R.id.snackbar_action);//action只适用于错误。
if(snackBarButton!=null){
if(snackBarButton.getVisibility()==View.VISIBLE){//如果是没有设置action按钮的,则此方法无效。
snackBarButton.callOnClick();
}else{//方法有一点点瑕疵,因为倒计时的handler并没有取消,如果要解决这个问题,需要反射SnackbarManager 拿到record进行操作,有兴趣并且成功的朋友可以留言交流。
Snackbar.SnackbarLayout snackbarLayout= (Snackbar.SnackbarLayout) snackBarButton.getParent().getParent();
ViewGroup parent = (ViewGroup) snackbarLayout.getParent();
if(parent!=null){
parent.removeView(snackbarLayout);
}
}

}
}
}


image.png