最近在做Gallery画廊效果时,搜索大量资料,发现很多博主都是2012年写的文章。对于现在的sdk版本,发现拿过来都没有用,效果变形:

android PageSnapHelper实现画廊效果_偏移量


非常遗憾,中间的图变形了,或者说没有把转角恢复。

查阅了大量资料后,发现,


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 PageSnapHelper实现画廊效果_偏移量_02