Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 546|回复: 0

解决android下超长图片显示不全出现空白的问题

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-5-23 00:17:46 | 显示全部楼层 |阅读模式

      本人有一个笑话类的app,里面可以看笑话段子、搞笑图片和搞笑视频。内容大多来自于互联网,最近发现,抓到不少超长的图片,在打开大图预览时,上面的部分显示正常,滑动到下面的时候,全是空白;效果如下:

         

     

      百度了一下,答案是硬件加速导致的openglRender大小限制引起的,具体原理这里不赘述,百度有很多,有兴趣了解的可以搜下。这里只关注解决方法,一是关闭硬件加速就可以,但会导致界面有卡顿现象,作为一个有追求的码农,果断放弃这个;第二个方案就是在拿到图片后,根据当前显示位置和当前缩放比例从图片中剪切合适的部分显示到屏幕中。

       今天主要讲下第二种方案的实现逻辑,文末会有源码提供,已封装为一个扩展LargeImageView组件,可直接替换系统ImageView。

      还是一贯的原则,主要讲解思想和逻辑,尽量用简单易懂的语言讲清楚,希望读者能够看完之后自己实现一套出来,如果不太关心这些,也可以直接跳到文末下载代码。

    一、背景

      超长图片显示不全,底部显示空白,无法正确浏览图片。

    二、目的

      继承实现一个新的LargeImageView组件,支持显示超长图片,支持触摸上下左右滑动,支持双指缩放,支持双击放大/还原。

    三、实现

       现在我们来一步一步实现LargeImageView组件。

      1. 首先新建一个类,继承自ImageView,并且设置缩放类型为ScaleType.FIT_XY

     1 public class LargeImageView extends ImageView {
     2     public LargeImageView(Context context)
     3     {
     4         this(context, null);
     5     }
     6 
     7     public LargeImageView(Context context, AttributeSet attrs)
     8     {
     9         super(context, attrs);
    10         this.setScaleType(ScaleType.FIT_XY);
    11     }
    12 }
    View Code

       2. 重载所有设置图片源的方法,将得到图片信息以Bitmap保存,后面备用

     1 public class LargeImageView extends ImageView {
     2     private Bitmap mBitmap;
     3 
     4     public LargeImageView(Context context)
     5     {
     6         this(context, null);
     7     }
     8 
     9     public LargeImageView(Context context, AttributeSet attrs)
    10     {
    11         super(context, attrs);
    12         this.setScaleType(ScaleType.FIT_XY);
    13     }
    14 
    15     @Override
    16     public void setImageBitmap(Bitmap bmp) {
    17         mBitmap = bmp;
    18         super.setImageBitmap(bmp);
    19     }
    20 
    21     @Override
    22     public void setImageDrawable(Drawable drawable) {
    23         mBitmap = getBitmapFromDrawable(drawable);
    24         super.setImageDrawable(drawable);
    25     }
    26 
    27     @Override
    28     public void setImageResource(int resId) {
    29         super.setImageResource(resId);
    30         mBitmap = getBitmapFromDrawable(getDrawable());
    31     }
    32 
    33     @Override
    34     public void setImageURI(Uri uri)     {
    35         super.setImageURI(uri);
    36         mBitmap = getBitmapFromDrawable(getDrawable());
    37     }
    38 }
    View Code

       3. 重载onDraw方法,根据缩放比例和滚动位置进行切图绘制

     1 @Override
     2 protected void onDraw(Canvas canvas) {
     3     if (!mIsDrawing) {
     4         mIsDrawing = true;
     5 
     6         //组件的宽度和高度,即图片显示区域的大小
     7         int width = getWidth();
     8         int height = getHeight();
     9 
    10         if (mBitmap == null || width <= 0 || height <= 0) {
    11             return;
    12         }
    13 
    14         //创建一个和显示区域大小相同的位图
    15         Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    16         Canvas bmpCanvas = new Canvas(bmp);
    17 
    18         try {
    19             //计算切图的宽度、高度
    20             int srcWidth = getRealSrcWidth();
    21             int srcHeight = getRealSrcHeight();
    22 
    23             //计算切图的X、Y坐标
    24             int srcOffsetX = mOffsetX;
    25             int srcOffsetY = mOffsetY;
    26             if (srcOffsetY + srcHeight > mBitmap.getHeight()) {
    27                 srcOffsetY = mBitmap.getHeight() - srcHeight;
    28             }
    29 
    30             //计算目标显示区域绘制的宽度、高度
    31             int desWidth = getRealDesWidth();
    32             int desHeight = getRealDesHeight();
    33 
    34             //计算目标显示区域绘制的X、Y坐标
    35             int desOffsetX = Math.max(0, (width - desWidth) / 2);
    36             int desOffsetY = 0;
    37             if (desHeight < height) {
    38                 desOffsetY = Math.max(0, (height - desHeight) / 2);
    39             }
    40 
    41             //绘制切图图片背景,切图大小小于显示区域时需要
    42             if (mDrawScale < 1 || desHeight < height) {
    43                 Paint fillPaint = new Paint();
    44                 fillPaint.setColor(Color.BLACK);
    45                 bmpCanvas.drawRect(0, 0, width, height, fillPaint);
    46             }
    47 
    48             //绘制切图图片
    49             bmpCanvas.drawBitmap(mBitmap,
    50                     new Rect(srcOffsetX, srcOffsetY, srcOffsetX + srcWidth, srcOffsetY + srcHeight),
    51                     new Rect(desOffsetX, desOffsetY, desOffsetX + desWidth, desOffsetY + desHeight),
    52                     null);
    53 
    54             //将切图图片绘制到画布
    55             canvas.drawBitmap(bmp,
    56                     new Rect(0, 0, bmp.getWidth(), bmp.getHeight()),
    57                     new Rect(0, 0, width, height),
    58                     null);
    59 
    60             //销毁切图图片,重要
    61             bmp.recycle();
    62         }
    63         catch (Exception exp) {
    64         }
    65         finally {
    66             mIsDrawing = false;
    67         }
    68     }
    69 }
    View Code

      4. 增加手势检测GestureDetector,处理滚动(onScroll)和双击处理(onDoubleTap)

     1 GestureDetector.SimpleOnGestureListener simpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
     2     @Override
     3     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
     4         if (!mIsScaling) {
     5             int width = getWidth();
     6             int height = getHeight();
     7 
     8             if (mBitmap != null && width > 0 && height > 0) {
     9                 boolean needInvalidate = false;
    10 
    11                 //计算原图的宽、高,需要考虑缩放比例
    12                 int srcWidth = getRealSrcWidth();
    13                 int srcHeight = getRealSrcHeight();
    14 
    15                 //计算滚动的X坐标
    16                 if (srcWidth < mBitmap.getWidth()) {
    17                     int oldOffsetX = mOffsetX;
    18                     oldOffsetX += distanceX;
    19                     if (oldOffsetX < 0) {
    20                         oldOffsetX = 0;
    21                     }
    22                     if (oldOffsetX + srcWidth > mBitmap.getWidth()) {
    23                         oldOffsetX = mBitmap.getWidth() - srcWidth;
    24                     }
    25                     if (mOffsetX != oldOffsetX) {
    26                         mOffsetX = oldOffsetX;
    27                         needInvalidate = true;
    28                     }
    29                 }
    30 
    31                 //计算滚动的Y坐标
    32                 if (srcHeight < mBitmap.getHeight()) {
    33                     int oldOffsetY = mOffsetY;
    34                     oldOffsetY += distanceY;
    35                     if (oldOffsetY < 0) {
    36                         oldOffsetY = 0;
    37                     }
    38                     if (oldOffsetY + srcHeight > mBitmap.getHeight()) {
    39                         oldOffsetY = mBitmap.getHeight() - srcHeight;
    40                     }
    41                     if (mOffsetY != oldOffsetY) {
    42                         mOffsetY = oldOffsetY;
    43                         needInvalidate = true;
    44                     }
    45                 }
    46 
    47                 //重新绘制
    48                 if (needInvalidate) {
    49                     invalidate();
    50                 }
    51             }
    52         }
    53 
    54         return super.onScroll(e1, e2, distanceX, distanceY);
    55     }
    56 
    57     @Override
    58     public boolean onDoubleTap(MotionEvent e) {
    59         //处理双击事件,放大或还原
    60         if (mDrawScale != 1) {
    61             scale(1);
    62             mScale = 1;
    63         } else {
    64             scale(2);
    65             mScale = 2;
    66         }
    67 
    68         return super.onDoubleTap(e);
    69     }
    70 };
    View Code

      5. 增加缩放手势检测ScaleGestureDetector,处理缩放事件

     1 ScaleGestureDetector.SimpleOnScaleGestureListener simpleOnScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
     2     @Override
     3     public boolean onScale(ScaleGestureDetector detector) {
     4         if (mIsScaling) {
     5             //计算缩放比例
     6             float currSpan = detector.getCurrentSpan();
     7             float prevSpan = detector.getPreviousSpan();
     8             mScaleFactor = currSpan / prevSpan;
     9 
    10             float currScale = mScale * mScaleFactor;
    11 
    12             //按新的缩放比例进行缩放
    13             scale(currScale);
    14         }
    15 
    16         return super.onScale(detector);
    17     }
    18 
    19     @Override
    20     public void onScaleEnd(ScaleGestureDetector detector) {
    21         super.onScaleEnd(detector);
    22 
    23         //重新计算缩放比例,别做结束缩放前的最后一次缩放
    24         mScale = mScale * mScaleFactor;
    25         if (mScale < 1) {
    26             mScale = 1;
    27         }
    28         scale(mScale);
    29 
    30         //恢复缩放标机
    31         mIsScaling = false;
    32     }
    33 
    34     @Override
    35     public boolean onScaleBegin(ScaleGestureDetector detector) {
    36         //开始缩放,打标记
    37         mIsScaling = true;
    38         return super.onScaleBegin(detector);
    39     }
    40 };
    View Code

     

      至此,我们的目的功能都已实现,有些细节实现,有兴趣的朋友可以下载代码了解。

    四、用法

      LargeImageView组件继承自ImageView,可以完全替代ImageView,使用方法相同。

    1 <com.puerlink.imagepreview.LargeImageView
    2     android:id="@+id/image_scale_view"
    3     android:layout_width="match_parent"
    4     android:layout_height="match_parent">
    5 
    6 </com.puerlink.imagepreview.LargeImageView>
    View Code

    五、最终效果

     

    六、未实现功能

      1. 滚动onFling事件未处理

      2. 细节优化和绘制性能

    七、源代码

      Git源码地址
      开源不易,谢谢Star:)

     

      如有错误,欢迎指正!

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-19 14:58 , Processed in 0.067636 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表