提示控件之自定义Toast

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”的分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值