案例描述: 经常看到清理内存用到这个动画,本质上是一个自定义的toast,绑定在服务中。
原理: 飞机喷火效果是两个图片不停的切换形成了喷火效果。那个喷气的效果 是一个单独的activity,背景色设置为透明,渐变的动画效果,并且在1s后自动结束该activity。
素材地址
https://github.com/MrITzhongzi/materialLibrary/tree/master/rocketManImage
package com.example.rocket;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(!Settings.canDrawOverlays(this)) {
startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 101);
}
}
public void startRocket(View view) {
startService(new Intent(getApplicationContext(), RocketService.class));
finish();
}
public void endRocket(View view) {
stopService(new Intent(getApplicationContext(), RocketService.class));
finish();
}
}
package com.example.rocket;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.view.Display;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
public class RocketService extends Service {
private WindowManager mWindowManager;
private int mScreenHeight;
private int mScreenWidth;
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
private View mMRocketView;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
mParams.y = (Integer) msg.obj;
mWindowManager.updateViewLayout(mMRocketView, mParams);
}
};
public RocketService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
Display defaultDisplay = mWindowManager.getDefaultDisplay();
Point point = new Point();
defaultDisplay.getSize(point);
mScreenWidth = point.x;
mScreenHeight = point.y;
showRocket();
super.onCreate();
}
private void showRocket() {
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_KEEP_SCREEN_ON;
params.format = PixelFormat.TRANSPARENT;
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
params.setTitle("Toast");
params.gravity = Gravity.LEFT + Gravity.TOP;
mMRocketView = View.inflate(this, R.layout.rocket_view, null);
//开启动画
final ImageView mRocketView = (ImageView) mMRocketView.findViewById(R.id.iv_rocket);
AnimationDrawable animationDrawable = (AnimationDrawable) mRocketView.getBackground();
animationDrawable.start();
// 挂在到window窗口
mWindowManager.addView(mMRocketView, params);
mRocketView.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:
//获取按下的xy坐标
startX = (int) event.getRawX();
startY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
//获取移动xy坐标和按下的xy坐标做差,做差得到的值小火箭移动的距离
//移动过程中做容错处理
//第一次移动到的位置,作为第二次移动的初始位置
int moveX = (int) event.getRawX();
int moveY = (int) event.getRawY();
int disX = moveX - startX;
int disY = moveY - startY;
params.x = params.x + disX;
params.y = params.y + disY;
//在窗体中仅仅告知吐司的左上角的坐标
if (params.x < 0) {
params.x = 0;
}
if (params.y < 0) {
params.y = 0;
}
if (params.x > mScreenWidth - mRocketView.getWidth()) {
params.x = mScreenWidth - mRocketView.getWidth();
}
if (params.y > mScreenHeight - 22 - mRocketView.getHeight()) {
params.y = mScreenHeight - 22 - mRocketView.getHeight();
}
//告知吐司在窗体上刷新
mWindowManager.updateViewLayout(mMRocketView, params);
//在第一次移动完成后,将最终坐标作为第二次移动的起始坐标
startX = (int) event.getRawX();
startY = (int) event.getRawY();
break;
case MotionEvent.ACTION_UP:
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
Display defaultDisplay = windowManager.getDefaultDisplay();
Point point = new Point();
defaultDisplay.getSize(point);
//手指放开的时候,如果放手坐标,则指定区域内
if (params.x > point.x/2 - 120 && params.x < point.x/2 - 20 && params.y > 300) {
//火箭的发射
sendRocket();
//在开启火箭过程中,去开启一个新的activity,activity透明,在此activity中放置两张图片(淡入淡出效果)
Intent intent = new Intent(getApplicationContext(), BackgroundActivity.class);
//指定开启新的activity任务栈
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
break;
}
return true;
}
});
}
private void sendRocket() {
new Thread(){
@Override
public void run() {
for (int i = 0; i < 11; i++) {
int y = 350 - i * 35;
try {
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
Message msg = Message.obtain();
msg.obj = y;
mHandler.sendMessage(msg);
}
}
}.start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
if (mWindowManager != null && mMRocketView != null) {
mWindowManager.removeView(mMRocketView);
}
super.onDestroy();
}
}
package com.example.rocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.ImageView;
public class BackgroundActivity extends AppCompatActivity {
private ImageView mIvTop;
private ImageView mIvBottm;
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_background);
hideStatusBar();
mIvTop = (ImageView) findViewById(R.id.iv_top);
mIvBottm = (ImageView) findViewById(R.id.iv_bottom);
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(500);
mIvTop.startAnimation(alphaAnimation);
mIvBottm.startAnimation(alphaAnimation);
mHandler.sendEmptyMessageDelayed(0, 1000);
}
private void hideStatusBar() {
View decorView = getWindow().getDecorView();
// Hide both the navigation bar and the status bar.
// SYSTEM_UI_FLAG_FULLSCREEN is only available on Android 4.1 and higher, but as
// a general rule, you should design your app to hide the status bar whenever you
// hide the navigation bar.
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
}
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/desktop_rocket_launch_1"
android:duration="200"/>
<item android:drawable="@drawable/desktop_rocket_launch_2"
android:duration="200"/>
animation-list>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BackgroundActivity">
<ImageView
android:id="@+id/iv_bottom"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/desktop_smoke_m" />
<ImageView
android:id="@+id/iv_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/iv_bottom"
app:layout_constraintEnd_toEndOf="@+id/iv_bottom"
app:srcCompat="@drawable/desktop_smoke_t" />
android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/bt_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="80dp"
android:layout_marginLeft="80dp"
android:layout_marginTop="68dp"
android:text="开启火箭人"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:onClick="startRocket"/>
<Button
android:id="@+id/bt_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="80dp"
android:layout_marginLeft="80dp"
android:layout_marginTop="72dp"
android:text="关闭火箭人"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bt_start"
android:onClick="endRocket"/>
android.support.constraint.ConstraintLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_rocket"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/rocket_bg" />
LinearLayout>
<resources>
<color name="colorPrimary">#008577color>
<color name="colorPrimaryDark">#00574Bcolor>
<color name="colorAccent">#D81B60color>
<color name="transparent">#0000color>
resources>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
- "colorPrimary"
>@color/colorPrimary
- "colorPrimaryDark">@color/colorPrimaryDark
- "colorAccent">@color/colorAccent
style>
<style name="MyActivityTheme" parent="Theme.AppCompat.Light.NoActionBar">
- "android:windowNoTitle"
>true
- "android:windowBackground">@color/transparent
- "android:windowIsTranslucent">true
style>
resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.rocket">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".BackgroundActivity" android:theme="@style/MyActivityTheme">activity>
<service
android:name=".RocketService"
android:enabled="true"
android:exported="true" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
application>
manifest>