功能分析:
1、小火箭遊離在activity之外,不依附於任何activity,不管activity是否開啓,不影響小火箭的代碼邏輯,所以小火箭的代碼邏輯是要寫在服務中;
2、小火箭掛載在手機窗體之上;
3、小火箭噴射火焰,兩種圖片輪流展示的動畫集;
4、小火箭可以拖拽到任意位置;
5、小火箭拖拽到指定區域纔可以觸發發射效果;
6、小火箭噴射尾氣動畫效果;
功能實現:
定義小火箭的服務,實現自定義Toast的創建,自定義Toast加載相應的佈局文件,然後把自定義Toast掛載到手機窗體之上;
public class RocketService extends Service {
// 手機窗體佈局的管理者
private WindowManager mWindowManager;
// 手機窗體的佈局
private WindowManager.LayoutParams mParams;
// 展示小火箭的自定義佈局
private View mToastRocketView;
// 展示小火箭的ImageView
private ImageView mRocketImage;
// 手機窗體的寬度
private int mWindowWidth;
// 手機窗體的高度
private int mWindowHeight;
// 消息傳遞機制
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mParams.y = (Integer) msg.obj;
mWindowManager.updateViewLayout(mToastRocketView, mParams);
}
};
@Override
public void onCreate() {
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
// 獲取手機屏幕的寬高值
mWindowWidth = mWindowManager.getDefaultDisplay().getWidth();
mWindowHeight = mWindowManager.getDefaultDisplay().getHeight();
mParams = new WindowManager.LayoutParams();
// 服務啓動,打開自定義Toast的控件
showRocketView();
// 拖拽小火箭到任意位置
dragRocket();
super.onCreate();
}
/**
* 拖拽小火箭到任意位置
*/
private void dragRocket() {
mToastRocketView.setOnTouchListener(new View.OnTouchListener() {
private int startX;
private int startY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = (int) event.getRawX();
startY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getRawX();
int moveY = (int) event.getRawY();
// 兩個方向上所移動的距離值
int disX = moveX - startX;
int disY = moveY - startY;
mParams.x = mParams.x + disX;
mParams.y = mParams.y + disY;
if (mParams.x < 0) {
mParams.x = 0;
}
if (mParams.y < 0) {
mParams.y = 0;
}
if (mParams.x > mWindowManager.getDefaultDisplay().getWidth() - v.getWidth()) {
mParams.x = mWindowManager.getDefaultDisplay().getWidth() - v.getWidth();
}
if (mParams.y > mWindowManager.getDefaultDisplay().getHeight() - 21 - v.getHeight()) {
mParams.y = mWindowManager.getDefaultDisplay().getHeight() - 21 - v.getHeight();
}
// 更新小火箭的座標位置X和Y值
mWindowManager.updateViewLayout(mToastRocketView, mParams);
startX = (int) event.getRawX();
startY = (int) event.getRawY();
break;
case MotionEvent.ACTION_UP:
// 小火箭拖拽到手機屏幕下方的中間時,觸發小火箭發射
if (mParams.x > mWindowWidth / 2 - 150 && mParams.x < mWindowWidth / 2 - mToastRocketView.getWidth() / 2 + 50
&& mParams.y > mWindowHeight - mToastRocketView.getHeight() - 25) {
// 小火箭發射升空
launchRocket();
Intent intent = new Intent(RocketService.this, SmokeBackActivity.class);
// 服務中開啓activity,需要設置任務棧
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
break;
}
return false;
}
});
}
/**
* 小火箭發射升空
*/
private void launchRocket() {
new Thread(new Runnable() {
@Override
public void run() {
int disY = mWindowHeight / 5;
for (int i = 0; i < 6; i++) {
int height = mWindowHeight - i * disY;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 子線程不能改變主線程中的UI的變化,因此,由消息機制告知主線程進行改變,並攜帶相應的值
Message msg = Message.obtain();
msg.obj = height;
mHandler.sendMessage(msg);
}
}
}).start();
}
/**
* 顯示小火箭的自定義View
*/
private void showRocketView() {
// 自定義Toast
mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
// 修改完左上角對齊
mParams.gravity = Gravity.LEFT + Gravity.TOP;
mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
mParams.format = PixelFormat.TRANSLUCENT;
mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
// 加載ToastRocketView顯示效果的佈局文件
mToastRocketView = View.inflate(this, R.layout.toast_rocket_view, null);
// 窗體佈局中加入自定義的展示小火箭的View
mWindowManager.addView(mToastRocketView, mParams);
mRocketImage = (ImageView) mToastRocketView.findViewById(R.id.rocket_image);
// 獲取動畫,並開啓動畫
AnimationDrawable animDraw = (AnimationDrawable) mRocketImage.getBackground();
animDraw.start();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
if (mWindowManager != null && mToastRocketView != null) {
mWindowManager.removeView(mToastRocketView);
}
super.onDestroy();
}
}
mWindowManager是所有桌面懸浮窗的管理器,這裏用於添加我們自定義的小火箭的Toast,然後獲取小火箭火焰噴射的動畫集,並開啓動畫效果。上述代碼中爲mToastRocketView添加了觸摸事件,用於監聽小火箭的拖拽事件,在監聽拖拽事件的同時,若小火箭被拖動到指定區域,則觸發小火箭的發射效果;
小火箭所在的佈局文件:
<?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:orientation="vertical">
<!--背景爲小火箭火焰噴射的動畫集-->
<ImageView
android:id="@+id/rocket_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rocket_anim_list"/>
</LinearLayout>
小火箭火焰噴射的動畫集:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<!--小火箭火焰噴射的動畫集-->
<item android:drawable="@drawable/rocket_launch_1" android:duration="200" />
<item android:drawable="@drawable/rocket_launch_2" android:duration="200" />
</animation-list>
小火箭發射時的尾氣噴射動畫效果,這裏爲了獲得好的效果,需要在AndroidManifest.xml中配置活動的主題爲透明、無標題和全屏;
public class SmokeBackActivity extends Activity {
// 尾氣噴射上部圖片所在的ImageView
private ImageView mSmokeTopImageView;
// 尾氣噴射下部圖片所在的ImageView
private ImageView mSmokeBottomImageView;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 尾氣噴射動畫結束後關閉活動
finish();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_smoke_back);
// 初始化佈局文件
initView();
}
/**
* 初始化佈局文件
*/
private void initView() {
mSmokeTopImageView = (ImageView) findViewById(R.id.smoke_top);
mSmokeBottomImageView = (ImageView) findViewById(R.id.smoke_bottom);
// 尾氣噴射設置動畫效果並開啓
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(500);
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 1f);
scaleAnimation.setDuration(500);
// 動畫集中添加縮放和透明動畫
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(scaleAnimation);
mSmokeTopImageView.startAnimation(animationSet);
mSmokeBottomImageView.startAnimation(alphaAnimation);
// 1秒之後關閉當期活動
mHandler.sendEmptyMessageDelayed(0, 1000);
}
}
所對應的佈局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/smoke_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/smoke_m"
android:layout_alignParentBottom="true"/>
<ImageView
android:id="@+id/smoke_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/smoke_bottom"
android:background="@drawable/smoke_t" />
</RelativeLayout>
AndroidManifest.xml中添加相應的權限:
<!--全局彈出對話框需要添加權限-->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
效果如下所示:
主活動中只是設置倆按鈕,一個開啓服務,一個關閉服務,這裏不再贅述,歡迎大家去GitHub上下載代碼;有什麼問題也可以評論交流一下;
代碼地址:GitHub地址