Android实现 经过手势随意缩放、移动ImageView图片
听说写博客可以丰富简历?好吧,正好无聊,我也写写看~
先放上一张效果图:
在这里,我对自己的笔记本全屏截图,然后当作自定义ImageView的
src内容放在真机上运行。
可以看到这里的图片是可以移动和缩放的。
在这里先说清一点,如果在xml的控件上设置src,则需要在代码上
通过getDrawable();获取,如果是通过setBackGround的,则通过
getBackground();获取即可。
- public class MyImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,
- View.OnTouchListener {
我在这里实现了一些待会会用到的接口。
- /**
- * 控件宽度
- */
- private int mWidth;
- /**
- * 控件高度
- */
- private int mHeight;
- /**
- * 拿到src的图片
- */
- private Drawable mDrawable;
- /**
- * 图片宽度(使用前判断mDrawable是否null)
- */
- private int mDrawableWidth;
- /**
- * 图片高度(使用前判断mDrawable是否null)
- */
- private int mDrawableHeight;
- /**
- * 初始化缩放值
- */
- private float mScale;
- /**
- * 双击图片的缩放值
- */
- private float mDoubleClickScale;
- /**
- * 最大的缩放值
- */
- private float mMaxScale;
- /**
- * 最小的缩放值
- */
- private float mMinScale;
- private ScaleGestureDetector scaleGestureDetector;
- /**
- * 当前有着缩放值、平移值的矩阵。
- */
- private Matrix matrix;
这些是我定义出来的一些成员变量,每个变量我都写上了作用。
- public MyImageView(Context context) {
- this(context, null);
- }
- public MyImageView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setOnTouchListener(this);
- scaleGestureDetector = new ScaleGestureDetector(context, this);
- initListener();
- }
这里是三个标准的构造器,直接用短的引用长的就是了。
先看一看initListener();干了什么事情。
- /**
- * 初始化事件监听
- */
- private void initListener() {
- // 强制设置模式
- setScaleType(ScaleType.MATRIX);
- // 添加观察者
- getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- // 移除观察者
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- // 获取控件大小
- mWidth = getWidth();
- mHeight = getHeight();
- //通过getDrawable获得Src的图片
- mDrawable = getDrawable();
- if (mDrawable == null)
- return;
- mDrawableWidth = mDrawable.getIntrinsicWidth();
- mDrawableHeight = mDrawable.getIntrinsicHeight();
- initImageViewSize();
- moveToCenter();
- }
- });
- }
这里唯一要注意的是我在初始化监听这个方法内,强制了ImageView的scaleType
模式为:MATRIX,因为等会会用到矩阵来缩放和平移,因此强制一下它的scaleT-
ype。
initImageViewSize();这个方法,光看名字就知道要初始化图片大小,来看一看怎么
样去初始化的。
- /**
- * 初始化资源图片宽高
- */
- private void initImageViewSize() {
- if (mDrawable == null)
- return;
- // 缩放值
- float scale = 1.0f;
- // 图片宽度大于控件宽度,图片高度小于控件高度
- if (mDrawableWidth > mWidth && mDrawableHeight < mHeight)
- scale = mWidth * 1.0f / mDrawableWidth;
- // 图片高度度大于控件宽高,图片宽度小于控件宽度
- else if (mDrawableHeight > mHeight && mDrawableWidth < mWidth)
- scale = mHeight * 1.0f / mDrawableHeight;
- // 图片宽度大于控件宽度,图片高度大于控件高度
- else if (mDrawableHeight > mHeight && mDrawableWidth > mWidth)
- scale = Math.min(mHeight * 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);
- // 图片宽度小于控件宽度,图片高度小于控件高度
- else if (mDrawableHeight < mHeight && mDrawableWidth < mWidth)
- scale = Math.min(mHeight * 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);
- mScale = scale;
- mMaxScale = mScale * 8.0f;
- mMinScale = mScale * 0.5f;
- }
先判断一下有没有src资源,没有的话,这个方法调用也没意义了。
这里是要图片的宽或高其中一边充满着屏幕,在最后的三句话,意思就是,
首先,我们假设初始化缩放后的图片面积是A,允许它的最大放大倍数为8A,
最小缩小倍数为0.5A,就是这个意思,(是基于初始化缩放后的面积,而不是
原图的面积,想想这是笔记本的屏幕面积,很大吧!)
moveToCenter();这个方法之前,先看一下不调用这个方法的效果图会咋样。
scaleType为MATRIX属性的图片都是不经过缩放直接显示在屏幕左上角,
这时候肯定会有童鞋问,咦,刚刚不是缩放过么,我这时候只能说,孩子,
你太天真了,那只是理论上的缩放值,还没经过实操呢(滑稽)。
好吧,接下来就看看moveToCenter();做了什么事情,
- /**
- * 移动控件中间位置
- */
- private void moveToCenter() {
- final float dx = mWidth / 2 - mDrawableWidth / 2;
- final float dy = mHeight / 2 - mDrawableHeight / 2;
- matrix = new Matrix();
- // 平移至中心
- matrix.postTranslate(dx, dy);
- // 以控件中心作为缩放
- matrix.postScale(mScale, mScale, mWidth / 2, mHeight / 2);
- setImageMatrix(matrix);
- }
dx的意思是取横方向上,控件中心到图片中心的值,如果大于0就向右移动,
反之向左移动相应的绝对值。
dy则换成纵向方向就是了。
在这里实例化了matrix对象(初始化一次就行),至于为什么只需要初始化一次,
因为图片的缩放值和平移值,都是通过matrix保存的,如果再一次初始化,缩放值
和平移值等等数据都会被清空。
我是先让它平时到控件正中心,然后以控件中心缩放mScale,mScale在initImageViewSize();的时候已经赋值了。
至于先缩放后平移,应该也是可以得,但可能计算公式相对麻烦些,
在这里本着方便为主的原则,就不再作计算了。
接下来会说到这个东西scaleGestureDetector = new ScaleGestureDetector(context, this);
通过这个方法,实现了监听事件,是手势滑动的监听事件。
- @Override
- public boolean onScale(ScaleGestureDetector detector) {
- return true;
- }
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- return true;
- }
- @Override
- public void onScaleEnd(ScaleGestureDetector detector) {
- }
- setOnTouchListener(this);
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return scaleGestureDetector.onTouchEvent(event);
- }
然后手势生效了(呵呵哒)。
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- return true;
- }
现在重点说一下onScale,因为这个方法是处理手势的缩放,
- @Override
- public boolean onScale(ScaleGestureDetector detector) {
- if (mDrawable == null) {
- return true;
- }
- // 系统定义的缩放值
- float scaleFactor = detector.getScaleFactor();
- // 获取已经缩放的值
- float scale = getmScale();
- float scaleResult = scale * scaleFactor;
- if (scaleResult >= mMaxScale && scaleFactor > 1.0f)
- scaleFactor = mMaxScale / scale;
- if (scaleResult <= mMinScale && scaleFactor < 1.0f)
- scaleFactor = mMinScale / scale;
- matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
- /。。。
- <span style="white-space:pre"> setImageMatrix(matrix);</span>
- }
- /**
- * @return 当前缩放的值
- */
- private float getmScale() {
- float[] floats = new float[9];
- matrix.getValues(floats);
- return floats[Matrix.MSCALE_X];
- }
看效果图后,会觉得比较奇葩,因为缩小的时候,位置好像偏了!(原本是在控件正中心)。
- /**
- * @param matrix 矩阵
- * @return matrix的 l t b r 和width,height
- */
- private RectF getRectf(Matrix matrix) {
- RectF f = new RectF();
- if (mDrawable == null)
- return null;
- f.set(0, 0, mDrawableWidth, mDrawableHeight);
- matrix.mapRect(f);
- return f;
- }
在将上面的/。。。补上
- <span style="white-space:pre"> </span>RectF f = getRectf(matrix);
- float dX = 0.0f;
- float dY = 0.0f;
- // 图片高度大于控件高度
- if (f.height() >= mHeight) {
- // 图片顶部出现空白
- if (f.top > 0) {
- // 往上移动
- dY = -f.top;
- }
- // 图片底部出现空白
- if (f.bottom < mHeight) {
- // 往下移动
- dY = mHeight - f.bottom;
- }
- }
- // 图片宽度大于控件宽度
- if (f.width() >= mWidth) {
- // 图片左边出现空白
- if (f.left > 0) {
- // 往左边移动
- dX = -f.left;
- }
- // 图片右边出现空白
- if (f.right < mWidth) {
- // 往右边移动
- dX = mWidth - f.right;
- }
- }
- if (f.width() < mWidth) {
- dX = mWidth / 2 - f.right + f.width() / 2;
- }
- if (f.height() < mHeight) {
- dY = mHeight / 2 - f.bottom + f.height() / 2;
- }
- matrix.postTranslate(dX, dY);
- setImageMatrix(matrix);
首先获取矩阵matrix的N维并赋值在f身上。
在这里其实是细分为2种情况(横竖类似的合一)
以纵向为例:
- // 图片高度大于控件高度
- if (f.height() >= mHeight) {
- // 图片顶部出现空白
- if (f.top > 0) {
- // 往上移动
- dY = -f.top;
- }
- // 图片底部出现空白
- if (f.bottom < mHeight) {
- // 往下移动
- dY = mHeight - f.bottom;
- }
- }
大概就是这个意思:当图片高度大于等于控件高度的时候,坚决不让控件高度方向上出现白色位置,此时,假设当图片和控件高度完全相同的时候,是不是图片的纵向刚好和控件完全重叠呢?
再看第二种情况:
- if (f.height() < mHeight) {
- dY = mHeight / 2 - f.bottom + f.height() / 2;
- }
之前说到,当图片和控件高度完全相同的时候,是不是图片的纵向刚好和控件完全重叠呢?其实,这句话不应该用假设句,而是肯定句,所以,想想,如果图片纵向恰好小于控件高度那么一点点,是不是图片纵向上瞬间就被移动到控件的中间呢?
这种情况的横向方向的道理完全一致,在此也说明一个道理,可以把复杂的事情细分了处理,反正方法是顺着执行的。
这样,图片的缩放就处理完了。
现在说一下移动图片:
- private float downX;
- private float downY;
- private float nowMovingX;
- private float nowMovingY;
- private float lastMovedX;
- private float lastMovedY;
- private boolean isFirstMoved = false;
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- switch (event.getAction() & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN:
- isFirstMoved = false;
- downX = event.getX();
- downY = event.getY();
- break;
- case MotionEvent.ACTION_POINTER_DOWN:
- isFirstMoved = false;
- break;
- case MotionEvent.ACTION_MOVE:
- nowMovingX = event.getX();
- nowMovingY = event.getY();
- if (!isFirstMoved) {
- isFirstMoved = true;
- lastMovedX = nowMovingX;
- lastMovedY = nowMovingY;
- }
- float dX = 0.0f;
- float dY = 0.0f;
- RectF rectf = getRectf(matrix);
- // 判断滑动方向
- final float scrollX = nowMovingX - lastMovedX;
- // 判断滑动方向
- final float scrollY = nowMovingY - lastMovedY;
- // 图片高度大于控件高度
- if (rectf.height() > mHeight && canSmoothY()) {
- dY = nowMovingY - lastMovedY;
- }
- // 图片宽度大于控件宽度
- if (rectf.width() > mWidth && canSmoothX()) {
- dX = nowMovingX - lastMovedX;
- }
- matrix.postTranslate(dX, dY);
- remedyXAndY(dX,dY);
- lastMovedX = nowMovingX;
- lastMovedY = nowMovingY;
- break;
- case MotionEvent.ACTION_UP:
- break;
- case MotionEvent.ACTION_POINTER_UP:
- isFirstMoved = false;
- break;
- }
- return scaleGestureDetector.onTouchEvent(event);
- }
MotionEvent.ACTION_POINTER_DOWN;这个也是压下的时候,区别在于只有不是第一根手指压下的时候才执行,
所以,我在压下的动作都初始化isFirstMoved=false;
当移动的时候,ACTION_MOVE也会执行。
由于移动的时候处理逻辑少的问题,出现屏幕越界后明显的白边反弹,因此在这里编辑了一部分代码。。。
滑动前,先判断能否滑动,滑动后,再次判断是否越界,因此,有效解决了白边反弹现象。
- /**
- * 判断x方向上能不能滑动
- * @return 可以滑动返回true
- */
- private boolean canSmoothX(){
- RectF rectf = getRectf(matrix);
- if (rectf.left >0 || rectf.right <getWidth())
- return false;
- return true;
- }
- /**
- * 判断y方向上可不可以滑动
- * @return 可以滑动返回true
- */
- private boolean canSmoothY(){
- RectF rectf = getRectf(matrix);
- if (rectf.top>0 || rectf.bottom < getHeight())
- return false;
- return true;
- }
- /**
- * 纠正出界的横和众线
- * @param dx 出界偏移的横线
- * @param dy 出街便宜的众线
- */
- private void remedyXAndY(float dx,float dy){
- if (!canSmoothX())
- matrix.postTranslate(-dx,0);
- if (!canSmoothY())
- matrix.postTranslate(0,-dy);
- setImageMatrix(matrix);
- }
完整的自定义控件代码:
- package com.test.gesturedemo.view;
- import android.content.Context;
- import android.graphics.Matrix;
- import android.graphics.RectF;
- import android.graphics.drawable.Drawable;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.ScaleGestureDetector;
- import android.view.View;
- import android.view.ViewTreeObserver;
- import android.widget.ImageView;
- /**
- * Created by 13798 on 2016/6/3.
- */
- public class MyImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener {
- /**
- * 控件宽度
- */
- private int mWidth;
- /**
- * 控件高度
- */
- private int mHeight;
- /**
- * 拿到src的图片
- */
- private Drawable mDrawable;
- /**
- * 图片宽度(使用前判断mDrawable是否null)
- */
- private int mDrawableWidth;
- /**
- * 图片高度(使用前判断mDrawable是否null)
- */
- private int mDrawableHeight;
- /**
- * 初始化缩放值
- */
- private float mScale;
- /**
- * 双击图片的缩放值
- */
- private float mDoubleClickScale;
- /**
- * 最大的缩放值
- */
- private float mMaxScale;
- /**
- * 最小的缩放值
- */
- private float mMinScale;
- private ScaleGestureDetector scaleGestureDetector;
- /**
- * 当前有着缩放值、平移值的矩阵。
- */
- private Matrix matrix;
- public MyImageView(Context context) {
- this(context, null);
- }
- public MyImageView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setOnTouchListener(this);
- scaleGestureDetector = new ScaleGestureDetector(context, this);
- initListener();
- }
- /**
- * 初始化事件监听
- */
- private void initListener() {
- // 强制设置模式
- setScaleType(ScaleType.MATRIX);
- // 添加观察者
- getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- // 移除观察者
- getViewTreeObserver().removeGlobalOnLayoutListener(this);
- // 获取控件大小
- mWidth = getWidth();
- mHeight = getHeight();
- //通过getDrawable获得Src的图片
- mDrawable = getDrawable();
- if (mDrawable == null)
- return;
- mDrawableWidth = mDrawable.getIntrinsicWidth();
- mDrawableHeight = mDrawable.getIntrinsicHeight();
- initImageViewSize();
- moveToCenter();
- }
- });
- }
- /**
- * 初始化资源图片宽高
- */
- private void initImageViewSize() {
- if (mDrawable == null)
- return;
- // 缩放值
- float scale = 1.0f;
- // 图片宽度大于控件宽度,图片高度小于控件高度
- if (mDrawableWidth > mWidth && mDrawableHeight < mHeight)
- scale = mWidth * 1.0f / mDrawableWidth;
- // 图片高度度大于控件宽高,图片宽度小于控件宽度
- else if (mDrawableHeight > mHeight && mDrawableWidth < mWidth)
- scale = mHeight * 1.0f / mDrawableHeight;
- // 图片宽度大于控件宽度,图片高度大于控件高度
- else if (mDrawableHeight > mHeight && mDrawableWidth > mWidth)
- scale = Math.min(mHeight * 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);
- // 图片宽度小于控件宽度,图片高度小于控件高度
- else if (mDrawableHeight < mHeight && mDrawableWidth < mWidth)
- scale = Math.min(mHeight * 1.0f / mDrawableHeight, mWidth * 1.0f / mDrawableWidth);
- mScale = scale;
- mMaxScale = mScale * 8.0f;
- mMinScale = mScale * 0.5f;
- }
- /**
- * 移动控件中间位置
- */
- private void moveToCenter() {
- final float dx = mWidth / 2 - mDrawableWidth / 2;
- final float dy = mHeight / 2 - mDrawableHeight / 2;
- matrix = new Matrix();
- // 平移至中心
- matrix.postTranslate(dx, dy);
- // 以控件中心作为缩放
- matrix.postScale(mScale, mScale, mWidth / 2, mHeight / 2);
- setImageMatrix(matrix);
- }
- /**
- * @return 当前缩放的值
- */
- private float getmScale() {
- float[] floats = new float[9];
- matrix.getValues(floats);
- return floats[Matrix.MSCALE_X];
- }
- /**
- * @param matrix 矩阵
- * @return matrix的 l t b r 和width,height
- */
- private RectF getRectf(Matrix matrix) {
- RectF f = new RectF();
- if (mDrawable == null)
- return null;
- f.set(0, 0, mDrawableWidth, mDrawableHeight);
- matrix.mapRect(f);
- return f;
- }
- @Override
- public boolean onScale(ScaleGestureDetector detector) {
- if (mDrawable == null) {
- return true;
- }
- // 系统定义的缩放值
- float scaleFactor = detector.getScaleFactor();
- // 获取已经缩放的值
- float scale = getmScale();
- float scaleResult = scale * scaleFactor;
- if (scaleResult >= mMaxScale && scaleFactor > 1.0f)
- scaleFactor = mMaxScale / scale;
- if (scaleResult <= mMinScale && scaleFactor < 1.0f)
- scaleFactor = mMinScale / scale;
- matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
- RectF f = getRectf(matrix);
- float dX = 0.0f;
- float dY = 0.0f;
- // 图片高度大于控件高度
- if (f.height() >= mHeight) {
- // 图片顶部出现空白
- if (f.top > 0) {
- // 往上移动
- dY = -f.top;
- }
- // 图片底部出现空白
- if (f.bottom < mHeight) {
- // 往下移动
- dY = mHeight - f.bottom;
- }
- }
- // 图片宽度大于控件宽度
- if (f.width() >= mWidth) {
- // 图片左边出现空白
- if (f.left > 0) {
- // 往左边移动
- dX = -f.left;
- }
- // 图片右边出现空白
- if (f.right < mWidth) {
- // 往右边移动
- dX = mWidth - f.right;
- }
- }
- if (f.width() < mWidth) {
- dX = mWidth / 2 - f.right + f.width() / 2;
- }
- if (f.height() < mHeight) {
- dY = mHeight / 2 - f.bottom + f.height() / 2;
- }
- matrix.postTranslate(dX, dY);
- setImageMatrix(matrix);
- return true;
- }
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- return true;
- }
- @Override
- public void onScaleEnd(ScaleGestureDetector detector) {
- float scale = getmScale();
- if (scale < mScale) {
- matrix.postScale(mScale / scale, mScale / scale, mWidth / 2, mHeight / 2);
- setImageMatrix(matrix);
- }
- }
- private float downX;
- private float downY;
- private float nowMovingX;
- private float nowMovingY;
- private float lastMovedX;
- private float lastMovedY;
- private boolean isFirstMoved = false;
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- switch (event.getAction() & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN:
- isFirstMoved = false;
- downX = event.getX();
- downY = event.getY();
- break;
- case MotionEvent.ACTION_POINTER_DOWN:
- isFirstMoved = false;
- break;
- case MotionEvent.ACTION_MOVE:
- nowMovingX = event.getX();
- nowMovingY = event.getY();
- if (!isFirstMoved) {
- isFirstMoved = true;
- lastMovedX = nowMovingX;
- lastMovedY = nowMovingY;
- }
- float dX = 0.0f;
- float dY = 0.0f;
- RectF rectf = getRectf(matrix);
- // 判断滑动方向
- final float scrollX = nowMovingX - lastMovedX;
- // 判断滑动方向
- final float scrollY = nowMovingY - lastMovedY;
- // 图片高度大于控件高度
- if (rectf.height() > mHeight && canSmoothY()) {
- dY = nowMovingY - lastMovedY;
- }
- // 图片宽度大于控件宽度
- if (rectf.width() > mWidth && canSmoothX()) {
- dX = nowMovingX - lastMovedX;
- }
- matrix.postTranslate(dX, dY);
- remedyXAndY(dX,dY);
- lastMovedX = nowMovingX;
- lastMovedY = nowMovingY;
- break;
- case MotionEvent.ACTION_UP:
- break;
- case MotionEvent.ACTION_POINTER_UP:
- isFirstMoved = false;
- break;
- }
- return scaleGestureDetector.onTouchEvent(event);
- }
- /**
- * 判断x方向上能不能滑动
- * @return 可以滑动返回true
- */
- private boolean canSmoothX(){
- RectF rectf = getRectf(matrix);
- if (rectf.left >0 || rectf.right <getWidth())
- return false;
- return true;
- }
- /**
- * 判断y方向上可不可以滑动
- * @return 可以滑动返回true
- */
- private boolean canSmoothY(){
- RectF rectf = getRectf(matrix);
- if (rectf.top>0 || rectf.bottom < getHeight())
- return false;
- return true;
- }
- /**
- * 纠正出界的横和众线
- * @param dx 出界偏移的横线
- * @param dy 出街便宜的众线
- */
- private void remedyXAndY(float dx,float dy){
- if (!canSmoothX())
- matrix.postTranslate(-dx,0);
- if (!canSmoothY())
- matrix.postTranslate(0,-dy);
- setImageMatrix(matrix);
- }
- }
activity的xml
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <com.test.gesturedemo.view.MyImageView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="matrix"
- android:src="@mipmap/tt1"/>
- </RelativeLayout>
相关推荐
- Android:实现最容易的单指移动、双指缩放的图片组件
- Android实现 经过手势随意缩放、移动ImageView图片
- android兑现手势缩放、移动图片
- Android经过手势实现的缩放处理
- Android 自定义ImageView添加手势后,移动或缩放如何判断越界的有关问题
- Android手势滑动实现两点触摸缩放图片
- Android实现ImageView图片缩放和拖动
- Android-imageView实现图片的旋转跟缩放
- Android手势控制实现缩放、移动图片
- Android实现通过手势控制图片大小缩放的方法
- 关于ImageView.setBackgroundResource()步骤不显示图片
- Android高效率ImageLoader的实现