Android实战:在桌面显示动画

项目GitHub地址:https://github.com/liaozhoubei/Rocket

这次我们要做一个简单的Demo,用于在桌面显示动画,效果如下:


Android实战:在桌面显示动画_第1张图片
rocket.gif

一个简单的火箭发射的小动画,在虚拟机上运行有点卡顿,但是大致的效果就是这样了。
效果是不是很炫,其中包含了以下几个知识点:

  • Service
  • WindowManager
  • Drawable Animation和AlphaAnimation
  • 触摸点击事件。
    好了,话不多说,我们直接上代码吧!

MainActivity

首先创建一个Android project,然后在MainActivity设置两个按钮点击时间,一个用于打开发射火箭的服务,一个用于关闭火箭的服务,由于界面简单,所以就不layout的代码了
MainActivity代码:

    public class MainActivity extends Activity implements OnClickListener{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button startRocket = (Button) findViewById(R.id.startRocket);
    Button endRocket = (Button) findViewById(R.id.endRocket);
    startRocket.setOnClickListener(this);
    endRocket.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
    switch (v.getId()) {
    case R.id.startRocket:
        //开启小火箭服务
        startService(new Intent(this,RocketService.class));
        finish();
        break;
    case R.id.endRocket:
        //关闭小火箭服务
        stopService(new Intent(this,RocketService.class));
        finish();
        break;
    default:
        break;
    }
    }
    }

RocketService

创建一个Service,由于火箭是在手机桌面中运行,所以必须进驻后台。
RocketService代码:

  public class RocketService extends Service {
private int widthPixels;
private int heightPixels;
private WindowManager.LayoutParams params;
private WindowManager windowManager;
private View view;
@Override
public IBinder onBind(Intent arg0) {
    return null;
}   
@Override
public void onCreate() {
    super.onCreate();       
    windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);       
    DisplayMetrics outMetrics= new DisplayMetrics();
    windowManager.getDefaultDisplay().getMetrics(outMetrics);
    widthPixels = outMetrics.widthPixels;
    heightPixels = outMetrics.heightPixels;
    
    view = View.inflate(getApplicationContext(), R.layout.rocket, null);
    ImageView iv_rocket = (ImageView)view.findViewById(R.id.iv_rocket);
    
    params = new WindowManager.LayoutParams();
    params.height = WindowManager.LayoutParams.WRAP_CONTENT;
    params.width = WindowManager.LayoutParams.WRAP_CONTENT;
    params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
    params.format = PixelFormat.TRANSLUCENT;
    params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE ;
    params.gravity = Gravity.LEFT | Gravity.TOP;
    
    AnimationDrawable animationDrawable  = (AnimationDrawable) iv_rocket.getBackground();
    animationDrawable.start();
    windowManager.addView(view, params );
    setTouch();
}

Ok,以上是RocketService中的全部代码,现在我们来详解这些代码的含义吧。
1、关于WindowManager
这是一个全局的窗口管理者,如果想在手机桌面显示图案,就必须使用WindowManager的addView()方法将布局加载,这样才能显示
2、关于DisplayMetrics
由于Android手机的屏幕太多,分辨率也各不一致,很难一次获取到所有手机的屏幕大小,因此需要使用DisplayMetrics类来获取手机屏幕的高度和宽度
3、WindowManager.LayoutParams
这是挺好理解的一个参数,就像普通视图一样,一个控件放在一个视图中是有大小以及放在布局的那个地方的。这个参数就是来设置空间在窗口之中的参数的。
其中有以下代码:

  params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE ;

这一段代码表示的是小火箭这个空间在屏幕中不会获得焦点,但是会在屏幕中一直出现。

params.gravity = Gravity.LEFT | Gravity.TOP;

这行代码表示的是视图在开始时位于窗口的左上角。
4、获取小火箭的布局
我们需要在桌面展示一个小火箭,那么就需要有个rocket的layout,代码如下:


    

4、AnimationDrawable
在RocketService中有一段代码:

 AnimationDrawable animationDrawable  = (AnimationDrawable) iv_rocket.getBackground();
  animationDrawable.start();

这段代码是帧动画,帧动画的资源是由res资源目录下的drawable目录下创建一个rocket.xml的文件决定的,代码如下:



    
    

这个文件是作为iv_rocket这个ImageView的background出现,所以需要从iv_rocket中获取。
所谓帧动画,就是一帧一帧的动画,我们这里是由两张图片不断循环组成的动画,及desktop_rocket_launch_1.png和desktop_rocket_launch_2.png。

6、最后我们在windowManager.addView(view, params );加载布局和设定好的参数,这个时候我们可以点开服务,发现可爱的火箭动画已经能出现在在桌面之中了!

当然了,现在我们仅仅是出现了一个火箭,但是它并不能随着手指移动而变化,也不能发射,因为我们还没有把setTouch()这个触摸方法完成嘛。

加入触摸方法

setTouch()方法的代码如下:

private Handler handler = new Handler(){
    public void handleMessage(android.os.Message msg) {
        params.y -= 10;
        windowManager.updateViewLayout(view, params);
    };
};
  }

private void setTouch() {
    view.setOnTouchListener(new 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 newX = (int) event.getRawX();
                int newY = (int) event.getRawY();
                
                int dx = newX - startX;
                int dy = newY - startY;
                
                params.x += dx;
                params.y += dy;
                
                if (params.y < 0) {
                    params.y = 0;
                }
                
                windowManager.updateViewLayout(view, params);
                
                startX = newX;
                startY = newY;                      
                break;                  
            case MotionEvent.ACTION_UP:
                
                if (params.y > 290 && params.x > 100 && params.x < 200) {
                    sendRocket();
                    Intent intent = new Intent(RocketService.this, BackGroundActivity.class);   
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                }                   
                break;
            default:
                break;
            }               
            return true;
        }
        
        private void sendRocket() {
            for(int i = 0; i < 45; i ++) {
                new Thread(){
                    public void run() {
                        SystemClock.sleep(200);
                        handler.sendEmptyMessage(0);
                    };
                }.start();                  
            }               
        }
    });     
    }

我们可以看到这个触摸方法与普通的点击事件稍微有点不同,因为它里面重写了一个onTouch()的方法,而这个方法是按照手指的触摸事件划分区域的。
一般来说触摸事件分为三种,即按下的时候,移动的时候,手指离开屏幕的时候。在这里也是如此。
我们在手指按下的时候获得手指按下时的坐标。
然后在手指移动的时候再次获得手指移动时的左边,然后减去开始的时候的坐标便获得了手指移动了多少距离,将这个距离赋予params.x和 params.y,然后通过windowManager.updateViewLayout(view, params)重新加载布局,这个时候就实现了小火箭能够随着手指在屏幕的移动而移动的效果。

最后我们需要手指在离开屏幕的时候火箭能够发射,其中的原理很简单,只需要改变小火箭在Y轴上的坐标便能够实现了。
但是我们不能让火箭随便发送,因此限定了火箭负责params.y > 290 && params.x > 100 && params.x < 200的条件时才能发射。
同时小火箭发射的速度不能太快,负责就看不到发射的效果了,因此我们设置火箭每隔200毫秒停顿一下,方便看到效果。但由于在主线程中不能停顿,所以使用Handler,每隔200毫秒发送空消息到Handler,然后在Handler中更新火箭的位置。

至此,我们的火箭发射效果也完成了。但是我们还有烟雾效果没有完成。

火箭发射烟雾效果

火箭发射的烟雾效果实际上由两张图片组成的渐变动画,它们在BackGroundActivity中,在火箭发射的时候直接启动这个Activity,从而达到效果。
BackGroundActivity的布局很简单就只是RelativeLayout包裹着两张图标,在这里就不写了。
BackGroundActivity代码:

public class BackGroundActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_background);
    
    ImageView smoke_m = (ImageView) findViewById(R.id.smoke_m);
    ImageView smoke_t = (ImageView) findViewById(R.id.smoke_t);
    
    AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
    alphaAnimation.setDuration(200);
    smoke_m.startAnimation(alphaAnimation);
    smoke_t.startAnimation(alphaAnimation);
    
    new Handler().postDelayed(new Runnable() {
        
        @Override
        public void run() {
            finish();
        }
    }, 1000);
}
}

BackGroundActivity中有AlphaAnimation,其中new AlphaAnimation(0, 1);代表着这个动画的效果是从透明到不透明的效果。
当然开启了这个Activity之后必须要销毁它,我们使用Handler延迟1秒钟发送销毁命令就行了。

需要注意的是之前在Rocket启动BackGroundActivity时,我们并没有如同普通的Activity之间相互启动那样,而是添加了:

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

这行代码。这是因为service在Android中是不存在任务栈的,而打开Activity这是需要一个任务栈。因此如果不加这行代码会导致程序崩溃。

OK,这个项目就到这里为止。
当然了,在代码写完之后记得在mainfest中注册Activity和service,已经添加权限
权限代码如下:

 

这个权限表示要在所有的窗口之前都能运行。
注册Activity和Service的代码如下:

    
    

android:theme="@android:style/Theme.Translucent.NoTitleBar">这行代码的意思是需要一个透明的没有标题栏的主题

项目GitHub地址:https://github.com/liaozhoubei/Rocket

你可能感兴趣的:(Android实战:在桌面显示动画)