Android 自定义控件之---3D画廊
发布日期:2021-06-30 22:37:40
浏览次数:3
分类:技术文章
本文共 17491 字,大约阅读时间需要 58 分钟。
一、效果图:
二、首先实现基本的框架
1、创建一个类继承Gallery
public class GalleryView extends Gallery { public GalleryView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GalleryView(Context context) { this(context, null); } public GalleryView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }}
2、创建布局,在布局里引用这个类
3、在主页里添加
public class GalleryActivity extends Activity { private GalleryView mGallery; private int mResIds[] = { R.drawable.pic_1, R.drawable.pic_2, R.drawable.pic_3, R.drawable.pic_4, R.drawable.pic_5, R.drawable.pic_6, R.drawable.pic_7, R.drawable.pic_8 }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_gallery); mGallery = (GalleryView) findViewById(R.id.galleryView); mGallery.setAdapter(new GalleryAdapter()); } private class GalleryAdapter extends BaseAdapter { @Override public int getCount() { return mResIds.length; } @Override public Object getItem(int position) { return mResIds[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView iv = null; if (convertView == null) { iv = new ImageView(GalleryActivity.this); }else { iv = (ImageView) convertView; } iv.setImageResource(mResIds[position]); LayoutParams params = new LayoutParams(160, 260); iv.setLayoutParams(params); iv.setScaleType(ScaleType.CENTER_CROP); return iv; } }}
三、图片处理;
1、如果是有倒影的图片,就不能直接iv.setImageResource(mResIds[position]);
而是通过工具类实现了倒影后把图片设置到imageview里Bitmap reverseBitmap = ImageUtil.getReverseBitmapById(mResIds[position]);iv.setImageBitmap(reverseBitmap);
2、开始写方法getReverseBitmapById(mResIds[position])
分析: 首先获取原图 然后根据原图创建一张根据y坐标对称的倒立图 把两张图片合成一张图片 把下面的图片加上遮罩1)创建原图
Bitmap sourceBitmap = BitmapFactory.decodeResource(context.getResources(), resId);
2)绘制原图一半的图片
以下面图片分析,这张图片的x轴是0,y轴是getHeight, 中间点的坐标是(1,getHeight()/2);//绘制原图的下一半图片Matrix matrix = new Matrix();//倒影matrix.setScale(1, -1);Bitmap inverseBitmap = Bitmap.createBitmap(sourceBitmap, 0, sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(), sourceBitmap.getHeight()/2, matrix, false);
3)创建合成图片
//合成图片Bitmap groupbBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight() + sourceBitmap.getHeight() / 2 + padding, sourceBitmap.getConfig());
4)根据合成图片创建画板
Canvas canvas = new Canvas(groupbBitmap);//把原图画在合成图片的上面 canvas.drawBitmap(sourceBitmap, 0, 0, null); //以图片的左上角与坐标 canvas.drawBitmap(inverseBitmap, 0, sourceBitmap.getHeight() + padding, null);
5)添加遮罩,主要使用了线性渲染器
//添加遮罩Paint paint = new Paint();//TileMode.CLAMP表示渲染时一直往下延伸TileMode tile = TileMode.CLAMP;LinearGradient shader = new LinearGradient(0, sourceBitmap.getHeight() + padding, 0, groupbBitmap.getHeight(), Color.TRANSPARENT, Color.BLACK, tile);paint.setShader(shader);//这里取的是矩形与图片的交集,所以用的是DST_INpaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));gCanvas.drawRect(0, sourceBitmap.getHeight() + padding, sourceBitmap.getWidth(), groupbBitmap.getHeight(), paint);
遮罩模式图
6)全部代码
/** * 根据图片id获得有倒影的图片 * @param resId * @return */public static Bitmap getReverseBitmapById(Context context, int resId) { int padding = context.getResources().getDimensionPixelOffset(R.dimen.image_paddding);//图片的间距 //绘制原图 Bitmap sourceBitmap = BitmapFactory.decodeResource(context.getResources(), resId); //图片的默认矩阵// float[] values = { // 1.0f, 0f, 0f,// 0f, 1.0f, 0f,// 0f, 0f, 1.0f// }; //绘制原图的下一半图片 Matrix matrix = new Matrix(); matrix.setScale(1, -1); //matrix.setValues(values); Bitmap inverseBitmap = Bitmap.createBitmap(sourceBitmap, 0, sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(), sourceBitmap.getHeight()/2, matrix, false); //合成图片 Bitmap groupbBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight() + sourceBitmap.getHeight() / 2 + padding, sourceBitmap.getConfig()); Canvas gCanvas = new Canvas(groupbBitmap); //把原图画在合成图片的上面 gCanvas.drawBitmap(sourceBitmap, 0, 0, null); //以图片的左上角与坐标 gCanvas.drawBitmap(inverseBitmap, 0, sourceBitmap.getHeight() + padding, null); //添加遮罩 Paint paint = new Paint(); TileMode tile = TileMode.CLAMP; LinearGradient shader = new LinearGradient(0, sourceBitmap.getHeight() + padding, 0, groupbBitmap.getHeight(), Color.TRANSPARENT, Color.BLACK, tile); paint.setShader(shader); //这里取的是矩形与图片的交集,所以用的是DST_IN paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); gCanvas.drawRect(0, sourceBitmap.getHeight() + padding, sourceBitmap.getWidth(), groupbBitmap.getHeight(), paint); return groupbBitmap;}
四、旋转处理
从上面的图片分析: 必须实现三种效果 1)旋转: 中间的图片完全在中间时没有旋转角度,只要移动就有 2)透明度:中间的图片完全显示,旁边的图片有些透明 3)放大效果:中间的图片大,旁边的图片越来越小 那如何实现呢?记得在gallery里有个方法getChildStaticTransformation,就是用来实现子view的变化效果的
好了,开始
1、判断图片是否在中间,只要判断child与gallery的中心点是否一致即可
child.getLeft() + child.width/2 = gallery.widht/2;
2、如果图片不在中间,设
两个中心的距离为dis = 20dp 图片的最大旋转角度 maxDegree = 50° 那图片的旋转角度 = dis / child.width * maxDegree3、分别获得gallery的中心点与图片的中心点
/** * gallery中心点 * @return */ public int getGalleryCenterX(){ return this.getWidth() / 2; } /** * child中心点 * @return */ public int getChildCenterX(View child){ return child.getLeft() + child.getWidth() / 2; }
4、 得到旋转的角度,设置参数
/** * 实现子view的变化效果 Transformation指定当前item的效果 */ @Override protected boolean getChildStaticTransformation(View child, Transformation t) { int rotateAngle = 0; // 如果child的中心点与gallery的中心点不一致,需要计算旋转角度 int childCenterX = getChildCenterX(child); if (childCenterX != mGalleryCenterX) { // 两个中心点距离 int distance = mGalleryCenterX - childCenterX; float percent = distance * 1.0f / child.getWidth(); rotateAngle = (int) (percent * mMaxAngle);// 得到旋转的角度 // 因为distance有可能大于图片的宽度,所以得到角度有可能大于最大的角度 if (Math.abs(rotateAngle) > mMaxAngle) { rotateAngle = rotateAngle > 0 ? mMaxAngle : -mMaxAngle; } } //设置变化之前,要把上面的一个动画清除 t.clear(); //设置变化的效果为矩阵类型 t.setTransformationType(Transformation.TYPE_MATRIX); //开始旋转 startAnimate(child, rotateAngle, t); return true; }
5、处理动画效果
gallery实现的动画效果必须使用android.graphics.Camera类, 在构造方法里直接 mCamera = new Camera();mCamera使用时必须先mCamera.save(),结束后mCamera.restore();
/** * 开始动画效果 * @param child * @param rotateAngle * @param t */ private void startAnimate(View child, int rotateAngle, Transformation t) { ImageView iv = (ImageView) child; int absAngle = Math.abs(rotateAngle); mCamera.save(); //3.实现放大效果 //仔细看图片,发现图片在x,y轴上都没有变化,但有一边缩小,另一边放大,是如何实现的? //这里就要用到了z轴了,只要改变轴的数值就能实现了 mCamera.translate(0, 0, 100); int zoom = -250 + (absAngle * 2); mCamera.translate(0, 0, zoom); //2.设置透明度 (0到255) 255完全显示,中间的absAngle=0,所以没有透明度 iv.setAlpha(255- absAngle * 2.5f); //3.旋转 mCamera.rotateY(rotateAngle); //4.转换成矩阵 Matrix matrix = t.getMatrix(); //给matrix赋值 mCamera.getMatrix(matrix); //矩阵前乘 matrix.preTranslate(-iv.getWidth()/2, -iv.getHeight()/2); //矩阵后乘 matrix.postTranslate(iv.getWidth()/2, iv.getHeight()/2); }
矩阵后前示意图 (矩阵后乘与其相反移动)
6、现在基本功能都实现了,是不是发现有锯齿不好看,如何去除锯齿呢?
有一个包装类可以实现BitmapDrawable所以在GalleryAdapter里的适配器的getView方法里就必须改代码
原来是Bitmap bm = ImageUtil.getReverseBitmapById(GalleryActivity.this, mResIds[position]);iv.setImageBitmap(bm);
现在加包装
Bitmap bm = ImageUtil.getReverseBitmapById(GalleryActivity.this, mResIds[position]);//去除锯齿BitmapDrawable bd = new BitmapDrawable(bm);bd.setAntiAlias(true);iv.setImageDrawable(bd);
7、为了提高图片的速度,我使用了lruCache来存取图片,如何实现?
首先在适配器定义LruCachemCache ;String key = "key";
在构造方法里初始化mCache
public GalleryAdapter(){ if (mCache == null) { // 最大使用的内存空间 int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4); mCache = new LruCache(maxSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } }; } }
最后在getview方法里
Bitmap bm = mCache.get(key); if (bm == null) { bm = ImageUtil.getReverseBitmapById(GalleryActivity.this, mResIds[position]); }else { mCache.put(key, bm);}
五、贴上全部代码
1、主页
/** * @描述 TODO * @项目名称 App_imooc * @包名 com.android.imooc.gallery * @类名 GalleryActivity * @author chenlin * @date 2012年6月5日 下午9:16:33 * @version 1.0 */@SuppressWarnings("all")public class GalleryActivity extends Activity { private GalleryView mGallery; private int mResIds[] = { R.drawable.pic_1, R.drawable.pic_2, R.drawable.pic_3, R.drawable.pic_4, R.drawable.pic_5, R.drawable.pic_6, R.drawable.pic_7, R.drawable.pic_8 }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_gallery); mGallery = (GalleryView) findViewById(R.id.galleryView); mGallery.setAdapter(new GalleryAdapter()); } private class GalleryAdapter extends BaseAdapter { LruCachemCache ; String key = "key"; public GalleryAdapter(){ if (mCache == null) { // 最大使用的内存空间 int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4); mCache = new LruCache (maxSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } }; } } @Override public int getCount() { return mResIds.length; } @Override public Object getItem(int position) { return mResIds[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView iv = null; if (convertView == null) { iv = new ImageView(GalleryActivity.this); }else { iv = (ImageView) convertView; } Bitmap bm = mCache.get(key); if (bm == null) { bm = ImageUtil.getReverseBitmapById(GalleryActivity.this, mResIds[position]); }else { mCache.put(key, bm); } //去除锯齿 BitmapDrawable bd = new BitmapDrawable(bm); bd.setAntiAlias(true); iv.setImageDrawable(bd); LayoutParams params = new LayoutParams(160, 260); iv.setLayoutParams(params); iv.setPadding(0, 0, 10, 0); iv.setScaleType(ScaleType.FIT_XY); return iv; } }}
2、自定义gallery视图
/** * @描述 TODO * @项目名称 App_imooc * @包名 com.android.imooc.async * @类名 GalleryView * @author chenlin * @date 2012年6月5日 下午9:14:48 * @version 1.0 */@SuppressWarnings("all")public class GalleryView extends Gallery { private static final String TAG = "gallery"; private int mGalleryCenterX = 0; private int mMaxAngle = 50;// 最大旋转角度 private Camera mCamera; public GalleryView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GalleryView(Context context) { this(context, null); } public GalleryView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setStaticTransformationsEnabled(true); mCamera = new Camera(); } /** * 实现子view的变化效果 Transformation指定当前item的效果 */ @Override protected boolean getChildStaticTransformation(View child, Transformation t) { int rotateAngle = 0; // 如果child的中心点与gallery的中心点不一致,需要计算旋转角度 int childCenterX = getChildCenterX(child); if (childCenterX != mGalleryCenterX) { // 两个中心点距离 int distance = mGalleryCenterX - childCenterX; float percent = distance * 1.0f / child.getWidth(); rotateAngle = (int) (percent * mMaxAngle);// 得到旋转的角度 // 因为distance有可能大于图片的宽度,所以得到角度有可能大于最大的角度 if (Math.abs(rotateAngle) > mMaxAngle) { rotateAngle = rotateAngle > 0 ? mMaxAngle : -mMaxAngle; } } //设置变化之前,要把上面的一个动画清除 t.clear(); //设置变化的效果为矩阵类型 t.setTransformationType(Transformation.TYPE_MATRIX); //开始旋转 startAnimate(child, rotateAngle, t); return true; } /** * 开始动画效果 * @param child * @param rotateAngle * @param t */ private void startAnimate(View child, int rotateAngle, Transformation t) { //if (child instanceof ImageView) { ImageView iv = (ImageView) child; int absAngle = Math.abs(rotateAngle); mCamera.save(); //3.实现放大效果 //仔细看图片,发现图片在x,y轴上都没有变化,但有一边缩小,另一边放大,是如何实现的? //这里就要用到了z轴了,只要改变轴的数值就能实现了 mCamera.translate(0, 0, 100); int zoom = -250 + (absAngle * 2); mCamera.translate(0, 0, zoom); //2.设置透明度 (0到255) 255完全显示,中间的absAngle=0,所以没有透明度 iv.setAlpha((int) (255 - absAngle * 2.5)); //3.旋转 mCamera.rotateY(rotateAngle); //4.转换成矩阵 Matrix matrix = t.getMatrix(); //给matrix赋值 mCamera.getMatrix(matrix); //矩阵前乘 matrix.preTranslate(-iv.getWidth()/2, -iv.getHeight()/2); //矩阵后乘 matrix.postTranslate(iv.getWidth()/2, iv.getHeight()/2); mCamera.restore(); //} } /** * gallery中心点 * * @return */ public int getGalleryCenterX() { return this.getWidth() / 2; } /** * child中心点 * * @return */ public int getChildCenterX(View child) { return child.getLeft() + child.getWidth() / 2; } /** * 设置最大旋转角度 * * @param maxAngel */ public void setAngle(int maxAngel) { this.mMaxAngle = maxAngel; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { mGalleryCenterX = getGalleryCenterX(); Logger.i(TAG, "mGalleryCenterX = " + mGalleryCenterX); Logger.i(TAG, "w/2 = " + w / 2); super.onSizeChanged(w, h, oldw, oldh); }}
3、工具类
/** * @描述 图片处理工具 * @项目名称 App_imooc * @包名 com.android.imooc.gallery * @类名 ImageUtil * @author chenlin * @date 2012年9月5日 下午10:05:38 * @version 1.0 */public class ImageUtil { /** * 根据图片id获得有倒影的图片 * @param resId * @return */ public static Bitmap getReverseBitmapById(Context context, int resId) { int padding = context.getResources().getDimensionPixelOffset(R.dimen.image_paddding);//图片的间距 //绘制原图 Bitmap sourceBitmap = BitmapFactory.decodeResource(context.getResources(), resId); //图片的默认矩阵// float[] values = { // 1.0f, 0f, 0f,// 0f, 1.0f, 0f,// 0f, 0f, 1.0f// }; //绘制原图的下一半图片 Matrix matrix = new Matrix(); matrix.setScale(1, -1); //matrix.setValues(values); Bitmap inverseBitmap = Bitmap.createBitmap(sourceBitmap, 0, sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(), sourceBitmap.getHeight()/2, matrix, false); //合成图片 Bitmap groupbBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight() + sourceBitmap.getHeight() / 2 + padding, sourceBitmap.getConfig()); Canvas gCanvas = new Canvas(groupbBitmap); //把原图画在合成图片的上面 gCanvas.drawBitmap(sourceBitmap, 0, 0, null); //以图片的左上角与坐标 gCanvas.drawBitmap(inverseBitmap, 0, sourceBitmap.getHeight() + padding, null); //添加遮罩 Paint paint = new Paint(); TileMode tile = TileMode.CLAMP; LinearGradient shader = new LinearGradient(0, sourceBitmap.getHeight() + padding, 0, groupbBitmap.getHeight(), 0x70ffffff, Color.TRANSPARENT, tile); paint.setShader(shader); paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); gCanvas.drawRect(0, sourceBitmap.getHeight() + padding, sourceBitmap.getWidth(), groupbBitmap.getHeight(), paint); return groupbBitmap; }}
六、源码下载
链接: 密码:kdx8
———————————————————————
(java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万) 有需求者请进站查看,非诚勿扰https://item.taobao.com/item.htm?spm=686.1000925.0.0.4a155084hc8wek&id=555888526201
01.高级架构师四十二个阶段高
02.Java高级系统培训架构课程148课时 03.Java高级互联网架构师课程 04.Java互联网架构Netty、Nio、Mina等-视频教程 05.Java高级架构设计2016整理-视频教程 06.架构师基础、高级片 07.Java架构师必修linux运维系列课程 08.Java高级系统培训架构课程116课时 (送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门) ——————————————————————–转载地址:https://lovoo.blog.csdn.net/article/details/51591580 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
表示我来过!
[***.240.166.169]2024年04月11日 08时46分04秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
CodeForces - 456C Boredom (dp)
2019-04-30
CodeForces - 675A Infinite Sequence(简单数论 细节)
2019-04-30
CodeForces - 1042B Vitamins (思维)
2019-04-30
ACM 2013 长沙区域赛 Collision (几何)
2019-04-30
ACM 2014 鞍山区域赛 E - Hatsune Miku (dp)
2019-04-30
反向传播&梯度下降 的直观理解程序(numpy)
2019-04-30
CodeForces - 931B World Cup (思维 模拟)
2019-04-30
ACM 2017 北京区域赛 J-Pangu and Stones(区间dp)
2019-04-30
mac || Linux 命令行下实现批量重命名
2019-04-30
java常用类 String面试题
2019-04-30
Windows10下的powershell美化教程
2019-04-30
利用ffmpeg合并音频和视频
2019-04-30
刷好老毛子系统进不了老毛子系统后台的解决办法
2019-04-30
Parallels Desktop 16 不能联网的解决办法
2019-04-30
solr修改schema文件(solr修改配置文件)
2019-04-30
select下拉框分组展示插件的使用--(select-mania插件的使用)
2019-04-30