Android仿头条、微信大图预览视图的方法详解

图片大图预览

在我现在的项目当中,也存在大图预览的功能,但其实现过于繁重,采用一个Activity实现,并且在图片展示的过程中会产生卡顿感,整体感觉很是不好,正巧项目也在重构过程中,所以决定将这一功能写成一个成型的控件。

话不多说,先上图看下效果。

整体实现思路

图片展示:PhotoView(大图支持双击放大)
图片加载:Glide(加载网络图片、本地图片、资源文件)
小图变大图时的实现:动画
图片的下载:插入系统相册

该控件采用自定义View的方式,通过一些基本的控件的组合,来形成一个具有大图预览的控件。上代码

使用方法

(1)在布局文件中引用该view

<com.demo.gallery.view.GalleryView
 android:id="@+id/photo_gallery_view"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:visibility="gone"
 app:animDuration="300"
 app:saveText="保存至相册"
 app:saveTextColor="#987622"/> 

(2)具体使用方法

GalleryView galleryView = findViewById(R.id.photo_gallery_view);
galleryView.showPhotoGallery(index, List, ImageView);

到这里就结束了,就是这么简单!

具体实现

(1)先从showPhotoGallery(index, List, ImageView)这个方法讲起

int index:我们想要展示的一个图片列表中的第几个
List list: 我们要传入的要展示的图片类型list(支持网络图片、资源图片、本地图片(本地图片与网络图片其实都是一个字符串地址))

public class GalleryPhotoModel {
 public Object photoSource;
 public GalleryPhotoModel(@DrawableRes int drawableRes) {
  this.photoSource = drawableRes;
 }
 public GalleryPhotoModel(String path) {
  this.photoSource = path;
 }
}

ImageView:即你点击想要展示的那个图片

(2)对传入GalleryView的数据进行处理

/**
  * @param index    想要展示的图片的索引值
  * @param photoList   图片集合(URL、Drawable、Bitmap)
  * @param clickImageView 点击的第一个图片
  */
 public void showPhotoGallery(int index, List<GalleryPhotoModel> photoList, ImageView clickImageView) {
  GalleryPhotoParameterModel photoParameter = new GalleryPhotoParameterModel();
  //图片
  photoParameter.photoObj = photoList.get(index).photoSource;
  //图片在list中的索引
  photoParameter.index = index;
  int[] locationOnScreen = new int[2];
  //图片位置参数
  clickImageView.getLocationOnScreen(locationOnScreen);
  photoParameter.locOnScreen = locationOnScreen;
  //图片的宽高
  int width = clickImageView.getDrawable().getBounds().width();
  int height = clickImageView.getDrawable().getBounds().height();
  photoParameter.imageWidth = clickImageView.getWidth();
  photoParameter.imageHeight = clickImageView.getHeight();
  photoParameter.photoHeight = height;
  photoParameter.photoWidth = width;
  //scaleType
  photoParameter.scaleType = clickImageView.getScaleType();
  //将第一个点击的图片参数连同整个图片列表传入
  this.setVisibility(View.VISIBLE);
  post(new Runnable() {
   @Override
   public void run() {
    requestFocus();
   }
  });
  setGalleryPhotoList(photoList, photoParameter);
 }

通过传递进来的ImageView,获取被点击View参数,并拼装成参数model,再进行数据的相关处理。

(3)GalleryView的实现机制

该View的实现思路主要是:最外层是一个RelativeLayout,内部有一个充满父布局的ImageView和ViewPager。ImageView用来进行图片的动画缩放,ViewPager用来进行最后的图片的展示。其实该View最主要的地方就是通过点击ImageView到最后ViewPager的展示的动画。接下来主要是讲解一下这个地方。先看一下被点击ImageView的参数Model。GalleryPhotoParameterModel

public class GalleryPhotoParameterModel {
 //索引
 public int index;
 // 图片的类型
 public Object photoObj;
 // 在屏幕上的位置
 public int[] locOnScreen = new int[]{-1, -1};
 // 图片的宽
 public int photoWidth = 0;
 // 图片的高
 public int photoHeight = 0;
 // ImageView的宽
 public int imageWidth = 0;
 // ImageView的高
 public int imageHeight = 0;
 // ImageView的缩放类型
 public ImageView.ScaleType scaleType;
}

3.1图片放大操作

private void handleZoomAnimation() {
  // 屏幕的宽高
  this.mScreenRect = GalleryScreenUtil.getDisplayPixes(getContext());
  //将被缩放的图片放在一个单独的ImageView上进行单独的动画处理。
  Glide.with(getContext()).load(firstClickItemParameterModel.photoObj).into(mScaleImageView);
  //开启动画
  mScaleImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
    //开始放大操作
    calculateScaleAndStartZoomInAnim(firstClickItemParameterModel);
    //
    mScaleImageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
   }
  });
 }
/**
  * 计算放大比例,开启放大动画
  *
  * @param photoData
  */
 private void calculateScaleAndStartZoomInAnim(final GalleryPhotoParameterModel photoData) {
  mScaleImageView.setVisibility(View.VISIBLE);
  // 放大动画参数
  int translationX = (photoData.locOnScreen[0] + photoData.imageWidth / 2) - (int) (mScreenRect.width() / 2);
  int translationY = (photoData.locOnScreen[1] + photoData.imageHeight / 2) - (int) ((mScreenRect.height() + GalleryScreenUtil.getStatusBarHeight(getContext())) / 2);
  float scale = getImageViewScale(photoData);
  // 开启放大动画
  executeZoom(mScaleImageView, translationX, translationY, scale, true, new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animation) {}

   @Override
   public void onAnimationEnd(Animator animation) {
    showOtherViews();
    tvPhotoSize.setText(String.format("%d/%d", viewPager.getCurrentItem() + 1, photoList.size()));
   }

   @Override
   public void onAnimationCancel(Animator animation) {
   }

   @Override
   public void onAnimationRepeat(Animator animation) {
   }
  });
 }

3.2 图片缩小操作

/**
  * 计算缩小比例,开启缩小动画
  */
 private void calculateScaleAndStartZoomOutAnim() {
  hiedOtherViews();

  // 缩小动画参数
  int translationX = (firstClickItemParameterModel.locOnScreen[0] + firstClickItemParameterModel.imageWidth / 2) - (int) (mScreenRect.width() / 2);
  int translationY = (firstClickItemParameterModel.locOnScreen[1] + firstClickItemParameterModel.imageHeight / 2) - (int) ((mScreenRect.height() + GalleryScreenUtil.getStatusBarHeight(getContext())) / 2);
  float scale = getImageViewScale(firstClickItemParameterModel);
  // 开启缩小动画
  executeZoom(mScaleImageView, translationX, translationY, scale, false, new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animation) {}

   @Override
   public void onAnimationEnd(Animator animation) {
    mScaleImageView.setImageDrawable(null);
    mScaleImageView.setVisibility(GONE);
    setVisibility(GONE);
   }

   @Override
   public void onAnimationCancel(Animator animation) {}

   @Override
   public void onAnimationRepeat(Animator animation) {}
  });
 }

3.3 计算图片缩放的比例

private float getImageViewScale(GalleryPhotoParameterModel photoData) {
  float scale;
  float scaleX = photoData.imageWidth / mScreenRect.width();
  float scaleY = photoData.photoHeight * 1.0f / mScaleImageView.getHeight();

  // 横向图片
  if (photoData.photoWidth > photoData.photoHeight) {
   // 图片的宽高比
   float photoScale = photoData.photoWidth * 1.0f / photoData.photoHeight;
   // 执行动画的ImageView宽高比
   float animationImageScale = mScaleImageView.getWidth() * 1.0f / mScaleImageView.getHeight();

   if (animationImageScale > photoScale) {
    // 动画ImageView宽高比大于图片宽高比的时候,需要用图片的高度除以动画ImageView高度的比例尺
    scale = scaleY;
   }
   else {
    scale = scaleX;
   }
  }
  // 正方形图片
  else if (photoData.photoWidth == photoData.photoHeight) {
   if (mScaleImageView.getWidth() > mScaleImageView.getHeight()) {
    scale = scaleY;
   }
   else {
    scale = scaleX;
   }
  }
  // 纵向图片
  else {
   scale = scaleY;
  }
  return scale;
 }

3.4 执行动画的缩放

 /**
  * 执行缩放动画
  * @param scaleImageView
  * @param translationX
  * @param translationY
  * @param scale
  * @param isEnlarge
  */
 private void executeZoom(final ImageView scaleImageView, int translationX, int translationY, float scale, boolean isEnlarge, Animator.AnimatorListener listener) {
  float startTranslationX, startTranslationY, endTranslationX, endTranslationY;
  float startScale, endScale, startAlpha, endAlpha;

  // 放大
  if (isEnlarge) {
   startTranslationX = translationX;
   endTranslationX = 0;
   startTranslationY = translationY;
   endTranslationY = 0;
   startScale = scale;
   endScale = 1;
   startAlpha = 0f;
   endAlpha = 0.75f;
  }
  // 缩小
  else {
   startTranslationX = 0;
   endTranslationX = translationX;
   startTranslationY = 0;
   endTranslationY = translationY;
   startScale = 1;
   endScale = scale;
   startAlpha = 0.75f;
   endAlpha = 0f;
  }

  //-------缩小动画--------
  AnimatorSet set = new AnimatorSet();
  set.play(
    ObjectAnimator.ofFloat(scaleImageView, "translationX", startTranslationX, endTranslationX))
    .with(ObjectAnimator.ofFloat(scaleImageView, "translationY", startTranslationY, endTranslationY))
    .with(ObjectAnimator.ofFloat(scaleImageView, "scaleX", startScale, endScale))
    .with(ObjectAnimator.ofFloat(scaleImageView, "scaleY", startScale, endScale))
    // ---Alpha动画---
    // mMaskView伴随着一个Alpha减小动画
    .with(ObjectAnimator.ofFloat(maskView, "alpha", startAlpha, endAlpha));
  set.setDuration(animDuration);
  if (listener != null) {
   set.addListener(listener);
  }
  set.setInterpolator(new DecelerateInterpolator());
  set.start();
 }

改View的主要实现如上,在图片进行缩放的时候,要考虑的情况:短边适配、图片原尺寸的宽高、展示图片的ImageView的宽高比、横竖屏时屏幕的尺寸。在此非常感谢震哥的帮助、抱拳了!老铁。如有更多想法的小伙伴。

请移步我的github  GalleryView地址

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

您可能感兴趣的文章:

  • android 大图片拖拽并缩放实现原理
  • Android 实现WebView点击图片查看大图列表及图片保存功能
  • Android中超大图片无法显示的问题解决
  • Android高效加载大图、多图解决方案 有效避免程序OOM
  • Android 实现加载大图片的方法
  • Android编程实现大图滚动显示的方法
  • Android 加载大图、多图和LruCache缓存详细介绍
  • Android实现网络加载图片点击大图后浏览可缩放
  • Android实现大图滚动显示效果
(0)

相关推荐

  • Android 加载大图、多图和LruCache缓存详细介绍

    我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状.不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小.比如说系统图片库里展示的图片大都是用手机摄像头拍出来的,这些图片的分辨率会比我们手机屏幕的分辨率高得多.大家应该知道,我们编写的应用程序都是有一定内存限制的,程序占用了过高的内存就容易出现OOM(OutOfMemory)异常.我们可以通过下面的代码看出每个应用程序最高可用内存是多少 int maxMemory = (int) (Runtime.ge

  • Android实现大图滚动显示效果

    问题: 我有一张比较大的图片,比如长宽都是屏幕的两倍大小,我想实现的功能是首先将图片居中显示,由于图片太大显然只能显示一部分,然后可以通过拖动,实现图片的平滑滚动(既看不出来滚动刷新痕迹). 就像google地图一样,如果用mapView这个控件,那么可以在屏幕上拖动整个地图,但是由于地图信息量太大,如果一次拖动过快,那么屏幕会暂时显示出一些刷新痕迹(灰白的格子). 想使用mapView来加载已有图片,但是没有成功,后来也使用了Srollview控件,和最常用的imageView,还是没有成功.

  • Android高效加载大图、多图解决方案 有效避免程序OOM

    本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工,英文好的朋友也可以直接去读原文. http://developer.android.com/training/displaying-bitmaps/index.html 高效加载大图片 我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状.不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小.比如说系统图片库里展示的图片大都是用手机摄像头拍出来的,这些图片的分辨率会比我们手机屏幕的分辨

  • Android 实现加载大图片的方法

    项目简介: 该项目为加载大图片 详细介绍: 对于超大的图片,如果不缩放的话,容易导致内存溢出.而经过处理后,无论多大的图片,都能够在手机屏幕上加载出来,不会导致内存溢出.当然,脸黑的除外 该应用涉及到的知识有: - 1.Bitmap的使用 - 2.Android手机中加载图片的原理 有的时候,我们加载一张不足1M的图片,尽管手机的堆内存有16M,仍然会导致内存溢出,why? 这就更计算机加载图片的原理有关了: 1).手机会解析图片的所有像素信息,把每个像素信息都存入到内存中 2).Android

  • Android实现网络加载图片点击大图后浏览可缩放

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 找了一些demo感觉没有自己想要的效果,于是借鉴一些改造一下并记录下来: 1.主Activity public class PictureViewFra extends Activity { private PicGallery gallery; // private ViewGroup tweetLayout; // 弹层 private boolean mTweetShow = false; // 弹层是否

  • android 大图片拖拽并缩放实现原理

    由于最近项目忙,博客一直没有时间更新,今天有点时间就连续更新两篇吧,过过瘾. 这篇图片拖拽缩放也是我在项目中用到的,今天整理一下,将源码奉献给大家,希望对大家以后碰到相似的问题有帮助. 这篇就不做过多介绍了,直接上源码: 复制代码 代码如下: public class SpacePageActivity extends Activity { private LinearLayout linnerLayout_spacepage; private RelativeLayout relativeLa

  • Android编程实现大图滚动显示的方法

    本文实例讲述了Android编程实现大图滚动显示的方法.分享给大家供大家参考,具体如下: 问题: 我有一张比较大的图片,比如长宽都是屏幕的两倍大小,我想实现的功能是首先将图片居中显示,由于图片太大显然只能显示一部分,然后可以通过拖动,实现图片的平滑滚动(既看不出来滚动刷新痕迹). 就像google地图一样,如果用mapView这个控件,那么可以在屏幕上拖动整个地图,但是由于地图信息量太大,如果一次拖动过快,那么屏幕会暂时显示出一些刷新痕迹(灰白的格子). 想使用mapView来加载已有图片,但是

  • Android中超大图片无法显示的问题解决

    发现问题 最近在做图片浏览功能时遇到了一个很蛋疼的问题,在开启硬件加速情况下,超大图无法正常显示(图的长宽有一个大于9000),而且程序不会crash,只是图片加载不出来,View显示为黑色.通过查看日志,发现系统打印出了下面的内容: W OpenGLRenderer( 4014): Bitmap too large to be uploaded into a texture (600x9518, max=8192x8192) 从日志内容可以看出,这是由OpenGL打印出来的日志,是由于图片的尺

  • Android 实现WebView点击图片查看大图列表及图片保存功能

    在日常开发过程中,有时候会遇到需要在app中嵌入网页,此时使用WebView实现效果,但在默认情况下是无法点击图片查看大图的,更无法保存图片.本文将就这一系列问题的实现进行说明. 图示: 项目的知识点: 加载网页后如何捕捉网页中的图片点击事件: 获取点击的图片资源后进行图片显示,获取整个页面所有的图片: 支持查看上下一张的图片以及对图片缩放显示: 对图片进行保存: 其他:图片缓存的处理(不用每次都重新加载已查看过的图片) 项目代码结构: 前期准备(添加权限.依赖和混淆设置): 添加权限: <us

  • Android仿头条、微信大图预览视图的方法详解

    图片大图预览 在我现在的项目当中,也存在大图预览的功能,但其实现过于繁重,采用一个Activity实现,并且在图片展示的过程中会产生卡顿感,整体感觉很是不好,正巧项目也在重构过程中,所以决定将这一功能写成一个成型的控件. 话不多说,先上图看下效果. 整体实现思路 图片展示:PhotoView(大图支持双击放大) 图片加载:Glide(加载网络图片.本地图片.资源文件) 小图变大图时的实现:动画 图片的下载:插入系统相册 该控件采用自定义View的方式,通过一些基本的控件的组合,来形成一个具有大图

  • jquery图片预览插件实现方法详解

    一.需求说明 效果如图: 二.代码实现 项目结构如图: example.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <he

  • Android通过json向MySQL中读写数据的方法详解【读取篇】

    本文实例讲述了Android通过json向MySQL中读取数据的方法.分享给大家供大家参考,具体如下: 首先 要定义几个解析json的方法parseJsonMulti,代码如下: private void parseJsonMulti(String strResult) { try { Log.v("strResult11","strResult11="+strResult); int index=strResult.indexOf("[");

  • Android通过json向MySQL中读写数据的方法详解【写入篇】

    本文实例讲述了Android通过json向MySQL中写入数据的方法.分享给大家供大家参考,具体如下: 先说一下如何通过json将Android程序中的数据上传到MySQL中: 首先定义一个类JSONParser.Java类,将json上传数据的方法封装好,可以直接在主程序中调用该类,代码如下 public class JSONParser { static InputStream is = null; static JSONObject jObj = null; static String j

  • Android开发之保存图片到相册的三种方法详解

    目录 方法一 方法二 方法三 有三种方法如下:三个方法都需要动态申请读写权限否则保存图片到相册也会失败 方法一 /** * 保存bitmap到本地 * * @param bitmap Bitmap */ public static void saveBitmap(Bitmap bitmap, String path) { String savePath; File filePic; if (Environment.getExternalStorageState().equals(Environm

  • Android中用Bmob实现短信验证码功能的方法详解

    这篇文章主要介绍发送验证码和校验验证码的功能,用到一个第三方平台Bmob,那Bmob是什么呢?Bmob可以开发一个云存储的移动应用软件,他提供了大量的标准的API接口,根据需要接入相关服务,开发者可以更加专注于应用的开发,让产品交付更快速,验证码功能就是其中一个. 一.跟其他第三方一样,我们开发之前要做一些准备工作. 1.首先,去官网注册一个帐号:http://www.bmob.cn/: 2.然后就可以创建应用了:具体怎么做Bmob说得很清楚了(官方操作介绍),如果你不想看,我简单说一下:点击右

  • 微信小程序 setData的使用方法详解

    微信小程序 setData的使用方法详解 最近在使用微信小程序的setData时,遇到了以下问题.如下: 官网文档在使用setData()设置数组对象的某个元素的属性时,是这么使用的: Page({ data: { array: [{text: 'init data'}], }, changeItemInArray: function() { this.setData({ 'array[0].text':'changed data' }) } }) 使用了 'array[0].text' : '

  • Spring Boot自定义错误视图的方法详解

    Spring Boot缺省错误视图解析器 Web应用在处理请求的过程中发生错误是非常常见的情况,SpringBoot中为我们实现了一个错误视图解析器(DefaultErrorViewResolver).它基于一些常见的约定,尝试根据HTTP错误状态码解析出错误处理视图.它会在目录/error下针对提供的HTTP错误状态码搜索模板或者静态资源,比如,给定了HTTP状态码404,它会尝试搜索如下模板或者静态资源: /<templates>/error/404.<ext> - 这里<

  • Android 滑动小圆点ViewPager的两种设置方法详解流程

    第一种方法: 一.测试如下,直接设置小圆点不是图标 二.准备工作 1.在drawable创建dot.xml,设置小圆点,比较方便 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="t

  • Android开发实现ListView异步加载数据的方法详解

    本文实例讲述了Android开发实现ListView异步加载数据的方法.分享给大家供大家参考,具体如下: 1.主Activity public class MainActivity extends Activity { private ListView listView; private ArrayList<Person> persons; private ListAdapter adapter; private Handler handler=null; //xml文件的网络地址 final

随机推荐