android从源码解析并实现各种Toast效果合集
2016-05-09 21:49
731 查看
前言
安卓中为了给用户的友好提示,一般的表现形式为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
相关文章推荐
- android 中的OnAcitvityResult
- Android中handler引用记录
- 05Android学习从零单排之Androidmultimedia(多媒体)
- Android 点击一个按钮,弹出一个对话框
- Android EditText
- 【Android学习】使用RxJava完成益智翻牌游戏
- 非常全面的 Android Bitmap 知识点梳理
- R文件的编译命名
- Xamarin.Android OOM 思路整理 (BitmapFactory.DecodeByteArray)
- Android之高德地图定位
- Android开发工具之Android Studio---gradle编译报错(一)
- 最全面的AndroidStudio配置指南总结-包括护眼模式
- Android4.4中获取资源路径问题
- 在低版本android系统上实现Material design应用
- Android新增API之AudioEffect中文API与应用实例
- Activity之affinity
- Android 6.0 向用户申请权限,运行时权限
- Android Dev Intro - An OpenGL ES renderer based on the GLSurfaceView rendering framework
- 大话Android项目中的常用技巧
- Android 设定横屏,禁止屏幕旋转,Activity重置