最近在做Gallery画廊效果时,搜索大量资料,发现很多博主都是2012年写的文章。对于现在的sdk版本,发现拿过来都没有用,效果变形:
非常遗憾,中间的图变形了,或者说没有把转角恢复。
查阅了大量资料后,发现,
4.0以下的版本,调用的是下面的方法:
@Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
mMatrix = t.getMatrix();
if (android.os.Build.VERSION.SDK_INT > 15) {
return false;
} else {
<span style="white-space:pre"> </span>//操作代码
}
}
4.0以上版本,调用的是下面的方法:
/**
* 核心方法
* @param canvas
* @param child
* @param drawingTime
* @return
*/
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean ret;
//Android SDK 4.1
if (android.os.Build.VERSION.SDK_INT > 15) {
//操作代码
} else {
ret = super.drawChild(canvas, child, drawingTime);
}
return ret;
}
修改后兼容4.0以上版本得完整代码:
package com.znke.tv3_test.widgets;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Transformation;
import android.widget.Gallery;
/**
* 兼容4.0以上版本
*/
@SuppressWarnings("deprecation")
public class CommonGallery extends Gallery {
private Camera mCamera;//相机类
private int mGalleryCenterX;//gallery容器的中心点位置
private Matrix mMatrix;
private int mMaxRotationAngle = 60;//最大转动角度
private boolean isNeedScrollY = true;//是否需要y轴角度旋转
private float xOffset = 80.0f;
private float yOffset = 70.0f;
private float zOffset = 50.0f;
/**
* 设置特定的参数
* @param xOffset x轴偏移量
* @param yOffset y轴偏移量
* @param zOffset z轴偏移量
* @param isNeedScrollY 是否需要两侧的图片旋转
* @param mMaxRotationAngle 两侧图片旋转的最大角度
*/
public void setGalleryData(float xOffset,float yOffset,float zOffset,boolean isNeedScrollY,int mMaxRotationAngle){
setxOffset(xOffset);
setyOffset(yOffset);
setzOffset(zOffset);
setNeedScrollY(isNeedScrollY);
setmMaxRotationAngle(mMaxRotationAngle);
}
public void setmMaxRotationAngle(int mMaxRotationAngle) {
this.mMaxRotationAngle = mMaxRotationAngle;
}
public void setNeedScrollY(boolean needScrollY) {
isNeedScrollY = needScrollY;
}
public void setxOffset(float xOffset) {
this.xOffset = xOffset;
}
public void setyOffset(float yOffset) {
this.yOffset = yOffset;
}
public void setzOffset(float zOffset) {
this.zOffset = zOffset;
}
public CommonGallery(Context context) {
this(context, null, 0);
}
public CommonGallery(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CommonGallery(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
//支持转换 ,执行getChildStaticTransformation方法
this.setStaticTransformationsEnabled(true);
this.setChildrenDrawingOrderEnabled(true);
mCamera = new Camera();
}
@Override
protected int getChildDrawingOrder(int childCount, int i) {
// Current selected index.
int selectedIndex = getSelectedItemPosition() - getFirstVisiblePosition();
if (selectedIndex < 0) {
return i;
}
if (i < selectedIndex) {
return i;
} else if (i >= selectedIndex) {
return childCount - 1 - i + selectedIndex;
} else {
return i;
}
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mGalleryCenterX = getGalleryCenterX();
super.onSizeChanged(w, h, oldw, oldh);
}
//获取父控件中心点 X 的位置
protected int getGalleryCenterX() {
// + 比 >> 优先级高, x>>1等于x/2,效率高
return ((getWidth() - getPaddingLeft() - getPaddingRight()) >> 1) + getPaddingLeft();
}
//获取 child 中心点 X 的位置
protected int getChildCenterX(View child) {
// + 比 >> 优先级高
return (child.getWidth() >> 1) + child.getLeft();
}
//计算 child 偏离 父控件中心的 offset 值, -1 <= offset <= 1
protected float calculateOffsetOfCenter(View view) {
final int pCenter = getGalleryCenterX();
final int cCenter = getChildCenterX(view);
float offset = (cCenter - pCenter) / (pCenter * 1.0f);
//下面两步操作处理,把offset拉回到[-1,1]之间,防止越界
offset = Math.min(offset, 1.0f);//当超出1时,取1
offset = Math.max(offset, -1.0f);//当小于-1时,取-1
return offset;
}
/**
* 计算该view对应的角度
*
* @param child
* @return
*/
private int calculateAngle(View child) {
int rotateAngle = 0;
//获取child的中心点
int childCenterX = getChildCenterX(child);
//如果当前child不在中心点,计算出对应的偏移的角度
if (childCenterX != mGalleryCenterX) {
// 两个中心点距离
int distance = mGalleryCenterX - childCenterX;
float percent = distance * 1.0f / child.getWidth();
rotateAngle = (int) (percent * mMaxRotationAngle);// 得到旋转的角度
// 因为distance有可能大于图片的宽度,所以得到角度有可能大于最大的角度
if (Math.abs(rotateAngle) > mMaxRotationAngle) {
rotateAngle = rotateAngle > 0 ? mMaxRotationAngle : -mMaxRotationAngle;
}
}
return rotateAngle;
}
/**
* 核心方法
* @param canvas
* @param child
* @param drawingTime
* @return
*/
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean ret;
//Android SDK 4.1
if (android.os.Build.VERSION.SDK_INT > 15) {
//计算child与中心点的偏移量
final float offset = calculateOffsetOfCenter(child);
transformImageBitmap(child,offset);
child.setAlpha(1 - Math.abs(offset));
final int saveCount = canvas.save();
canvas.concat(mMatrix);
ret = super.drawChild(canvas, child, drawingTime);
canvas.restoreToCount(saveCount);
} else {
ret = super.drawChild(canvas, child, drawingTime);
}
return ret;
}
@Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
mMatrix = t.getMatrix();
if (android.os.Build.VERSION.SDK_INT > 15) {
return false;
} else {
/**
* 兼容老版本,VERSION <= 15 才执行
*/
//设置变化之前,要把上面的一个动画清除
t.clear();
//设置变化的效果为矩阵类型
t.setTransformationType(Transformation.TYPE_MATRIX);
//计算child与中心点的偏移量
final float offset = calculateOffsetOfCenter(child);
transformImageBitmap(child,offset);
//设置 alpha 变换
t.setAlpha(1 - Math.abs(offset));
return true;
}
}
/**
* 图像变换
* @param child
* @param offset
*/
private void transformImageBitmap(View child,float offset){
//获取child的宽高的一半
final int halfWidth = getChildCenterX(child);
final int halfHeight = child.getMeasuredHeight() >> 1;
mCamera.save();
if(isNeedScrollY){
//是否需要角度旋转
if (halfWidth == mGalleryCenterX) {
//正中间的childView
mCamera.rotateY(0);
} else {
//两侧的childView
int rotateAngle = calculateAngle(child);
mCamera.rotateY(rotateAngle);
}
}
// 平移 X、Y、Z 轴已达到立体效果
mCamera.translate(-offset * xOffset, yOffset, Math.abs(offset) * zOffset);
//也可设置旋转效果
mCamera.getMatrix(mMatrix);
//以 child 的中心点变换
mMatrix.preTranslate(-halfWidth, -halfHeight);
mMatrix.postTranslate(halfWidth, halfHeight);
mCamera.restore();
}
}
需要说明的是:
1、有的需求对两侧图片的要求是不需要转角,
2、有的对中间图片的大小有要求
3、有的对旁边图片的距离和层叠效果有要求
其实无非就是修改最大转角、是否需要转角、x轴偏移、y轴偏移、z轴偏移等5个参数的修改,故我提供方法:
/**
* 设置特定的参数
* @param xOffset x轴偏移量
* @param yOffset y轴偏移量
* @param zOffset z轴偏移量
* @param isNeedScrollY 是否需要两侧的图片旋转
* @param mMaxRotationAngle 两侧图片旋转的最大角度
*/
public void setGalleryData(float xOffset,float yOffset,float zOffset,boolean isNeedScrollY,int mMaxRotationAngle){
setxOffset(xOffset);
setyOffset(yOffset);
setzOffset(zOffset);
setNeedScrollY(isNeedScrollY);
setmMaxRotationAngle(mMaxRotationAngle);
}
其他的逻辑代码都是通用的,根据你自己的需求,只需要设置好这5个参数就可以了。