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

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

  1. Android进阶笔记08:Android 中Activity、Window和View之间的关系

    1. Android 中Activity.Window和View之间的关系(比喻): Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图) LayoutI ...

  2. 动态加载Layout 与 论Activity、 Window、View的关系

    1)动态加载Layout的代码是 getWindow().setContentView(LayoutInflater.from(this).inflate(R.layout.main, null)); ...

  3. 动态载入Layout 与 论Activity、 Window、View的关系

    1)动态载入Layout的代码是 getWindow().setContentView(LayoutInflater.from(this).inflate(R.layout.main, null)); ...

  4. activity 、window与view的关系 (上)

    我在研究任玉刚老师的<android开发艺术探索>的关于windowmanager那一章时,我发现自己对于acitivity.window和view之间的概念还是比较模糊. 然后查了一下a ...

  5. Activity、Window、View三者之间的联系

    Activity类:Android四大组件之一,是开发者最常用的一个组件 Window类:是一个抽象类,具有窗口管理的功能,实现类为PhoneWindow View类:提供对View的操作,包括绘制测 ...

  6. activity 、window与view的关系(下)

    在activity的attacth方法中,通过policymanager 的makenewwindow来创建window 而window的具体实现是phonewindow 接下来通过setconten ...

  7. Android杂谈--Activity、Window、View的关系

    转自 http://www.cnblogs.com/loulijun/archive/2012/02/09/2344681.html Activity其实更像一个控制单元,控制window上显示的Vi ...

  8. Android GUI之Activity、Window、View

    相信大家在接触Android之初就已经知道了Activity中的setContentView方法的作用了,很明显此方法是用于为Activity填充相应的布局的.那么,Activity是如何将填充的布局 ...

  9. Activity、Window和View三者间的关系有一定的见解

    一.简述如何将Activity展现在手机上 Tips: Activity本身是没办法处理显示什么控件(view)的,是通过PhoneWindow进行显示的 换句话说:activity就是在造Phone ...

随机推荐

  1. hdu 4897 树链剖分(重轻链)

    Little Devil I Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others ...

  2. bzoj 3998: [TJOI2015]弦论

    Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个. ...

  3. Entity Framework DBContext 增删改查深度解析

    Entity Framework DBContext 增删改查深度解析 有一段时间没有更新博客了,赶上今天外面下雨,而且没人约球,打算把最近对Entity Framework DBContext使用的 ...

  4. SQL优化实用方法

    SQL优化:避免索引失效 1.不使用NULL 任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的.因为只有该字段中有null值,即使创建了索引其实也是没有用 ...

  5. synchronized修饰static方法与非static方法的区别

    1. 当synchronized修饰一个static方法时,多线程下,获取的是类锁(即Class本身,注意:不是实例),作用范围是整个静态方法,作用的对象是这个类的所有对象. 2. 当synchron ...

  6. Python中模块之os的功能介绍

    Python中模块之os的功能介绍 1. os的变量 path 模块路径 方法:os.path 返回值:module 例如:print(os.path) >>> <module ...

  7. (转)Linux下C++开发初探

    1.开发工具 Windows下,开发工具多以集成开发环境IDE的形式展现给最终用户.例如,VS2008集成了编辑器,宏汇编ml,C /C++编译器cl,资源编译器rc,调试器,文档生成工具, nmak ...

  8. pytorch_SRU(Simple Recurrent Unit)

    导读 本文讨论了最新爆款论文(Training RNNs as Fast as CNNs)提出的LSTM变种SRU(Simple Recurrent Unit),以及基于pytorch实现了SRU,并 ...

  9. 用 ConfigMap 管理配置 - 每天5分钟玩转 Docker 容器技术(159)

    Secret 可以为 Pod 提供密码.Token.私钥等敏感数据:对于一些非敏感数据,比如应用的配置信息,则可以用 ConfigMap. ConfigMap 的创建和使用方式与 Secret 非常类 ...

  10. 开源Spring解决方案--lm.solution

    Github 项目地址: https://github.com/liumeng0403/lm.solution 一.说明 1.本项目未按java项目传统命名方式命名项目名,包名 如:org.xxxx. ...