一、效果图:

二、首先实现基本的框架

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、创建布局,在布局里引用这个类

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><com.android.imooc.gallery.GalleryView
        android:id="@+id/galleryView"android:layout_width="match_parent"android:layout_height="match_parent" ></com.android.imooc.gallery.GalleryView></LinearLayout>

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};@Overrideprotected 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 {@Overridepublic int getCount() {return mResIds.length;}@Overridepublic Object getItem(int position) {return mResIds[position];}@Overridepublic long getItemId(int position) {return position;}@Overridepublic 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_IN
paint.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_INpaint.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 * maxDegree

3、分别获得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的效果*/@Overrideprotected 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来存取图片,如何实现?
首先在适配器定义

LruCache<String, Bitmap> mCache ;
String key = "key";

在构造方法里初始化mCache

public GalleryAdapter(){if (mCache == null) {// 最大使用的内存空间int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4);mCache = new LruCache<String, Bitmap>(maxSize) {@Overrideprotected 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};@Overrideprotected 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 {LruCache<String, Bitmap> mCache ;String key = "key";public GalleryAdapter(){if (mCache == null) {// 最大使用的内存空间int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4);mCache = new LruCache<String, Bitmap>(maxSize) {@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes() * value.getHeight();}};}}@Overridepublic int getCount() {return mResIds.length;}@Overridepublic Object getItem(int position) {return mResIds[position];}@Overridepublic long getItemId(int position) {return position;}@Overridepublic 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的效果*/@Overrideprotected 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;}@Overrideprotected 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;}}

六、源码下载

链接:http://pan.baidu.com/s/1boAkuy7 密码: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入门)
——————————————————————–

Android 自定义控件之---3D画廊相关推荐

  1. android viewPage 打造3d画廊

    之前学习时学习了ViewPager打造3d画廊,今天找出来把他贴上去,给需要的小伙伴用!废话不多说直接贴! 一.Activity /*** 3da 画廊*/ public class Main3Act ...

  2. Android自定义控件之3D上下翻页效果的倒计时控件

    这是一个自定义的倒计时控件,具有3D上下翻页翻转效果.最近项目中需要做一个倒计时控件,需要和iOS端的效果保持一样.大致效果是这样的,如下图所示: 由于暂时还不会怎么样制作gif动态图,所以想看具体效 ...

  3. Android自定义控件--图片3D翻转(其他控件或布局可以)

    这是一个基础控件,粘贴过去就可以了 import android.graphics.Camera; import android.graphics.Matrix; import android.vie ...

  4. android3d画廊自动切换,Android实例(一)—— 3D画廊

    3D画廊 之前我都是写的学习的内容,我在写这些教程时遇到有趣的炫酷的小例子也会专门拿出来写一篇文章,今天就写一个酷炫的小例子,叫3D画廊,它是属于ViewPage的进阶版. 下面的指示器是使用的一大神 ...

  5. Android自定义控件三部曲文章索引

    前言:在我从C++转到Android时,就被Android里炫彩斑斓的自定义控件深深折服,想知道如果想利用C++实现这些功能,那是相当困难的.从那时候起,我就想,等我学会了自定义控件,一定要写一篇系列 ...

  6. Android TV Menu 3D星体旋转效果

    在Android中,如果想要实现3D动画效果一般有两种选择:一是使用Open GL ES,二是使用Camera.Open GL ES使用起来太过复杂,一般是用于比较高级的3D特效或游戏,并且这个也不是 ...

  7. android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...

    转载:http://blog.csdn.net/xiabing082/article/details/48781489 1.  大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...

  8. Gallery的基本使用(实现了3D画廊的效果)

    Gallery是一个内部元素可以水平滚动,并且可以把当前选择的子元素定位在它中心的布局组件.下面是一个实现3D画廊的小demo MainActivity.class package com.czh.g ...

  9. Andrid自定义view:打造3D画廊

    昨日朋友给我看了下这样的效果,我觉得很有意思,今日闲下来便写了这篇文章,并且传到了github,本文的末尾也附上了链接.网上有很多关于使用Gallery来打造3D画廊的博客,但是做出来的效果我不是很满 ...

  10. Android自定义控件:打造自己的QQ空间主页

    前面已经实现过仿QQ的List抽屉效果以及仿QQ未读消息拖拽效果,具体请见: Android自定义控件:类QQ抽屉效果 Android自定义控件:类QQ未读消息拖拽效果 趁热打铁,这次我们实现QQ空间 ...

最新文章

  1. 读农民工兄弟学C#文章后的感觉
  2. 【MATLAB】单元数组类型
  3. gitlab添加用户
  4. Gartner分享物联网和智慧城市最新数据
  5. 项目管理——任务分配闲谈
  6. ubuntu 串口调试工具推荐_串口调试能有多便捷?——FUR组件的应用
  7. vuex commit 模块_分享一个Vuex的使用的新姿势
  8. DFA 敏感词过滤算法
  9. Android keyevent值中文表
  10. Android自定义控件开发入门与实战(7)SVG动画,android底层架构
  11. #让我们用python跑回归#Fama-French三因素模型(一)
  12. android 金额输入弹窗,金额输入框_小罗的安卓学习记录的技术博客_51CTO博客
  13. 一级计算机基础知识考试,一级计算机基础知识与应用能力等级考试-20210324100302.doc-原创力文档...
  14. 数字ic验证学习ing
  15. [Android编译(二)] 从谷歌官网下载android 6.0源码、编译并刷入nexus 6p手机
  16. 理解PE文件相对虚拟地址(RVA)到文件偏移的转换
  17. 园林智能灌溉方案优势
  18. unity制作体积光实现丁达尔效应
  19. Spring Security入门基础
  20. Java定时器(实现每月1号、每日、每15分钟自动执行任务)

热门文章

  1. 节点name在graph中无法展示_小节点 · 大奥秘│这正是你想学习的精妙休闲观景空间!(内含视频讲解)...
  2. turtle库使用——谢尔宾斯基三角形
  3. matlab 元胞数组
  4. 工程、技术与工程师三者之间的相互关系
  5. TFS 2010 使用手册(四)备份与恢复
  6. cmake linux windows,Cmake 64位下载_Cmake Windows Linux下载 3.7.1 官网免费版_当载软件站...
  7. CTFSHOW【萌新计划】Writeup
  8. 自学c语言需要什么要求,学习c语言需要什么基础
  9. 第一章 空间解析几何与向量代数(1)
  10. linux多级菜单脚本教程,Linux下使用readline库编程实现多级CLI菜单