天天看點

Android Gallery畫廊 相容4.0以上版本

最近在做Gallery畫廊效果時,搜尋大量資料,發現很多部落客都是2012年寫的文章。對于現在的sdk版本,發現拿過來都沒有用,效果變形:

Android Gallery畫廊 相容4.0以上版本

非常遺憾,中間的圖變形了,或者說沒有把轉角恢複。

查閱了大量資料後,發現,

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個參數就可以了。

Android Gallery畫廊 相容4.0以上版本