锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

Toast的高级自定义方式-循序渐进带你了解toast

时间:2022-09-02 04:00:00 sub微型d连接器mwdm2l

写在前面
对于Toast相信只要用过,android童鞋并不陌生,它是一个不需要与用户互动的提示框。接下来,让我们一步一步地定制它Toast,全方位玩Toast,实现其不同的显示需求。从那时起,我们不再害怕提示的各种异常需求。~
先看效果图,华为手机,4.4版本,没root,只能连上电脑,再通过录制电脑屏幕上的手机画面录屏,求推荐好方法录屏。~


1.最基本的Toast
Toast.makeText(getApplicationContext(),"最基本的Toast",Toast.LENGTH_SHORT).show();
1
不用多说。

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三个结构函数
...
//b.初始化画笔的参数和矩形参数
...

}

a.部分变量初始化,实现三个结构函数。

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 CustomToastViw(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的样式。)

    android:shape="rectangle">
   
   
   



一个text_toast.xml(设置textView的样式)

    android:shape="rectangle">
   
   
            android:bottomRightRadius="10dp"
        android:topRightRadius="10dp">



一个是smile_toast.xml(显示的toast的view布局方式)

    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#00000000"
    android:orientation="vertical">
            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">
                    android:id="@+id/linearLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center">
                            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" />
       

                    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" />
   


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();
1
2
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;
    }
}
1

接着在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();

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章