写了几个月的小东西要上线了,突然同事问我,如果用户禁止了app的通知,你的Toast还能弹得出来吗?于是我试了一下关闭我的app的通知,结果Toast还真弹不出来了,这可急坏了我,因为我的app里大部分的用户提示,比如网络连接状态的提醒,都是用Toast实现的,用户如果关闭了通知可不得了啊。
上网搜寻了一番没有找到偷懒的办法,使用第三方库的话又有点小题大做,于是想到了自定义一个Toast出来。
以下是最后的效果:
WechatIMG1.jpeg
下面是系统的Toast,上面是自定义的Toast.
上代码:
public class MyToastDialog extends Dialog {
private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
private String message;
private boolean isShow = false;
public static void show(Context context, String message) {
//如果用户没有禁止通知权限则使用系统Tosat,否则使用自定义Toast
if (isNotificationEnabled(context)) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
return;
}
MyToastDialog.create(context).init(message).show();
}
public MyToastDialog(Context context, int themeResId) {
super(context, themeResId);
}
public static MyToastDialog create(Context context) {
return new MyToastDialog(context, R.style.MyToastDialog);
}
public MyToastDialog init(String message) {
this.message = message;
return this;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isShow) {
return;
}
isShow = true;
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.my_toast_dialog);
final TextView msg = (TextView) findViewById(R.id.my_toast_text);
msg.setText(message);
//可以在这里给TextView设置动画
//设置1s后消失
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
dismiss();
isShow = false;
}
}, 000);
}
/**
* 用来判断用户是否开启通知权限
* */
private static boolean isNotificationEnabled(Context context){
AppOpsManager mAppOps;
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
return false;
}
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
Class appOpsClass; /* Context.APP_OPS_MANAGER */
try {
appOpsClass = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
int value = (int)opPostNotificationValue.get(Integer.class);
return ((int)checkOpNoThrowMethod.invoke(mAppOps,value, uid, pkg) == AppOpsManager.MODE_ALLOWED);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
R.layout.my_toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="@+id/my_toast_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="64dp"
android:padding="12dp"
android:background="@drawable/frame_toast"
android:text="toast"
android:textColor="@color/white_text"
android:textSize="12sp"/>
</RelativeLayout>
</RelativeLayout>
在drawable文件夹中定义样式frame_toast.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="10dp"/>
<solid android:color="#555555"/>
</shape>
由于MyToastDialog继承自Dialog,在显示时背景会变成半透明的黑色,所以要在style.xml文件中加上
<style name="MyToastDialog" parent="Base.Theme.AppCompat.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<!--消除半透明背景-->
<item name="android:backgroundDimEnabled">false</item>
<!--使Dialog位置处于view底部-->
<item name="android:gravity">bottom</item>
</style>
搞定!
不过,今天发现了一个bug。。。当使用该自定义dialog时,每当出现弹出toast时立即进行页面跳转时,会发生view not attached to window 异常。
网友评论