[置顶] 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
[置顶] android 自定义ListView实现动画特效的更多相关文章
- [置顶] android 自定义TextView
		系统自带的控件TextView有时候没满一行就换行了,为了解决这个问题,自定义了一个TextView,只有一行显示不完全的情况下才会去换行显示,代码如下: package com.open.textv ... 
- [置顶] android 自定义圆角ImageView以及锯齿的处理
		看到很多人开发过程中要使用圆角图片时,解决方法有: 1.重新绘制一张图片 2.通过布局来配置 3.通过重写View来实现 其中1,2在这里就不讲了,重点讲讲方法三的实现. 实现一:通过截取画布一个圆形 ... 
- Android 自定义ListView
		本文讲实现一个自定义列表的Android程序,程序将实现一个使用自定义的适配器(Adapter)绑定 数据,通过contextView.setTag绑定数据有按钮的ListView. 系统显示列表(L ... 
- Android自定义底部带有动画的Dialog
		Android自定义底部带有动画的Dialog 效果图 先看效果图,是不是你想要的呢 自定义Dialog package --.view; import android.app.Dialog; imp ... 
- Android 自定义 ListView 上下拉动“刷新最新”和“加载更多”歌曲列表
		本文内容 环境 测试数据 项目结构 演示 参考资料 本文演示,上拉刷新最新的歌曲列表,和下拉加载更多的歌曲列表.所谓"刷新最新"和"加载更多"是指日期.演示代码 ... 
- [置顶]
        android ListView包含Checkbox滑动时状态改变
		题外话: 在xamarin android的开发中基本上所有人都会遇到这个小小的坎,的确有点麻烦,当时我也折腾了好一半天,如果你能看到这篇博客,说明你和我当初也是一样的焦灼,如果你想解决掉这个小小的坎 ... 
- [置顶] Android开发笔记(成长轨迹)
		分类: 开发学习笔记2013-06-21 09:44 26043人阅读 评论(5) 收藏 Android开发笔记 1.控制台输出:called unimplemented OpenGL ES API ... 
- Android的Activity切换动画特效库SwitchLayout,视图切换动画库,媲美IOS
		由于看了IOS上面很多开发者开发的APP的视图界面切换动画体验非常好,这些都是IOS自带的,但是Android的Activity等视图切换动画并没有提供原生的,所以特此写了一个可以媲美IOS视图切换动 ... 
- android自定义listview实现圆角
		在项目中我们会经常遇到这种圆角效果,因为直角的看起来确实不那么雅观,可能大家会想到用图片实现,试想上中下要分别做三张图片,这样既会是自己的项目增大也会增加内存使用量,所以使用shape来实现不失为一种 ... 
随机推荐
- mysql数据类型——整型INT(m)
			1.整形分为四种 tinyint smallint mediumint int bigint 注意: 右侧的取值范围是在未加unsigned关键字的情况下,如果加了unsigned,则最大值翻倍,如t ... 
- .NET序列化的一点技巧
			介绍 序列化是将对象状态转换为可保持或传输的形式的过程.序列化的补集是反序列化,后者将流转换为对象.这两个过程一起保证数据易于存储和传输. .NET Framework 提供了两个序列化技术: 二进制 ... 
- IOS设计模式之三:MVC模式
			IOS设计模式之三:MVC模式 模型-视图-控制器 这个模式其实应该叫做MCV,用控制器把model与view隔开才对,也就是model与view互相不知道对方的存在,没有任何瓜葛,他们就像一个团 ... 
- !a && !b 和 !(a || b) 的故事
			// awk代码,当continue执行时说明书是免费的 || )) { continue } 这段代码大概是半年前写的,半年后过来读,发现已经不理解这段代码了,虽然理解当contniue执行时意味着 ... 
- 2、MyBatis.NET学习笔记之CodeSmith使用
			说明:本系列随笔会与CSDN同步发布,当然这里先发,因为这里可以用WLW.但刚才由于误操作,没有重新发上来.只好先在CSDN先发了.重往这里发时图片无法处理,索性直接粘过来吧! 使用框架后一些相关的配 ... 
- ios入门之c语言篇——基本函数——1——随机数生成
			1.随机数函数 参数返回值解析: 参数: a:int,数字范围最小值: b:int,数字范围最大值: 返回值: 1:闰年: 0:非闰年: 备注: a-b的绝对值不能超过int的最大值(65535); ... 
- 转:Lua简明教程
			需要注意的是:lua中的变量如果没有特殊说明,全是全局变量,那怕是语句块或是函数里. 这里很奇怪,为什么在函数内部声明的变量默认也是global的呢? 函数的返回值 和Go语言一样,可以一条语句上赋多 ... 
- 更改Visual Studio 2010/2012/2008的主题设置
			一.更改主题: 主题网站:http://studiostyl.es/ Visual Studio 2010发布也已经有一段时间了,不过安装后默认的白底的主题长时间看代码可能会感觉眼睛酸痛,况且时间长了 ... 
- 2B The least round way
			题目大意: 一个n*n的矩阵,从矩阵的左上角开始,每次移动到下面或者右面,移动到右下角结束. 要求走的路径上的所有数字乘起来,乘积得到的值后面的0最少. #include <iostream ... 
- HDU 2255  奔小康赚大钱
			题目分析:这个是个KM的模板题. #include<stdio.h> #include<string.h> #include<algorithm> #include ... 
