Android实现图片查看功能_Android

来源:脚本之家  责任编辑:小易  

一、效果图

二、支持功能

本地与网络图片 可用于各大图片加载框架(Fresco,Glide,Picasso) 图片缩放 放大后的图片惯性滑动 下拉缩小退出

三、核心实现方法

3.1 缩放 Matrix.postScale(float sx, float sy, float px, float py)

参数解析:

sx: 目标宽度 / 现有宽度 sy: 目标高度 / 现有高度 (px,py): 缩放焦点坐标

使用示例:

/**
 * 缩放手势监听
 */
private ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {

  @Override
  public boolean onScale(ScaleGestureDetector detector) {
    float scaleFactor = detector.getScaleFactor();
    float wantScale = mScale * scaleFactor;
    if (wantScale >= MIN_SCALE) {
      mScale = wantScale;
      focusX = detector.getFocusX();
      focusY = detector.getFocusY();
      mMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
      invalidate();
    }
    return true;
  }
};

3.2 移动 Matrix.postTranslate(float dx, float dy)

参数解析:

dx: 目标位置X坐标 - 当前位置X坐标 sy: 目标位置Y坐标 - 当前位置Y坐标

使用示例:

/**
 * 简单手势监听
 */
private GestureDetector.SimpleOnGestureListener mOnGestureListener = new GestureDetector.SimpleOnGestureListener() {

  ...

  @Override
  public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    if (!isAlwaysSingleTouch) {
      return true;
    }
    mMatrix.postTranslate(-distanceX, -distanceY);
    invalidate();
    return false;
  }

  ...
};

3.3 将Matrix的操作关联到ImageView上

View提供onDraw的方法,可以操作到Canvas,Canvas提供concat的方法来关联Matrix。每次针对Matrix有操作之后调用invalidate()刷新一下onDraw()即可。这就是个操作配置,而且是View早就提供好了的配置。

调用示例:

@Override
protected void onDraw(Canvas canvas) {
  int saveCount = canvas.save();
  canvas.concat(mMatrix);
  super.onDraw(canvas);
  canvas.restoreToCount(saveCount);
}

3.4 惯性滑动 

OverScroller.fling(int startX, int startY, int velocityX, int velocityY,int minX, int maxX, int minY, int maxY)

参数解析:

(startX, startY): 初始位置坐标 (velocityX, velocityY): XY方向的初始速度 (minX, maxX, minY, maxY): 限定了移动后的位置边界

使用示例:

/**
 * 惯性滑动工具类
 * 使用fling方法开始滑动
 * 使用stop方法停止滑动
 */
private class FlingUtil implements Runnable {
  private int mLastFlingX = 0;
  private int mLastFlingY = 0;
  private OverScroller mScroller;
  private boolean mEatRunOnAnimationRequest = false;
  private boolean mReSchedulePostAnimationCallback = false;

  /**
   * RecyclerView使用的惯性滑动插值器
   * f(x) = (x-1)^5 + 1
   */
  private final Interpolator sQuinticInterpolator = new Interpolator() {
    @Override
    public float getInterpolation(float t) {
      t -= 1.0f;
      return t * t * t * t * t + 1.0f;
    }
  };

  public FlingUtil() {
    mScroller = new OverScroller(getContext(), sQuinticInterpolator);
  }

  @Override
  public void run() {
    disableRunOnAnimationRequests();
    final OverScroller scroller = mScroller;
    if (scroller.computeScrollOffset()) {
      final int y = scroller.getCurrY();
      int dy = y - mLastFlingY;
      final int x = scroller.getCurrX();
      int dx = x - mLastFlingX;
      mLastFlingY = y;
      mLastFlingX = x;
      constrainScrollBy(dx, dy);
      postOnAnimation();
    }
    enableRunOnAnimationRequests();
  }

  public void fling(int velocityX, int velocityY) {
    mLastFlingX = 0;
    mLastFlingY = 0;
    mScroller.fling(0, 0, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
    postOnAnimation();
  }

  public void stop() {
    removeCallbacks(this);
    mScroller.abortAnimation();
  }

  private void disableRunOnAnimationRequests() {
    mReSchedulePostAnimationCallback = false;
    mEatRunOnAnimationRequest = true;
  }

  private void enableRunOnAnimationRequests() {
    mEatRunOnAnimationRequest = false;
    if (mReSchedulePostAnimationCallback) {
      postOnAnimation();
    }
  }

  void postOnAnimation() {
    if (mEatRunOnAnimationRequest) {
      mReSchedulePostAnimationCallback = true;
    } else {
      removeCallbacks(this);
      ViewCompat.postOnAnimation(ZoomImageView.this, this);
    }
  }
}

Scroller只提供在基于已有位置和已有速度下的位置计算,需要主动调用scroller.getCurrY()和scroller.getCurrX()方法去获取位置信息。
这里使用的是RecyclerView中的惯性滑动的实现方式。

四、三个必要的细节处理

在有了上面的4个方法,基本上一个可缩放的ImageView所需要的功能都可以实现了。但是,一些细节方面的问题也不可忽视,比如说:

移动不能超过图片的边缘

在ImageView的ScaleType为FIT_CENTER时,获取真实的图片内容的宽高,毕竟需要缩放的是图片的内容

图片是否移动到最左侧或最右侧,用于ViewPager中的滑动判断

4.1 边缘处理

在移动前,校验此次的移动是否会造成图片内容是否会移动超出边界。Canvas关联的Matrix是针对整个ImageView的,我们需要知道ImageView中图片部分在ImageView中的初始位置信息,如图:

在得到图片内容在初始状态下的展示区域Rect(a,b,c,d)后,使用Matrix提供的Matrix.mapRect(Rect)方法,可以得到经历缩放后的展示区域。得到内容缩放后的展示区域后,与ImageView的展示区域Rect(0,0,W,H)作比较便可得出是否超出边界。

示例方法:

/**
 * 获得缩放移动后的图片内容的位置区域
 *
 * @param matrix
 * @return RectF
 */
private RectF getScaledRect(Matrix matrix) {
  RectF rectF = new RectF(mImageRectF);
  //转化为缩放后的相对位置
  matrix.mapRect(rectF);
  return rectF;
}

/**
 * 针对边缘问题,约束移动
 *
 * @param dx
 * @param dy
 */
private void constrainScrollBy(float dx, float dy) {
  RectF rectF = getScaledRect(mMatrix);
  float scaleImageWidth = rectF.width();
  float scaleImageHeight = rectF.height();

  if (scaleImageWidth > mWidth) {
    //right
    if (rectF.right + dx < mWidth) {
      dx = -rectF.right + mWidth;
    }
    //left
    if (rectF.left + dx > 0) {
      dx = -rectF.left;
    }
  } else {
    //center
    dx = -rectF.left + ((float) mWidth - scaleImageWidth) / 2;
  }

  if (scaleImageHeight > mHeight) {
    //bottom
    if (rectF.bottom + dy < mHeight) {
      dy = -rectF.bottom + mHeight;
    }
    //top
    if (rectF.top + dy > 0) {
      dy = -rectF.top;
    }
  } else {
    //center
    dy = -rectF.top + ((float) mHeight - scaleImageHeight) / 2;
  }

  mMatrix.postTranslate(dx, dy);
  invalidate();
  checkBorder();
}

4.2 获取ImageView中内容的宽高

针对不同的网络加载框架有不同的操作方式,这里一Fresco位示例:
PipelineDraweeControllerBuilder提供setControllerListener方法,可以设置一个图片加载的监听。

示例代码:

private ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {
  @Override
  public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable anim) {
    if (imageInfo == null) {
      return;
    }
    int preWidth = imageInfo.getWidth();
    int preHeight = imageInfo.getHeight();
    if (preWidth != mWidth || preHeight != mHeight) {
      //获取到最新的图片内容的宽高
      mWidth = preWidth;
      mHeight = preHeight;
    }
  }

  @Override
  public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
    Log.d("zhufeng", "Intermediate image received");
  }

  @Override
  public void onFailure(String id, Throwable throwable) {
    throwable.printStackTrace();
  }
};

public void loadImage(int resizeX, int resizeY, Uri uri) {
  ImageRequest request = ImageRequestBuilder
      .newBuilderWithSource(uri)
      .setResizeOptions(new ResizeOptions(resizeX, resizeY))
      .build();
  PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder().setControllerListener(controllerListener).setOldController(getController()).setImageRequest(request).build();
  setController(controller);
}

4.3 处理与ViewPager的滑动冲突

需要明确:

左滑时,当图片内容到达右侧边界,进行图片切换的处理(事件交由ViewPager处理)

右滑时,当图片内容到达左侧边界,进行图片切换的处理(事件交由ViewPager处理)

剩下的ImageView自己处理

ImageView中的处理:
在约束移动的时候标记图片是否已经触及左右边界。并提供方法:

/**
 * 用于ViewPager滑动拦截
 *
 * @param direction
 * @return
 */
public boolean canScroll(int direction) {
  return !((direction < 0 && isRightSide()) || (direction > 0 && isLeftSide()));
}

ViewPager中的处理:
在canScroll方法中进行状态判断。重写ViewPager:

/**
 * 相册ViewPager
 *
 * @author zhufeng on 2017/10/22
 */
public class AlbumViewPager extends ViewPager {

  ...

  @Override
  protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if (v instanceof ZoomImageView) {
      return ((ZoomImageView) v).canScroll(dx) || super.canScroll(v, checkV, dx, x, y);
    }
    return super.canScroll(v, checkV, dx, x, y);
  }

  ...

}

源码地址:

https://github.com/zhufeng1222/Gallery

到这里就结束啦.

以上就是Android实现图片查看功能的详细内容,更多关于Android 图片查看功能的资料请关注真格学网其它相关文章!

您可能感兴趣的文章:android查看网络图片的实现方法Android图片处理教程之全景查看效果实现Android仿百度图片查看功能Android 简单的图片查看器源码实现android自定义Camera拍照并查看图片Android 通过网络图片路径查看图片实例详解Android 网络图片查看器与网页源码查看器android网络图片查看器简单实现代码Android 实现WebView点击图片查看大图列表及图片保存功能

  • 本文相关:
  • android手势操作简单实例讲解
  • android多线程下载示例详解
  • android layerdrawable使用实例
  • 解决android应用冷启动时出现的白屏问题的方法
  • android编程之绝对布局absolutelayout和相对布局relativelayout实
  • android开发中launcher3常见默认配置修改方法总结
  • android listview里控件添加监听方法的实例详解
  • android开发实现可拖动排序的listview功能【附源码下载】
  • android仿微信单击拍照长按录像功能实例代码
  • android编程实现在底端显示选项卡的方法
  • android如何实现图片预览
  • Android实现图库功能,查看本应用drawable下的所有...
  • android图片查看器
  • android如何实现图片中的功能
  • 想写一个android应用,实现随机显示图片的功能。
  • 安卓上功能强大的图片查看器有哪些?
  • android 如何实现查看原图
  • android怎么调用系统图库查看图片并且设置查看时可...
  • android开发 模仿ios或者小米查看相册图片效果
  • Android怎么实现图片的点击功能,需求描述如下:(...
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全c#教程vbvb.netc 语言java编程delphijavaandroidiosswiftscala易语言汇编语言r语言其它相关首页android查看网络图片的实现方法android图片处理教程之全景查看效果实现android仿百度图片查看功能android 简单的图片查看器源码实现android自定义camera拍照并查看图片android 通过网络图片路径查看图片实例详解android 网络图片查看器与网页源码查看器android网络图片查看器简单实现代码android 实现webview点击图片查看大图列表及图片保存功能android手势操作简单实例讲解android多线程下载示例详解android layerdrawable使用实例解决android应用冷启动时出现的白屏问题的方法android编程之绝对布局absolutelayout和相对布局relativelayout实android开发中launcher3常见默认配置修改方法总结android listview里控件添加监听方法的实例详解android开发实现可拖动排序的listview功能【附源码下载】android仿微信单击拍照长按录像功能实例代码android编程实现在底端显示选项卡的方法一看就懂的android app开发入门教微信公众平台开发入门教程(图文详android基础之使用fragment控制切六款值得推荐的android(安卓)开android textview设置中文字体加android应用开发sharedpreferencandroid 动画之translateanimatiandroid压力测试命令monkey详解android按钮单击事件的四种常用写android调试工具ddms的使用详解android自定义滑动解锁控件使用详解android自定义控件和自定义回调函数步骤示android仿支付宝密码输入框效果android中文件的压缩和解压缩实例代码android开机自启动apk的方法android开发实现浏览器全屏显示功能android实现根据评分添加星级条android系统模拟位置的使用方法android自定义照相机的实例android中定时执行任务的3种实现方法(推荐
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved