Toast的高级自定义方式-循序渐进带你了解toast
Posted Mario_oo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Toast的高级自定义方式-循序渐进带你了解toast相关的知识,希望对你有一定的参考价值。
写在前面
对于Toast的使用,相信只要是使用过android的童鞋都不会陌生,它是不需要和用户进行交互的一个提示框。接下来,让我们一步步自定义Toast,全方位的玩转Toast,实现它的不同显示需求。从此再也不怕提示的各种变态需求。~
先来看看效果图,苦逼的华为手机,4.4版本,没root,只能连上电脑,再通过录制电脑屏幕上的手机画面录屏,求推荐好方法录屏。~
1.最基本的Toast
Toast.makeText(getApplicationContext(),"最基本的Toast",Toast.LENGTH_SHORT).show();
这个不用多讲。
2.自定义位置的Toast
通过Toast类自带的定义位置的方法来设置toast出现的位置。
Toast toast=Toast.makeText(getApplicationContext(),"自定义位置的Toast",Toast.LENGTH_SHORT);
/**
*Toast.setGravity(gravity,xOffset,yOffset);
*@gravity:toast的位置
*@xOffset:相对于gravity x方向上的偏移量。yOffset:相对于gravity y方向的偏移量。
*/
toast.setGravity(Gravity.LEFT,50,0);
toast.show();
3.带图片的toast
讲了前面的两种最基本的Toast,我们现在先来看看toast.markText的源码,看看toast到底是如何显示的。
源码位置:frameworks/base/core/java/Android/widght/Toast.java (Toast#makeText())
其中要增加的view的布局方式在:frameworks/base/core/res/res/layout/transient_notification.xml。(里面只有一个TextView没啥讲的。)
public static Toast makeText(Context context, CharSequence text, int duration)
Toast result = new Toast(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
从上面源码可以得到的东西:1.new一个Toast类,获得当前的context;2.LayoutInflater,动态载入layout的类,将view的布局载入。3.获得view中的textview中的文字信息。4.设置toast的view和duration属性。5.返回toast.从而实现了toast的markText方法。
ok!,搞懂了toast.markText这部分的源码,我们就可以设置自定义view的toast.
1.创建一个custom_toast.xml,一个LinearLayout,水平方向,ImageView+TextView。设置它们的各种属性。(代码很简单,就不贴了。)
2.自定义Toast.直接上代码
Toast customToast = new Toast(MainActivity.this.getApplicationContext());
//获得view的布局
View customView = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_toast,null);
ImageView img = (ImageView) customView.findViewById(R.id.iv);
TextView tv = (TextView) customView.findViewById(R.id.tv);
//设置ImageView的图片
img.setBackgroundResource(R.drawable.ab);
//设置textView中的文字
tv.setText("我是带图片的自定义位置的toast");
//设置toast的View,Duration,Gravity最后显示
customToast.setView(customView);
customToast.setDuration(Toast.LENGTH_SHORT);
customToast.setGravity(Gravity.CENTER,0,0);
customToast.show();
4.自定义View带动画超高级的Toast.
其实,这个就是3.带图片的toast的加强版。将里面其中的图片,改换成我们自定义的view,通过自定义view,来实现多种多样的Toast.
1.创建自定义的view.CustomToastView继承View.
整体的Custom的结构(下文会有具体实现代码):
public class CustomToastView extends View
//a.初始化其中的一些变量。
......
//a.实现CustomToastView的3个构造函数
......
//b.初始化画笔的参数和矩形参数
.....
a.初始化其中的一些变量,实现3个构造函数。
public class CustomToastView extends View
//矩形,设置toast布局时用
RectF rectF =new RectF();
//属性动画
ValueAnimator valueAnimator;
float mAnimatedValue = 0f;
//自定义view的画笔
private Paint mPaint;
private float mWidth = 0f; //view的宽
private float mEyeWidth = 0f; //笑脸的眼睛半径
private float mPadding = 0f; //view的偏移量。
private float endAngle = 0f; //圆弧结束的度数
//是左眼还是右眼
private boolean isSmileLeft = false;
private boolean isSmileRight = false;
public CustomToastView(Context context)
super(context);
public CustomToastView(Context context, AttributeSet attrs)
super(context, attrs);
public CustomToastView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
.......
b.设置画笔的参数以及矩形的参数。
private void initPaint()
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("#5cb85c"));
mPaint.setStrokeWidth(dip2px(2));
private void initRect()
rectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding);
//dip转px。为了支持多分辨率手机
public int dip2px(float dpValue)
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
c.重写onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
initPaint();
initRect();
mWidth = getMeasuredWidth(); //当前view在父布局里的宽度。即view所占宽度。
mPadding = dip2px(10);
mEyeWidth = dip2px(3);
d.重写OnDraw
@Override
protected void onDraw(Canvas canvas)
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.STROKE);
//画微笑弧(从左向右画弧)
canvas.drawArc(rectF, 180, endAngle, false, mPaint);
//设置画笔为实心
mPaint.setStyle(Paint.Style.FILL);
//左眼
if (isSmileLeft)
canvas.drawCircle(mPadding + mEyeWidth + mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
//右眼
if (isSmileRight)
canvas.drawCircle(mWidth - mPadding - mEyeWidth - mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
e.自定义View中的动画效果实现
/**
* startAnim()不带参数的方法
*/
public void startAnim()
stopAnim();
startViewAnim(0f, 1f, 2000);
/**
* 停止动画的方法
*
*/
public void stopAnim()
if (valueAnimator != null)
clearAnimation();
isSmileLeft = false;
isSmileRight = false;
mAnimatedValue = 0f;
valueAnimator.end();
/**
* 开始动画的方法
* @param startF 起始值
* @param endF 结束值
* @param time 动画的时间
* @return
*/
private ValueAnimator startViewAnim(float startF, final float endF, long time)
//设置valueAnimator 的起始值和结束值。
valueAnimator = ValueAnimator.ofFloat(startF, endF);
//设置动画时间
valueAnimator.setDuration(time);
//设置补间器。控制动画的变化速率
valueAnimator.setInterpolator(new LinearInterpolator());
//设置监听器。监听动画值的变化,做出相应方式。
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator)
mAnimatedValue = (float) valueAnimator.getAnimatedValue();
//如果value的值小于0.5
if (mAnimatedValue < 0.5)
isSmileLeft = false;
isSmileRight = false;
endAngle = -360 * (mAnimatedValue);
//如果value的值在0.55和0.7之间
else if (mAnimatedValue > 0.55 && mAnimatedValue < 0.7)
endAngle = -180;
isSmileLeft = true;
isSmileRight = false;
//其他
else
endAngle = -180;
isSmileLeft = true;
isSmileRight = true;
//重绘
postInvalidate();
);
if (!valueAnimator.isRunning())
valueAnimator.start();
return valueAnimator;
好了,就这么多,自定义view大功告成。
2.toast要用的view的xml。没啥多说的直接上代码。有三个xml.
一个是backgroud_toast.xml(设置view的样式。)
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFF"></solid>
<stroke android:color="#C4CDE0"></stroke>
<corners android:radius="10dp"></corners>
</shape>
一个text_toast.xml(设置textView的样式)
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#5cb85c"></solid>
<stroke android:color="#C4CDE0"></stroke>
<corners
android:bottomRightRadius="10dp"
android:topRightRadius="10dp"></corners>
</shape>
一个是smile_toast.xml(显示的toast的view布局方式)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:orientation="vertical">
<LinearLayout
android:id="@+id/base_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="25dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:layout_marginTop="25dp"
android:background="@drawable/backgroud_toast"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.example.yyh.toasttest.CustomToastView
android:id="@+id/successView"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical|left"
android:layout_margin="10px"
android:gravity="center_vertical|left" />
</LinearLayout>
<TextView
android:id="@+id/toastMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="10dp"
android:text="New Text" />
</LinearLayout>
</LinearLayout>
3.接下来就是在代码中将自定义的toast,加入进去。大功告成。
//在MainActivity中声明CustomToastView
static CustomToastView customToastView;
Toast smileToast=new Toast(MainActivity.this.getApplicationContext());
//view布局
View smileView =LayoutInflater.from(MainActivity.this.getApplicationContext()).inflate(R.layout.smile_toast,null,false);
TextView text = (TextView) smileView.findViewById(R.id.toastMessage);
text.setText("我是带动画的toast");
//给customToastView增加动画效果
customToastView=(CustomToastView)smileView.findViewById(R.id.smileView);
customToastView.startAnim();
//设置text的背景样式
text.setBackgroundResource(R.drawable.text_toast);
text.setTextColor(Color.parseColor("#FFFFFF"));
smileToast.setView(smileView);
smileToast.setDuration(Toast.LENGTH_SHORT);
smileToast.show();
是不是脑光一亮~,笑脸的toast只是个启发,有了这个思路我们就可以设置不同的效果显示方式。
5.带出入效果的Toast
大家如果用的小米手机,就会发现,小米手机弹出的toast,有一个从底部上移弹出的效果。这个效果也是比较特别的。我们就来试试也实现下这个效果。
我们将4.自定义View带动画超高级的Toast.进行进一步的扩展,利用悬浮窗的原理,完成从底部弹出toast的效果。(其实查看源码 Toast实质上就是用到了悬浮窗的知识WindowManager.addView;和 mWM.removeView(mView);
)来实现Toast的显示和消失的。当然,这里我们不再剖析源码,大家知道就行了
对悬浮窗的知识不是很了解的童鞋,可以去看我的上一篇文章: 仿360加速球。(实现内存释放)
首先,新建一个MiUiToast类。
1.一些需要的变量。这里用到了上面的自定义的view.
//窗口管理类,用来管理Toast的显示和隐藏。
private WindowManager mWdm;
//自定义的view.
private CustomToastView mToastView;
//toast的参数
private WindowManager.LayoutParams mParams;
//是否显示toast.
private int showTime;
private boolean mIsShow;//记录当前Toast的内容是否已经在显示
//要显示的view.
private final View smileView;
2.相关函数的设置。
//显示时间的设置相关
public int getShowTime()
return showTime;
public void setShowTime(int showTime)
this.showTime = showTime;
public static MiUiToast MakeText(Context context, String text, int showTime)
MiUiToast result = new MiUiToast(context, text, showTime);
return result;
private MiUiToast(Context context, String text, int time)
setShowTime(time);
mIsShow = false;//记录当前Toast的内容是否已经在显示
mWdm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
smileView = LayoutInflater.from(context.getApplicationContext()).inflate(R.layout.smile_toast, null, false);
TextView text1 = (TextView) smileView.findViewById(R.id.toastMessage);
text1.setText("我是带动画的toast");
mToastView=(CustomToastView) smileView.findViewById(R.id.smileView);
mToastView.startAnim();
text1.setBackgroundResource(R.drawable.text_toast);
text1.setTextColor(Color.parseColor("#FFFFFF"));
//设置布局参数
setParams();
//toast.布局参数的设置。
private void setParams()
mParams = new WindowManager.LayoutParams();
mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mParams.format = PixelFormat.TRANSLUCENT;
mParams.windowAnimations = R.style.anim_view;//设置进入退出动画效果
mParams.type = WindowManager.LayoutParams.TYPE_TOAST;
mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
mParams.gravity = Gravity.CENTER_HORIZONTAL;
mParams.y = 250;
//显示Toast.
public void show()
if(!mIsShow)//如果Toast没有显示,则开始加载显示
mIsShow = true;
mWdm.addView(smileView, mParams);//将其加载到windowManager上
//取消toast.
public void cancel()
mWdm.removeView(smileView);
mIsShow = false;
3.在ManiActivity中设置MiUiToast,显示。大功告成。
private Handler handler=new Handler()
@Override
public void handleMessage(Message msg)
super.handleMessage(msg);
if (msg.what==007)
miUitoast.cancel();
;
private MiUiToast miUitoast;
if(miUitoast == null)
miUitoast = MiUiToast.MakeText(this, "仿小米Toast", 2000);
miUitoast.show();
handler.sendEmptyMessageDelayed(007,miUitoast.getShowTime());
6.关于Toast几个不为人知的秘密(敲黑板,认真脸,加分项啊加分项。)
1.toast的显示时间只有两种可能。我们查看源码可以得知它只有2秒和3.5秒。(
long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
)
private void scheduleTimeoutLocked(ToastRecord r)
mHandler.removeCallbacksAndMessages(r);
Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
mHandler.sendMessageDelayed(m, delay);
private static final int LONG_DELAY = 3500; // 3.5 seconds
private static final int SHORT_DELAY = 2000; // 2 seconds
那如果我们要控制toast的显示时间随意该怎么办呢,认真看过的上面的文章的童鞋,相信已经有了思路。没错,自定义view的toast,利用WindowManager+Handler,确定一定时间来remove这个自定义的Toast.就行了。参考5.带出入效果的Toast
当然还有另一种思路,利用反射拿到show方法,我试了好久,都是有错误,最后寻找原因但是好像在andorid4.0往上,这种方法就用了不了。这里就不讲了。。。
还有一种思路。利用Timer+Handler也可以来控制Toast的显示时间问题。那么开始吧~
新建一个TimeToast类,定义两个Timer,一个是显示Toast,一个是取消Toast.代码不难直接上~
/**
* Created by yyh on 2016/10/27.
*/
public class TimeToast
//定义的显示时间
private double time;
private static Handler handler;
//显示的计时器
private Timer showTimer;
//取消的计时器
private Timer cancelTimer;
private Toast toast;
private TimeToast()
showTimer = new Timer();
cancelTimer = new Timer();
public void setTime(double time)
this.time = time;
public void setToast(Toast toast)
this.toast = toast;
public static TimeToast makeText(Context context, String text, double time)
TimeToast toast1= new TimeToast();
toast1.setTime(time);
toast1.setToast(Toast.makeText(context, text, Toast.LENGTH_SHORT));
handler = new Handler(context.getMainLooper());
return toast1;
public void show()
toast.show();
if(time > 2)
showTimer.schedule(new TimerTask()
@Override
public void run()
handler.post(new ShowRunnable());
, 0, 1900);
cancelTimer.schedule(new TimerTask()
@Override
public void run()
handler.post(new CancelRunnable());
, (long)(time * 1000));
private class CancelRunnable implements Runnable
@Override
public void run()
showTimer.cancel();
toast.cancel();
private class ShowRunnable implements Runnable
@Override
public void run()
toast.show();
之后在ManiActivity中调用这个类就行了
TimeToast timeToast=TimeToast.makeText(getApplicationContext(),"显示时间自定的Toast",6);
timeToast.show();
2.Toast显示的问题,当我们连续点击Toast的时候,居然一直在显示,点击30多下,结果这条Toast显示了将近2分钟。这样用户的体验很不好。(原因是因为Toast的管理是在队列中,点击一次,就会产生一个新的Toast,所以要等这个队列中的Toast处理完,这个显示Toast的任务才算结束。) so~ 思路来了,我们可以把Toast改成单例模式,没有Toast再新建它,这样也就解决了连续点击Toast,一直在显示的问题。~
先上新建的单例模式的SingleToast类:
public class SingleToast
private static Toast mToast;
/**双重锁定,使用同一个Toast实例*/
public static Toast getInstance(Context context)
if (mToast == null)
synchronized (SingleToast.class)
if (mToast == null)
mToast = new Toast(context);
return mToast;
接着在ManiActivity中进行应用,就是这么随意~ 大功告成~
Toast singleToast=SingleToast.getInstance(getApplicationContext());
View singleCustomView = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_toast,null);
ImageView img1 = (ImageView) singleCustomView.findViewById(R.id.iv);
TextView tv1 = (TextView) singleCustomView.findViewById(R.id.tv);
img1.setBackgroundResource(R.drawable.ic_launcher);
tv1.setText("这是第"+num+++"遍 点击我了~~");
singleToast.setView(singleCustomView);
singleToast.setDuration(Toast.LENGTH_SHORT);
singleToast.show();
ok~该讲的已经讲完了,不该讲的也不知道讲啥了,以后有机会再详细讲解下Toast的源码。
上完整的代码:csdn:
以上是关于Toast的高级自定义方式-循序渐进带你了解toast的主要内容,如果未能解决你的问题,请参考以下文章