前言
安卓中为了给用户的友好提示,一般的表现形式为Dialog、PopWindow、Toast,自从Snackbar的出现绝对是秒杀Toast了,Snackbar不仅能够简单实现toast的效果而且还能setAction,但是还是有很多的应用是使用了Toast的,并且安卓死丢丢也有toast的插件,可见toast还是有他存在的价值。
看效果
csdn传图片不能太大,马蛋来2张好了:
从源码解读安卓的Toast
Toast窗口其实和前面分析的Activity、Dialog、PopWindow都是不同的,因为它和输入法、墙纸类似,都是系统窗口。
我们先看下Toast的静态makeText方法吧,如下:
public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
//new一个Toast对象
Toast result = new Toast(context);
//获取前面有篇文章分析的LayoutInflater
LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//加载解析Toast的布局,实质transient_notification.xml是一个LinearLayout中套了一个@android:id/message的TextView而已
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
//取出布局中的TextView
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
//把我们的文字设置到TextView上
tv.setText(text);
//设置一些属性
result.mNextView = v;
result.mDuration = duration;
//返回新建的Toast
return result;
}
可以看见,这个方法构造了一个Toast,然后把要显示的文本放到这个View的TextView中,然后初始化相关属性后返回这个新的Toast对象。
当我们有了这个Toast对象之后,可以通过show方法来显示出来,如下看下show方法源码:
public void show() {
......
//通过AIDL(Binder)通信拿到NotificationManagerService的服务访问接口,当前Toast类相当于上面例子的客户端!!!相当重要!!!
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
try {
//把TN对象和一些参数传递到远程NotificationManagerService中去
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}
继续来看Toast中的show方法的service.enqueueToast(pkg, tn, mDuration);语句,service实质是远程的NotificationManagerService,所以enqueueToast方法就是 NotificationManagerService类的,如下:
private final IBinder mService = new INotificationManager.Stub() {
@Override
public void enqueueToast(String pkg, ITransientNotification callback, int duration)
{
......
synchronized (mToastQueue) {
int callingPid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
try {
ToastRecord record;
//查看该Toast是否已经在队列当中
int index = indexOfToastLocked(pkg, callback);
// If it‘s already in the queue, we update it in place, we don‘t
// move it to the end of the queue.
//注释说了,已经存在则直接取出update
if (index >= 0) {
record = mToastQueue.get(index);
record.update(duration);
} else {
// Limit the number of toasts that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
......
//将Toast封装成ToastRecord对象,放入mToastQueue中
record = new ToastRecord(callingPid, pkg, callback, duration);
//把他添加到ToastQueue队列中
mToastQueue.add(record);
index = mToastQueue.size() - 1;
//将当前Toast所在的进程设置为前台进程
keepProcessAliveLocked(callingPid);
}
//如果index为0,说明当前入队的Toast在队头,需要调用showNextToastLocked方法直接显示
if (index == 0) {
showNextToastLocked();
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
}
}
不多看了,我们知道Toast都是加入到一个enqueueToast,然后通过handle来处理,因为是系统的窗口,所以我们换个界面然后toast还没有完你也不要感到奇怪。
在开发应用程序时使用Toast注意事项:
- 通过分析TN类的handler可以发现,如果想在非UI线程使用Toast需要自行声明Looper,否则运行会抛出Looper相关的异常;UI线程不需要,因为系统已经帮忙声明。
- 在使用Toast时context参数尽量使用getApplicationContext(),可以有效的防止静态引用导致的内存泄漏。
- 有时候我们会发现Toast弹出过多就会延迟显示,因为上面源码分析可以看见Toast.makeText是一个静态工厂方法,每次调用这 个方法都会产生一个新的Toast对象,当我们在这个新new的对象上调用show方法就会使这个对象加入到 NotificationManagerService管理的mToastQueue消息显示队列里排队等候显示;所以如果我们不每次都产生一个新的 Toast对象(使用单例来处理)就不需要排队,也就能及时更新了。
自定义Toast
package com.losileeya.toastmaster.view;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import android.widget.Toast;
import com.losileeya.toastmaster.R;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
/**
* 超级自定义的Toast,单例模式获取本类
*/
public class SuperCustomToast {
/**
* 获取当前Android系统版本
*/
static int currentapiVersion = Build.VERSION.SDK_INT;
/**
* 入场动画持续时间
*/
private static final int TIME_START_ANIM = 500;
/**
* 离场动画持续时间
*/
private static final int TIME_END_ANIM = 500;
/**
* 每条Toast显示持续时间
*/
private static final int TIME_DURATION = 2500;
/**
* UI线程句柄
*/
Handler mHandler;
/**
* 内容对象
*/
Context mContext;
/**
* 顶层布局
*/
LinearLayout mTopView, mTopView2;
/**
* 内容布局
*/
public LinearLayout mView;
/**
* 布局属性
*/
LayoutParams lp_WW, lp_MM;
/**
* 屏幕宽度
*/
int screenWidth;
/**
* 屏幕高度
*/
int screenHeight;
/**
* 默认背景的resid
*/
Integer defaultBackgroundResid;
/**
* 默认背景的颜色
*/
Drawable defaultBackgroundColor;
/**
* 默认文字颜色
*/
int defaultTextColor;
private View layout;
/**
* 反射过程中是否出现异常的标志
*/
boolean hasReflectException = false;
/**
* 单例
*/
private static SuperCustomToast instance;
/**
* 获得单例
*
* @param context
* @return
*/
public static SuperCustomToast getInstance(Context context) {
if (instance == null) {
instance = new SuperCustomToast(context);
}
return instance;
}
private SuperCustomToast(Context context) {
if (context == null || context.getApplicationContext() == null) {
throw new NullPointerException("context can‘t be null");
}
mContext = context.getApplicationContext();
initView();
initTN();
// 防反射获取实例
if (instance != null)
throw new NullPointerException("error");
}
/**
* 初始化视图控件
*/
public void initView() {
mHandler = new Handler(mContext.getMainLooper());
lp_WW = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp_MM = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
//获取屏幕密度
DisplayMetrics mDisplayMetrics = mContext.getResources()
.getDisplayMetrics();
//获取屏幕宽高
screenWidth = mDisplayMetrics.widthPixels;
screenHeight = mDisplayMetrics.heightPixels;
mTopView = new LinearLayout(mContext);
mTopView.setLayoutParams(lp_MM);
mTopView.setOrientation(LinearLayout.VERTICAL);
mTopView.setGravity(Gravity.CENTER);
mTopView2 = new LinearLayout(mContext);
LayoutParams params = new LayoutParams(screenWidth, screenHeight);
mTopView2.setLayoutParams(params);
mTopView2.setOrientation(LinearLayout.VERTICAL);
mTopView2.setGravity(Gravity.BOTTOM);
mView = new LinearLayout(mContext);
mView.setLayoutParams(lp_MM);
mView.setOrientation(LinearLayout.VERTICAL);
mView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
View gapView = new View(mContext);
gapView.setLayoutParams(new LayoutParams(screenWidth, screenHeight / 4));
mView.addView(gapView);
mTopView.addView(mTopView2);
mTopView2.addView(mView);
resetDefaultBackgroundAndTextColor();
}
/**
* 显示一条Toast
* layoutId 自定义布局
* @param msg
* 消息内容
*/
public void show(String msg, int layoutId, int rootId, Activity activity) {
LayoutInflater inflater = activity.getLayoutInflater();
layout = inflater.inflate(layoutId,
(ViewGroup) activity.findViewById(rootId));
layout.setLayoutParams(lp_WW);
TextView title = (TextView) layout.findViewById(R.id.title);
title.setText(msg);
title.setTextColor(defaultTextColor);
show(layout, null, null, null);
}
/**
* 显示一条Toast
*
* @param msg
* 消息内容
*/
public void show(String msg) {
show(getTextView(msg), null, null, null);
}
/**
* 显示一条Toast
*
* @param v
* 消息内容
*/
public void show(View v) {
show(v, null, null, null);
}
/**
* 显示一条Toast
*
* @param msg
* 消息内容
* @param duration
* 持续时间,单位为毫秒
*/
public void show(String msg, long duration) {
show(getTextView(msg), duration, null, null);
}
/**
* 一个toast已经显示,在下一个msg相同的情况下, 一定时间不显示新的toast. 如果同样的消息,在两次显示时间差
* 大于duration的情况下,才出现新的toast
*
* @param msg
* 消息内容
* @param duration
* 持续时间,单位为毫秒
*/
Long totalTime;
public void setTime(Long time) {
this.totalTime = time;
}
private boolean firstCalled = true;
String lastMsg;
long startTime;
public void showSameMsg(String msg, long duration) {
if (firstCalled) {
lastMsg = msg;
show(getTextView(msg), duration, null, null);
firstCalled = false;
// 第一次调用的时间
startTime = (new Date()).getTime();
} else if (msg.equals(lastMsg)) {
long endTime = (new Date()).getTime();
long totalTime = endTime - startTime;
Log.e("time", "开始时间:" + startTime + " 结束时间: " + endTime + " 总时间"
+ totalTime);
// 第二次相同
if (totalTime > duration) {
show(getTextView(msg), duration, null, null);
startTime = endTime;
}
}
}
/**
* 显示一条Toast
*
* @param v
* 消息内容
* @param duration
* 持续时间,单位为毫秒
*/
public void show(View v, long duration) {
show(v, duration, null, null);
}
/**
* 显示一条Toast
*
* @param msg
* 消息内容
* @param duration
* 持续时间,单位为毫秒
* @param startAnim
* 入场动画
* @param endAnim
* 离场动画
*/
public void show(String msg, Long duration, Animation startAnim,
Animation endAnim) {
show(getTextView(msg), duration, startAnim, endAnim);
}
/**
* 显示一条图文并存Toast
*
* @param resid
* 图片资源
* @param msg
* 消息内容
* @param duration
* 持续时间,单位为毫秒
* @param startAnim
* 入场动画
* @param endAnim
* 离场动画
*/
public void show(int resid, String msg, Long duration, Animation startAnim,
Animation endAnim) {
show(getView(resid, msg), duration, null, null);
}
/**
* 显示一条Toast
*
* @param v
* 显示的内容
* @param duration
* 持续时间,单位为毫秒
* @param startAnim
* 入场动画
* @param endAnim
* 离场动画
*/
public final void show(final View v, Long duration, Animation startAnim,
final Animation endAnim) {
// 反射过程异常时则使用源生Toast
if (hasReflectException) {
Toast t = new Toast(mContext);
t.setView(v);
t.setDuration(Toast.LENGTH_SHORT);
t.show();
// 重新获取反射对象
initTN();
return;
}
// 显示顶层容器控件
if (mView.getChildCount() == 1)
showToast();
// 获得入场动画
if (startAnim == null) {
startAnim = getStartAnimation();
}
v.clearAnimation();
v.startAnimation(startAnim);
// 把传入的toast显示出来
mView.addView(v, 0);
// 延迟后隐藏传入toast
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
hide(v, endAnim);
}
}, duration == null ? TIME_DURATION : duration);
}
/**
* 设置默认背景颜色
*
* @param color
* 颜色值
* @param alpha
* 透明度
*/
public void setDefaultBackgroundColor(int color, Integer alpha) {
defaultBackgroundColor = new ColorDrawable(color);
if (alpha != null)
defaultBackgroundColor.setAlpha(alpha);
defaultBackgroundResid = null;
}
/**
* 设置默认背景资源
*
* @param resid
* 图片资源文件
*/
public void setDefaultBackgroundResource(int resid) {
defaultBackgroundResid = resid;
}
/**
* 设置默认文字颜色
*
* @param color
*/
public void setDefaultTextColor(int color) {
defaultTextColor = color;
}
/**
* 重置背景和文字颜色
*/
public void resetDefaultBackgroundAndTextColor() {
defaultTextColor = Color.WHITE;
defaultBackgroundColor = new ColorDrawable(Color.BLACK);
defaultBackgroundColor.setAlpha(200);
defaultBackgroundResid = null;
}
/**
* 隐藏指定控件
*
* @param v
* 需要隐藏的控件
* @param endAnim
* 结束动画
*/
public final void hide(final View v, Animation endAnim) {
if (v == null || mView.indexOfChild(v) < 0)
return;
// 获得出场动画
if (endAnim == null)
endAnim = getEndAnimation();
v.clearAnimation();
// 开始出场动画
v.startAnimation(endAnim);
// 动画结束后移除控件
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (v == null || mView.indexOfChild(v) < 0)
return;
// 移除指定控件
mView.removeView(v);
// 隐藏顶层容器控件
if (mView.getChildCount() == 1)
hideToast();
}
}, TIME_END_ANIM);
}
/**
* 获得一个设置好属性的TextView
*
* @param msg
* @return
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public TextView getTextView(String msg) {
TextView tv = new TextView(mContext);
tv.setLayoutParams(lp_WW);
tv.setText(msg);
tv.setTextColor(defaultTextColor);
Drawable background = null;
if (defaultBackgroundResid != null) {
background = mContext.getResources().getDrawable(
defaultBackgroundResid);
} else {
background = defaultBackgroundColor;
}
if (currentapiVersion > 10)
tv.setBackground(background);
else
tv.setBackgroundDrawable(background);
tv.setPadding(5, 5, 5, 5);
tv.setGravity(Gravity.CENTER);
return tv;
}
/**
* 获得一图片或图文并存的toast
*
* @param msg
* @return
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public LinearLayout getView(int resId, String msg) {
LinearLayout layout = new LinearLayout(mContext);
layout.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
layout.setOrientation(LinearLayout.VERTICAL);
layout.setGravity(Gravity.CENTER);
layout.setPadding(10, 10, 10, 10);
ImageView imageView = new ImageView(mContext);
imageView.setLayoutParams(lp_WW);
imageView.setImageResource(resId);
layout.addView(imageView);
if(!TextUtils.isEmpty(msg)){
TextView tv = new TextView(mContext);
tv.setLayoutParams(lp_WW);
tv.setText(msg);
tv.setTextColor(defaultTextColor);
tv.setPadding(5, 5, 5, 5);
tv.setGravity(Gravity.CENTER);
layout.addView(tv);
}
Drawable background = null;
if (defaultBackgroundResid != null) {
background = mContext.getResources().getDrawable(
defaultBackgroundResid);
} else {
background = defaultBackgroundColor;
}
if (currentapiVersion > 10)
layout.setBackground(background);
else
layout.setBackgroundDrawable(background);
return layout;
}
/**
* 获得入场动画
*
* @return
*/
protected Animation getStartAnimation() {
AlphaAnimation animAlpha = new AlphaAnimation(0, 1);
animAlpha.setDuration(TIME_START_ANIM);
animAlpha.setFillAfter(true);
TranslateAnimation animTrans = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 1.5f,
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT,
0f, Animation.RELATIVE_TO_PARENT, 0f);
animTrans.setDuration(TIME_START_ANIM);
animTrans.setFillAfter(true);
animTrans.setInterpolator(new DecelerateInterpolator());
AnimationSet sets = new AnimationSet(true);
sets.addAnimation(animAlpha);
sets.addAnimation(animTrans);
return sets;
}
/**
* 获得离场动画
*
* @return
*/
protected Animation getEndAnimation() {
AlphaAnimation animAlpha = new AlphaAnimation(1, 0);
animAlpha.setDuration(TIME_END_ANIM);
animAlpha.setFillAfter(true);
TranslateAnimation animTrans = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT,
-1.5f, Animation.RELATIVE_TO_PARENT, 0f,
Animation.RELATIVE_TO_PARENT, 0f);
animTrans.setDuration(TIME_END_ANIM);
animTrans.setFillAfter(true);
animTrans.setInterpolator(new AccelerateInterpolator());
AnimationSet sets = new AnimationSet(true);
sets.addAnimation(animAlpha);
sets.addAnimation(animTrans);
return sets;
}
/* 以下为反射相关内容 */
Toast mToast;
Field mTN;
Object mObj;
Method showMethod, hideMethod;
/**
* 通过反射获得mTN下的show和hide方法
*/
private void initTN() {
mToast = new Toast(mContext);
mToast.setView(mTopView);
Class<Toast> clazz = Toast.class;
try {
mTN = clazz.getDeclaredField("mTN");
mTN.setAccessible(true);
mObj = mTN.get(mToast);
showMethod = mObj.getClass().getDeclaredMethod("show",
new Class<?>[0]);
hideMethod = mObj.getClass().getDeclaredMethod("hide",
new Class<?>[0]);
hasReflectException = false;
} catch (NoSuchFieldException e) {
hasReflectException = true;
System.out.println(e.getMessage());
} catch (IllegalAccessException e) {
hasReflectException = true;
System.out.println(e.getMessage());
} catch (IllegalArgumentException e) {
hasReflectException = true;
System.out.println(e.getMessage());
} catch (NoSuchMethodException e) {
hasReflectException = true;
System.out.println(e.getMessage());
}
}
/**
* 通过反射获得的show方法显示指定View
*/
private void showToast() {
try {
// 高版本需要再次手动设置mNextView属性
if (currentapiVersion > 10) {
Field mNextView = mObj.getClass().getDeclaredField("mNextView");
mNextView.setAccessible(true);
mNextView.set(mObj, mTopView);
}
showMethod.invoke(mObj, new Object[0]);
hasReflectException = false;
} catch (Exception e) {
hasReflectException = true;
System.out.println(e.getMessage());
}
}
/**
* 通过反射获得的hide方法隐藏指定View
*/
public void hideToast() {
try {
hideMethod.invoke(mObj, new Object[0]);
hasReflectException = false;
} catch (Exception e) {
hasReflectException = true;
System.out.println(e.getMessage());
}
}
public void removeView() {
}
}
从上面的代码可以看出:里面反射来创建Toast,通过反射来获得mTN下的show和hide方法来控制toast的show和hide,然后获得handle来处理ui的刷新,代码通过一些参数来设置toast,比如显示的文字,背景颜色,背景图片,显示的图片和时长。
然后就是activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:gradientRadius="80%p"
android:stateListAnimator="@drawable/touch_raise"
app:cardBackgroundColor="#303069"
app:cardCornerRadius="4dp"
app:cardElevation="5dp"
app:cardMaxElevation="10dp"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true"
>
<TextView
android:id="@+id/showToast1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="默认Toast"
android:textColor="#ffffff"/>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:gradientRadius="80%p"
android:stateListAnimator="@drawable/touch_raise"
app:cardBackgroundColor="#303069"
app:cardCornerRadius="4dp"
app:cardElevation="5dp"
app:cardMaxElevation="10dp"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true">
<TextView
android:id="@+id/showToast2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="自定义5秒"
android:textColor="#ffffff"/>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:gradientRadius="80%p"
android:stateListAnimator="@drawable/touch_raise"
app:cardBackgroundColor="#303069"
app:cardCornerRadius="4dp"
app:cardElevation="5dp"
app:cardMaxElevation="10dp"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true">
<TextView
android:id="@+id/showToast3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="背景+文字"
android:textColor="#ffffff"/>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:gradientRadius="80%p"
android:stateListAnimator="@drawable/touch_raise"
app:cardBackgroundColor="#303069"
app:cardCornerRadius="4dp"
app:cardElevation="5dp"
app:cardMaxElevation="10dp"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true">
<TextView
android:id="@+id/showToast4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="背景为图片"
android:textColor="#ffffff"/>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:gradientRadius="80%p"
android:stateListAnimator="@drawable/touch_raise"
app:cardBackgroundColor="#303069"
app:cardCornerRadius="4dp"
app:cardElevation="5dp"
app:cardMaxElevation="10dp"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true">
<TextView
android:id="@+id/showToast5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="自定义动画"
android:textColor="#ffffff"/>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:gradientRadius="80%p"
android:stateListAnimator="@drawable/touch_raise"
app:cardBackgroundColor="#303069"
app:cardCornerRadius="4dp"
app:cardElevation="5dp"
app:cardMaxElevation="10dp"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true">
<TextView
android:id="@+id/showToast6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="显示图片"
android:textColor="#ffffff"/>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:gradientRadius="80%p"
android:stateListAnimator="@drawable/touch_raise"
app:cardBackgroundColor="#303069"
app:cardCornerRadius="4dp"
app:cardElevation="5dp"
app:cardMaxElevation="10dp"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true">
<TextView
android:id="@+id/showToast7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="图文共存"
android:textColor="#ffffff"/>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:stateListAnimator="@drawable/touch_raise"
app:cardBackgroundColor="#303069"
app:cardCornerRadius="4dp"
app:cardElevation="5dp"
app:cardMaxElevation="10dp"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true"
android:gradientRadius="80%p">
<TextView
android:id="@+id/showToast8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp"
android:text="自定义布局"
android:textColor="#ffffff"/>
</android.support.v7.widget.CardView>
</LinearLayout>
为了效果好看点使用CardView,也有点阴影和动画效果。
接下来就是代码的示例使用了:
package com.losileeya.toastmaster;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.TextView;
import com.losileeya.toastmaster.view.SuperCustomToast;
import java.lang.ref.WeakReference;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
SuperCustomToast toast;//自定义Toast
int i = 0;
final StringBuffer sb = new StringBuffer("默认Toast");
final String info = "默认Toast-";
final String sameString = "相同信息Toast";
Handler mHandler;
@BindView(R.id.showToast1)
TextView showToast1;
@BindView(R.id.showToast2)
TextView showToast2;
@BindView(R.id.showToast3)
TextView showToast3;
@BindView(R.id.showToast4)
TextView showToast4;
@BindView(R.id.showToast5)
TextView showToast5;
@BindView(R.id.showToast6)
TextView showToast6;
@BindView(R.id.showToast7)
TextView showToast7;
@BindView(R.id.showToast8)
TextView showToast8;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//绑定视图
ButterKnife.bind(MainActivity.this);
//初始化handle
mHandler = new MyHandler(this);
//获取自定义toast的实例
toast = SuperCustomToast.getInstance(getApplicationContext());
}
/**
* 入场动画
*/
private void showAnimate() {
// 旋转
RotateAnimation rAnim = new RotateAnimation(0, 720,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rAnim.setDuration(500);
rAnim.setFillAfter(true);
// 缩放
ScaleAnimation sAnim = new ScaleAnimation(0, 1, 0, 1,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
sAnim.setDuration(500);
sAnim.setFillAfter(true);
// 透明度
AlphaAnimation aAnim1 = new AlphaAnimation(0, 1);
aAnim1.setDuration(500);
aAnim1.setFillAfter(true);
AnimationSet startAnim = new AnimationSet(false);
startAnim.addAnimation(rAnim);
startAnim.addAnimation(aAnim1);
startAnim.addAnimation(sAnim);
// 移动
TranslateAnimation animTrans = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0f,
Animation.RELATIVE_TO_PARENT, 0f,
Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 1f);
animTrans.setDuration(500);
animTrans.setFillAfter(true);
// 透明度
AlphaAnimation aAnim2 = new AlphaAnimation(1, 0);
aAnim2.setDuration(500);
aAnim2.setFillAfter(true);
AnimationSet endAnim = new AnimationSet(false);
endAnim.addAnimation(animTrans);
endAnim.addAnimation(aAnim2);
toast.show("自定义动画的Toast-" + i++, null, startAnim, endAnim);
}
private static class MyHandler extends Handler {
private WeakReference<MainActivity> activityWeakReference;
public MyHandler(MainActivity activity) {
activityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityWeakReference.get();
if (activity != null) {
}
}
}
@Override
protected void onPause() {
super.onPause();
toast.hideToast();
toast.mView.removeAllViews();
toast.initView();
}
@OnClick({R.id.showToast1, R.id.showToast2, R.id.showToast3, R.id.showToast4, R.id.showToast5, R.id.showToast6, R.id.showToast7,R.id.showToast8})
public void onClick(View view) {
switch (view.getId()) {
case R.id.showToast1:
mHandler.post(new Runnable() {
@Override
public void run() {
//默认toast
toast.show(info + i++);
}
});
break;
case R.id.showToast2:
// 持续5秒的toast
toast.showSameMsg(sameString, 5000);
break;
case R.id.showToast3:
//带背景色Toast
toast.setDefaultBackgroundColor(Color.RED, 200);//Toast背景
toast.setDefaultTextColor(Color.BLUE);//文字颜色
toast.show("带有背景色Toast");
break;
case R.id.showToast4:
//给Toast添加背景图片
toast.setDefaultBackgroundResource(R.drawable.bg);
toast.setDefaultTextColor(Color.BLACK);
toast.show("背景图片的Toast");
break;
case R.id.showToast5:
//给Toast加动画
showAnimate();
break;
case R.id.showToast6:
//带图片的Toast
toast.setDefaultBackgroundResource(R.drawable.frame_bg_theme_light);
toast.show(R.drawable.tips_smile, "", null, null, null);
break;
case R.id.showToast7:
//图文混合的toast
toast.setDefaultTextColor(Color.RED);
toast.setDefaultBackgroundResource(R.drawable.frame_bg_theme_light);
toast.show(R.drawable.tips_smile, "我笑了", null, null, null);
break;
case R.id.showToast8:
//自定义布局Toast
toast.setDefaultTextColor(Color.RED);
toast.show(info + i++, R.layout.super_toast_theme_light,
R.id.content_toast, MainActivity.this);
break;
}
}
}
上面的代码呢就是对8种toast的效果的展示,基本上也能适合开发的所有场景了,实在不行的话那么你就使用snackbar咯,效果或许不好,但是也还是能学到一些东西的。
注意:
由于使用的butterknife5.0.1我手贱更新到了8.0.1,差点被坑了
我是这样做的:
compile ‘com.jakewharton:butterknife:8.0.1’
然后代码也能使用:@BindView,@OnClick特么怎么出鬼了,提示unused(木有使用) ,木有办法只能去github上了,然后改成这样
apply plugin: ‘com.neenbedankt.android-apt‘
dependencies {
compile ‘com.jakewharton:butterknife:8.0.1‘
apt ‘com.jakewharton:butterknife-compiler:8.0.1‘
}
然后工程的也要改:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath ‘com.android.tools.build:gradle:2.0.0‘
classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8‘
}
}
总结
对toast的源码翻出来一部分,然后跟我们一般的toast的封装还是有些不同,当然封装一下toast对开发的效率还是能够提高的 ,代码中用到了动画有温习了一下,学习就是这样从难到易。
好了,所有的讲解就到这里了,能力有限见丑了,就到这里。感觉不错麻烦帮顶一下,赞一个,哪里有问题指出来。
demo 传送门:ToastMaster