本:1.0
日期:2014.8.16
2014.8.23 2014.8.26
版权:© 2014 kince 转载注明出处
一、概述
开发中发现在
WindowManager上像在Activity中使用动画效果无效,比如下面的代码:
ImageView iv = new ImageView(this);
iv.setImageResource(R.drawable.ic_launcher);
TranslateAnimation animation = new TranslateAnimation(
Animation.ABSOLUTE, 20, Animation.ABSOLUTE, 300,
Animation.ABSOLUTE, 100, Animation.ABSOLUTE, 400);
animation.setDuration(1000);
animation.setFillAfter(false);
iv.setAnimation(animation);
WindowManager mWindowManager = (WindowManager) this
.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
mLayoutParams.x = 20;
mLayoutParams.y = 100;
mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParams.gravity = Gravity.TOP;
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
mWindowManager.addView(iv, mLayoutParams);
animation.start();
二、分析 为什么会不执行动画呢,原因在于:the view which is going to be animated must not be directly added to the top window, because top window of android is not a real ViewGroup. so the view must be added to a ViewGroup like FrameLayout first and then this ViewGroup be added to the top window.意思是说动画执行的条件是不能直接添加到最顶层的Window,而是需要一个容器。比如,在xml中定义的控件就可以使用动画。
后来发现一种解决方案是WindowManager.LayoutParams有一个动画属性:windowAnimations,可以这样使用
lp = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.RGBA_8888);
lp.gravity = Gravity.LEFT |Gravity.TOP;
lp.windowAnimations = R.style.anim_view;//动画
wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
wm.addView(view, lp);
但是,这是对整个View的一个动画,而不是View中某个控件的动画。而且,使用的时候需要在View状态改变的时候才会出现动画效果。比如消失/出现的时候才会有动画效果。因此这个方案也是行不通的。
既然WindowManager不是一个ViewGroup,那么就构造一个容器来装载WindowManager,可以如下:
/**
*
*/
package com.kince.apus.widget;
import com.kince.apus.R;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
/**
* @author kince
* @category Windowmanager在Layout中的应用
*
*
*/
public class GuideLayout extends FrameLayout {
private WindowManager wManager;
private WindowManager.LayoutParams wmParams;
private View addView;
private TextView mTextView;
private ImageView mImageView;
private boolean isAddView;
private AnimatorSet mShowAnimatorSet, mHideAnimatorSet;
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
showAnimator();
break;
default:
break;
}
};
};
/**
* @param context
*/
public GuideLayout(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
}
/**
* @param context
* @param attrs
*/
public GuideLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
}
/**
* @param context
* @param attrs
* @param defStyle
*/
public GuideLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
addView = LayoutInflater.from(context).inflate(R.layout.guide_layout,
this);
mTextView = (TextView) addView.findViewById(R.id.tv);
mImageView = (ImageView) addView.findViewById(R.id.iv);
mTextView.setVisibility(View.GONE);
mImageView.setVisibility(View.GONE);
setAnimator();
getWindowManager(context);
}
/**
* @category 实例化WindowManager 初次模拟位置时候使用
* @param context
*/
private void getWindowManager(final Context context) {
wManager = (WindowManager) context.getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE);
wmParams = new WindowManager.LayoutParams();
wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
wmParams.format = PixelFormat.TRANSPARENT;
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
wmParams.gravity = 17;
wmParams.width = WindowManager.LayoutParams.MATCH_PARENT;
wmParams.height = WindowManager.LayoutParams.MATCH_PARENT;
}
private void setAnimator() {
mShowAnimatorSet = new AnimatorSet();
Animator[] showAnimator = new Animator[2];
showAnimator[0] = ObjectAnimator.ofFloat(mTextView, "alpha",
new float[] { 0.0F, 1.0F });
showAnimator[1] = ObjectAnimator.ofFloat(mImageView, "alpha",
new float[] { 0.0F, 1.0F });
mShowAnimatorSet.playTogether(showAnimator);
mShowAnimatorSet.setDuration(1500l);
mHideAnimatorSet = new AnimatorSet();
Animator[] hideAnimator = new Animator[2];
hideAnimator[0] = ObjectAnimator.ofFloat(mTextView, "alpha",
new float[] { 1.0F, 0.0F });
hideAnimator[1] = ObjectAnimator.ofFloat(mImageView, "alpha",
new float[] { 1.0F, 0.0F });
mHideAnimatorSet.playTogether(hideAnimator);
mHideAnimatorSet.setDuration(1500l);
}
public void showAnimator() {
mTextView.setVisibility(View.VISIBLE);
mImageView.setVisibility(View.VISIBLE);
mShowAnimatorSet.start();
isAddView=true;
}
public void hideAnimator() {
mHideAnimatorSet.start();
mHideAnimatorSet.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationRepeat(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
mTextView.setVisibility(View.INVISIBLE);
mImageView.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationCancel(Animator animation) {
// TODO Auto-generated method stub
}
});
}
public void sendMessage() {
if (isAddView) {
wManager.removeView(this);
mHandler.removeMessages(1);
isAddView=false;
}
mHandler.sendEmptyMessage(1);
wManager.addView(this, wmParams);
}
}
这样一来,就可以实现在WindowManager上的动画效果了。其实,造成这种现象的原因在于对Android API以及其体系的理解不够深刻。忽略了动画执行所需要的基本条件,影射的问题就是考虑问题不够全面。所以,不论开发哪种功能,使用哪个API,前期的规划、调研很重要。知己知彼,仅此而已。