CoverFlow效果
来源:互联网 发布:微信跳转淘宝app 编辑:程序博客网 时间:2024/04/30 09:49
源码介绍皆转载:
最近研究了一下如何在Android上实现CoverFlow效果的控件,其实早在2010年,就有Neil Davies开发并开源出了这个控件,Neil大神的这篇博客地址http://www.inter-fuser.com/2010/02/android-coverflow-widget-v2.html。首先是阅读源码,弄明白核心思路后,自己重新写了一遍这个控件,并加入了详尽的注释以便日后查阅;而后在使用过程中,发现了有两点可以改进:(1)初始图片位于中间,左边空了一半空间,比较难看,可以改为重复滚动地展示、(2)由于图片一开始就需要加载出来,所以对内存开销较大,很容易OOM,需要对图片的内存空间进行压缩。
这个自定义控件包括4个部分,用于创建及提供图片对象的ImageAdapter,计算图片旋转角度等的自定义控件GalleryFlow,压缩采样率解析Bitmap的工具类BitmapScaleDownUtil,以及承载自定义控件的Gallery3DActivity。
首先是ImageAdapter,代码如下:
1 package pym.test.gallery3d.widget; 2 3 import pym.test.gallery3d.util.BitmapScaleDownUtil; 4 import android.content.Context; 5 import android.graphics.Bitmap; 6 import android.graphics.Bitmap.Config; 7 import android.graphics.Canvas; 8 import android.graphics.LinearGradient; 9 import android.graphics.Matrix; 10 import android.graphics.Paint; 11 import android.graphics.PaintFlagsDrawFilter; 12 import android.graphics.PorterDuff.Mode; 13 import android.graphics.PorterDuffXfermode; 14 import android.graphics.Shader.TileMode; 15 import android.view.View; 16 import android.view.ViewGroup; 17 import android.widget.BaseAdapter; 18 import android.widget.Gallery; 19 import android.widget.ImageView; 20 21 /** 22 * @author pengyiming 23 * @date 2013-9-30 24 * @function GalleryFlow适配器 25 */ 26 public class ImageAdapter extends BaseAdapter 27 { 28 /* 数据段begin */ 29 private final String TAG = "ImageAdapter"; 30 private Context mContext; 31 32 //图片数组 33 private int[] mImageIds ; 34 //图片控件数组 35 private ImageView[] mImages; 36 //图片控件LayoutParams 37 private GalleryFlow.LayoutParams mImagesLayoutParams; 38 /* 数据段end */ 39 40 /* 函数段begin */ 41 public ImageAdapter(Context context, int[] imageIds) 42 { 43 mContext = context; 44 mImageIds = imageIds; 45 mImages = new ImageView[mImageIds.length]; 46 mImagesLayoutParams = new GalleryFlow.LayoutParams(Gallery.LayoutParams.WRAP_CONTENT, Gallery.LayoutParams.WRAP_CONTENT); 47 } 48 49 /** 50 * @function 根据指定宽高创建待绘制的Bitmap,并绘制到ImageView控件上 51 * @param imageWidth 52 * @param imageHeight 53 * @return void 54 */ 55 public void createImages(int imageWidth, int imageHeight) 56 { 57 // 原图与倒影的间距5px 58 final int gapHeight = 5; 59 60 int index = 0; 61 for (int imageId : mImageIds) 62 { 63 /* step1 采样方式解析原图并生成倒影 */ 64 // 解析原图,生成原图Bitmap对象 65 // Bitmap originalImage = BitmapFactory.decodeResource(mContext.getResources(), imageId); 66 Bitmap originalImage = BitmapScaleDownUtil.decodeSampledBitmapFromResource(mContext.getResources(), imageId, imageWidth, imageHeight); 67 int width = originalImage.getWidth(); 68 int height = originalImage.getHeight(); 69 70 // Y轴方向反向,实质就是X轴翻转 71 Matrix matrix = new Matrix(); 72 matrix.setScale(1, -1); 73 // 且仅取原图下半部分创建倒影Bitmap对象 74 Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height / 2, width, height / 2, matrix, false); 75 76 /* step2 绘制 */ 77 // 创建一个可包含原图+间距+倒影的新图Bitmap对象 78 Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + gapHeight + height / 2), Config.ARGB_8888); 79 // 在新图Bitmap对象之上创建画布 80 Canvas canvas = new Canvas(bitmapWithReflection); 81 // 抗锯齿效果 82 canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG)); 83 // 绘制原图 84 canvas.drawBitmap(originalImage, 0, 0, null); 85 // 绘制间距 86 Paint gapPaint = new Paint(); 87 gapPaint.setColor(0xFFCCCCCC); 88 canvas.drawRect(0, height, width, height + gapHeight, gapPaint); 89 // 绘制倒影 90 canvas.drawBitmap(reflectionImage, 0, height + gapHeight, null); 91 92 /* step3 渲染 */ 93 // 创建一个线性渐变的渲染器用于渲染倒影 94 Paint paint = new Paint(); 95 LinearGradient shader = new LinearGradient(0, height, 0, (height + gapHeight + height / 2), 0x70ffffff, 0x00ffffff, TileMode.CLAMP); 96 // 设置画笔渲染器 97 paint.setShader(shader); 98 // 设置图片混合模式 99 paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));100 // 渲染倒影+间距101 canvas.drawRect(0, height, width, (height + gapHeight + height / 2), paint);102 103 /* step4 在ImageView控件上绘制 */104 ImageView imageView = new ImageView(mContext);105 imageView.setImageBitmap(bitmapWithReflection);106 imageView.setLayoutParams(mImagesLayoutParams);107 // 打log108 imageView.setTag(index);109 110 /* step5 释放heap */111 originalImage.recycle();112 reflectionImage.recycle();113 // bitmapWithReflection.recycle();114 115 mImages[index++] = imageView;116 }117 }118 119 @Override120 public int getCount()121 {122 return Integer.MAX_VALUE;123 }124 125 @Override126 public Object getItem(int position)127 {128 return mImages[position];129 }130 131 @Override132 public long getItemId(int position)133 {134 return position;135 }136 137 @Override138 public View getView(int position, View convertView, ViewGroup parent)139 {140 return mImages[position % mImages.length];141 }142 /* 函数段end */143 }
其次是GalleryFlow,代码如下:
1 package pym.test.gallery3d.widget; 2 3 import android.content.Context; 4 import android.graphics.Camera; 5 import android.graphics.Matrix; 6 import android.util.AttributeSet; 7 import android.util.Log; 8 import android.view.View; 9 import android.view.animation.Transformation; 10 import android.widget.Gallery; 11 12 /** 13 * @author pengyiming 14 * @date 2013-9-30 15 * @function 自定义控件 16 */ 17 public class GalleryFlow extends Gallery 18 { 19 /* 数据段begin */ 20 private final String TAG = "GalleryFlow"; 21 22 // 边缘图片最大旋转角度 23 private final float MAX_ROTATION_ANGLE = 75; 24 // 中心图片最大前置距离 25 private final float MAX_TRANSLATE_DISTANCE = -100; 26 // GalleryFlow中心X坐标 27 private int mGalleryFlowCenterX; 28 // 3D变换Camera 29 private Camera mCamera = new Camera(); 30 /* 数据段end */ 31 32 /* 函数段begin */ 33 public GalleryFlow(Context context, AttributeSet attrs) 34 { 35 super(context, attrs); 36 37 // 开启,在滑动过程中,回调getChildStaticTransformation() 38 this.setStaticTransformationsEnabled(true); 39 } 40 41 /** 42 * @function 获取GalleryFlow中心X坐标 43 * @return 44 */ 45 private int getCenterXOfCoverflow() 46 { 47 return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft(); 48 } 49 50 /** 51 * @function 获取GalleryFlow子view的中心X坐标 52 * @param childView 53 * @return 54 */ 55 private int getCenterXOfView(View childView) 56 { 57 return childView.getLeft() + childView.getWidth() / 2; 58 } 59 60 /** 61 * @note step1 系统调用measure()方法时,回调此方法;表明此时系统正在计算view的大小 62 */ 63 @Override 64 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 65 { 66 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 67 68 mGalleryFlowCenterX = getCenterXOfCoverflow(); 69 Log.d(TAG, "onMeasure, mGalleryFlowCenterX = " + mGalleryFlowCenterX); 70 } 71 72 /** 73 * @note step2 系统调用layout()方法时,回调此方法;表明此时系统正在给child view分配空间 74 * @note 必定在onMeasure()之后回调,但与onSizeChanged()先后顺序不一定 75 */ 76 @Override 77 protected void onLayout(boolean changed, int l, int t, int r, int b) 78 { 79 super.onLayout(changed, l, t, r, b); 80 81 mGalleryFlowCenterX = getCenterXOfCoverflow(); 82 Log.d(TAG, "onLayout, mGalleryFlowCenterX = " + mGalleryFlowCenterX); 83 } 84 85 /** 86 * @note step2 系统调用measure()方法后,当需要绘制此view时,回调此方法;表明此时系统已计算完view的大小 87 * @note 必定在onMeasure()之后回调,但与onSizeChanged()先后顺序不一定 88 */ 89 @Override 90 protected void onSizeChanged(int w, int h, int oldw, int oldh) 91 { 92 super.onSizeChanged(w, h, oldw, oldh); 93 94 mGalleryFlowCenterX = getCenterXOfCoverflow(); 95 Log.d(TAG, "onSizeChanged, mGalleryFlowCenterX = " + mGalleryFlowCenterX); 96 } 97 98 @Override 99 protected boolean getChildStaticTransformation(View childView, Transformation t)100 {101 // 计算旋转角度102 float rotationAngle = calculateRotationAngle(childView);103 104 // 计算前置距离105 float translateDistance = calculateTranslateDistance(childView);106 107 // 开始3D变换108 transformChildView(childView, t, rotationAngle, translateDistance);109 110 return true;111 }112 113 /**114 * @function 计算GalleryFlow子view的旋转角度115 * @note1 位于Gallery中心的图片不旋转116 * @note2 位于Gallery中心两侧的图片按照离中心点的距离旋转117 * @param childView118 * @return119 */120 private float calculateRotationAngle(View childView)121 {122 final int childCenterX = getCenterXOfView(childView);123 float rotationAngle = 0;124 125 rotationAngle = (mGalleryFlowCenterX - childCenterX) / (float) mGalleryFlowCenterX * MAX_ROTATION_ANGLE;126 127 if (rotationAngle > MAX_ROTATION_ANGLE)128 {129 rotationAngle = MAX_ROTATION_ANGLE;130 }131 else if (rotationAngle < -MAX_ROTATION_ANGLE)132 {133 rotationAngle = -MAX_ROTATION_ANGLE;134 }135 136 return rotationAngle;137 }138 139 /**140 * @function 计算GalleryFlow子view的前置距离141 * @note1 位于Gallery中心的图片前置142 * @note2 位于Gallery中心两侧的图片不前置143 * @param childView144 * @return145 */146 private float calculateTranslateDistance(View childView)147 {148 final int childCenterX = getCenterXOfView(childView);149 float translateDistance = 0;150 151 if (mGalleryFlowCenterX == childCenterX)152 {153 translateDistance = MAX_TRANSLATE_DISTANCE;154 }155 156 return translateDistance;157 }158 159 /**160 * @function 开始变换GalleryFlow子view161 * @param childView162 * @param t163 * @param rotationAngle164 * @param translateDistance165 */166 private void transformChildView(View childView, Transformation t, float rotationAngle, float translateDistance)167 {168 t.clear();169 t.setTransformationType(Transformation.TYPE_MATRIX);170 171 final Matrix imageMatrix = t.getMatrix();172 final int imageWidth = childView.getWidth();173 final int imageHeight = childView.getHeight();174 175 mCamera.save();176 177 /* rotateY */178 // 在Y轴上旋转,位于中心的图片不旋转,中心两侧的图片竖向向里或向外翻转。179 mCamera.rotateY(rotationAngle);180 /* rotateY */181 182 /* translateZ */183 // 在Z轴上前置,位于中心的图片会有放大的效果184 mCamera.translate(0, 0, translateDistance);185 /* translateZ */186 187 // 开始变换(我的理解是:移动Camera,在2D视图上产生3D效果)188 mCamera.getMatrix(imageMatrix);189 imageMatrix.preTranslate(-imageWidth / 2, -imageHeight / 2);190 imageMatrix.postTranslate(imageWidth / 2, imageHeight / 2);191 192 mCamera.restore();193 }194 /* 函数段end */195 }
Bitmap解析用具BitmapScaleDownUtil,代码如下:
1 package pym.test.gallery3d.util; 2 3 import android.content.res.Resources; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.view.Display; 7 8 /** 9 * @author pengyiming10 * @date 2013-9-3011 * @function Bitmap缩放处理工具类12 */13 public class BitmapScaleDownUtil14 {15 /* 数据段begin */16 private final String TAG = "BitmapScaleDownUtil";17 /* 数据段end */18 19 /* 函数段begin */20 /**21 * @function 获取屏幕大小22 * @param display23 * @return 屏幕宽高24 */25 public static int[] getScreenDimension(Display display)26 {27 int[] dimension = new int[2];28 dimension[0] = display.getWidth();29 dimension[1] = display.getHeight();30 31 return dimension;32 }33 34 /**35 * @function 以取样方式加载Bitmap 36 * @param res37 * @param resId38 * @param reqWidth39 * @param reqHeight40 * @return 取样后的Bitmap41 */42 public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight)43 {44 // step1,将inJustDecodeBounds置为true,以解析Bitmap真实尺寸45 final BitmapFactory.Options options = new BitmapFactory.Options();46 options.inJustDecodeBounds = true;47 BitmapFactory.decodeResource(res, resId, options);48 49 // step2,计算Bitmap取样比例50 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);51 52 // step3,将inJustDecodeBounds置为false,以取样比列解析Bitmap53 options.inJustDecodeBounds = false;54 return BitmapFactory.decodeResource(res, resId, options);55 }56 57 /**58 * @function 计算Bitmap取样比例59 * @param options60 * @param reqWidth61 * @param reqHeight62 * @return 取样比例63 */64 private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)65 {66 // 默认取样比例为1:167 int inSampleSize = 1;68 69 // Bitmap原始尺寸70 final int width = options.outWidth;71 final int height = options.outHeight;72 73 // 取最大取样比例74 if (height > reqHeight || width > reqWidth)75 {76 final int widthRatio = Math.round((float) width / (float) reqWidth);77 final int heightRatio = Math.round((float) height / (float) reqHeight);78 79 // 取样比例为X:1,其中X>=180 inSampleSize = Math.max(widthRatio, heightRatio);81 }82 83 return inSampleSize;84 }85 /* 函数段end */86 }
测试控件的Gallery3DActivity,代码如下:
1 package pym.test.gallery3d.main; 2 3 import pym.test.gallery3d.R; 4 import pym.test.gallery3d.util.BitmapScaleDownUtil; 5 import pym.test.gallery3d.widget.GalleryFlow; 6 import pym.test.gallery3d.widget.ImageAdapter; 7 import android.app.Activity; 8 import android.content.Context; 9 import android.os.Bundle;10 11 /**12 * @author pengyiming13 * @date 2013-9-3014 */15 public class Gallery3DActivity extends Activity16 {17 /* 数据段begin */18 private final String TAG = "Gallery3DActivity";19 private Context mContext;20 21 // 图片缩放倍率(相对屏幕尺寸的缩小倍率)22 public static final int SCALE_FACTOR = 8;23 24 // 图片间距(控制各图片之间的距离)25 private final int GALLERY_SPACING = -10;26 27 // 控件28 private GalleryFlow mGalleryFlow;29 /* 数据段end */30 31 /* 函数段begin */32 @Override33 protected void onCreate(Bundle savedInstanceState)34 {35 super.onCreate(savedInstanceState);36 mContext = getApplicationContext();37 38 setContentView(R.layout.gallery_3d_activity_layout);39 initGallery();40 }41 42 private void initGallery()43 {44 // 图片ID45 int[] images = {46 R.drawable.picture_1,47 R.drawable.picture_2,48 R.drawable.picture_3,49 R.drawable.picture_4,50 R.drawable.picture_5,51 R.drawable.picture_6,52 R.drawable.picture_7 };53 54 ImageAdapter adapter = new ImageAdapter(mContext, images);55 // 计算图片的宽高56 int[] dimension = BitmapScaleDownUtil.getScreenDimension(getWindowManager().getDefaultDisplay());57 int imageWidth = dimension[0] / SCALE_FACTOR;58 int imageHeight = dimension[1] / SCALE_FACTOR;59 // 初始化图片60 adapter.createImages(imageWidth, imageHeight);61 62 // 设置Adapter,显示位置位于控件中间,这样使得左右均可"无限"滑动63 mGalleryFlow = (GalleryFlow) findViewById(R.id.gallery_flow);64 mGalleryFlow.setSpacing(GALLERY_SPACING);65 mGalleryFlow.setAdapter(adapter);66 mGalleryFlow.setSelection(Integer.MAX_VALUE / 2);67 }68 /* 函数段end */69 }
see效果图~~~
0 0
- CoverFlow效果
- CoverFlow效果
- CoverFlow 效果欣赏
- openFlow实现CoverFlow效果
- WPF效果- CoverFlow
- Android实现CoverFlow效果
- Android实现CoverFlow效果
- Android实现CoverFlow效果
- Android实现CoverFlow效果
- Android实现CoverFlow效果
- Android实现CoverFlow效果
- Android实现CoverFlow效果
- Android实现CoverFlow效果
- Android-Coverflow 效果
- flash特效原理:CoverFlow 效果
- Android 用CoverFlow的效果
- android UI效果一: coverFlow
- wp7 上实现coverflow效果
- 箱子排序
- 快速排序
- linux find命令详解
- 数据挖掘(数据分析)从业指南
- (初学)简单实用的JAVA分页
- CoverFlow效果
- hdu 4968
- 王爽老师《汇编语言》第二章——寄存器 笔记
- Service+(Notification+)Thread+HttpPost
- Ubuntu下的Vim的Golang语法配置
- vs2012 opencv2.4.9配置 出现msvcp120d.dll文件丢失问题
- Map
- C++设计模式之建造者模式(二)
- 基数排序