Browse Source

1:恭喜大家,你们终于可以查看Girl大图了。

guiying712 8 years ago
parent
commit
84e3036bd5

+ 1 - 1
build.gradle

@@ -36,7 +36,7 @@ ext {
     //localBuildToolsVersion是gradle.properties中的数据
     buildToolsVersion = localBuildToolsVersion
     compileSdkVersion = 25
-    minSdkVersion = 15
+    minSdkVersion = 16
     targetSdkVersion = 25
     versionCode = 1
     versionName = "1.0"

+ 699 - 0
lib_common/src/main/java/com/guiying/common/widget/PinchImageView.java

@@ -0,0 +1,699 @@
+package com.guiying.common.widget;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+/**
+ * 手势图片控件
+ */
+public class PinchImageView extends android.support.v7.widget.AppCompatImageView {
+
+    //图片缩放动画时间
+    public static final int SCALE_ANIMATOR_DURATION = 200;
+
+    //惯性动画衰减参数
+    public static final float FLING_DAMPING_FACTOR = 0.9f;
+
+    //图片最大放大尺寸
+    private static final float MAX_SCALE = 4f;
+
+
+    //手势状态:自由状态
+    public static final int PINCH_MODE_FREE = 0;
+
+    //手势状态:单指滚动状态
+    public static final int PINCH_MODE_SCROLL = 1;
+
+    //手势状态:多指缩放状态
+    public static final int PINCH_MODE_SCALE = 2;
+
+    //手势状态:禁止手势
+    public static final int PINCH_MODE_NO_PINCH = 3;
+
+
+    //外界点击事件
+    private OnClickListener mOnClickListener;
+
+    //外界长按事件
+    private OnLongClickListener mOnLongClickListener;
+
+
+    //外层变换矩阵,如果是单位矩阵,那么图片是fit center状态
+    private Matrix mOuterMatrix = new Matrix();
+
+    //手势状态,值为PINCH_MODE_FREE,PINCH_MODE_SCROLL,PINCH_MODE_SCALE
+    private int mPinchMode = PINCH_MODE_FREE;
+
+
+    //在单指模式下是上次手指触碰的点
+    //在多指模式下两个缩放控制点的中点
+    private PointF mLastMovePoint = new PointF();
+
+    //缩放模式下图片的缩放中点,这个点是在原图进行内层变换后的点
+    private PointF mScaleCenter = new PointF();
+
+    //缩放模式下的缩放比例,为 外层缩放值 / 开始缩放时两指距离
+    private float mScaleBase = 0;
+
+
+    //矩阵动画,缩放模式把图片的位置大小超出限制之后触发;双击图片放大或缩小时触发
+    private ScaleAnimator mScaleAnimator;
+
+    //滑动产生的惯性动画
+    private FlingAnimator mFlingAnimator;
+
+
+    public PinchImageView(Context context) {
+        super(context);
+        initView();
+    }
+
+    public PinchImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initView();
+    }
+
+    public PinchImageView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initView();
+    }
+
+    private void initView() {
+        //强制设置图片scaleType为matrix
+        super.setScaleType(ScaleType.MATRIX);
+    }
+
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        //在绘制前设置变换矩阵
+        if (getDrawable() != null) {
+            setImageMatrix(getCurrentImageMatrix());
+        }
+        super.onDraw(canvas);
+    }
+
+    @Override
+    public void setOnClickListener(OnClickListener l) {
+        //默认的click会在任何点击情况下都会触发,所以搞成自己的
+        mOnClickListener = l;
+    }
+
+    @Override
+    public void setOnLongClickListener(OnLongClickListener l) {
+        //默认的long click会在任何长按情况下都会触发,所以搞成自己的
+        mOnLongClickListener = l;
+    }
+
+    //不允许设置scaleType,只能用内部设置的matrix
+    @Override
+    public void setScaleType(ScaleType scaleType) {
+    }
+
+
+    //获取外部矩阵
+    public Matrix getOuterMatrix() {
+        return new Matrix(mOuterMatrix);
+    }
+
+    //获取内部矩阵,换了图之后如果图片大小不一样,会重新计算个新的从而保证fit center状态
+    //返回的是copy值
+    public Matrix getInnerMatrix() {
+        Matrix result = new Matrix();
+        if (getDrawable() != null) {
+            //控件大小
+            float displayWidth = getMeasuredWidth();
+            float displayHeight = getMeasuredHeight();
+            if (displayWidth > 0 && displayHeight > 0) {
+                //原图大小
+                float imageWidth = getDrawable().getIntrinsicWidth();
+                float imageHeight = getDrawable().getIntrinsicHeight();
+                if (imageWidth > 0 && imageHeight > 0) {
+                    float scale;
+                    //如果计算fit center状态所需的scale大小
+                    if (imageWidth / imageHeight > displayWidth / displayHeight) {
+                        scale = displayWidth / imageWidth;
+                    } else {
+                        scale = displayHeight / imageHeight;
+                    }
+                    //设置fit center状态的scale和位置
+                    result.postScale(scale, scale, imageWidth / 2f, imageHeight / 2f);
+                    result.postTranslate((displayWidth - imageWidth) / 2f, (displayHeight - imageHeight) / 2f);
+                }
+            }
+        }
+        return result;
+    }
+
+    //获取图片总变换矩阵
+    public Matrix getCurrentImageMatrix() {
+        Matrix result = getInnerMatrix();
+        result.postConcat(mOuterMatrix);
+        return result;
+    }
+
+    //获取当前图片变换后的矩形,如果没有图片则返回null
+    public RectF getImageBound() {
+        if (getDrawable() == null) {
+            return null;
+        } else {
+            Matrix matrix = getCurrentImageMatrix();
+            RectF bound = new RectF(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
+            matrix.mapRect(bound);
+            return bound;
+        }
+    }
+
+    //获取当前手势状态
+    public int getPinchMode() {
+        return mPinchMode;
+    }
+
+    //开始手势禁止模式
+    public void startNoPinch() {
+        mPinchMode = PINCH_MODE_NO_PINCH;
+        mLastMovePoint = new PointF();
+        mScaleCenter = new PointF();
+        mScaleBase = 0;
+    }
+
+    public boolean animFromTo(Matrix matrixFrom, Matrix matrixTo) {
+        if (getDrawable() == null) {
+            return false;
+        }
+        if (mScaleAnimator != null) {
+            mScaleAnimator.cancel();
+            mScaleAnimator = null;
+        }
+        if (mFlingAnimator != null) {
+            mFlingAnimator.cancel();
+            mFlingAnimator = null;
+        }
+        mScaleAnimator = new ScaleAnimator(matrixFrom, matrixTo);
+        mScaleAnimator.start();
+        return true;
+    }
+
+    //停止手势禁止
+    public void endNoPinch() {
+        mPinchMode = PINCH_MODE_FREE;
+    }
+
+    //停止所有动画,重置位置到fit center状态
+    public void reset() {
+        mOuterMatrix = new Matrix();
+        onOuterMatrixChanged();
+        mPinchMode = PINCH_MODE_FREE;
+        mLastMovePoint = new PointF();
+        mScaleCenter = new PointF();
+        mScaleBase = 0;
+        if (mScaleAnimator != null) {
+            mScaleAnimator.cancel();
+            mScaleAnimator = null;
+        }
+        if (mFlingAnimator != null) {
+            mFlingAnimator.cancel();
+            mFlingAnimator = null;
+        }
+        invalidate();
+    }
+
+
+    //获取图片最大可放大的比例,如果放大大于这个比例则不被允许
+    protected float getMaxScale() {
+        return MAX_SCALE;
+    }
+
+    //计算双击之后图片应该被缩放的比例,如果值大于getMaxScale或者小于fit center尺寸,则取边界值
+    protected float calculateNextScale(float innerScale, float outerScale) {
+        float currentScale = innerScale * outerScale;
+        if (currentScale < MAX_SCALE) {
+            return MAX_SCALE;
+        } else {
+            return innerScale;
+        }
+    }
+
+    //当外层矩阵变换时触发
+    protected void onOuterMatrixChanged() {
+        //用于超大图分片加载
+    }
+
+
+    //点击,双击,长按,滑动等手势处理
+    private GestureDetector mGestureDetector = new GestureDetector(PinchImageView.this.getContext(), new GestureDetector.SimpleOnGestureListener() {
+        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+            fling(velocityX, velocityY);
+            return true;
+        }
+
+        public void onLongPress(MotionEvent e) {
+            if (mOnLongClickListener != null) {
+                mOnLongClickListener.onLongClick(PinchImageView.this);
+            }
+        }
+
+        public boolean onDoubleTap(MotionEvent e) {
+            doubleTap(e.getX(), e.getY());
+            return true;
+        }
+
+        public boolean onSingleTapConfirmed(MotionEvent e) {
+            if (mOnClickListener != null) {
+                mOnClickListener.onClick(PinchImageView.this);
+            }
+            return true;
+        }
+    });
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        super.onTouchEvent(event);
+        if (mPinchMode == PINCH_MODE_NO_PINCH) {
+            return true;
+        }
+        //无论如何都处理各种外部手势
+        mGestureDetector.onTouchEvent(event);
+        int action = event.getAction() & MotionEvent.ACTION_MASK;
+        //最后一个点抬起或者取消,结束所有模式
+        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+            if (mPinchMode == PINCH_MODE_SCALE) {
+                scaleEnd();
+            }
+            mPinchMode = PINCH_MODE_FREE;
+        } else if (action == MotionEvent.ACTION_POINTER_UP) {
+            if (mPinchMode == PINCH_MODE_SCALE) {
+                //抬起的点如果大于2,那么缩放模式还有效,但是有可能初始点变了,重新测量初始点
+                if (event.getPointerCount() > 2) {
+                    //如果还没结束缩放模式,但是第一个点抬起了,那么让第二个点和第三个点作为缩放控制点
+                    if (event.getAction() >> 8 == 0) {
+                        saveScaleContext(event.getX(1), event.getY(1), event.getX(2), event.getY(2));
+                        //如果还没结束缩放模式,但是第二个点抬起了,那么让第一个点和第三个点作为缩放控制点
+                    } else if (event.getAction() >> 8 == 1) {
+                        saveScaleContext(event.getX(0), event.getY(0), event.getX(2), event.getY(2));
+                    }
+                }
+            }
+            //第一个点按下,开启滚动模式,记录开始滚动的点
+        } else if (action == MotionEvent.ACTION_DOWN) {
+            //在矩阵动画过程中不允许启动滚动模式
+            if (!(mScaleAnimator != null && mScaleAnimator.isRunning())) {
+                //停止惯性滚动
+                if (mFlingAnimator != null) {
+                    mFlingAnimator.cancel();
+                    mFlingAnimator = null;
+                }
+                mPinchMode = PINCH_MODE_SCROLL;
+                mLastMovePoint.set(event.getX(), event.getY());
+            }
+            //非第一个点按下,关闭滚动模式,开启缩放模式,记录缩放模式的一些初始数据
+        } else if (action == MotionEvent.ACTION_POINTER_DOWN) {
+            //停止惯性滚动
+            if (mFlingAnimator != null) {
+                mFlingAnimator.cancel();
+                mFlingAnimator = null;
+            }
+            //停止矩阵动画
+            if (mScaleAnimator != null) {
+                mScaleAnimator.cancel();
+                mScaleAnimator = null;
+            }
+            mPinchMode = PINCH_MODE_SCALE;
+            saveScaleContext(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
+        } else if (action == MotionEvent.ACTION_MOVE) {
+            if (!(mScaleAnimator != null && mScaleAnimator.isRunning())) {
+                //在滚动模式下移动
+                if (mPinchMode == PINCH_MODE_SCROLL) {
+                    //每次移动产生一个差值累积到图片位置上
+                    scrollBy(event.getX() - mLastMovePoint.x, event.getY() - mLastMovePoint.y);
+                    //记录新的移动点
+                    mLastMovePoint.set(event.getX(), event.getY());
+                    //在缩放模式下移动
+                } else if (mPinchMode == PINCH_MODE_SCALE && event.getPointerCount() > 1) {
+                    //两个缩放点间的距离
+                    float distance = MathUtils.getDistance(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
+                    //保存缩放点中点
+                    float[] lineCenter = MathUtils.getCenterPoint(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
+                    mLastMovePoint.set(lineCenter[0], lineCenter[1]);
+                    //处理缩放
+                    scale(mScaleCenter, mScaleBase, distance, mLastMovePoint);
+                }
+            }
+        }
+        return true;
+    }
+
+    //让图片移动一段距离,返回是否真的移动了
+    private boolean scrollBy(float xDiff, float yDiff) {
+        if (getDrawable() == null) {
+            return false;
+        }
+        //原图方框
+        RectF bound = getImageBound();
+        //控件大小
+        float displayWidth = getMeasuredWidth();
+        float displayHeight = getMeasuredHeight();
+        //如果当前图片宽度小于控件宽度,则不能移动
+        if (bound.right - bound.left < displayWidth) {
+            xDiff = 0;
+            //如果图片左边在移动后超出控件左边
+        } else if (bound.left + xDiff > 0) {
+            //如果在移动之前是没超出的,计算应该移动的距离
+            if (bound.left < 0) {
+                xDiff = -bound.left;
+                //否则无法移动
+            } else {
+                xDiff = 0;
+            }
+            //如果图片右边在移动后超出控件右边
+        } else if (bound.right + xDiff < displayWidth) {
+            //如果在移动之前是没超出的,计算应该移动的距离
+            if (bound.right > displayWidth) {
+                xDiff = displayWidth - bound.right;
+                //否则无法移动
+            } else {
+                xDiff = 0;
+            }
+        }
+        //以下同理
+        if (bound.bottom - bound.top < displayHeight) {
+            yDiff = 0;
+        } else if (bound.top + yDiff > 0) {
+            if (bound.top < 0) {
+                yDiff = -bound.top;
+            } else {
+                yDiff = 0;
+            }
+        } else if (bound.bottom + yDiff < displayHeight) {
+            if (bound.bottom > displayHeight) {
+                yDiff = displayHeight - bound.bottom;
+            } else {
+                yDiff = 0;
+            }
+        }
+        //应用移动变换
+        mOuterMatrix.postTranslate(xDiff, yDiff);
+        onOuterMatrixChanged();
+        //触发重绘
+        invalidate();
+        //检查是否有变化
+        if (xDiff != 0 || yDiff != 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    //记录缩放前的一些信息
+    private void saveScaleContext(float x1, float y1, float x2, float y2) {
+        mScaleBase = MathUtils.getMatrixScale(mOuterMatrix)[0] / MathUtils.getDistance(x1, y1, x2, y2);
+        //获取缩放缩放点中点在第一层变换后的图片上的坐标
+        float[] center = MathUtils.inverseMatrixPoint(MathUtils.getCenterPoint(x1, y1, x2, y2), mOuterMatrix);
+        mScaleCenter.set(center[0], center[1]);
+    }
+
+    /**
+     * 对图片进行缩放
+     *
+     * @param scaleCenter 图片的缩放中心,是一层变换后的左边
+     * @param scaleBase   缩放比例
+     * @param distance    新的缩放点距离
+     * @param lineCenter  缩放点中心
+     */
+    private void scale(PointF scaleCenter, float scaleBase, float distance, PointF lineCenter) {
+        if (getDrawable() == null) {
+            return;
+        }
+        //计算第二层缩放值
+        float scale = scaleBase * distance;
+        Matrix matrix = new Matrix();
+        //按照图片缩放中心缩放,并且让缩放中心在缩放点中点上
+        matrix.postScale(scale, scale, scaleCenter.x, scaleCenter.y);
+        matrix.postTranslate(lineCenter.x - scaleCenter.x, lineCenter.y - scaleCenter.y);
+        //应用变换
+        mOuterMatrix = matrix;
+        onOuterMatrixChanged();
+        //重绘
+        invalidate();
+    }
+
+    //双击后放大或者缩小
+    //当当前缩放比例大于等于1,那么双击放大到MaxScale
+    //当当前缩放比例小于1,双击放大到1
+    //当当前缩放比例等于MaxScale,双击缩小到屏幕大小
+    private void doubleTap(float x, float y) {
+        //不允许动画过程中再触发
+        if ((mScaleAnimator != null && mScaleAnimator.isRunning()) || getDrawable() == null) {
+            return;
+        }
+        //获取第一层变换矩阵
+        Matrix innerMatrix = getInnerMatrix();
+        //当前总的缩放比例
+        float innerScale = MathUtils.getMatrixScale(innerMatrix)[0];
+        float outerScale = MathUtils.getMatrixScale(mOuterMatrix)[0];
+        float currentScale = innerScale * outerScale;
+        //控件大小
+        float displayWidth = getMeasuredWidth();
+        float displayHeight = getMeasuredHeight();
+        //最大放大大小
+        float maxScale = getMaxScale();
+        //接下来要放大的大小
+        float nextScale = calculateNextScale(innerScale, outerScale);
+        //如果接下来放大大于最大值或者小于fit center值,则取边界
+        if (nextScale < innerScale) {
+            nextScale = innerScale;
+        } else if (nextScale > maxScale) {
+            nextScale = maxScale;
+        }
+        //缩放动画初始矩阵为当前矩阵值
+        Matrix animStart = new Matrix(mOuterMatrix);
+        //开始计算缩放动画的结果矩阵
+        Matrix animEnd = new Matrix(mOuterMatrix);
+        //计算还需缩放的倍数
+        animEnd.postScale(nextScale / currentScale, nextScale / currentScale, x, y);
+        //将放大点移动到控件中心
+        animEnd.postTranslate(displayWidth / 2 - x, displayHeight / 2 - y);
+        //得到放大之后的图片方框
+        Matrix testMatrix = new Matrix(innerMatrix);
+        testMatrix.postConcat(animEnd);
+        RectF testBound = new RectF(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
+        testMatrix.mapRect(testBound);
+        //修正位置
+        float postX = 0;
+        float postY = 0;
+        if (testBound.right - testBound.left < displayWidth) {
+            postX = displayWidth / 2 - (testBound.right + testBound.left) / 2;
+        } else if (testBound.left > 0) {
+            postX = -testBound.left;
+        } else if (testBound.right < displayWidth) {
+            postX = displayWidth - testBound.right;
+        }
+        if (testBound.bottom - testBound.top < displayHeight) {
+            postY = displayHeight / 2 - (testBound.bottom + testBound.top) / 2;
+        } else if (testBound.top > 0) {
+            postY = -testBound.top;
+        } else if (testBound.bottom < displayHeight) {
+            postY = displayHeight - testBound.bottom;
+        }
+        //应用修正位置
+        animEnd.postTranslate(postX, postY);
+        //如果正在执行惯性动画,则取消掉
+        if (mFlingAnimator != null) {
+            mFlingAnimator.cancel();
+            mFlingAnimator = null;
+        }
+        //启动矩阵动画
+        mScaleAnimator = new ScaleAnimator(animStart, animEnd);
+        mScaleAnimator.start();
+    }
+
+    //当缩放操作结束如果不在正确位置用动画恢复
+    private void scaleEnd() {
+        //不允许动画过程中再触发
+        if ((mScaleAnimator != null && mScaleAnimator.isRunning()) || getDrawable() == null) {
+            return;
+        }
+        //是否修正了位置
+        boolean change = false;
+        //获取图片整体的变换矩阵
+        Matrix currentMatrix = getCurrentImageMatrix();
+        //整体缩放比例
+        float currentScale = MathUtils.getMatrixScale(currentMatrix)[0];
+        //第二层缩放比例
+        float outerScale = MathUtils.getMatrixScale(mOuterMatrix)[0];
+        //控件大小
+        float displayWidth = getMeasuredWidth();
+        float displayHeight = getMeasuredHeight();
+        float maxScale = getMaxScale();
+        //比例修正
+        float scalePost = 1;
+        //位置修正
+        float postX = 0;
+        float postY = 0;
+        //如果整体缩放比例大于最大比例,进行缩放修正
+        if (currentScale > maxScale) {
+            scalePost = maxScale / currentScale;
+        }
+        //如果缩放修正后整体导致第二层缩放小于1(就是图片比fit center状态还小),重新修正缩放
+        if (outerScale * scalePost < 1) {
+            scalePost = 1 / outerScale;
+        }
+        //如果修正不为1,说明进行了修正
+        if (scalePost != 1) {
+            change = true;
+        }
+        //尝试根据缩放点进行缩放修正
+        Matrix testMatrix = new Matrix(currentMatrix);
+        testMatrix.postScale(scalePost, scalePost, mLastMovePoint.x, mLastMovePoint.y);
+        RectF testBound = new RectF(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
+        //获取缩放修正后的图片方框
+        testMatrix.mapRect(testBound);
+        //检测缩放修正后位置有无超出,如果超出进行位置修正
+        if (testBound.right - testBound.left < displayWidth) {
+            postX = displayWidth / 2 - (testBound.right + testBound.left) / 2;
+        } else if (testBound.left > 0) {
+            postX = -testBound.left;
+        } else if (testBound.right < displayWidth) {
+            postX = displayWidth - testBound.right;
+        }
+        if (testBound.bottom - testBound.top < displayHeight) {
+            postY = displayHeight / 2 - (testBound.bottom + testBound.top) / 2;
+        } else if (testBound.top > 0) {
+            postY = -testBound.top;
+        } else if (testBound.bottom < displayHeight) {
+            postY = displayHeight - testBound.bottom;
+        }
+        //如果位置修正不为0,说明进行了修正
+        if (postX != 0 || postY != 0) {
+            change = true;
+        }
+        //只有有执行修正才执行动画
+        if (change) {
+            //如果up的时候触发惯性,这里需要取消掉,以修正动画为主
+            if (mFlingAnimator != null) {
+                mFlingAnimator.cancel();
+                mFlingAnimator = null;
+            }
+            //动画开始举证
+            Matrix animStart = new Matrix(mOuterMatrix);
+            //计算结束举证
+            Matrix animEnd = new Matrix(mOuterMatrix);
+            animEnd.postScale(scalePost, scalePost, mLastMovePoint.x, mLastMovePoint.y);
+            animEnd.postTranslate(postX, postY);
+            //启动矩阵动画
+            mScaleAnimator = new ScaleAnimator(animStart, animEnd);
+            mScaleAnimator.start();
+        }
+    }
+
+    private void fling(float vx, float vy) {
+        //以修正动画为大,遇到修正动画正在执行,就不执行惯性动画
+        if (!(mScaleAnimator != null && mScaleAnimator.isRunning()) && getDrawable() != null) {
+            if (mFlingAnimator != null) {
+                mFlingAnimator.cancel();
+                mFlingAnimator = null;
+            }
+            mFlingAnimator = new FlingAnimator(new float[]{vx / 1000 * 16, vy / 1000 * 16});
+            mFlingAnimator.start();
+        }
+    }
+
+    //惯性动画
+    private class FlingAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
+
+        private float[] mVector;
+
+        public FlingAnimator(float[] vector) {
+            super();
+            setFloatValues(0, 1);
+            setDuration(1000000);
+            addUpdateListener(this);
+            mVector = vector;
+        }
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            boolean result = scrollBy(mVector[0], mVector[1]);
+            mVector[0] *= FLING_DAMPING_FACTOR;
+            mVector[1] *= FLING_DAMPING_FACTOR;
+            if (!result || MathUtils.getDistance(0, 0, mVector[0], mVector[1]) < 1) {
+                animation.cancel();
+            }
+        }
+    }
+
+    //缩放动画
+    private class ScaleAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
+
+        private float[] mStart = new float[9];
+        private float[] mEnd = new float[9];
+
+        public ScaleAnimator(Matrix start, Matrix end) {
+            super();
+            setFloatValues(0, 1);
+            setDuration(SCALE_ANIMATOR_DURATION);
+            addUpdateListener(this);
+            start.getValues(mStart);
+            end.getValues(mEnd);
+        }
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            float value = (Float) animation.getAnimatedValue();
+            float[] result = new float[9];
+            for (int i = 0; i < 9; i++) {
+                result[i] = mStart[i] + (mEnd[i] - mStart[i]) * value;
+            }
+            mOuterMatrix.setValues(result);
+            onOuterMatrixChanged();
+            invalidate();
+        }
+    }
+
+    //数学计算工具类
+    private static class MathUtils {
+
+        //获取两点距离
+        public static float getDistance(float x1, float y1, float x2, float y2) {
+            float x = x1 - x2;
+            float y = y1 - y2;
+            return (float) Math.sqrt(x * x + y * y);
+        }
+
+        //获取两点中间点
+        public static float[] getCenterPoint(float x1, float y1, float x2, float y2) {
+            return new float[]{(x1 + x2) / 2f, (y1 + y2) / 2f};
+        }
+
+        //获取矩阵的缩放值
+        public static float[] getMatrixScale(Matrix matrix) {
+            if (matrix != null) {
+                float[] value = new float[9];
+                matrix.getValues(value);
+                return new float[]{value[0], value[4]};
+            } else {
+                return new float[2];
+            }
+        }
+
+        //计算点除以矩阵之后的值
+        public static float[] inverseMatrixPoint(float[] point, Matrix matrix) {
+            if (point != null && matrix != null) {
+                float[] dst = new float[2];
+                Matrix inverse = new Matrix();
+                matrix.invert(inverse);
+                inverse.mapPoints(dst, point);
+                return dst;
+            } else {
+                return new float[2];
+            }
+        }
+    }
+}

+ 4 - 0
module_girls/src/main/AndroidManifest.xml

@@ -5,6 +5,10 @@
         <activity
             android:name=".main.GirlsActivity"
             android:screenOrientation="portrait" />
+        <activity
+            android:name=".girl.GirlActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/AppTheme.NoActionBar" />
     </application>
 
 </manifest>

+ 3 - 0
module_girls/src/main/java/com/guiying/girls/Constants.java

@@ -10,4 +10,7 @@ public interface Constants {
      */
     String GAN_HUO_API = "http://gank.io/api/data/";
 
+    String INTENT_GIRLS = "girls";
+    String INTENT_INDEX = "index";
+
 }

+ 54 - 0
module_girls/src/main/java/com/guiying/girls/girl/GirlActivity.java

@@ -0,0 +1,54 @@
+package com.guiying.girls.girl;
+
+import android.os.Bundle;
+import android.support.v4.view.ViewPager;
+import android.view.WindowManager;
+
+import com.guiying.common.base.BaseActivity;
+import com.guiying.girls.Constants;
+import com.guiying.girls.data.bean.Girls;
+
+import java.util.List;
+
+/**
+ * <p> </p>
+ *
+ * @author 张华洋 2017/5/19 20:24
+ * @version V1.1
+ * @name GirlActivity
+ */
+public class GirlActivity extends BaseActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+        if (getIntent() != null) {
+            List<Girls> mData = getIntent().getParcelableArrayListExtra(Constants.INTENT_GIRLS);
+            int mCurrentIndex = getIntent().getIntExtra(Constants.INTENT_INDEX, 0);
+            ViewPager viewPager = new ViewPager(this);
+            setContentView(viewPager);
+            GirlAdapter adapter = new GirlAdapter(this, mData);
+            viewPager.setAdapter(adapter);
+            viewPager.setCurrentItem(mCurrentIndex);
+            viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+                @Override
+                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+                }
+
+                @Override
+                public void onPageSelected(int position) {
+
+                }
+
+                @Override
+                public void onPageScrollStateChanged(int state) {
+
+                }
+            });
+        }
+    }
+
+}

+ 76 - 0
module_girls/src/main/java/com/guiying/girls/girl/GirlAdapter.java

@@ -0,0 +1,76 @@
+package com.guiying.girls.girl;
+
+import android.content.Context;
+import android.support.v4.view.PagerAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.bumptech.glide.Glide;
+import com.guiying.common.widget.PinchImageView;
+import com.guiying.girls.R;
+import com.guiying.girls.data.bean.Girls;
+
+import java.util.List;
+
+/**
+ * <p> </p>
+ *
+ * @author 张华洋 2017/5/19 20:31
+ * @version V1.1
+ * @name GirlAdapter
+ */
+public class GirlAdapter extends PagerAdapter {
+
+    private Context mContext;
+    private List<Girls> mData;
+    private LayoutInflater layoutInflater;
+    private View mCurrentView;
+
+    public GirlAdapter(Context context, List<Girls> data) {
+        mContext = context;
+        mData = data;
+        layoutInflater = LayoutInflater.from(this.mContext);
+    }
+
+    @Override
+    public int getCount() {
+        if (mData == null) {
+            return 0;
+        }
+        return mData.size();
+    }
+
+    @Override
+    public void setPrimaryItem(ViewGroup container, int position, Object object) {
+        super.setPrimaryItem(container, position, object);
+        mCurrentView = (View) object;
+    }
+
+    @Override
+    public View instantiateItem(ViewGroup container, int position) {
+        final String imageUrl = mData.get(position).getUrl();
+        View view = layoutInflater.inflate(R.layout.item_girl_detail, null, false);
+        PinchImageView imageView = (PinchImageView) view.findViewById(R.id.girl_image);
+        Glide.with(mContext)
+                .load(imageUrl)
+                .thumbnail(0.2f)
+                .into(imageView);
+        container.addView(view);
+        return view;
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object object) {
+        container.removeView((View) object);
+    }
+
+    @Override
+    public boolean isViewFromObject(View view, Object object) {
+        return view == object;
+    }
+
+    public View getPrimaryItem() {
+        return mCurrentView;
+    }
+}

+ 10 - 1
module_girls/src/main/java/com/guiying/girls/main/GirlsView.java

@@ -1,6 +1,8 @@
 package com.guiying.girls.main;
 
 import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.ActivityOptionsCompat;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.widget.SwipeRefreshLayout;
 import android.support.v7.widget.StaggeredGridLayoutManager;
@@ -9,8 +11,11 @@ import android.view.View;
 import android.view.ViewStub;
 import android.widget.FrameLayout;
 
+import com.guiying.common.utils.Utils;
+import com.guiying.girls.Constants;
 import com.guiying.girls.R;
 import com.guiying.girls.data.bean.Girls;
+import com.guiying.girls.girl.GirlActivity;
 import com.jude.easyrecyclerview.EasyRecyclerView;
 import com.jude.easyrecyclerview.adapter.BaseViewHolder;
 import com.jude.easyrecyclerview.adapter.RecyclerArrayAdapter;
@@ -69,7 +74,11 @@ public class GirlsView extends FrameLayout implements GirlsContract.View, SwipeR
         mAdapter.setOnMyItemClickListener(new GirlsAdapter.OnMyItemClickListener() {
             @Override
             public void onItemClick(int position, BaseViewHolder holder) {
-
+                Intent intent = new Intent(Utils.getActivity(GirlsView.this), GirlActivity.class);
+                intent.putParcelableArrayListExtra(Constants.INTENT_GIRLS, mData);
+                intent.putExtra(Constants.INTENT_INDEX, position);
+                ActivityOptionsCompat options = ActivityOptionsCompat.makeScaleUpAnimation(holder.itemView, holder.itemView.getWidth() / 2, holder.itemView.getHeight() / 2, 0, 0);
+                Utils.getActivity(GirlsView.this).startActivity(intent, options.toBundle());
             }
         });
 

+ 4 - 0
module_girls/src/main/module/AndroidManifest.xml

@@ -18,6 +18,10 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".girl.GirlActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/AppTheme.NoActionBar" />
     </application>
 
 </manifest>

+ 14 - 0
module_girls/src/main/res/layout/item_girl_detail.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#000000">
+
+    <com.guiying.common.widget.PinchImageView
+        android:id="@+id/girl_image"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:scaleType="fitCenter" />
+
+</FrameLayout>

+ 0 - 2
module_main/src/main/java/com/guiying/main/MainActivity.java

@@ -12,9 +12,7 @@ import com.guiying.common.base.BaseApplication;
 
 public class MainActivity extends BaseActivity implements View.OnClickListener {
 
-
     private long exitTime = 0;
-
     protected Button newsButton;
     protected Button girlsButton;
 

+ 1 - 1
module_main/src/main/res/values/strings.xml

@@ -1,6 +1,6 @@
 <resources>
     <string name="app_main">main组件</string>
 
-    <string name="app_exit_hint">再按一次退出程序哦~</string>
+    <string name="app_exit_hint">再按一次退出程序</string>
 
 </resources>