Toast使用简单,性能优良,在Android APP中使用非常广泛。
但实际开发中我们并不仅仅满足于系统提供Toast的简单使用,同一行代码在不同的Android手机上就可能有不同的显示样式。为了匹配统一的界面风格,我们需要对Toast的弹出位置,字体及样式做出一些自定义设置,以此来达到我们的目的。
闲话少说,直接开始我们的自定义旅程。
1.创建布局文件
新建layout_mytoast.xml,布局很简单,使用一个TextView文本,代码如下:
<?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/my_toast_style"
android:gravity="center">
<TextView
android:id="@+id/id_text_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="10dp"
android:gravity="center"
android:textColor="#ffffff"
android:textSize="14sp" />
</LinearLayout>
layout_mytoast.xml中使用了一个自定义的背景样式my_toast_style.xml,圆角和90%透明度的背景色,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#90000000" />
<corners android:radius="5dp"/>
</shape>
2.自定义Toast
自定义MyToast类,继承Toast,按照屏幕的大小指定Toast的弹出位置,自定义makeText方法,根据id_text_message获取layout_mytoast.xml中TextView,并设置其文本,代码如下:
package com.alone.custome.toast;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
import com.alone.R;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by Alone on 2016/7/30.
* 自定义Toast
* 1.可自由设置样式与位置
* 2.实现了不受消息队列影响的Toast
*/
public class MyToast extends Toast {
public static final int OFFEST_TOP = 1;//顶部弹出
public static final int OFFEST_CENTER = 2;//中部弹出
public static final int OFFEST_BOTTOM = 3;//底部弹出
private static Context mContext;
private Toast toast;
private View layout;
private boolean isShow;
/**
* Construct an empty Toast object. You must call {@link #setView} before you
* can call {@link #show}.
*
* @param context The context to use. Usually your {@link Application}
* or {@link Activity} object.
*/
private MyToast(Context context) {
super(context);
this.mContext = context;
}
public MyToast(Context context, CharSequence text, int yOffest) {
super(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layout = inflate.inflate(R.layout.layout_mytoast, null);
TextView tv = (TextView)layout.findViewById(R.id.id_text_message);
tv.setText(text);
toast = new Toast(context);
int offest = 0;
switch (yOffest) {
case OFFEST_TOP:
offest = -(int)(getScreenHeight() * 0.25);
break;
case OFFEST_CENTER:
break;
case OFFEST_BOTTOM:
offest = (int)(getScreenHeight() * 0.25);
break;
}
toast.setGravity(Gravity.CENTER_VERTICAL, 0, offest);
}
public static Toast makeText(Context context, CharSequence text, int duration) {
return makeText(context, text, duration, OFFEST_TOP);
}
public static Toast makeText(Context context, CharSequence text, int duration, int yOffest) {
MyToast result = new MyToast(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(R.layout.layout_mytoast, null);
TextView tv = (TextView)v.findViewById(R.id.id_text_message);
tv.setText(text);
result.setView(v);
//setGravity方法用于设置位置,此处为垂直居中,水平1/4,1/2,3/4
int offest = 0;
switch (yOffest) {
case OFFEST_TOP:
offest = -(int)(getScreenHeight() * 0.25);
break;
case OFFEST_CENTER:
break;
case OFFEST_BOTTOM:
offest = (int)(getScreenHeight() * 0.25);
break;
}
result.setGravity(Gravity.CENTER_VERTICAL, 0, offest);
result.setDuration(duration);
return result;
}
/**
* 弹出永久存在的toast
*/
public void showForever() {
//从Toast对象中获得mTN变量
try {
Field field = toast.getClass().getDeclaredField("mTN");
field.setAccessible(true);
Object obj = field.get(toast);
Field mNextView = obj.getClass().getDeclaredField("mNextView");
mNextView.setAccessible(true);
//TN对象中获得了show方法
Method mShow = obj.getClass().getDeclaredMethod("show");
//调用show方法来显示Toast信息提示框
mNextView.set(obj, layout);
mShow.invoke(obj, new Object[]{});
isShow = true;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 关闭永久存在的toast
*/
public void hideForever() {
//从Toast对象中获得mTN变量
try {
Field field = toast.getClass().getDeclaredField("mTN");
field.setAccessible(true);
Object obj = field.get(toast);
//TN对象中获得了hide方法
Method mShow = obj.getClass().getDeclaredMethod("hide");
//调用hide方法来显示Toast信息提示框
mShow.invoke(obj, new Object[]{});
isShow = false;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 得到当前屏幕的高度
*/
private static int getScreenHeight() {
WindowManager wm = (WindowManager) mContext
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.heightPixels;
}
public void toggle() {
if (isShow) {
hideForever();
} else {
showForever();
}
}
public boolean isShow() {
return isShow;
}
}
3.效果测试
创建一个测试Activity,点击按钮进行测试,分别弹出对话框。
Button ToastBomBtn = (Button) findViewById(R.id.id_btn_toast_bom);
ToastBomBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyToast.makeText(ToastActivity.this, "网络错误", Toast.LENGTH_SHORT, MyToast.OFFEST_BOTTOM).show();
}
});
Button ToastCtrBtn = (Button) findViewById(R.id.id_btn_toast_ctr);
ToastCtrBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyToast.makeText(ToastActivity.this, "网络错误", Toast.LENGTH_SHORT, MyToast.OFFEST_CENTER).show();
}
});
Button ToastTopBtn = (Button) findViewById(R.id.id_btn_toast_top);
ToastTopBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyToast.makeText(ToastActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
}
});
效果如下:
此处截取一张屏幕截图,不要关注文本的字体,和本实例没有关系,不过如果要实现Toast弹出特殊字体文本,项目中导入字体文件设置Typeface也是很容易实现的。如果要实现弹出一个✔️的图片提示表示操作成功,取一张背景透明的图在自定义布局文件中也是非常好实现的,既然是自定义布局,那么一切都可以根据实际需求来。
4.趣味扩展
前文的MyToast中还实现了一种不受消息队列影响的Toast,即创建一种永不消失的Toast,使用反射获取Toast中属性及show方法,弹出一个永不消失的Toast,并可以使用hide方法隐藏Toast,有兴趣的同学可以试一下,只要app不被杀死,Toast可以永远存在哦。好了,本期就到这里,下次进行“提示控件之自定义Dialog”的分享。