今天来实现一个3D画廊的效果,这个效果的难点在于计算旋转角度,当然里面会有好几个知识点要讲,针对Paint的2个方法,一个是setShader(),一个是setXfermode(),首先看下大概的效果,
大概是这种,这是我在网上随便找了一个类似的图片,因为我的效果还没写,没关系,这没啥影响,这个效果我准备分开写,然后后面合成起来,上面的效果可以分为如下几步
1:首先是怎么截取一张图片中的一部分
2:怎么把多张图片合成一张图片
3:怎么生成倒影效果,
4:怎么改变倒影中的图片透明度
5:最后一步是怎么计算它滑动时候的旋转角度问题
首先把第一步的效果实现出来,就是怎么截图一张图片中的一部分,如图:
现在开始写代码
public class MainActivity extends Activity { private ImageView iv,originalIv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.iv); originalIv = (ImageView) findViewById(R.id.originalIv); originalIv.setImageResource(R.mipmap.gird); Bitmap bitmap = compoundBitmap(R.mipmap.gird); iv.setImageBitmap(bitmap); } /** * 截图图片 * @param resId 图片 id */ public Bitmap compoundBitmap(int resId){ Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(),resId);//把资源图片变成一个Bitmap对象 //生成下面的一半图片 Matrix matrix = new Matrix(); Bitmap invertBitmap = Bitmap.createBitmap(originalBitmap,0,originalBitmap.getHeight()/2,originalBitmap.getWidth(),originalBitmap.getHeight()/2,matrix,false); return invertBitmap; } }
效果:第一个而是原图 第二张是截图一半的图,为了进行更好的对比,
上面就使用了一个Bitmap的createBitmap方法就可以实现截图了,现在对createBitmap的方法参数进行说明
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)
参数说明:
source:原位图
x:位图的x轴坐标
y:位图的y轴坐标
width:新位图的宽度
height:新位图的高度
m:矩阵
filter:这个参数比较难懂 先看下google官网文档介绍
true if the source should be filtered. * Only applies if the matrix contains more than just * translation
当进行的不只是平移变换时,filter参数为true可以进行滤波处理,有助于改善新图像质量;flase时,计算机不做过滤处理
画图解释下:
现在怎么考虑把这二张图片合成一张图片了,这个要用到Canvas了,之前我们canvas是从自定义view中的onDraw()方法给我们提供的,现在我们要创建一个画布,然后在这画布上把这二张图片画上去,
代码如下:
/** * 合成图片 * @param resId 图片 id */ public Bitmap compoundBitmap(int resId){ Paint paint = new Paint(); Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(),resId);//把资源图片变成一个Bitmap对象 //生成下面的一半图片 Matrix matrix = new Matrix(); Bitmap invertBitmap = Bitmap.createBitmap(originalBitmap,0,originalBitmap.getHeight()/2,originalBitmap.getWidth(),originalBitmap.getHeight()/2,matrix,false); //创建一个空的位图 Bitmap compoundBitmap = Bitmap.createBitmap(originalBitmap.getWidth(),originalBitmap.getHeight()+invertBitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(compoundBitmap); canvas.drawBitmap(originalBitmap,0,0,paint); canvas.drawBitmap(invertBitmap,0,originalBitmap.getHeight(),paint); return compoundBitmap; }
效果:
ok,现在我们实现了2张图片怎么合成一张图片,首先是先绘制一个空的位图,这个位图的宽和高要设置好,然后通过canvas的drawBitmap()把这二张图片绘制到画布上去,绘制上要注意坐标点就行,还有个问题就是截图的一半图片不是垂直的拼接在下面,上面我们讲截图图片一部分的方法createBitmap()方法进行参数说明的时候,其中有一个Matirs矩阵,当时为什么要使用这个方法呢?Bitmap的createBitmap有很多重载的方法,就是因为提供了Matris类,让我们可以对位图进行像动画那样操作,现在看下Matirs一些常用的方法:
我们发现Matris提供了平移,缩放,旋转,透明等操作,我们只要加入一行代码就可以让下面的一半图片改成在原来的图片上进行垂直后显示,
matrix.setScale(1,-1);
public void setScale(float sx, float sy)
参数说明:
sx:x轴水平翻转 -1表示翻转 1表示不翻转
sy:y轴方向翻转 -1表示翻转
1表示不翻转
/** * 合成图片 * @param resId 图片 id */ public Bitmap compoundBitmap(int resId){ Paint paint = new Paint(); Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(),resId);//把资源图片变成一个Bitmap对象 //生成下面的一半图片 Matrix matrix = new Matrix(); matrix.setScale(1,-1); Bitmap invertBitmap = Bitmap.createBitmap(originalBitmap,0,originalBitmap.getHeight()/2,originalBitmap.getWidth(),originalBitmap.getHeight()/2,matrix,false); //创建一个空的位图 Bitmap compoundBitmap = Bitmap.createBitmap(originalBitmap.getWidth(),originalBitmap.getHeight()+invertBitmap.getHeight()+10, Bitmap.Config.ARGB_8888);//+10是为了2张图片之间有空隙 Canvas canvas = new Canvas(compoundBitmap); canvas.drawBitmap(originalBitmap,0,0,paint); canvas.drawBitmap(invertBitmap,0,originalBitmap.getHeight()+10,paint); return compoundBitmap; }
效果:
现在我们把前三步实现了,还有个倒影图片的透明度问题,这就使用到Paint中的setShader()方法了,这是设置画笔的颜色渲染方法,发现Shader有5个子类,每一个子类实现的功能效果不一样,
Shader的直接子类以及作用:
BitmapShader : 位图图像渲染
LinearGradient : 线性渲染
RadialGradient : 环形渲染
SweepGradient : 扫描渐变渲染/梯度渲染
ComposeShader : 组合渲染,可以和其他几个子类组合起来使用
现在举例讲每个大概的功能,见识下平时效果使我们想不到是通过这个api实现的,
BitmapShader
先看下它的构造函数:
public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY)
参数说明:
bitmap:渲染器使用的位图
tileX:在位图x轴方向渲染器的平铺模式
tileY:在位图y轴方向渲染器的平铺模式
渲染器的模式有如下三种:
public enum TileMode { /** * replicate the edge color if the shader draws outside of its * original bounds */ CLAMP (0), /** * repeat the shader‘s image horizontally and vertically */ REPEAT (1), /** * repeat the shader‘s image horizontally and vertically, alternating * mirror images so that adjacent images always seam */ MIRROR (2); TileMode(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; }
CLAMP:超过边缘部分会在超过边缘范围给染色 也就是拉伸 比如imageview大小为100,100,但是bitmap大小为80,80如果使用了这个模式,会把其余的20都给染色
REPEAT:横向和纵向的重复渲染器图片,平铺
MIRROR:横向不断翻转重复,纵向不断翻转重复
我们平时使用比较多的圆角图片也可以使用这个实现
public class CustomImageView extends View { private Paint mPaint; private ShapeDrawable mShapeDrawable; private Bitmap shaderBitmap; private int bitmapWidth; private int bitmapHeight; private BitmapShader mBitmapShader; public CustomImageView(Context context) { this(context, null); } public CustomImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mShapeDrawable = new ShapeDrawable(new OvalShape()); mShapeDrawable.getPaint().setShader(mBitmapShader); //这是设置一个矩形区域 表示这个drawable所绘制的区域 mShapeDrawable.setBounds(20, 20, bitmapWidth, bitmapHeight); mShapeDrawable.draw(canvas); } public void setImageRes(int resId){ shaderBitmap = BitmapFactory.decodeResource(getContext().getResources(),resId); bitmapWidth = shaderBitmap.getWidth(); bitmapHeight = shaderBitmap.getHeight(); mBitmapShader = new BitmapShader(shaderBitmap, Shader.TileMode.MIRROR,Shader.TileMode.REPEAT); } }
效果:
其实还有别的图形模式,比如圆角矩形
public ShapeDrawable(Shape s) { this(new ShapeState(null), null); mShapeState.mShape = s; }
多个Shape子类
这样好像看起来不容易理解,现在再举个例子,我先准备一张图片,
public class CustomView extends View { private Paint mPaint; public CustomView(Context context) { this(context,null); } public CustomView(Context context, AttributeSet attrs) { this(context, attrs,0); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test); // 设置shader BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); mPaint.setShader(shader); // 用设置好的画笔绘制一个矩形 mPaint.setColor(Color.GREEN); canvas.drawRect(0, 0, 1000, 1000, mPaint); mPaint.reset(); } }
我x轴和y轴都是重复这张图片的,效果如下:
我现在把x轴改为MIRROR也就是镜像,y轴不变,
这就是x轴镜像y轴重复这个图片的效果,现在就剩下一个模式没讲就是CLAMP,就是边缘像素拉伸,
为了演示这个效果,要换一张图片,
我现在把x轴改为REPEAT,y轴改为CLAMP,效果如下:
如果x,y轴都是clamp模式也就是边缘拉伸,如下
我现在再把图片边缘颜色改下,这下总可以知道这个模式是干嘛的吧,‘
x,y轴方向都CLAMP模式,效果如下:
这个黑色没显示出来,是因为我没画好,好了,这个模式我相信我已经讲的很清楚了,为了弄清这个花了很久的时间去想怎么写出来才看的懂这个模式,
LinearGradient
这个是线性渐变颜色渲染器
它有2个构造函数
public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile)
参数说明:
x0: 渐变起始点x坐标
y0:渐变起始点y坐标
x1:渐变结束点x坐标
y1:渐变结束点y坐标
color0: 起始渐变色
color1: 结束渐变色
tile: 渲染器平铺模式
这就是2个点起始点为(x0,y0)终点为(x1,y1) 起始点的颜色为color0,终点的颜色为color1
现在写个例子再理解上面几个坐标
public class CustomImageView extends View { private Paint mPaint; private LinearGradient linearGradient,linearGradient1,linearGradient2; public CustomImageView(Context context) { this(context, null); } public CustomImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); linearGradient = new LinearGradient(0, 0, 100, 100,Color.RED , Color.YELLOW, Shader.TileMode.REPEAT); linearGradient1 = new LinearGradient(0, 0, 0, 100,Color.RED , Color.YELLOW, Shader.TileMode.REPEAT); linearGradient2= new LinearGradient(0, 0, 100, 0,Color.RED , Color.YELLOW, Shader.TileMode.REPEAT); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //设置渲染器 mPaint.setShader(linearGradient); canvas.drawColor(Color.BLUE); canvas.drawRect(0, 0, 300, 300, mPaint); mPaint.setShader(linearGradient1); canvas.drawRect(0, 350, 300, 650, mPaint); mPaint.setShader(linearGradient2); canvas.drawRect(0, 700, 300, 1000, mPaint); } }
效果:
看下第二个和第三个LinearGradient构造函数中给的值
linearGradient1 = new LinearGradient(0, 0, 0, 100,Color.RED , Color.YELLOW, Shader.TileMode.REPEAT); linearGradient2= new LinearGradient(0, 0, 100, 0,Color.RED , Color.YELLOW, Shader.TileMode.REPEAT);
分析图:
分析图二:
关于它的最好一个参数模式在讲BitmapShader已经讲的很清楚了,不在这多讲,
首先看下它的构造函数,
public RadialGradient(float centerX, float centerY, float radius,int centerColor, int edgeColor, TileMode tileMode)
参数说明:
centerX:圆心的x轴坐标
centerY:圆心的y轴坐标
radius:圆的半径
centerColor:圆心的颜色
edgeColor:圆心边缘的颜色
tileMode:平铺模式
public class CustomView extends View { private Paint mPaint; private RadialGradient mRadialGradient; public CustomView(Context context) { this(context,null); } public CustomView(Context context, AttributeSet attrs) { this(context, attrs,0); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); //1.圆心X坐标2.Y坐标3.半径 4.颜色数组 5.相对位置数组,可为null 6.渲染器平铺模式 mRadialGradient = new RadialGradient(240, 360, 60, Color.RED, Color.YELLOW, Shader.TileMode.REPEAT); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.GRAY); mPaint.setShader(mRadialGradient); mPaint.setAntiAlias(true); canvas.drawCircle(240, 360, 200, mPaint); } }
效果图:
通过这个可以做一个垃圾版的水波纹效果,
代码如下:
public class CustomView extends View { private Paint mPaint; private Handler mHandler; private int radius = 20; private int tag = 100001; private RadialGradient mRadialGradient; public CustomView(Context context) { this(context,null); } public CustomView(Context context, AttributeSet attrs) { this(context, attrs,0); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if(msg.what==tag){ if(radius>200){ radius=20; }else{ radius+=5; } invalidate(); } } }; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.GRAY); mRadialGradient = new RadialGradient(240, 360, radius, Color.RED, Color.YELLOW, Shader.TileMode.REPEAT); mPaint.setShader(mRadialGradient); canvas.drawCircle(240, 360, 200, mPaint); mHandler.sendEmptyMessageAtTime(tag,300); } }
RadialGradient还有一个构造函数:
public RadialGradient(float centerX, float centerY, float radius,int colors[], float stops[], TileMode tileMode)
构造函数
centerX:圆心的x轴坐标
cneterY:圆心的y轴坐标
radius:圆的半径
colors:int[] 数组其实就是多个颜色值的集合
stops:该数组中每一个stop对应colors数组中每个颜色在半径中的相对位置,stop取值范围为[0,1],0表示圆心位置,1表示圆周位置。如果stops数组为null,那么Android会自动为colors设置等间距的位置
tileMode:模式
好了 这个有时间再把剩余2个补上,不然这个博客还不知道什么时候写完,现在讲paint另一个很重要的方法
mPaint.setXfermode(Xfermode);
Xfermode就是图形混合模式,它有三个子类
关于这个知识点一时也讲不清楚,准备另外写一篇博客讲解下,在这里贴下代码,自己可以去百度下,
/** * 合成图片 * @param resId 图片 id */ public Bitmap compoundBitmap(int resId){ Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(),resId);//把资源图片变成一个Bitmap对象 //生成下面的一半图片 Matrix matrix = new Matrix(); matrix.setScale(1,-1); Bitmap invertBitmap = Bitmap.createBitmap(originalBitmap,0,originalBitmap.getHeight()/2,originalBitmap.getWidth(),originalBitmap.getHeight()/2,matrix,false); //创建一个空的位图 Bitmap compoundBitmap = Bitmap.createBitmap(originalBitmap.getWidth(),originalBitmap.getHeight()+invertBitmap.getHeight()+10, Bitmap.Config.ARGB_8888);//+10是为了2张图片之间有空隙 Canvas canvas = new Canvas(compoundBitmap); canvas.drawBitmap(originalBitmap,0,0,null); canvas.drawBitmap(invertBitmap,0,originalBitmap.getHeight()+10,null); Paint paint = new Paint(); // 设置渐变颜色 LinearGradient shader = new LinearGradient(0, originalBitmap.getHeight() + 10, 0, compoundBitmap.getHeight(), 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP); paint.setShader(shader); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); canvas.drawRect(0, originalBitmap.getHeight() + 5, originalBitmap.getWidth(), compoundBitmap.getHeight(), paint); return compoundBitmap; }
渐变颜色染色器上面是讲了,现在看下效果,
ok,现在完成了倒影的效果,
public class MainActivity extends Activity { private Gallery gallery; private MyAdapter adapter; private int[] ids ={R.mipmap.a,R.mipmap.b,R.mipmap.c,R.mipmap.d,R.mipmap.e,R.mipmap.f,R.mipmap.g,R.mipmap.h,R.mipmap.i}; private int screenWidth; private int screenHeigh; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getScreenWidthAndHeight(); gallery = (Gallery) findViewById(R.id.gallery); adapter = new MyAdapter(); gallery.setAdapter(adapter); } class MyAdapter extends BaseAdapter { @Override public int getCount() { return ids.length; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { // TODO Auto-generated method stub return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView iv = null; if(convertView == null) { iv = new ImageView(MainActivity.this); } else { iv = (ImageView) convertView; } Bitmap bitmap = Utils.compoundBitmap( MainActivity.this.getResources(), ids[position]); BitmapDrawable bd = new BitmapDrawable(bitmap); bd.setAntiAlias(true); // 消除锯齿 iv.setImageDrawable(bd); Gallery.LayoutParams params = new Gallery.LayoutParams(screenWidth/2, screenHeigh/2); iv.setLayoutParams(params); return iv; } } /** * 获取屏幕的宽和高 */ public void getScreenWidthAndHeight(){ DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); screenWidth = dm.widthPixels; screenHeigh = dm.heightPixels; } }
效果:
现在就是计算滑动时候旋转的角度问题了,看下原型图
发现这有几个效果
1:中间的图片明显比二边的图片要大,
2:中间的图片没有旋转,二边的图片还有旋转
3:在滑动过程中还有二边的图片还有透明的效果
尼玛 鬼做的出来,经过百度再百度,找到一些别人实现好的方法,懂了原理,原来是这样的,首先给别人点赞,太牛逼
这个控件我们是自定义Gallery来实现的,
public class MyGallery extends Gallery { public MyGallery(Context context) { super(context); } public MyGallery(Context context, AttributeSet attrs) { super(context, attrs); } public MyGallery(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected boolean getChildStaticTransformation(View child, Transformation t) { return super.getChildStaticTransformation(child, t); } }
发现什么也没写,对吧 ,是的 在思考,
上面自定义的gallery中重写了protected boolean getChildStaticTransformation(View child, Transformation t)方法,这是表示gallery在滑动过程中返回它item的子视图的效果,
参数说明:
child:就是你gallery中滑动的veiw
t:就是当前item的变化效果,
现在我在adapter中的getView()方法中给每个imageview设置下tag,tag的值就是当前的position,当我们运行起来什么都没干的时候,getChildStaticTransformation()方法打印出来的log
05-26 03:23:16.931 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.931 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.941 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->1
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->1
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:25.644 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:25.644 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->1
05-26 03:23:59.180 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:23:59.180 755-755/? E/MyGalleryHAHA: tag--->
05-26 03:23:59.189 755-755/? E/MyGalleryHAHA: tag--->1
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->1
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->0
进来就发现只显示2个item,当我往右滑动的一个item的时候,也就是说第二个item出现的时候,log
05-26 03:26:25.186 755-755/? E/MyGalleryHAHA: tag--->2
05-26 03:26:25.187 755-755/? E/MyGalleryHAHA: tag--->2
05-26 03:26:25.678 755-755/? E/MyGalleryHAHA: tag--->1
05-26 03:26:25.678 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:26:25.682 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:26:25.682 755-755/? E/MyGalleryHAHA: tag--->1
发现是刚滑动出来的先打印出来,其他没发现有什么规律,
旋转的计算分析图:
gallery中的代码全部如下:
package com.example.customgallery; import android.content.Context; import android.graphics.Camera; import android.graphics.Matrix; import android.util.AttributeSet; import android.view.View; import android.view.animation.Transformation; import android.widget.Gallery; import android.widget.ImageView; /** * Created by admin on 2016/5/26. */ public class MyGallery extends Gallery { private int centerPoint; private static final String TAG = "MyGalleryHAHA"; private Camera mCamera; private int maxRoate = 60;//旋转的最大角度 public MyGallery(Context context) { super(context); mCamera = new Camera(); setStaticTransformationsEnabled(true); } public MyGallery(Context context, AttributeSet attrs) { super(context, attrs); mCamera = new Camera(); setStaticTransformationsEnabled(true); } public MyGallery(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mCamera = new Camera(); setStaticTransformationsEnabled(true); } @Override protected boolean getChildStaticTransformation(View child, Transformation t) { int viewCenterPoint = getItemViewCenterPoint(child); // item的中心点 int rotateAngle = 0; // 默认旋转角度为0 // 如果当前的View的中心点不等于gallery的中心点, 就是两边的图片, 需要计算旋转角度 if(viewCenterPoint != centerPoint) { // gallery中心点 - 图片中心点 = 差值 int diff = centerPoint - viewCenterPoint; // 差值 / 图片的宽度 = 比值 float scale = (float)diff / (float)child.getWidth(); // 比值 * 最大旋转角度 = 最终的旋转角度 rotateAngle = (int) (scale * maxRoate); if(Math.abs(rotateAngle) > maxRoate) { // 当前角度超过了50, 需要赋值到50 或者 -50 rotateAngle = rotateAngle > 0 ? maxRoate : -maxRoate; } } // 设置变换效果之前, 需要把Transformation中的上一个item的变换效果清楚 t.clear(); t.setTransformationType(Transformation.TYPE_MATRIX); // 设置变换效果的类型为矩阵类型 setItemStartAnim((ImageView) child, rotateAngle, t); return true; } /** * 设置变换效果 * @param iv gallery的item * @param rotateAngle 旋转的角度 * @param t 变换的对象 */ private void setItemStartAnim(ImageView iv, int rotateAngle, Transformation t) { mCamera.save(); // 保存状态 int absRotateAngle = Math.abs(rotateAngle); // 取旋转角度的绝对值 // 放大效果 mCamera.translate(0, 0, 100f); // 给摄像机定位 int zoom = -240 +(absRotateAngle * 2); mCamera.translate(0, 0, zoom); // 透明度(中间的图片是完全显示, 两边有一定的透明度) int alpha = (int) (255 - (absRotateAngle * 2.5)); iv.setAlpha(alpha); // 透明度取值范围: 0 ~ 255, 0 就是完全隐藏, 255 完全显示 // 旋转(在中间的图片没有旋转角度, 只要不在中间就有旋转角度) mCamera.rotateY(rotateAngle); Matrix matrix = t.getMatrix(); // 变换的矩阵, 需要把变换的效果添加到矩阵中 // 给matrix赋值 mCamera.getMatrix(matrix); // 把matrix矩阵给camera对象, camera对象就会把上面添加的效果转换成矩阵添加到matrix对象中 // 矩阵前乘 matrix.preTranslate(-iv.getWidth() / 2, -iv.getHeight() / 2); // 矩阵后乘 matrix.postTranslate(iv.getWidth() / 2, iv.getHeight() / 2); mCamera.restore(); // 恢复到之前保存的状态 } /** * 获取gallery的中心点 * @return */ public int getCenterPoint(){ return getWidth()/2; } /** * 获取item view的中心点 */ public int getItemViewCenterPoint(View itemView) { if (itemView != null) { return itemView.getWidth() / 2 + itemView.getLeft(); } return 0; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); centerPoint = getCenterPoint(); } }
在这要注意点:
getChildStaticTransformation()方法的返回值一定要返回true,否则没效果,
最终的效果如下:
写了2天多了,终于写完了!也许写的不够好,但真的尽力了!