仿 QQ 弱提示

设计给的图:


弱提示-2



弱提示-2


这这弱提示和 QQ 的非常像:


弱提示-2



弱提示-2


看到这个我第一想法是 SnakeBar ,但是会发现一个问题,SnackBar 需要当前的 Context ,而且当前界面消失的时候,显示的 Snackbar 也会消失。最重要的是工程中的所有 Toast 都要替换成这种弱提示,而之前的方法只需要接受一个 String 参数就能够完成了,所以为了减少工作量,我们在实现该功能的时候,最好也只是需要传入相同参数。

最终决定也是用 Toast 来完成这个效果:

首先设置 Toast 的宽度高度和动画效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//全屏和动画的设置方法
private static void initToast(Toast toast) {
try {
Field mTN = toast.getClass().getDeclaredField("mTN");
mTN.setAccessible(true);
Object mTNObj = mTN.get(toast);
Field mParams = mTNObj.getClass().getDeclaredField("mParams");
mParams.setAccessible(true);
WindowManager.LayoutParams params = (WindowManager.LayoutParams) mParams.get(mTNObj);
params.width = -1;//-1表示全屏, 你也可以设置任意宽度.
params.height = 130;// (int) dpToPx(context, T_HEIGHT);
params.windowAnimations = R.style.PopupAnimation;//设置动画, 需要是style类型
} catch (Exception e) {
e.printStackTrace();
}
}

上面的 height 只是在 Demo 中使用的高度,或者使用 -2 会自适应布局。

在 styles 中需要添加:

1
2
3
4
<style name="PopupAnimation" parent="android:Animation" mce_bogus="1">
<item name="android:windowEnterAnimation">@anim/popup_enter</item>
<item name="android:windowExitAnimation">@anim/popup_exit</item>
</style>

其中 anim 文件下的两个动画文件分别为:

enter:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

<translate
android:duration="500"
android:toYDelta="0"
android:fromYDelta="-200" />
</set>

exit:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

<translate
android:duration="500"
android:toYDelta="-200"
android:fromYDelta="0" />
</set>

动画的执行时间和移动的距离可以自己适当的调整。

接下来,就是让 Toast 的位置在顶部出现,这个顶部必须是状态栏下面,而不是标题栏下面,还有很重要的一点是,如果想要达到 QQ 所显示的那种效果,这个应用必须是所有状态栏都是透明的,否则将几乎不能实现这种效果。

给主题加上状态栏透明的效果:

1
2
3
4
5
6
7
<style name="changeBar" parent="Theme.AppCompat.Light.NoActionBar">
<!--和 代码中效果一样-->
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:windowTranslucentStatus">true</item>
<!--恢复状态栏的 位置空间-->
<item name="android:fitsSystemWindows">true</item>
</style>

Toast 的显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Toast toast;

public void show(Context context, CharSequence charSequence, int type) {

View layout;
toast = Toast.makeText(context, "", Toast.LENGTH_SHORT);
initToast(toast);
if (type == 1) {
layout = LayoutInflater.from(context).inflate(R.layout.toast, null);//自定义的布局
} else {
layout = LayoutInflater.from(context).inflate(R.layout.toaste, null);//自定义的布局
}
((TextView) layout.findViewById(R.id.base_toast_text_view)).setText(charSequence);
toast.setView(layout);
toast.setGravity(Gravity.TOP, 0, 0);//从顶部开始显示
toast.getView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);//设置Toast可以布局到系统状态栏的下面
toast.show();
}

其中两个 Toast 的布局都是根据需求自定义的,分别是:

toast:

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/toasttt"
android:orientation="vertical">

<Space
android:layout_width="wrap_content"
android:layout_height="20dp"/>

<TextView
android:id="@+id/base_toast_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:drawableLeft="@mipmap/gantanh"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:paddingBottom="10dp"
android:paddingLeft="8dp"
android:text="sdfasd"
android:textColor="#000"
android:textSize="16sp"/>
</LinearLayout>

toaste:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/toasttte"
android:orientation="vertical">
<Space
android:layout_width="wrap_content"
android:layout_height="20dp"/>

<TextView
android:id="@+id/base_toast_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:drawableLeft="@mipmap/gantanh"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:paddingBottom="10dp"
android:paddingLeft="8dp"
android:textColor="#fff"
android:textSize="16sp"/>
</LinearLayout>

仔细观察会发现,是有 Toast 下面是有一定阴影的,所以需要给 Toast 的背景加上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Bottom 2dp Shadow -->
<item>
<shape android:shape="rectangle">
<!--阴影的渐变颜色-->
<gradient
android:angle="-90"
android:centerColor="#000"
android:endColor="#00000000"
android:startColor="#000"
android:type="linear"
/>
</shape>
</item>

<!--&lt;!&ndash; White Top color &ndash;&gt;-->
<item android:bottom="4dp">
<shape android:shape="rectangle">
<solid android:color="#f00"/>
</shape>
</item>
</layer-list>

最后根据不同的状态,显示不同的 Toast

1
2
3
4
5
6
7
8
9
10
11
12
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
show(MainActivity.this, "每天最多给它点10个赞", 1);
}
});
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
show(MainActivity.this, "当前网络不可用,请检查网络设置", 2);
}
});

大致的效果图,懒的找图标了,就用一样的了:


弱提示-2



弱提示-2


最后,这只是个简单的 Demo ,实际中那样改变主题是非常不好的,因为那个只适用于 API 19 及以上的版本,如果真要改变状态栏的颜色,可以参考 StatusBarCompatSystemBarTint 或者 status-bar-compat,使用它们都可以轻松的动态改变状态栏的颜色,但是他们都有局限性,都需要一个参数,就是当前的 Activity

另外,其实 QQ 的那个 Toast 的顶部其实还有渐变的阴影,所以看上效果很更好看一点,新版的 QQ 标题栏的颜色也改成了渐变蓝色,也是很好看的。