• listview左右滑动动画实现


    关于listview的左右滑动实现,在网上其实已经有很多示例代码了,多数都是将listview嵌套在horiscrollview或是viewpager里,而这两种实现方式都是基于一个父容器里有多个子控件横向排列,在移动过程中通过手势最终实现视图的切换。但有些应用是不需要多个子控件对象,换言之出于节省内存的考究,比如十个item页,每页视图其实都是一个listview,而如果放上10个listview实属浪费。所以将一个listview放置在自定义的容器内,那么在处理手势的时候在判定可左右移动时做个动画,待动画结束后将另一个item页的数据显示出来即可。

         下面先上SlideViewGroup的代码:

       

    public class SlideViewGroup extends RelativeLayout implements AnimationListener{
    
    	public static interface onItemChangeListener{
    		public void onItemChange(int curItem);
    	}
    
    	private final static int OFFSET_X_DISTANCE = 50;
        private float mLastMotionX = 0;     
    	private boolean mIsHookTouchEvent = false;
    	private boolean mAnimationStart = false;
    	private boolean mMoveFinish = true;
    	private boolean mMoveLeft = true;
    	
    	private int mItemCount = 1;
    	private int mCurItemIndex = 0;
    	private onItemChangeListener mItemChangeListener;
    	
    	private TranslateAnimation mMoveLeftAnimation;
    	private TranslateAnimation mMoveRightAnimation;
    	private int mScreenWidth = 0;
    	
    	
    	public SlideViewGroup(Context context) {
    		super(context);
    
    		init(context);
    	}
    	
    	public SlideViewGroup(Context context, AttributeSet attrs) {
    		super(context, attrs);
    
    		init(context);
    	}
    
    	public void setOnItemChangeListener(onItemChangeListener listener){
    		mItemChangeListener = listener;
    	}
    	
    	public void setItemCount(int itemCount){
    		mItemCount = itemCount;
    	}
    	
    	public int getItemCount(){
    		return mItemCount;
    	}
    	
    	public void setCurItem(int index){
    		if (index < 0){
    			index = 0;
    		}else if (index >= mItemCount){
    			index = mItemCount - 1;
    		}
    		mCurItemIndex = index;
    		clearAnimation();
    		mAnimationStart = false;
    	}
    	
    	public int getCurItem(){
    		return mCurItemIndex;
    	}
    
    	private void init(Context context)
    	{  
    
    		reset();
    		
    		mScreenWidth = getScreenWidth(context);
    
    		mMoveLeftAnimation = new TranslateAnimation(0.0f, -mScreenWidth,0.0f,0.0f);  
    		mMoveLeftAnimation.setDuration(500);
        	
    		mMoveRightAnimation = new TranslateAnimation(0.0f, mScreenWidth,0.0f,0.0f);  
    		mMoveRightAnimation.setDuration(500);
    	
    		mMoveLeftAnimation.setAnimationListener(this);
    		mMoveRightAnimation.setAnimationListener(this);
    	}
    	
    	private void reset(){
    	   mLastMotionX = 0;    
    	   mIsHookTouchEvent = false;
    	   mAnimationStart = false;
    	   mMoveFinish = true;
    	   mMoveLeft = true;
    	   mItemCount = 1;
    	   mCurItemIndex = 0;
    	}
    
    	
    	public boolean onInterceptTouchEvent(MotionEvent ev) {
    		
    		int action = ev.getAction();
    		
    		final float x = ev.getX();
            final float y = ev.getY();
            switch (action) {
            case MotionEvent.ACTION_MOVE:
            		if (mIsHookTouchEvent){
            			return true;
            		}
            	  	final int xDiff = (int) Math.abs(x - mLastMotionX);
                    if (xDiff > OFFSET_X_DISTANCE) {
                    	mIsHookTouchEvent = true;
                    }           
                    break;
    
            case MotionEvent.ACTION_DOWN:
                    mLastMotionX = x;
                    mIsHookTouchEvent = mAnimationStart;
                    break;
    
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
            		mIsHookTouchEvent = false;
                    break;
                   default:
                	   break;
            }
    		
        
    		return mIsHookTouchEvent;
    	}
    
    	@Override
    	public boolean onTouchEvent(MotionEvent event) {
    
    	    if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
                return false;
            }
    		
    	    int action = event.getAction();
    		
    		final float x = event.getX();
            final float y = event.getY();
            switch (action) {
            case MotionEvent.ACTION_MOVE:
            	 	if (mIsHookTouchEvent && !mMoveFinish){	       
            	 		if (!mAnimationStart){
            	 			boolean directionLeft = x < mLastMotionX ? true : false;
            	 			if (isCanMoveDirection(directionLeft)){
            	 				startMoveAnimotion(directionLeft);
            	 			}
            	 		} 	  
            	 		mMoveFinish = true;
            	 	}
                    break;
    
            case MotionEvent.ACTION_DOWN:
                    mLastMotionX = x;
                    break;
    
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
        			mMoveFinish = false;
                    break;
                   default:
                	   break;
            }
    		
        
    		return true;
    		
    	}
    	
    
    	private void startMoveAnimotion(boolean isLeft){
    		if (isLeft){
    			startAnimation(mMoveLeftAnimation);
    		}else{
    			startAnimation(mMoveRightAnimation);
    		}
    		
    		mMoveLeft = isLeft;
    	}
    	
    	private boolean isCanMoveDirection(boolean moveLeft){
    		
    		if (moveLeft){
    			if (mCurItemIndex < mItemCount - 1){
    				return true;
    			}
    		}else{
    			if (mCurItemIndex > 0){
    				return true;
    			}
    		}		
    		
    		return false;
    	}
    	
    	private void changeItemAuto(boolean moveLeft){
    
    		if (moveLeft){
    			mCurItemIndex++;
    			if (mCurItemIndex >= mItemCount){
    				mCurItemIndex = mItemCount - 1;
    			}
    		}else{
    			mCurItemIndex--;
    			if (mCurItemIndex < 0){
    				mCurItemIndex = 0;
    			}
    		}
    		if (mItemChangeListener != null){
    			mItemChangeListener.onItemChange(mCurItemIndex);
    		}
    	}
    	
    	
    	private  int getScreenWidth(Context context) {
    		WindowManager manager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
    		Display display = manager.getDefaultDisplay();
    		return display.getWidth();
    	}
    
    	@Override
    	public void onAnimationStart(Animation animation) {
    		mAnimationStart = true;
    	}
    
    	@Override
    	public void onAnimationEnd(Animation animation) {
    		mAnimationStart = false;
    		changeItemAuto(mMoveLeft);
    	}
    
    	@Override
    	public void onAnimationRepeat(Animation animation) {
    		
    	}
    }

    考虑到包含着的是listview子控件时touch事件会被listview优先处理掉,所以需要重写onInterceptTouchEvent,判断  final int xDiff = (int) Math.abs(x - mLastMotionX);

    大于某值时认为左右滑动的条件成立,返回false,即将touch事件钩住,由容器自身直接处理不传递给子控件(对android的touch事件传递机制还不熟的同学先百度下补补课)

    之后onTouchEvent的处理就是做动画那些事了,里面那些mAnimationStart和mMoveFinish这些变量都是为了在做动画的时候屏蔽外界的触屏事件,避免出现混乱。

    看到这里,有些同学可能会疑惑,既然容器的onInterceptTouchEvent里在左右移动到达满足条件时就会将touch事件勾走,那listview在上下可滑动发生后就应该不能再触发容器左右移动的事件,这又当如何处理?其实通过剖析listview的源码,准确来说应是AbsListView的onTouchEvent事件的实现,在move的事件处理有有个方法startScrollIfNeeded(int deltaY)

    截取部分代码:

    final int distance = Math.abs(deltaY);
            if (distance > mTouchSlop){
    
                ........
    
                  ........
    
            requestDisallowInterceptTouchEvent(true);
                return true;
               }
    
        public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    
            if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
                // We're already in this state, assume our ancestors are too
                return;
            }
    
            if (disallowIntercept) {
                mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
            } else {
                mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
            }
    
            // Pass it up to our parent
            if (mParent != null) {
                mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
            }
        }

    mParent.requestDisallowInterceptTouchEvent(disallowIntercept);即使其父容器不再处理onInterceptTouchEvent事件而防止touch事件被勾走。

    由此我们还可以发现,listview在上下滑动时只有值超过mTouchSlop时才认为可移动,经测试,此值为24.之所以谈到这个,是因为若我们的子控件使用下拉刷新的那种listview的话,若不做额外处理,在下拉未超出24像素情况下再左右滑动是会触发容器事件的,而这样的视觉效果是不对的,具体详看listview里的代码处理。

    最后再截两张图:

    附上代码工程:http://download.csdn.net/detail/geniuseoe2012/4958745

    更多精彩,请留意窝的博客更新。。。

  • 相关阅读:
    Gremlin基本使用
    SpringData JdbcTemplate Jdbc使用简介
    DOS命令行使用pscp实现远程文件和文件夹传输(转)
    vscode:让文件支持右键vscode打开
    vue-webpack项目本地开发环境设置代理解决跨域问题
    VueJS中学习使用Vuex详解
    Object.create()和new 创建对象的区别
    vue组件和插件的区别
    创建vue组件与自定义一个vue组件时的区别
    [Vue] : 自定义指令
  • 原文地址:https://www.cnblogs.com/lance2016/p/5204241.html
Copyright © 2020-2023  润新知