自定义Toast的实现

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









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先需要了解一下 Android 中通知的相关知识。Android 中的通知是通过 NotificationManager 来管理的,通知的显示效果是由 Notification 类的实例来控制的。一般情况下,我们可以使用 NotificationCompat 类来构造通知,可以兼容不同版本的 Android 系统。 接下来,我们来介绍一下如何通过自定义 Toast 实现悬浮通知效果: 1. 首先,在 AndroidManifest.xml 文件中添加权限声明: ```xml <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> ``` 2. 在代码中创建自定义Toast 类,并重写其 onWindowFocusChanged() 方法,用于创建悬浮通知: ```java public class FloatingToast extends Toast { private WindowManager mWindowManager; private View mView; private WindowManager.LayoutParams mParams; public FloatingToast(Context context) { super(context); mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mView = LayoutInflater.from(context).inflate(R.layout.floating_toast, null); mParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); mParams.gravity = Gravity.TOP | Gravity.START; } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { mWindowManager.addView(mView, mParams); } else { mWindowManager.removeView(mView); } } } ``` 3. 在布局文件 floating_toast.xml 中定义悬浮通知的样式: ```xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/floating_toast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/bg_floating_toast" android:orientation="horizontal"> <ImageView android:id="@+id/iv_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_notification" /> <TextView android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="这是一条悬浮通知" /> </LinearLayout> ``` 4. 在 Activity 中使用自定义 Toast 实现悬浮通知: ```java FloatingToast toast = new FloatingToast(this); toast.setDuration(Toast.LENGTH_LONG); toast.setView(LayoutInflater.from(this).inflate(R.layout.floating_toast, null)); toast.show(); ``` 5. 最后,记得在 Activity 的 onDestroy() 方法中销毁自定义 Toast 对象: ```java @Override protected void onDestroy() { super.onDestroy(); if (toast != null) { toast.cancel(); } } ``` 上述代码中的布局文件和相关资源文件可以根据需要自行修改,以实现不同的悬浮通知样式。同时,需要注意的是,由于 Android 8.0 及以上版本对通知权限进行了限制,如果需要在这些系统版本上显示悬浮通知,需要申请权限并设置 targetSdkVersion 为 25 或以下。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值