Toast在我们的应用开发中是最常见不过的控件了,系统默认的控件样式固定,如果要做出比较炫的Toast就需要自定义Toast了。用过腾讯手机管家的朋友一定不会忘了那个在主界面可以发射的小火箭:
其实这就是一个自定义的Toast。下面将讲述自定义Toast的方法。
首先分析系统Toast的实现:
在Toast中有一个叫TN的内部类,它的构造函数如下:
TN() {
// XXX This should be changed to use a Dialog, with a Theme.Toast
// defined that sets up the layout params appropriately.
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
}
这里其实就是设置了Toast显示时的大小,布局等参数。
TN还有一个方法叫handleShow()由名字可以看出这是对显示的控制,它的源码如下:
public void handleShow() {
if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ " mNextView=" + mNextView);
if (mView != mNextView) {
// remove the old view if necessary
handleHide();
mView = mNextView;
mWM = WindowManagerImpl.getDefault();
final int gravity = mGravity;
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
mParams.verticalWeight = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
if (mView.getParent() != null) {
if (localLOGV) Log.v(
TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
mWM.addView(mView, mParams);
}
}
在这个方法的第25行,我们看到
mWM.addView(mView, mParams);//mWM是WIndowManager对象,mView就是显示的Toast的内容,mParams是一个WindowManager.LayoutParams对象在之前在TN构造函数中设置过
这句就是显示的关键代码。
由以上的源码分析,我们得出Toast是加载到WindowManager中的,显示的步骤是:
1 自定义一个要显示的view
2 建立一个WindowManager.LayoutParams对象设置显示参数
3 调用WindowManager的addView方法即可显示
按上面的步骤显示的view并不会自动消失,继续查看Toast.TN的方法我们看到:
public void handleHide() {
if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
if (mView != null) {
// note: checking parent() just to make sure the view has
// been added... i have seen cases where we get here when
// the view isn't yet added, so let's try not to crash.
if (mView.getParent() != null) {
if (localLOGV) Log.v(
TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
mView = null;
}
这样就一目了然了,只需要调用WindowManager的removeView方法就可让显示的view消失了
下面代码展示如何制作手机管家的小火箭:
package com.example.service;
import com.example.activity.AdvanceFunctionActivity;
import com.example.activity.R;
import com.example.activity.RocketActivity;
import com.example.activity.SettingActivity;
import com.example.utils.Constans;
import com.example.utils.SpUtils;
import com.example.view.AddressStyle;
import com.example.view.SettingItem;
import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.drawable.AnimationDrawable;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import android.os.Vibrator;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
public class RocketService extends Service{
protected static final int UPDATE_ROCKET = 0;
private WindowManager mwm;
private View rocket_view;
private WindowManager.LayoutParams rocket_params;
private View rocket_image;
private AnimationDrawable rocketAnimation;
private Handler handler=new Handler(){
public void handleMessage(Message msg){
switch(msg.what)
{
case UPDATE_ROCKET:mwm.updateViewLayout(rocket_view, rocket_params);
}
}
};
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
initView();
showRocket();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
dismissRocket();
}
/**
* 让火箭消失
*/
protected void dismissRocket() {
// TODO Auto-generated method stub
if (rocket_view != null) {
mwm.removeView(rocket_view);
rocket_view = null;
}
}
private void initView() {
initRocket();
}
/**
* 初始化火箭的显示参数
*/
private void initRocket() {
// TODO Auto-generated method stub
rocket_params = new WindowManager.LayoutParams();;
rocket_params.height = WindowManager.LayoutParams.WRAP_CONTENT;
rocket_params.width = WindowManager.LayoutParams.WRAP_CONTENT;
rocket_params.format = PixelFormat.TRANSLUCENT;
rocket_params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;// 改变类型,系统的Toast类型是TYPE_TOAST是不能响应触摸事件的
rocket_params.setTitle("Toast");
rocket_params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
}
/**
* 显示火箭,并为火箭设置触模事件
*/
private void showRocket() {
// TODO Auto-generated method stub
mwm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
rocket_view = View.inflate(this, R.layout.rocket, null);
rocket_image =rocket_view.findViewById(R.id.rocket_image);
rocket_image.setBackgroundResource(R.drawable.rocket_anim);
rocketAnimation = (AnimationDrawable) rocket_image.getBackground();
rocket_view.setOnTouchListener(new OnTouchListener() {
float downX = 0, downY = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getRawX();
downY = event.getRawY();
rocketAnimation.start();
break;
case MotionEvent.ACTION_UP:
if (rocket_params.x < 0) {
rocket_params.gravity = Gravity.TOP | Gravity.LEFT;
rocket_params.x = 0;
} else if (rocket_params.x > mwm.getDefaultDisplay().getWidth()
- rocket_view.getWidth()) {
rocket_params.x = mwm.getDefaultDisplay().getWidth()
- rocket_view.getWidth();
rocket_params.gravity = Gravity.TOP | Gravity.LEFT;
}
if (rocket_params.y < 0) {
rocket_params.y = 0;
rocket_params.gravity = Gravity.TOP | Gravity.LEFT;
} else if (rocket_params.y > mwm.getDefaultDisplay().getHeight()
- rocket_view.getHeight()) {
rocket_params.y = mwm.getDefaultDisplay().getHeight();
rocket_params.gravity = Gravity.TOP | Gravity.LEFT;
}
mwm.updateViewLayout(rocket_view, rocket_params);
rocketAnimation.stop();
if(event.getRawY()>1500)
{
Vibrator vib = (Vibrator)getSystemService(Service.VIBRATOR_SERVICE);
vib.vibrate(300);
launch();
}
break;
case MotionEvent.ACTION_MOVE:
rocket_params.x += (int) (event.getRawX() - downX);
rocket_params.y += (int) (event.getRawY() - downY);
downX = event.getRawX();
downY = event.getRawY();
mwm.updateViewLayout(rocket_view, rocket_params);
break;
}
return true;
}
});
mwm.addView(rocket_view, rocket_params);
}
/**
* 火箭发射
*/
protected void launch() {
rocket_params.x=mwm.getDefaultDisplay().getWidth()/2-rocket_view.getWidth()/2;
mwm.updateViewLayout(rocket_view, rocket_params);
new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
while(rocket_params.y>0)
{
SystemClock.sleep(1);
rocket_params.y-=3;
Message message=new Message();
message.what=UPDATE_ROCKET;
handler.sendMessage(message);
}
}
}).start();
/**
* RocketActivity是一个我定义的一个背景透明的Activity,里面显示了两张火箭发射烟雾的图片,
* 注意在AndroidManifest文件中设置它android:launchMode="singleInstance"
*/
Intent intent=new Intent(this,RocketActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}