Activity和Window的View的移动的一些思考与体会,腾讯悬浮小火箭的实现策略

Activity和Window的View的移动的一些思考与体会,腾讯悬浮小火箭的实现策略


事实上写这个也是因为自己实际在项目中用到了才会去研究已经写文章,对于View的移动,其实说实话,已经有很多文章了,既然如此的话,那我实在是不好意思再去重复的讲解,但是和Window的View还是有一些区别的,接下来,我会实际的讲解一下这些区别已经坐标函数的计算方法,当然,最后再讲一下如何实现腾讯的悬浮小火箭,这些都是比较好的干货,我也相信大家都是比较喜欢的,而你在本文中将学会使用View的移动计算坐标,有三个目录

  • 1.Activity中View的移动
  • 2.Window中View的移动
  • 3.实现腾讯悬浮小火箭

我们首先新建一个项目ViewAndWindow来实现三个按钮作为这三个功能的三个Activity跳转,三个Activity分别是

  • ActivityActivity
  • WindowActivity
  • TencentActivity

所以主布局-activity_layout.xml应该是这么写的

<?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:gravity="center"
    android:orientation="vertical"
    android:padding="10dp">

    <Button
        android:id="@+id/btnActivity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Activity中View的移动"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/btnWindow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Window中View的移动"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/btnTencent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="腾讯小火箭"
        android:textAllCaps="false" />

</LinearLayout>

不可否认,我们的MainActivty只是作为程序的入口,所以他的代码是十分的简单的

package com.lgl.viewandwindow;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btnActivity, btnWindow, btnTencent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    //初始化View
    private void initView() {
        btnActivity = (Button) findViewById(R.id.btnActivity);
        btnActivity.setOnClickListener(this);
        btnWindow = (Button) findViewById(R.id.btnWindow);
        btnWindow.setOnClickListener(this);
        btnTencent = (Button) findViewById(R.id.btnTencent);
        btnTencent.setOnClickListener(this);
    }

    //点击事件
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnActivity:
                startActivity(new Intent(this, ActivityActivity.class));
                break;
            case R.id.btnWindow:
                startActivity(new Intent(this, WindowActivity.class));
                break;
            case R.id.btnTencent:
                startActivity(new Intent(this, TencentActivity.class));
                break;
        }
    }
}

而我们的重点也不在这里,而在这些子Activity

一.Activity中View的移动

实际上,View在Activity上移动,还是要依靠事件去传递,总所周知,View的绘制流程一般都是先onMeasure测量,接下来是onLayout确定位置,最后才是onDraw绘制,所以,我们的更新坐标其实是在onLayout进行的,好吧,说这些再多都不如代码来的实际一点,我们在Activity中写一个ImageView

    <ImageView
        android:id="@+id/ivDraw"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher" />

我们就是要对他下手了,是的,就在OnTouchListener中进行,OnTouchListener回调中有一个MotionEvent类,他封装了我们触摸事件的大部分动作,其中就包括三大将军

 switch (event.getAction()) {
        //按下
       case MotionEvent.ACTION_DOWN:

             break;
        //抬起
       case MotionEvent.ACTION_UP:

             break;
        //移动
       case MotionEvent.ACTION_MOVE:

            break;

而我们如果单单只是移动这个View的话,其实是用不到抬起这个UP的动作的,我们要想实现这个View的移动,首先得知道这个View在哪里,所以我们需要先定义两个变量

 //起点坐标
 private int startX, startY;

而我们什么时候得到View的初始坐标呢?肯定是在按下这个动作上获取

 startX = (int) event.getRawX();
 startY = (int) event.getRawY();

而这里,肯定就会有人问,这个getX和getRowX有什么区别,其实区别还是挺大的,前者是获取当前父容器的X坐标,后者是相对于整个屏幕的坐标,OK,获取到之后,我们应该干什么?这个时候我们应该使用到MOVE这个动作了,你在拖动,我计算偏移量并且更新这个View的位置,来达到移动的视觉效果,那我们还得定义几个变量

首先是你的重点坐标,有始有终

//终点坐标
 private int endX, endY;

紧接着,会让终点坐标减去起点坐标,来计算这个偏移量,所以有了偏移量的变量

//偏移量
 private int dx, dy;

所以,我们MOVE的动作里计算公式应该是这样的

 endX = (int) event.getRawX();
 endY = (int) event.getRawY();

 //计算移动偏移量
 dx = endX - startX;
 dy = endY - startY;

获取到你移动的偏移量,我们就可以拿到移动后的坐标了,还记得我们在绘制矩形的时候用到的那套公式吗

我们直接套用这套公式,其实就可以得到左上右下的坐标了

int left = tvAddress.getLeft() + dx;
int top = tvAddress.getTop() + dy;
int right = tvAddress.getRight() + dx;
int bottom = tvAddress.getBottom() + dy;

OK,这里,其实有点类似于测量,测量结束之后就可以确定位置了,就得用到我们的onLayout了

 //重新部署位置
 ivDraw.layout(left, top, right, bottom);

到这里,其实很多人就以为走完了的,其实更新完位置之后,你还要把初始位置给初始化一下,也就是赋值成你更新后的坐标点

//重新初始化坐标
startX = (int) event.getRawX();
startY = (int) event.getRawY();

对了。记得return true,这里你会问,为什么是true,因为true代表我要消耗掉这个事件,你其他的就不要接收了,你不信的话可以设置一个点击事件看看有没有效果!

这里,我们就算大功告成了,如果你想记录这个坐标点,你就会用到UP了,不多说,我们运行看看效果

但是这里,还需要优化一下,比如,我移动到边上的时候直接就进去了,我们应该放置这个View超过屏幕,对吧,那我们应该怎么做?我们首先先获取到整个屏幕的宽高

 wm = (WindowManager) getSystemService(WINDOW_SERVICE);
 width = wm.getDefaultDisplay().getWidth();
 height = wm.getDefaultDisplay().getHeight();

这样,我们通过WindowManager就能直接拿到宽高了,然后我们在移动的时候可以这样做

//防止上下
 if (top < 0 || bottom > height - 20) {
        return true;
 }

//防止左右
 if (left < 0 || right > width) {
       return true;
  }

这样,我们就可以直接看到效果了

这里的减去20是状态栏的,但是下面的虚拟按键倒是没有考虑进去,不过思路真的可行

好了,上面是步骤,我们就直接把代码全部贴出来吧

package com.lgl.viewandwindow;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;

/**
 * Created by LGL on 2016/7/28.
 */
public class ActivityActivity extends AppCompatActivity {

    private ImageView ivDraw;

    //起点坐标
    private int startX, startY;
    //终点坐标
    private int endX, endY;
    //偏移量
    private int dx, dy;
    //窗口管理器
    private WindowManager wm;
    //屏幕宽高
    private int width, height;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_activity);

        wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        width = wm.getDefaultDisplay().getWidth();
        height = wm.getDefaultDisplay().getHeight();

        ivDraw = (ImageView) findViewById(R.id.ivDraw);

        ivDraw.setOnTouchListener(new View.OnTouchListener() {
            @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_UP:

                        break;
                    case MotionEvent.ACTION_MOVE:
                        endX = (int) event.getRawX();
                        endY = (int) event.getRawY();

                        //计算移动偏移量
                        dx = endX - startX;
                        dy = endY - startY;

                        /**
                         *根据偏移量更新位置(重新部署位置)
                         */
                        int left = ivDraw.getLeft() + dx;
                        int top = ivDraw.getTop() + dy;
                        int right = ivDraw.getRight() + dx;
                        int bottom = ivDraw.getBottom() + dy;

                        //防止上下
                        if (top < 0 || bottom > height - 20) {
                            return true;
                        }

                        //防止左右
                        if (left < 0 || right > width) {
                            return true;
                        }

                        //重新部署位置
                        ivDraw.layout(left, top, right, bottom);

                        //重新初始化坐标
                        startX = (int) event.getRawX();
                        startY = (int) event.getRawY();

                        break;
                }
                return true;
            }
        });
    }
}

二.Window中View的移动

Activity上毕竟是有迹可循,那Window上呢?其实窗体上逻辑是差不多的,唯一差的,就是那些函数的调用了,OK,我们进入WindowActivity中,先写个Button启动这个Window

    <Button
        android:id="@+id/showWindow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Show Window"
        android:textAllCaps="false" />

他所对应的点击事件

        //点击事件
        showWindow = (Button) findViewById(R.id.showWindow);
        showWindow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showMoveWindow();
            }
        });

而我们这个小节的重点就的照顾一下 showMoveWindow()这个方法了,怎么实现一个Window不是今天的重点,而且也确实没什么可讲的,我就直接上代码了

        //窗口管理器
        wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        //布局参数
        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                //WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | 不能触摸
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
        //格式
        layoutParams.format = PixelFormat.TRANSLUCENT;
        //类型
        layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;

        ivDraw = new ImageView(this);
        ivDraw.setBackgroundResource(R.mipmap.ic_launcher);

        //加载view
        wm.addView(ivDraw, layoutParams);

这段代码就能实现一个window了,我们可以看一下

我们需要权限哦

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

我们是直接new了一个ImageView的,但是不妨碍我们使用View的移动,我们直接实现它的触摸事件

     //触摸事件
        ivDraw.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:

                        break;
                    case MotionEvent.ACTION_MOVE:

                        break;
                }
                return true;
            }
        });

我这里暂时也只是实现了两个动作,因为作为演示我们的UP确实用不上,我们有了前车之鉴,我们直接定义我们需要的变量吧

    //起始坐标
    private int startX, startY;
    //终点坐标
    private int endX, endY;
    //偏移量
    private int dx, dy;

OK,老套路,在DOWN中,我们只是获取当前的坐标

 startX = (int) event.getRawX();
 startY = (int) event.getRawY();

但是移动的时候,获取的就不是左上右下了,而是他的x和y坐标,因为他是window,所以我们用到的是LayoutParams,更新位置也是使用的LayoutParams,他有一个updateViewLayout的方法

 endX = (int) event.getRawX();
 endY = (int) event.getRawY();

 //计算移动偏移量
 dx = endX - startX;
 dy = endY - startY;

 /**
  *根据偏移量更新位置(重新部署位置)
  */
 layoutParams.x += dx;
 layoutParams.y += dy;

 //更新位置
 wm.updateViewLayout(ivDraw, layoutParams);

 //重新初始化坐标
 startX = (int) event.getRawX();
 startY = (int) event.getRawY();

当然,最后return true;我们试试

而因为他是window的改哪里,他并不需要去做一些超出边距的处理,很nice

把这部分代码也全部贴上来

package com.lgl.viewandwindow;

import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;

/**
 * Created by LGL on 2016/7/28.
 */
public class WindowActivity extends AppCompatActivity {

    private Button showWindow;

    //窗口管理器
    private WindowManager wm;
    //图片
    private ImageView ivDraw;
    //起始坐标
    private int startX, startY;
    //终点坐标
    private int endX, endY;
    //偏移量
    private int dx, dy;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_window);

        //点击事件
        showWindow = (Button) findViewById(R.id.showWindow);
        showWindow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showMoveWindow();
            }
        });
    }

    //显示窗口
    private void showMoveWindow() {
        //窗口管理器
        wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
        //布局参数
        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                //WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | 不能触摸
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
        //格式
        layoutParams.format = PixelFormat.TRANSLUCENT;
        //类型
        layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;

        ivDraw = new ImageView(this);
        ivDraw.setBackgroundResource(R.mipmap.ic_launcher);

        //触摸事件
        ivDraw.setOnTouchListener(new View.OnTouchListener() {
            @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:
                        endX = (int) event.getRawX();
                        endY = (int) event.getRawY();

                        //计算移动偏移量
                        dx = endX - startX;
                        dy = endY - startY;

                        /**
                         *根据偏移量更新位置(重新部署位置)
                         */
                        layoutParams.x += dx;
                        layoutParams.y += dy;

                        //更新位置
                        wm.updateViewLayout(ivDraw, layoutParams);

                        //重新初始化坐标
                        startX = (int) event.getRawX();
                        startY = (int) event.getRawY();

                        break;
                }
                return true;
            }
        });

        //加载view
        wm.addView(ivDraw, layoutParams);
    }

}

三.实现腾讯悬浮小火箭

到这里,其实已经算是知道点逻辑了,我们就是用window去做的一个操作,既然如此,那我们就直接基于上面的代码去去实现这个小火箭吧,还是原来的代码,只是把图片更换成了一个小火箭,然后为了使它是是一个动态的效果,我们可以给他设置一个切换的动画

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:drawable="@drawable/desktop_rocket_launch_in"
        android:duration="200"/>
    <item
        android:drawable="@drawable/desktop_rocket_launch_out"
        android:duration="200"/>

</animation-list>

这个其实就是两张图片的切换效果,我们直接去开启他

 mView = View.inflate(getApplicationContext(), R.layout.rocket_window, null);
 ivRocket = (ImageView) mView.findViewById(R.id.ivRocket);
 AnimationDrawable anim = (AnimationDrawable) ivRocket.getBackground();
 anim.start();

OK,现在我们去计算他的起飞了,而且还要考虑到他背景,我们其实可以大胆的使用一个Activity去做,我们在UP这个动作结束的时候就去计算坐标,当满足某一个坐标范围的时候就去启动动画和启动背景动画,那我们应该这样计算

   case MotionEvent.ACTION_UP:
   Log.i(TAG,"抬起");
   Log.i(TAG,"抬起坐标:" + startX + ":" + startY);
   Log.i(TAG,"条件 : 200 < x >" + (width - 100) + "并且 y > " + (height - 200));
   //设置大致的发射范围
   if (layoutParams.x > 50 && layoutParams.x < 250 && layoutParams.y > 350) {
          //发射火箭
          sendRocket();
          new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                //启动动画
               Intent i = new Intent(getApplicationContext(), BackgroundActivity.class);
               i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
               startActivity(i);
               }
     },1000);
 }
      break;

这里其实偷了个懒,没有去做屏幕的严格适配,有兴趣的伙伴可以参考一下

可以看到我们有一个 sendRocket();方法就是启动小火箭,启动背景就是启动这个BackgroundActivity,我们先看小火箭

 /**
     * 发射火箭的方法
     */
    private void sendRocket() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //动画 y坐标一直减少,实现上升动画
                for (int i = 0; i <= height / 50; i++) {
                    //每循环一次减去乘以5
                    int y = height - i * 100;
                    Log.i(TAG,"y = " + y);

                    Message msg = new Message();
                    msg.arg1 = y;

                    handler.sendMessage(msg);
                    //暂停一下
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

因为要有节奏感,所以开了个handler来延迟一下,但是子线程不能更新主UI的,所以我们需要发一个handler来更新坐标

private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            layoutParams.y = msg.arg1;
            //更新窗口
            wm.updateViewLayout(mView, layoutParams);

        }
    };

最后就是这个Activity了,里面真的啥也没有

package com.lgl.viewandwindow;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.animation.AlphaAnimation;
import android.widget.ImageView;

/**
 * 烟雾动画
 * Created by LGL on 2016/7/30.
 */
public class BackgroundActivity extends Activity {

    private ImageView smoke_m, smoke_t;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_background);

        initView();
    }

    //初始化
    private void initView() {

        smoke_m = (ImageView) findViewById(R.id.smoke_m);
        smoke_t = (ImageView) findViewById(R.id.smoke_t);

        //渐变动画
        AlphaAnimation alpha = new AlphaAnimation(0, 1);
        alpha.setDuration(2000);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                finish();
            }
        },4000);

    }
}

要注意的一点就是他党主题需要透明下

 android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"

看我们最终的效果

当然,我这做的是比较挫的,我想表达的其实就只是一种编码的思路罢了,掌握了思想,怎么去优化,那都是比较简单的事情了,好的,最后把代码发上来,当然,也提供了Demo下载的

package com.lgl.viewandwindow;

import android.app.Service;
import android.content.Context;
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.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;

/**
 * 小火箭
 * Created by LGL on 2016/7/28.
 */
public class RocketService extends Service {

    public static final String TAG = RocketService.class.getSimpleName();

    //窗口管理器
    private WindowManager wm;
    //图片
    private View mView;
    //起始坐标
    private int startX, startY;
    //终点坐标
    private int endX, endY;
    //偏移量
    private int dx, dy;
    //小火箭
    private ImageView ivRocket;
    private WindowManager.LayoutParams layoutParams;
    //屏幕宽高
    private int width, height;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            layoutParams.y = msg.arg1;
            //更新窗口
            wm.updateViewLayout(mView, layoutParams);

        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        getWindowBig();
        showRocket();
    }

    //显示小火箭
    private void showRocket() {
        //窗口管理器
        wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        //布局参数
        layoutParams = new WindowManager.LayoutParams();
        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                //WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | 不能触摸
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
        //格式
        layoutParams.format = PixelFormat.TRANSLUCENT;
        //类型
        layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;

        mView = View.inflate(getApplicationContext(), R.layout.rocket_window, null);
        ivRocket = (ImageView) mView.findViewById(R.id.ivRocket);
        AnimationDrawable anim = (AnimationDrawable) ivRocket.getBackground();
        anim.start();

        //触摸事件
        mView.setOnTouchListener(
                new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        switch (event.getAction()) {
                            case MotionEvent.ACTION_DOWN:
                                Log.i(TAG,"按下");
                                startX = (int) event.getRawX();
                                startY = (int) event.getRawY();

                                break;
                            case MotionEvent.ACTION_MOVE:
                                //L.i("移动");
                                endX = (int) event.getRawX();
                                endY = (int) event.getRawY();

                                //计算移动偏移量
                                dx = endX - startX;
                                dy = endY - startY;

                                /**
                                 *根据偏移量更新位置(重新部署位置)
                                 */
                                layoutParams.x += dx;
                                layoutParams.y += dy;

                                //更新位置
                                wm.updateViewLayout(mView, layoutParams);

                                //重新初始化坐标
                                startX = (int) event.getRawX();
                                startY = (int) event.getRawY();

                                break;
                            case MotionEvent.ACTION_UP:
                                Log.i(TAG,"抬起");
                                Log.i(TAG,"抬起坐标:" + startX + ":" + startY);
                                Log.i(TAG,"条件 : 200 < x >" + (width - 100) + "并且 y > " + (height - 200));
                                //设置大致的发射范围
                                if (layoutParams.x > 50 && layoutParams.x < 250 && layoutParams.y > 350) {
                                    //发射火箭
                                    sendRocket();
                                   new Handler().postDelayed(new Runnable() {
                                       @Override
                                       public void run() {
                                           //启动动画
                                           Intent i = new Intent(getApplicationContext(), BackgroundActivity.class);
                                           i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                           startActivity(i);
                                       }
                                   },1000);
                                }
                                break;
                        }
                        return true;
                    }
                }
        );
        //加载view
        wm.addView(mView, layoutParams);
    }

    /**
     * 发射火箭的方法
     */
    private void sendRocket() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //动画 y坐标一直减少,实现上升动画
                for (int i = 0; i <= height / 50; i++) {
                    //每循环一次减去乘以5
                    int y = height - i * 100;
                    Log.i(TAG,"y = " + y);

                    Message msg = new Message();
                    msg.arg1 = y;

                    handler.sendMessage(msg);
                    //暂停一下
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        wm.removeView(mView);
    }

    /**
     * 获取屏幕的宽高
     */
    private void getWindowBig() {
        wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        width = wm.getDefaultDisplay().getWidth();
        height = wm.getDefaultDisplay().getHeight();
        Log.i(TAG,"屏幕的宽高" + width + ":" + height);
    }
}

今天的博客就先到这里,谢谢大家,有兴趣的可以加群

通往Android的神奇之旅:555974449

Demo下载:http://download.csdn.net/detail/qq_26787115/9590415

时间: 2024-10-24 07:19:58

Activity和Window的View的移动的一些思考与体会,腾讯悬浮小火箭的实现策略的相关文章

程序的启动和退出以及activity和window和视图树的关系

应用程序启动过程: 第一步 系统为此分配一个进程 第二步 系统为此分配一个虚拟机.在Android中每个进程独占一个虚拟机.所以说android是多进程多虚拟机的 第三步 此应用程序在此虚拟机中运行   所以平时退出了一个应用程序,但是长按HOME键后即可看见它的图标.再次点击后,即可再次启动 这是因为我们是在第三个层次退出的应用程序,它的进程和虚拟机还是存在的. 应用程序启动过程详解: 在点击了图标以后.在OnClick()方法中调用startActivitySafely(intent) 然后

Android中Window添加View的底层原理_Android

一.WIndow和windowManagerWindow是一个抽象类,它的具体实现是PhoneWindow,创建一个window很简单,只需要创建一个windowManager即可,window具体实现在windowManagerService中,windowManager和windowManagerService的交互是一个IPC的过程. 下面是用windowManager的例子: mFloatingButton = new Button(this); mFloatingButton.setT

Android中Window添加View的底层原理

一.WIndow和windowManager Window是一个抽象类,它的具体实现是PhoneWindow,创建一个window很简单,只需要创建一个windowManager即可,window具体实现在windowManagerService中,windowManager和windowManagerService的交互是一个IPC的过程. 下面是用windowManager的例子: mFloatingButton = new Button(this); mFloatingButton.set

android中window、view与activity的关系

通过讨论这个问题,我们能够见识到google是对面向对象模式的理解,能够理解android底层的一些调用. 这也是一道很常见的面试题. 我们这篇文章就来解决这四个问题: Android  中view的显示视图么? Activity,window,View的关系是什么? LayOutInflater 填充是什么? LayOutInflater 具体怎么做? 首先,我们从activity开始说起,说起activity我们都要知道setcontentview和attach方法. setcontentv

Android View的加载过程

问题描述 本帖最后由 邂逅潇湘 于 2015-5-22 18:19 编辑 大家都知道Android中加载view是从Activity的onCreate方法调用setContentView开始的,那么View的具体加载过程又是怎么的呢?下文做了详细的讲解:首先追踪一下代码:Activity中:public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID);}public Window get

Android入门之ActivityGroup+GridView实现Tab分页标签的方法_Android

在Android程序中很多客户端软件和浏览器软件都喜欢用Tab分页标签来搭建界面框架.读者也许会马上想到使用TabHost 与 TabActivity的组合,其实最常用的不是它们,而是由GridView与ActivityGroup的组合.每当用户在GridView选中一项,ActivityGroup就把该项对应的Activity的Window作为View添加到ActivityGroup所指定的容器(LinearLayout)中. 先来贴出本例运行的效果图如下: ImageAdapter是本实例的

Android面试二三事儿

最近开始接触Android方面的面试,收到Hr推荐过来的一份简历,看过之后大喜,工作技能完全符合要求,从事同样的产品开发(从竞品那里找来的).技能水平里的描述如下 个人技能: (1) 有良好的JAVA基础,熟练掌握面向对象思想. (2) 熟练使用集合.IO流及多线程. (3) 熟练掌握Android四大组件,常用的布局文件,自定义控件等. (4) 熟悉掌握ListView的优化及异步任务加载网络数据. (5) 熟悉XML/JSON解析数据,以及数据存储方式. (6) 精通Android下的Han

ActivityGroup + GridView 实现Tab分页标签

很多客户端软件和浏览器软件都喜欢用Tab分页标签来搭建界面框架.读者也许会马上想到使用TabHost 与 TabActivity的组合,其实最常用的不是它们,而是由GridView与ActivityGroup的组合.每当用户在GridView选中一项,ActivityGroup就把该项对应的Activity的Window作为View添加到ActivityGroup所指定的容器(LinearLayout)中. 接下来贴出本例运行的效果图: ImageAdapter是本实例的关键之一,它继承于Bas

札记:android手势识别功能实现(利用MotionEvent)_Android

摘要 本文是手势识别输入事件处理的完整学习记录.内容包括输入事件InputEvent响应方式,触摸事件MotionEvent的概念和使用,触摸事件的动作分类.多点触摸.根据案例和API分析了触摸手势Touch Gesture的识别处理的一般过程.介绍了相关的GestureDetector,Scroller和VelocityTracker.最后分析drag和scale等一些手势的识别. 输入源分类 虽然android本身是一个完整的系统,它主要运行在移动设备的特性决定了我们在它上面开的app绝大数