最近在做Gallery畫廊效果時,搜尋大量資料,發現很多部落客都是2012年寫的文章。對于現在的sdk版本,發現拿過來都沒有用,效果變形:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jN5kDO1gjMzEzMycDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
非常遺憾,中間的圖變形了,或者說沒有把轉角恢複。
查閱了大量資料後,發現,
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個參數就可以了。