Android ViewGroup实现页面滑动效果并实现不同的动画效果

其实实现像Launcher那样的滑动效果有几种方法,不过我在这里用继承ViewGroup的方法去实现。要是实现ViewGroup必须重写onLayout()和onMeasure()这两个方法,这个两个方法在创建的时候会自动调用,调用的顺序是onMeasure()-->onLayout()。 

onMeasure()方法中是存储在ViewGroup里面每个子View的默认宽度和高度,以下代码就是把每个子View的默认宽度和高度都设置与父类的一样。 

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
	Log.i("onMeasure", "onMeasure----------"); 
	super.onMeasure(widthMeasureSpec, heightMeasureSpec);  

	final int width = MeasureSpec.getSize(widthMeasureSpec); //转换宽度 
	final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
	if (widthMode != MeasureSpec.EXACTLY) {  
	    throw new IllegalStateException("ScrollLayout only canmCurScreen run at EXACTLY mode!"); 
	}  
	final int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
	if (heightMode != MeasureSpec.EXACTLY) {  
	    throw new IllegalStateException("ScrollLayout only can run at EXACTLY mode!"); 
	}  

	// 将所有的子View高度和宽度都定义成和父View一样 
	final int count = getChildCount();  
	for (int i = 0; i < count; i++) {  
	    getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);  
	} 

	//滑动到默认屏幕 
	scrollTo(mCurScreen * width, 0);        
} 

而onLayout()就是设置每个子View在屏幕那个位置来显示出子View,现在在OnLayout()方法里面设置是从左到右横向绘出每个子View,其中onLayout这个方法的实现是在什么地方显示子view,显示的范围是多大,这个方法不能改变每个view原本的大小的 

protected void onLayout(boolean changed, int l, int t, int r, int b) { 
     Log.i("onLayout", "onLayout----------"); 
  if(mTouchState == TOUCH_STATE_REST){ 
   int childLeft = 0;//从坐标0开始绘画 
   final int childCount = getChildCount(); 
   
   for (int i=0; i<childCount; i++) { 
    final View childView = getChildAt(i); 
    if (childView.getVisibility() != View.GONE) { 
     final int childWidth = childView.getMeasuredWidth();    
     //根据left , top ,right,bottom 四个参数,调用子View的layout()方法将其放置在父类中,参照物是父View 
     childView.layout(childLeft, 0, 
       childLeft+childWidth, childView.getMeasuredHeight()); 
     childLeft += childWidth; 
    } 
   } 
  } 
} 

onMeasure和onLayout这个两个方法只是设置子View大小和绘画位置,真正把每个子View绘画出来是在onDispathDraw()方法里(一开始我还以为在onDraw,但是ViewGroup那里是不会调用onDraw方法)实现的,要是实现子view滑动时候的效果就可以再这里实现。 
到了这里就把整个页面里的子view都画出来,要实现滑动的效果就需要在onTouchEvent那里做功夫,主要实现滑动动作是在MotionEvent.Action_Move里的scrollBy(x,y),x表示水平滑动的距离,y表示垂直滑动的距离,因为这里只水平方向的滑动,所以y可以设置为0。 
如果要像一些桌面应用那样,当手松开后就滑动到某个完整的view的页面,这就需要用Scroller和在MotionEvent.Action_Up上设置,Scroller是可以控制整个页面的滑动,而在MotionEvent.Action_Up上面时判断手指滑动的速度,可以自己定义一个速度的基准,当达到就跨屏滑动,没有达到就判断滑动的位置是否超过当前屏幕的一半,超过就跨屏,否则仍然恢复到当前屏幕,具体还有一些数值需要自己去计算定义自己喜欢效果。代码如下: 

public boolean onTouchEvent(MotionEvent event) { 
  // TODO Auto-generated method stub 
  
  //获得动作捕捉器 
  if (mVelocityTracker == null) { 
   mVelocityTracker = VelocityTracker.obtain(); 
  } 
  mVelocityTracker.addMovement(event);//开始捕捉动作 
  
  
  final int action = event.getAction(); 
  final float x = event.getX(); 
  final float y = event.getY(); 
  
  switch (action) { 
  case MotionEvent.ACTION_DOWN: 
   if (!mScroller.isFinished()){ 
    mScroller.abortAnimation(); 
   } 
   mLastMotionX = x; 
   break; 
   
  case MotionEvent.ACTION_MOVE: 
   int deltaX = (int)(mLastMotionX - x); 
   mLastMotionX = x; 
   
            scrollBy(deltaX, 0); 
   break; 
   
  case MotionEvent.ACTION_UP://放手后的动作 
          
            final VelocityTracker velocityTracker = mVelocityTracker;   
            
            //计算滑动速度,参数1000表示以1000毫秒为单位计算,即以秒为单位 
            velocityTracker.computeCurrentVelocity(1000);   
            int velocityX = (int) velocityTracker.getXVelocity(); 

            
            //如果向左或者向右的滑动速度超过了基础线,则直接切换屏幕;如果没有,则判断滑动的距离是否超过了屏幕一半的距离,也就是View的一半Width 
            if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {   
                // Fling enough to move left   

                snapToScreen(mCurScreen - 1);   
            } else if (velocityX < -SNAP_VELOCITY   
                    && mCurScreen < getChildCount() - 1) {   
                // Fling enough to move right   
                snapToScreen(mCurScreen + 1);   
            } else {   
                snapToDestination();   
            }  
            if (mVelocityTracker != null) {   
                mVelocityTracker.recycle();   
                mVelocityTracker = null;   
            }   
            mTouchState = TOUCH_STATE_REST;   
   break; 
   
  case MotionEvent.ACTION_CANCEL: 
   mTouchState = TOUCH_STATE_REST; 
   break; 
  } 
  
  return true; 
} 
public void snapToDestination() { 
     final int screenWidth = getWidth(); 
     final int destScreen = (getScrollX()+ screenWidth/2)/screenWidth; 
     snapToScreen(destScreen); 
    } 
public void snapToScreen(int whichScreen) { 
     // get the valid layout page 
     whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1)); 
     if (getScrollX() != (whichScreen*getWidth())) { 
      
      final int delta = whichScreen*getWidth()-getScrollX(); 
      mScroller.startScroll(getScrollX(), 0, 
        delta, 0, Math.abs(delta)*2); 
      mCurScreen = whichScreen; 
      invalidate();  // Redraw the layout 
     } 
} 

现在基本算是实现了整个屏幕滑动的效果,要实现滑动的动画就必须要在dispathDraw(Canvas canvas)的canvas来实现了,在ViewGroup里面有一个方法drawChild(),通过这个将每个view都重绘上去,其中的变换不外乎translate,scale这个几种,当然要实现其他比较复杂的效果就需要其他操作了,所以现在要重写dispatchDraw里面的方法,而我现在要实现像Go桌面那样的弹跳效果,这里是通过获取屏幕的左位置和每个子view的左坐标来进行绘画,因为子view的位置是固定的,而屏幕的左坐标却在不断的变化。在这里是用一个判断为基准,就是每个子view的左坐标与屏幕左坐标的距离是否超过一个子view的宽度,超过距离的子view将不做变换。 
int dx=Math.abs(getScrollX()-view.getLeft())通过这个获取每个子view的左坐标和屏幕左坐标的距离,这里我是实现弹跳的效果,其实只是对子view的y轴上的位置进行变换,用dx/view.getWidth()来获取滑动距离的比例,通过canvas.translate(0.0f, -(1.0f*dx/view.getWidth())*getHeight())来进行变化,其实当前子view初始化的位置(0.0f,0.0f),也就是这时dx是0,当我进行滑动的时候,dx就递增,当前子view就开始向上移动,而相对隔壁的子view,dx是子view的宽度,dx是递减的,就开始向下移动,这个过程全都是根据每个子view的左坐标的坐标和屏幕左坐标距离进行判断的,就是越接近屏幕左坐标的就向下移动,远离就向上移动,可能我讲得也不大清楚,只要自己用纸画一下应该就会明白的,代码如下: 

int saveCount = canvas.save(); 
        int count = getChildCount(); 
        int width = getWidth(); 
        int scrollX=getScrollX(); 
        
//        for (int i = 0; i < count; i++) { 
//            canvas.save();           
//            View view=getChildAt(i); 
//            int childLeft=view.getLeft(); 
//            int dx=Math.abs(scrollX-childLeft); 
//            //Log.i("child"+i, x+"-----------"); 
//            if(dx<=width){ 
//                canvas.scale(1.0f, 1.0f-(float)0.5*dx/width); 
//                canvas.translate(0.0f, (0.5f*dx/width)*getHeight()); 
//                Log.i("heigh", getHeight()+"----------"); 
//                drawChild(canvas, view, getDrawingTime()); 
//                canvas.restore(); 
//            }else{ 
//                canvas.scale(1.0f, 0.5f); 
//                canvas.translate(0.0f, 0.5f*getHeight()); 
//                drawChild(canvas, view, getDrawingTime()); 
//                canvas.restore(); 
//            }           
//        } 
        
        for (int i = 0; i < count; i++) { 
            canvas.save();           
            View view=getChildAt(i); 
            int childLeft=view.getLeft(); 
            int dx=Math.abs(scrollX-childLeft); 
            //Log.i("child"+i, dx+"-----------"); 
            if(dx<=width){ 
                canvas.translate(0.0f, -(1.0f*dx/width)*getHeight()); 
                Log.i("heigh", getHeight()+"----------"); 
                drawChild(canvas, view, getDrawingTime()); 
                canvas.restore(); 
            }else{ 
                canvas.translate(0.0f, -1.0f*getHeight()); 
                drawChild(canvas, view, getDrawingTime()); 
                canvas.restore(); 
            }           
        } 
        canvas.restoreToCount(saveCount); 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值