• [置顶] android 自定义ListView实现动画特效


    通过自定义ListView实现动画特效,被点击元素A向前移,A之前元素往后移动.

    重点在于动画的实现:

    具体代码如下:

    package com.open.widget;
    
    import java.util.ArrayList;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.PaintFlagsDrawFilter;
    import android.graphics.PorterDuff;
    import android.graphics.PorterDuffXfermode;
    import android.graphics.Rect;
    import android.os.Handler;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.GestureDetector.SimpleOnGestureListener;
    import android.view.MotionEvent;
    import android.view.View;
    
    /**
     * 
     * @author yanglonghui
     *
     */
    public class HorImageListView extends View {
    
    	private int current_OffsetX=0;//当前屏X轴的偏移量
    	private int cuurent_OffsetY=0;//当前屏Y轴的偏移量
    	private int moving_OffsetX=0;//当前S手势X轴偏移量
    	private int moving_OffsetY=0;//当前S手势Y轴偏移量
    	private int current_Page=1;//当前页码,开始页码为1
    	private int portraitNumberPerScreen;//每屏头像个数
    	private int charHeight=0;//一个字的高度
    	private int maxPage=1;//最大页码
    	
    	private int current_foucsIndex=0;//当前焦点
    	private int current_longPressIndex=-1;//当前长按焦点
    	private int current_clickIndex=0;//当前点击
    	
    	private int headWidth=0;//头像宽度
    	private int paddingLeft;//左边距
    	private Bitmap []bitmapArray;//头像数组
    	private Rect headRectArray[]=null;//头像位置
    	private Rect drawingRect=new Rect();
    
        private Bitmap mCircleBitmap=null;
        private PorterDuffXfermode xfermode=new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);
        private PaintFlagsDrawFilter pdf=new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
    	private Paint paint = new Paint();
        {
    	    paint.setStyle(Paint.Style.STROKE);
    	    paint.setFlags(Paint.ANTI_ALIAS_FLAG);
    	    paint.setAntiAlias(true);// 设置画笔的锯齿效果。 true是去除,大家一看效果就明白了 
        }
        
        private static final int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG |
                Canvas.CLIP_SAVE_FLAG |
                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
                Canvas.CLIP_TO_LAYER_SAVE_FLAG;
        
        private IHeadClick headClickListener;
        
        private Handler mHandler=new Handler();
        private ArrayList<String> headList=new ArrayList<String>();
        
    	public HorImageListView(Context context, AttributeSet attrs, int defStyle) {
    		super(context, attrs, defStyle);
    		init();
    	}
    
    	public HorImageListView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		init();
    	}
    
    	public HorImageListView(Context context) {
    		super(context);
    		init();
    	}
    	
    	private void init()
    	{
    		try {  
                if(android.os.Build.VERSION.SDK_INT>=11)  
                {  
                    setLayerType(LAYER_TYPE_SOFTWARE, null);  
                }  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
    		
    		mGestureDetector=new GestureDetector(new CusGestureListener());
    		setLongClickable(true); 
    		setOnTouchListener(onTouchListener);
    	}
    	
    	public void setAdapter(ArrayList<String> headList)
    	{
    		this.headList=headList;
    		current_Page=1;
    		current_OffsetX=0;
    		current_clickIndex=0;
    		current_foucsIndex=0;
    		current_longPressIndex=-1;
    		
    		if(headWidth==0)
    		{
    			headWidth=DensityUtil.dip2px(getContext(), 60);
    		}
    		mCircleBitmap=WindowMgr.getInstance().getCircleBitmap(headWidth, headWidth);
    		bitmapArray=WindowMgr.getInstance().getAllBitmaps(headList);
    		headRectArray=new Rect[bitmapArray.length];
    		for(int i=0;i<headRectArray.length;i++)//随时滑动
    		{
    			headRectArray[i]=new Rect();
    		}
    		requestLayout();
    	}
    	
    	private void calculate()
    	{
    		if(getMeasuredWidth()<=0)
    		{
    			return;
    		}
    		portraitNumberPerScreen=getMeasuredWidth()/headWidth;
    		portraitNumberPerScreen--;//少一个元素,使不会那么拥挤
    		paddingLeft=(int)((float)(getMeasuredWidth()-portraitNumberPerScreen*headWidth)/(float)(portraitNumberPerScreen+1)+0.5f);
    		
    		if(bitmapArray.length>portraitNumberPerScreen)
    		{
    			maxPage=(bitmapArray.length%portraitNumberPerScreen==0)?bitmapArray.length/portraitNumberPerScreen:this.bitmapArray.length/portraitNumberPerScreen+1;
    		}
    		else
    		{
    			maxPage=1;
    		}
    		int left = 0;
    		int top = DensityUtil.dip2px(getContext(), 10);
    		int right = 0;
    		int bottom = top+headWidth;
    		
    		for(int i=0;i<maxPage;i++)//分页效果
    		{
    			int pageWidthPadding=i*getMeasuredWidth();
    			for(int j=0;j<portraitNumberPerScreen&&(i*portraitNumberPerScreen+j)<bitmapArray.length;j++)
    			{
    				left=pageWidthPadding+paddingLeft*(j+1)+j*headWidth;
    				right=left+headWidth;
    				headRectArray[i*portraitNumberPerScreen+j].set(left, top, right, bottom);
    			}
    		}
    	}
    	
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		int height=DensityUtil.dip2px(getContext(), 80);
    		setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height); 
    		calculate();
    	}
    	
    	@Override
    	protected void onDraw(Canvas canvas) {
    		super.onDraw(canvas);
    		
    		if(null!=bitmapArray)
    		{
    			if(current_clickIndex>0)
    			{
    					canvas.setDrawFilter(pdf);
    					for(int i=0;i<bitmapArray.length;i++)
    					{
    						drawingRect.left=headRectArray[i].left+current_OffsetX+clickOffsetBackDx;
    						drawingRect.top=headRectArray[i].top+cuurent_OffsetY;
    						drawingRect.right=headRectArray[i].right+current_OffsetX+clickOffsetBackDx;
    						drawingRect.bottom=headRectArray[i].bottom+cuurent_OffsetY;
    						
    						if(i>current_clickIndex)//还需要优化
    						{
    							if(current_Page==1)
    							{
    								drawingRect.left=headRectArray[i].left+current_OffsetX;
    								drawingRect.top=headRectArray[i].top+cuurent_OffsetY;
    								drawingRect.right=headRectArray[i].right+current_OffsetX;
    								drawingRect.bottom=headRectArray[i].bottom+cuurent_OffsetY;
    							}
    							else if(current_Page>1)
    							{
    								
    							}
    						}
    						
    						if(drawingRect.right<getLeft())
    						{
    							continue;
    						}
    						else if(drawingRect.left>getRight())
    						{
    							break;
    						}
    						
    						if(current_clickIndex!=i)
    						{
    							//绘制头像
    					        canvas.drawBitmap(mCircleBitmap, null, drawingRect, paint);
    					        paint.setXfermode(xfermode);
    					        canvas.drawBitmap(bitmapArray[i], null, drawingRect, paint);
    					        paint.setXfermode(null);
    					        
    						}
    					}
    					
    					drawingRect.left=headRectArray[0].left+clickOffsetforwarDx;
    					drawingRect.top=headRectArray[0].top;
    					drawingRect.right=headRectArray[0].right+clickOffsetforwarDx;
    					drawingRect.bottom=headRectArray[0].bottom;
    					
    	                int sc = canvas.saveLayer(drawingRect.left, drawingRect.top, drawingRect.right, drawingRect.bottom, null,LAYER_FLAGS);
    					//绘制头像
    			        canvas.drawBitmap(mCircleBitmap, null, drawingRect, paint);
    			        paint.setXfermode(xfermode);
    			        canvas.drawBitmap(bitmapArray[current_clickIndex], null, drawingRect, paint);
    			        paint.setXfermode(null);
    			        
    			        canvas.restoreToCount(sc);
    			}
    			else
    			{
    					canvas.setDrawFilter(pdf);
    					for(int i=0;i<bitmapArray.length;i++)
    					{
    						drawingRect.left=headRectArray[i].left+current_OffsetX+moving_OffsetX;
    						drawingRect.top=headRectArray[i].top+cuurent_OffsetY;
    						drawingRect.right=headRectArray[i].right+current_OffsetX+moving_OffsetX;
    						drawingRect.bottom=headRectArray[i].bottom+cuurent_OffsetY;
    						
    						if(drawingRect.right<getLeft())
    						{
    							continue;
    						}
    						else if(drawingRect.left>getRight())
    						{
    							break;
    						}
    						
    						if(current_longPressIndex==i)
    						{
    							int insetDx=(int)((float)drawingRect.height()/(float)8);
    							drawingRect.inset(insetDx, insetDx);
    						}
    						
    						//绘制头像
    				        canvas.drawBitmap(mCircleBitmap, null, drawingRect, paint);
    				        paint.setXfermode(xfermode);
    				        canvas.drawBitmap(bitmapArray[i], null, drawingRect, paint);
    				        paint.setXfermode(null);
    					}
    			}
    		}
    	}
    	
    	private OnTouchListener onTouchListener=new OnTouchListener() {
    		
    		@Override
    		public boolean onTouch(View v, MotionEvent event) {
    			boolean isConsumed =mGestureDetector.onTouchEvent(event);
    			if (isConsumed) return true;
    			
    			if(event.getAction()==MotionEvent.ACTION_CANCEL||event.getAction()==MotionEvent.ACTION_UP)
    			{
    				//----分页代码
    				int direct=0;//往左边为1,右边为-1,保持不变为0
    				if(moving_OffsetX>getMeasuredWidth()/2)//向右边滑过半屏
    				{
    					current_Page--;
    					direct=1;
    					if(current_Page<1)
    					{
    						current_Page=1;
    						direct=0;
    					}
    				}
    				else if(moving_OffsetX<-getMeasuredWidth()/2)//向左边滑过半屏
    				{
    					current_Page++;
    					direct=-1;
    					if(current_Page>maxPage)
    					{
    						current_Page=maxPage;
    						direct=0;
    					}
    				}
    				
    				int old=current_OffsetX+moving_OffsetX;
    				int newX=current_OffsetX+direct*getMeasuredWidth();
    				if(direct!=0)
    				{
    					current_foucsIndex=(current_Page-1)*portraitNumberPerScreen;
    					
    					if(null!=headClickListener)
    					headClickListener.onItemClick(current_foucsIndex);
    				}
    				
    Log.v("dx:"+current_OffsetX, "----------------------");
    				
    				if(current_longPressIndex!=-1)
    				{
    					invalidate();
    					current_longPressIndex=-1;//恢复
    					return false;
    				}
    				else
    				{
    					mHandler.post(new SmoothRunnable(old, newX));
    				}
    				return false;
    			}
    			
    			invalidate();
    			return isConsumed; 
    		}
    	};
    	
    	private GestureDetector mGestureDetector;
    	private class CusGestureListener extends SimpleOnGestureListener
    	{
    		private Rect mDragRect=new Rect();
    		@Override
    		public boolean onSingleTapUp(MotionEvent e) {
    			Log.v("CusGestureListener", "onSingleTapUp");
    			
    			int x=(int) e.getX();
    			int y=(int) e.getY();
    			
    			boolean isInner=false;
    			Rect mRect=new Rect();
    			for(int i=0;i<bitmapArray.length;i++)
    			{
    				mRect.left=headRectArray[i].left+current_OffsetX+moving_OffsetX;
    				mRect.top=headRectArray[i].top+cuurent_OffsetY;
    				mRect.right=headRectArray[i].right+current_OffsetX+moving_OffsetX;
    				mRect.bottom=headRectArray[i].bottom+cuurent_OffsetY;
    				
    				if(!isInner)
    				{
    					if(mRect.top<=y&&mRect.bottom>=y)
    					{
    						isInner=true;
    					}
    					else
    					{
    						break;
    					}
    				}
    				
    				if(mRect.contains(x, y))
    				{
    					current_foucsIndex=i;
    					current_clickIndex=i;
    					
    					//动画:被点击向前移动,未点击向后移动
    					if(null!=mClickRunnable)
    					{
    						mClickRunnable.stop();
    					}
    					mClickRunnable=new ClickRunnable(current_clickIndex);
    					mHandler.post(mClickRunnable);
    				}
    			} 
    			return super.onSingleTapUp(e);
    		}
    
    		@Override
    		public void onLongPress(MotionEvent e) {
    //			Log.v("CusGestureListener", "onLongPress");
    			
    			int x=(int) e.getRawX();
    			int y=(int) (e.getRawY()-WindowMgr.getInstance().getStatusBarHeight(getContext()));
    			
    			Rect mRect=new Rect();
    			for(int i=0;i<bitmapArray.length;i++)
    			{
    				mRect.left=headRectArray[i].left+current_OffsetX+moving_OffsetX;
    				mRect.top=headRectArray[i].top+cuurent_OffsetY;
    				mRect.right=headRectArray[i].right+current_OffsetX+moving_OffsetX;
    				mRect.bottom=headRectArray[i].bottom+cuurent_OffsetY;
    				
    				if(mRect.contains(x, y))
    				{
    					current_longPressIndex=i;
    					return ;
    				}
    			}
    			super.onLongPress(e);
    		}
    
    		@Override
    		public boolean onScroll(MotionEvent e1, MotionEvent e2,
    				float distanceX, float distanceY) {
    //			Log.v("CusGestureListener", "onScroll");
    			
    			int tx=(int) e1.getRawX();
    			int ty=(int) (e1.getRawY()-WindowMgr.getInstance().getStatusBarHeight(getContext()));
    			
    			int tx2=(int) e2.getRawX();
    			int ty2=(int) (e2.getRawY()-WindowMgr.getInstance().getStatusBarHeight(getContext()));
    			
    			if(mDragRect.contains(tx, ty)&&mDragRect.contains(tx2, ty2))
    			{
    				moving_OffsetX=(int) (e2.getRawX()-e1.getRawX());
    			}
    			return super.onScroll(e1, e2, distanceX, distanceY);
    		}
    
    		@Override
    		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
    				float velocityY) {
    //			Log.d("CDH", "onFling currentPage:"+currentPage+" x1:"+e1.getRawX()+" x2:"+e2.getRawX()+" velocityX:"+velocityX+"velocityY:"+velocityY);
    			
    			if (Math.abs(velocityX) < 1000) return false;
    			
    			int direct=0;//往左边为1,右边为-1,保持不变为0
    			if(velocityX > 0)
    			{
    				current_Page--;
    				direct=1;
    				if(current_Page<1) 
    				{
    					current_Page=1;
    					direct=0;
    				}
    			} 
    			else 
    			{
    				current_Page++;
    				direct=-1;
    				if(current_Page>maxPage) 
    				{
    					current_Page=maxPage;
    					direct=0;
    				}
    			}
    
    			int old=current_OffsetX+moving_OffsetX;
    			int newX=current_OffsetX+direct*getMeasuredWidth();
    			mHandler.post(new SmoothRunnable(old, newX));
    			
    			return true;
    		}
    
    		@Override
    		public void onShowPress(MotionEvent e) {
    //			Log.v("CusGestureListener", "onShowPress");
    			
    			super.onShowPress(e);
    		}
    
    		@Override
    		public boolean onDown(MotionEvent e) {
    //			Log.v("CusGestureListener", "onDown");
    			
    			moving_OffsetX=0;
    			mDragRect.set(getLeft(), getTop(), getRight(), getBottom());
    			return super.onDown(e);
    		}
    
    		@Override
    		public boolean onDoubleTap(MotionEvent e) {
    //			Log.v("CusGestureListener", "onDoubleTap");
    			return super.onDoubleTap(e);
    		}
    
    		@Override
    		public boolean onDoubleTapEvent(MotionEvent e) {
    //			Log.v("CusGestureListener", "onDoubleTapEvent");
    			
    			return super.onDoubleTapEvent(e);
    		}
    
    		@Override
    		public boolean onSingleTapConfirmed(MotionEvent e) {
    //			Log.v("CusGestureListener", "onSingleTapConfirmed");
    			
    			return super.onSingleTapConfirmed(e);
    		}
    	}
    	
    	//平滑移动
    	private class SmoothRunnable implements Runnable
    	{
    		private int startDx;
    		private int endDx;
    		private long duration=250;
    		private long interval=10;
    		private long startTime;
    		private long endTime;
    		
    		public SmoothRunnable(int startDx, int endDx) {
    			super();
    			this.startDx = startDx;
    			this.endDx = endDx;
    		}
    
    		@Override
    		public void run() {
    			if(startTime==0)
    			{
    				moving_OffsetX=0;
    				startTime=System.currentTimeMillis();
    				endTime=startTime+duration;
    			}
    			
    			long currentTime=System.currentTimeMillis();
    			if(currentTime<endTime)
    			{
    				current_OffsetX=(int)(startDx+(endDx-startDx)*((float)(currentTime-startTime)/(float)(duration)));
    				invalidate();
    				mHandler.postDelayed(this, interval);
    			}
    			else
    			{
    				current_OffsetX=endDx;
    				moving_OffsetX=0;
    				invalidate();
    			}
    		}
    	} 
    
    	private int clickOffsetforwarDx=0;
    	private int clickOffsetBackDx;
    	//点击移动到队头部,其他往后面移动
    	private ClickRunnable mClickRunnable=null;
    	private class ClickRunnable implements Runnable
    	{
    		private int current_clickIndex;
    		private int forwardDx=0;//到队列头部的总长度
    		private int backwardsDx=0;//到第二个的长度
    		private long duration=300;
    		private long interval=10;
    		private long startTime;
    		private long endTime;
    		
    		public ClickRunnable(int current_clickIndex)
    		{
    			this.current_clickIndex=current_clickIndex;
    			duration=Math.max((long) (duration*(float)current_clickIndex/(float)portraitNumberPerScreen),duration);
    			duration=Math.min(duration, 700);
    		}
    		
    		public void stop()
    		{
    			mHandler.removeCallbacks(this);
    		}
    
    		@Override
    		public void run() {
    			if(startTime==0)
    			{
    				Rect mRect=new Rect();
    				mRect.left=headRectArray[current_clickIndex].left+current_OffsetX+moving_OffsetX;
    				mRect.top=headRectArray[current_clickIndex].top+cuurent_OffsetY;
    				mRect.right=headRectArray[current_clickIndex].right+current_OffsetX+moving_OffsetX;
    				mRect.bottom=headRectArray[current_clickIndex].bottom+cuurent_OffsetY;
    				
    				forwardDx=mRect.left-headRectArray[0].left;
    				
    				int left=paddingLeft*2+headWidth;
    				int top=DensityUtil.dip2px(getContext(), 10);
    				Rect secord=new Rect(left,top, left+headWidth, top+headWidth);
    				backwardsDx=secord.left-headRectArray[0].left+Math.abs(current_OffsetX);
    				
    				startTime=System.currentTimeMillis();
    				endTime=startTime+duration;
    			}
    			
    			long currentTime=System.currentTimeMillis();
    			if(currentTime<endTime)
    			{
    				clickOffsetforwarDx=(int) (forwardDx-forwardDx*((float)(currentTime-startTime)/(float)(duration)));
    				clickOffsetBackDx=(int) (backwardsDx*((float)(currentTime-startTime)/(float)(duration)));
    				invalidate();
    				mHandler.postDelayed(this, interval);
    			}
    			else
    			{
    				clickOffsetforwarDx=0;
    				clickOffsetBackDx=0;
    				
    				String tmp=headList.remove(current_clickIndex);
    				headList.add(0, tmp);
    				bitmapArray=WindowMgr.getInstance().getAllBitmaps(headList);
    				
    				HorImageListView.this.current_clickIndex=0;
    				HorImageListView.this.current_Page=1;
    				HorImageListView.this.current_OffsetX=0;
    				HorImageListView.this.current_clickIndex=0;
    				HorImageListView.this.current_foucsIndex=0;
    				HorImageListView.this.current_longPressIndex=-1;
    				invalidate();
    				
    				if(null!=headClickListener)
    				headClickListener.onItemClick(current_clickIndex);
    			}
    		}
    	}
    	
    	public void setOnHeadClickListener(IHeadClick headClickListener)
    	{
    		this.headClickListener=headClickListener;
    	}
    	
    	public static interface IHeadClick
    	{
    		public void onItemClick(int position);
    	}
    }
    


    具体的动画效果如下(效果图是丑了点,有录制gif图片软件请推荐一下吧):




    Demo下载地址: http://download.csdn.net/detail/zz7zz7zz/6340747



  • 相关阅读:
    windows下安装,配置gcc编译器
    【Oracle】-【ROWNUM与索引】-索引对ROWNUM检索的影响
    古代文化与典故
    古代文化与典故
    Opencv+Zbar二维码识别(一维码校正)
    美国历史与文化
    美国历史与文化
    地道的英文 —— 固定搭配、固定短语
    地道的英文 —— 固定搭配、固定短语
    英式英语 vs 美式英语
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3348107.html
Copyright © 2020-2023  润新知