最近在做一个拖动View效果的时候遇到一个问题,就是拖动的时候屏幕花屏了.如下图
具体过程是这样的.
1.自定义了一个MyViewGroup 继承ViewGroup,重写了onLayou ,onMeasure,onTouchEvent方法t就是上图背景为红色的View
2.当点击添加按钮的时候会动态添加一个View到MyViewGroup上.点击移动按钮的时候会执行TranslateAnimation动画移动第一个(此处只能移动第一个Add的view)添加的view.
3.启动程序后添加两个view到MyViewGroup上,在点击移动按钮后就出现了上图的情况
4.继续点移动按钮,发现每次移动的时候该view都会一闪回到最初的位置在进行移动
原因分析:
1.因为在点击移动按钮的时候开启动画,动画结束后没有清理动画clearAnimation();
2.由于动画设置了setFillAfter(true),所以虽然view移动了,但是view绑定的位置还是原来的位置,所以继续点移动按钮的时候i会回到原来的位置再移动
解决办法:
在动画结束后即:onAnimationEnd方法中 view.clearAnimation() 和 view.layout(l,t,r,b);
下面是代码:
package cn.futao.test.measureAndLayout;
import cn.futao.test.serviceAndBinder.TestActivity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.ImageView;
public class MyViewGroup extends ViewGroup {
private static final String TAG = "MyViewGroup";
private View moveView;
private int srocllY;
private int downX;
private int downY;
private int specSize_Width;
private int specSize_Heigth;
private int childWidthSize;
private int childHeigthSize;
private int translationDistance; //每次移动的距离
private boolean reset = true;//避免每次点添加按钮后所有view 恢复到最初位置,因为ViewGroup addView的时候会调用onlayout方法
public MyViewGroup(Context context) {
super(context);
setBackgroundColor(Color.RED);
init();
}
public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
Runnable runnable = new Runnable() {
@Override
public void run() {
onLayout(true, 0, srocllY, 0, 0);
}
};
private int startX;
private int startTop;
private void init(){
//1.添加一个button
Button button = new Button(getContext());
button.setText("添加View");
Button button2 = new Button(getContext());
button2.setText("移动View");
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final View v1 = getChildAt(2);
Animation animation1 = new TranslateAnimation(0, -translationDistance,0, 0);
animation1.setDuration(1000);
//setFillAfter后View 绑定还是原来的坐标,并不是移动过后的坐标.
animation1.setFillAfter(true);
v1.clearAnimation();
v1.startAnimation(animation1);
//invalidate();
animation1.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
//在结束动画后如果不clearAnimation的话,在OnTouch的move 时候 会出现花屏的情况
// v1.clearAnimation();
//clear 后 如果如果不去请求重绘的话,该view 会显示不全或者消失,所以在clear后要请求重绘
//但是调用v1.invalidate();由于上面调用了 animation1.setFillAfter(true); v1还是绑定的原来的坐标所以在动画执行完后还是会回到原来位置
//v1.invalidate();
//所以需要重新给v1设置位置,调用v1.layout(),次方执行后系统会调用v1重绘,所以有了次方法可以把 v1.invalidate();注释掉
//v1.layout(v1.getLeft()-translationDistance, v1.getTop(), v1.getLeft() + v1.getMeasuredWidth()-translationDistance, v1.getMeasuredHeight()+v1.getTop());
}
});
}
});
addView(button2);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
reset = false;
ImageView imageView = new ImageView(getContext());
imageView.setBackgroundResource(R.drawable.ic_launcher);
Animation animation = new TranslateAnimation(specSize_Width- startX, 0,0, 0);
animation.setFillAfter(true);
animation.setDuration(1000);
imageView.setAnimation(animation);
MyViewGroup.this.addView(imageView);
}
});
this.addView(button);
}
@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
int childCount = getChildCount();
if(reset){
// 设置第一个每一个子view的起始X坐标,第一个默认为0
startX = 0;
startTop = 0; // 设置每一个子view距父view 的top 距离
for(int i = 0 ;i < childCount; i++){
View v = getChildAt(i);
v.layout(startX, startTop, startX + v.getMeasuredWidth(), startTop + v.getMeasuredHeight());
startX = startX + v.getMeasuredWidth() + 10;// 10 表示每个view 之间的间隔
// 新添加的view 显示在下一行
if(i == 1){
startX = specSize_Width/3;
startTop = v.getMeasuredHeight() + 10;
}
if(startX > specSize_Width){
startX = 0;
startTop = startTop + v.getMeasuredHeight() + 10;
}
}
}else{
View v = getChildAt(getChildCount() -1);
v.layout(startX, startTop, startX + v.getMeasuredWidth(), startTop + v.getMeasuredHeight());
startX = startX + v.getMeasuredWidth() + 10;
if(startX > specSize_Width){
startX = 0;
startTop = startTop + v.getMeasuredHeight() + 10;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值。我们需要通过int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式
//,用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。
int childCount = getChildCount();
//获取该ViewGroup的实际长和宽 涉及到MeasureSpec类的使用
specSize_Width = MeasureSpec.getSize(widthMeasureSpec);
specSize_Heigth = MeasureSpec.getSize(heightMeasureSpec);
translationDistance = specSize_Width /10;
// 设置子view的宽高
if(specSize_Width > specSize_Heigth){
childWidthSize = specSize_Width /8;
childHeigthSize = specSize_Heigth /5;
}else{
childWidthSize = specSize_Width /5;
childHeigthSize = specSize_Heigth /8;
}
//设置MyviewGroup的实际大小
setMeasuredDimension(specSize_Width , specSize_Heigth );
//measure 每一个子view,即我们添加进去的那三个
for(int i = 0; i< childCount ; i++){
View v = getChildAt(i); 获得每个对象的引用
//measure函数的参数由类measureSpec的makeMeasureSpec函数方法生成的一个32位整数,该整数的高两位表示模式(Mode),低30位则是具体的尺寸大小(specSize)。
//或者可以调用ViewGroup父类方法measureChild()或者measureChildWithMargins()方法
v.measure(MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(childHeigthSize, MeasureSpec.EXACTLY)); 简单的设置每个子View对象的宽高为 50px , 50px
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
getViewFromIndex(downX, downY);
break;
case MotionEvent.ACTION_MOVE:
int x = (int)event.getX(), y = (int)event.getY();
int l = x - (3 * moveView.getMeasuredWidth() / 4), t = y - (3 * moveView.getMeasuredWidth() / 4);
int r = l + (moveView.getMeasuredWidth()), b = t + (moveView.getMeasuredWidth());
moveView.layout(l,t,r,b);
break;
case MotionEvent.ACTION_UP:
//removeView(moveView);
//moveView.setVisibility(View.GONE);
default:
break;
}
return true;
}
private void getViewFromIndex(int x ,int y){
Rect rect = new Rect();
for(int i = 1; i< getChildCount(); i ++ ){
View view = getChildAt(i);
view.clearAnimation();
view.getHitRect(rect);
if(rect.contains(x, y)){
moveView = view;
}
rect.setEmpty();
}
}
}