自定义Notification和Toast

一、自定义Notification
在Android开发中,我们经常会使用Notification,首先来看看我们使用系统默认的Notification的通常做法。

CharSequence title = "I am Notification";
int icon = R.drawable.ic_launcher;
// 1、定义一个Notification对象
Notification noti = new Notification(icon, title, System.currentTimeMillis());

// 2、设置状态栏的显示类型,可选
noti.flags = Notification.FLAG_INSISTENT;

// 3、设置提示类型,可选
noti.defaults |= Notification.DEFAULT_SOUND;

// 4、定义点击通知触发的Intent跳转,可选
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent("android.settings.SETTINGS"), 0);

// 5、设置默认的RemoteViews对象
noti.setLatestEventInfo(MainActivity.this, "标题", "内容",contentIntent);

// 6、得到NotificationManager服务
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

// 7、触发notify
mNotificationManager.notify(2, noti);

完整的使用流程就是上面的这个,下面我们对上面的几步进行分析分析。

1、Notification.flags

// 屏幕LED打开
public static final int FLAG_SHOW_LIGHTS        = 0x00000001;
// 通知放置在正在运行
public static final int FLAG_ONGOING_EVENT      = 0x00000002;
// 提示声音一直重复直到通知被取消或者被打开
public static final int FLAG_INSISTENT          = 0x00000004;
// 每次发生通知都会有声音或者振动提示
public static final int FLAG_ONLY_ALERT_ONCE    = 0x00000008;
// 该通知能被清除掉
public static final int FLAG_AUTO_CANCEL        = 0x00000010;
// 该通知不能被直接清除掉
public static final int FLAG_NO_CLEAR           = 0x00000020;
// 代表当前正在运行的服务
public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;

2、Notification.defaults

// 默认所有
public static final int DEFAULT_ALL = ~0;
// 默认声音
public static final int DEFAULT_SOUND = 1;
// 默认振动
public static final int DEFAULT_VIBRATE = 2;
// 默认闪光
public static final int DEFAULT_LIGHTS = 4;

3、setLatestEventInfo到底做了哪些工作?
这个对我们自定义Notification相当的重要,所以我们需要把里面的代码进行分析分析。

直接进入setLatestEventInfo函数:

public void setLatestEventInfo(Context context,
        CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
    Notification.Builder builder = new Notification.Builder(context);

    // First, ensure that key pieces of information that may have been set directly
    // are preserved
    // 显示时间
    builder.setWhen(this.when);
    // 小图标
    builder.setSmallIcon(this.icon);
    // 优先级 
    builder.setPriority(this.priority);
    // 提示信息
    builder.setTicker(this.tickerText);
    // 设置信息最大数目
    builder.setNumber(this.number);
    // 设置Flags
    builder.mFlags = this.flags;
    // 设置提示声音
    builder.setSound(this.sound, this.audioStreamType);
    // 设置Default
    builder.setDefaults(this.defaults);
    // 设置振动模式
    builder.setVibrate(this.vibrate);

    // now apply the latestEventInfo fields
    // 设置内容标题
    if (contentTitle != null) {
        builder.setContentTitle(contentTitle);
    }
    // 设置内容文本
    if (contentText != null) {
        builder.setContentText(contentText);
    }
    //设置PendingIntent
    builder.setContentIntent(contentIntent);

    builder.buildInto(this);
}

它使用了一个Builder构建者来对Notification的各个选项进行设置,这个Builder对象保存了各个设置项,这些设置项跟Notification里面的选项是对应的。

下面我们来看看buildInto函数:

public Notification buildInto(Notification n) {
    build().cloneInto(n, true);
    return n;
}

可以看到它首先执行了build()函数,接着执行了cloneInto函数:

public Notification build() {
    Notification n = buildUnstyled();

    if (mStyle != null) {
        n = mStyle.buildStyled(n);
    }

    n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle();

    addExtras(n.extras);
    if (mStyle != null) {
        mStyle.addExtras(n.extras);
    }

    return n;
}

这里住要看看buildUnstyled函数:

public Notification buildUnstyled() {
    Notification n = new Notification();
    n.when = mWhen;
    n.icon = mSmallIcon;
    n.iconLevel = mSmallIconLevel;
    n.number = mNumber;
    n.contentView = makeContentView();
    n.contentIntent = mContentIntent;
    n.deleteIntent = mDeleteIntent;
    n.fullScreenIntent = mFullScreenIntent;
    n.tickerText = mTickerText;
    n.tickerView = makeTickerView();
    n.largeIcon = mLargeIcon;
    n.sound = mSound;
    n.audioStreamType = mAudioStreamType;
    n.vibrate = mVibrate;
    n.ledARGB = mLedArgb;
    n.ledOnMS = mLedOnMs;
    n.ledOffMS = mLedOffMs;
    n.defaults = mDefaults;
    n.flags = mFlags;
    n.bigContentView = makeBigContentView();
    if (mLedOnMs != 0 || mLedOffMs != 0) {
        n.flags |= FLAG_SHOW_LIGHTS;
    }
    if ((mDefaults & DEFAULT_LIGHTS) != 0) {
        n.flags |= FLAG_SHOW_LIGHTS;
    }
    if (mKindList.size() > 0) {
        n.kind = new String[mKindList.size()];
        mKindList.toArray(n.kind);
    } else {
        n.kind = null;
    }
    n.priority = mPriority;
    if (mActions.size() > 0) {
        n.actions = new Action[mActions.size()];
        mActions.toArray(n.actions);
    }

    return n;
}

从上面的代码可以看到,其实真正的通知不是我们自己new的那个Notification,而是这里帮我们new的Notification,上面我们自己new的Notification主要干了一件事,就是保存设置项,在setLatestEventInfo函数里面,就会把这些设置项统一的用一个builder保存起来,而上面代码的工作就是创建一个真正需要发送的通知n,然后把builder里面保存的设置项设置给这个通知n,最终会把这个通知返回回去,cloneInto的工作就是把我们返回回去的这个通知里面的所有内容全部拷贝到我们自己new的那个Notificaton里面去,这种最终就可以通过NotificationManager把这个通知发送出去。

从上面我们就可以知道自定义Notification的过程了,系统搞来搞去其实真正的工作就是上面的代码,它已经很好的展现了自定义Notification的过程,主要分为4步:

// 1、创建一个Notification对象
Notification n = new Notification();

// 2、设置Notification内容项
n.when = mWhen;
n.icon = mSmallIcon;
n.iconLevel = mSmallIconLevel;
n.number = mNumber;
n.contentView = makeContentView();
n.contentIntent = mContentIntent;
n.deleteIntent = mDeleteIntent;
n.fullScreenIntent = mFullScreenIntent;
n.tickerText = mTickerText;
n.tickerView = makeTickerView();
n.largeIcon = mLargeIcon;
n.sound = mSound;
n.audioStreamType = mAudioStreamType;
n.vibrate = mVibrate;
n.ledARGB = mLedArgb;
n.ledOnMS = mLedOnMs;
n.ledOffMS = mLedOffMs;
n.defaults = mDefaults;
n.flags = mFlags;
n.bigContentView = makeBigContentView();

// 3、得到NotificationManager服务
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

// 4、发送通知
mNotificationManager.notify(2, n);

对于上面的过程,我们重点需要看看第二步里面的contentView的设置,它是Notification里面的显示视图,我们自定义Notification的时候,往往需要设计里面的显示内容,下面来看看makeContentView函数:

private RemoteViews makeContentView() {
    if (mContentView != null) {
        return mContentView;
    } else {
        return applyStandardTemplate(R.layout.notification_template_base, true); // no more special large_icon flavor
    }
}

从这个里面我们就可以看到,其实contentView是一个RemoteViews对象,下面需要看看applyStandardTemplate函数:

private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) {
    //创建一个RemoteViews对象,这个对象传入了一个布局文件resId
    RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
    boolean showLine3 = false;
    boolean showLine2 = false;
    int smallIconImageViewId = R.id.icon;
    // 下面的工作就是为这个布局文件View里面的各个对象赋值,主要是通过id来进行识别
    if (mLargeIcon != null) {
        contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
        smallIconImageViewId = R.id.right_icon;
    }
    if (mPriority < PRIORITY_LOW) {
        contentView.setInt(R.id.icon,
                "setBackgroundResource", R.drawable.notification_template_icon_low_bg);
        contentView.setInt(R.id.status_bar_latest_event_content,
                "setBackgroundResource", R.drawable.notification_bg_low);
    }
    if (mSmallIcon != 0) {
        contentView.setImageViewResource(smallIconImageViewId, mSmallIcon);
        contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE);
    } else {
        contentView.setViewVisibility(smallIconImageViewId, View.GONE);
    }
    if (mContentTitle != null) {
        contentView.setTextViewText(R.id.title, mContentTitle);
    }
    if (mContentText != null) {
        contentView.setTextViewText(R.id.text, mContentText);
        showLine3 = true;
    }
    if (mContentInfo != null) {
        contentView.setTextViewText(R.id.info, mContentInfo);
        contentView.setViewVisibility(R.id.info, View.VISIBLE);
        showLine3 = true;
    } else if (mNumber > 0) {
        final int tooBig = mContext.getResources().getInteger(
                R.integer.status_bar_notification_info_maxnum);
        if (mNumber > tooBig) {
            contentView.setTextViewText(R.id.info, mContext.getResources().getString(
                        R.string.status_bar_notification_info_overflow));
        } else {
            NumberFormat f = NumberFormat.getIntegerInstance();
            contentView.setTextViewText(R.id.info, f.format(mNumber));
        }
        contentView.setViewVisibility(R.id.info, View.VISIBLE);
        showLine3 = true;
    } else {
        contentView.setViewVisibility(R.id.info, View.GONE);
    }

    // Need to show three lines?
    if (mSubText != null) {
        contentView.setTextViewText(R.id.text, mSubText);
        if (mContentText != null) {
            contentView.setTextViewText(R.id.text2, mContentText);
            contentView.setViewVisibility(R.id.text2, View.VISIBLE);
            showLine2 = true;
        } else {
            contentView.setViewVisibility(R.id.text2, View.GONE);
        }
    } else {
        contentView.setViewVisibility(R.id.text2, View.GONE);
        if (mProgressMax != 0 || mProgressIndeterminate) {
            contentView.setProgressBar(
                    R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
            contentView.setViewVisibility(R.id.progress, View.VISIBLE);
            showLine2 = true;
        } else {
            contentView.setViewVisibility(R.id.progress, View.GONE);
        }
    }
    if (showLine2) {
        if (fitIn1U) {
            // need to shrink all the type to make sure everything fits
            final Resources res = mContext.getResources();
            final float subTextSize = res.getDimensionPixelSize(
                    R.dimen.notification_subtext_size);
            contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
        }
        // vertical centering
        contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
    }

    if (mWhen != 0 && mShowWhen) {
        if (mUseChronometer) {
            contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
            contentView.setLong(R.id.chronometer, "setBase",
                    mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
            contentView.setBoolean(R.id.chronometer, "setStarted", true);
        } else {
            contentView.setViewVisibility(R.id.time, View.VISIBLE);
            contentView.setLong(R.id.time, "setTime", mWhen);
        }
    } else {
        contentView.setViewVisibility(R.id.time, View.GONE);
    }

    contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
    contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE);
    return contentView;
}

上面的工作就是创建了一个RemoteViews对象,并为它传入了一个布局文件,这个布局文件就是我们定义通知要显示的布局文件,所以我们如果需要自定义Notificaion,我们可以把我们需要显示的RemoteViews传递给contentView就可以了。

弄清楚了上面的过程,下面我们就可以来自定义一个Notification了。

CharSequence title = "i am new";
int icon = R.drawable.ic_launcher;
long when = System.currentTimeMillis();

// 1、创建一个Notification对象,这里直接通过构造函数初始了三个内容项,icon,title,when
Notification noti = new Notification(icon, title, when);

// 2、设置Notification内容项
noti.flags = Notification.FLAG_INSISTENT;

RemoteViews remoteView = new RemoteViews(this.getPackageName(),R.layout.notification);
remoteView.setImageViewResource(R.id.image, R.drawable.ic_launcher);
remoteView.setTextViewText(R.id.text , "通知类型为:自定义View");
noti.contentView = remoteView;

//这儿点击后简单启动Settings模块
PendingIntent contentIntent = PendingIntent.getActivity
        (MainActivity.this, 0,new Intent("android.settings.SETTINGS"), 0);
noti.contentIntent = contentIntent;

// 3、得到NotificationManager服务
NotificationManager mnotiManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 4、发送通知
mnotiManager.notify(0, noti);

二、自定义Toast

这个比较简单,我们平时在使用系统的Toast的时候,只需要一行代码就可以搞定;

Toast.makeText(this, "我的Toast", Toast.LENGTH_LONG).show();

下面我们来看看这行代码到底为我们做了哪些事情。

首先重点来看看makeText函数:

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;
}

从这个里面我们就可以看到自定义Toast的整个过程:

// 1、创建Toast
Toast result = new Toast(context);

// 2、自定义的布局
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);

// 3、设置内容项              
result.mNextView = v;
result.mDuration = duration;

// 4、显示
result.show();

下面我们通过上面的过程来自定义一个Toast:

// 1、创建Toast
Toast toast = new Toast(context);

// 2、自定义的布局
LayoutInflater inflater = context.getLayoutInflater();
View view=inflater.inflate(R.layout.toast_info, null);
TextView txt=(TextView) view.findViewById(R.id.txt_tips);
txt.setText(info);

 // 3、设置内容项               
toast.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM, 0, 80);
toast.setDuration(time);
toast.setView(view);

// 4、显示
toast.show();

参考文章:

Android中通知的使用—–Notification详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值