zoukankan      html  css  js  c++  java
  • Android仿腾讯手机管家实现桌面悬浮窗小火箭发射的动画效果

    功能分析: 
    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地址

    转: https://blog.csdn.net/u010347226/article/details/77677908

  • 相关阅读:
    (CSDN 迁移) jFinal找不到或无法加载主类
    (CSDN迁移) jFinal无法启动_JFinalDemoGenerator问题解决办法
    (CSDN迁移) JAVA多线程实现-可控最大并发数线程池(newFixedThreadPool)
    (CSDN迁移)JAVA多线程实现-单线程化线程池newSingleThreadExecutor
    (CSDN迁移) JAVA多线程实现-实现Runnable接口
    (CSDN迁移)JAVA多线程实现-继承Thread
    (CSDN迁移) html中的rel属性
    通过microtask和macrotask理解Vue.nextTick()的实现
    javascript处理异步的三种方法
    git常用的基本命令
  • 原文地址:https://www.cnblogs.com/blosaa/p/9523564.html
Copyright © 2011-2022 走看看