Android 高清加载巨图方案 拒绝压缩图片(1)

if (is != null) is.close();

} catch (Exception e)

{

}

}

}

public void init()

{

mDetector = new MoveGestureDetector(getContext(), new MoveGestureDetector.SimpleMoveGestureDetector()

{

@Override

public boolean onMove(MoveGestureDetector detector)

{

int moveX = (int) detector.getMoveX();

int moveY = (int) detector.getMoveY();

if (mImageWidth > getWidth())

{

mRect.offset(-moveX, 0);

checkWidth();

invalidate();

}

if (mImageHeight > getHeight())

{

mRect.offset(0, -moveY);

checkHeight();

invalidate();

}

return true;

}

});

}

private void checkWidth()

{

Rect rect = mRect;

int imageWidth = mImageWidth;

int imageHeight = mImageHeight;

if (rect.right > imageWidth)

{

rect.right = imageWidth;

rect.left = imageWidth - getWidth();

}

if (rect.left < 0)

{

rect.left = 0;

rect.right = getWidth();

}

}

private void checkHeight()

{

Rect rect = mRect;

int imageWidth = mImageWidth;

int imageHeight = mImageHeight;

if (rect.bottom > imageHeight)

{

rect.bottom = imageHeight;

rect.top = imageHeight - getHeight();

}

if (rect.top < 0)

{

rect.top = 0;

rect.bottom = getHeight();

}

}

public LargeImageView(Context context, AttributeSet attrs)

{

super(context, attrs);

init();

}

@Override

public boolean onTouchEvent(MotionEvent event)

{

mDetector.onToucEvent(event);

return true;

}

@Override

protected void onDraw(Canvas canvas)

{

Bitmap bm = mDecoder.decodeRegion(mRect, options);

canvas.drawBitmap(bm, 0, 0, null);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

{

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int width = getMeasuredWidth();

int height = getMeasuredHeight();

int imageWidth = mImageWidth;

int imageHeight = mImageHeight;

//默认直接显示图片的中心区域,可以自己去调节

mRect.left = imageWidth / 2 - width / 2;

mRect.top = imageHeight / 2 - height / 2;

mRect.right = mRect.left + width;

mRect.bottom = mRect.top + height;

}

}

根据上述源码:

  1. setInputStream里面去获得图片的真实的宽度和高度,以及初始化我们的mDecoder

  2. onMeasure里面为我们的显示区域的rect赋值,大小为view的尺寸

  3. onTouchEvent里面我们监听move的手势,在监听的回调里面去改变rect的参数,以及做边界检查,最后invalidate

  4. 在onDraw里面就是根据rect拿到bitmap,然后draw了

ok,上面并不复杂,不过大家有没有注意到,这个监听用户move手势的代码写的有点奇怪,恩,这里模仿了系统的ScaleGestureDetector,编写了MoveGestureDetector,代码如下:

  • MoveGestureDetector

package com.zhy.blogcodes.largeImage.view;

import android.content.Context;

import android.graphics.PointF;

import android.view.MotionEvent;

public class MoveGestureDetector extends BaseGestureDetector

{

private PointF mCurrentPointer;

private PointF mPrePointer;

//仅仅为了减少创建内存

private PointF mDeltaPointer = new PointF();

//用于记录最终结果,并返回

private PointF mExtenalPointer = new PointF();

private OnMoveGestureListener mListenter;

public MoveGestureDetector(Context context, OnMoveGestureListener listener)

{

super(context);

mListenter = listener;

}

@Override

protected void handleInProgressEvent(MotionEvent event)

{

int actionCode = event.getAction() & MotionEvent.ACTION_MASK;

switch (actionCode)

{

case MotionEvent.ACTION_CANCEL:

case MotionEvent.ACTION_UP:

mListenter.onMoveEnd(this);

resetState();

break;

case MotionEvent.ACTION_MOVE:

updateStateByEvent(event);

boolean update = mListenter.onMove(this);

if (update)

{

mPreMotionEvent.recycle();

mPreMotionEvent = MotionEvent.obtain(event);

}

break;

}

}

@Override

protected void handleStartProgressEvent(MotionEvent event)

{

int actionCode = event.getAction() & MotionEvent.ACTION_MASK;

switch (actionCode)

{

case MotionEvent.ACTION_DOWN:

resetState();//防止没有接收到CANCEL or UP ,保险起见

mPreMotionEvent = MotionEvent.obtain(event);

updateStateByEvent(event);

break;

case MotionEvent.ACTION_MOVE:

mGestureInProgress = mListenter.onMoveBegin(this);

break;

}

}

protected void updateStateByEvent(MotionEvent event)

{

final MotionEvent prev = mPreMotionEvent;

mPrePointer = caculateFocalPointer(prev);

mCurrentPointer = caculateFocalPointer(event);

//Log.e(“TAG”, mPrePointer.toString() + " , " + mCurrentPointer);

boolean mSkipThisMoveEvent = prev.getPointerCount() != event.getPointerCount();

//Log.e(“TAG”, "mSkipThisMoveEvent = " + mSkipThisMoveEvent);

mExtenalPointer.x = mSkipThisMoveEvent ? 0 : mCurrentPointer.x - mPrePointer.x;

mExtenalPointer.y = mSkipThisMoveEvent ? 0 : mCurrentPointer.y - mPrePointer.y;

}

/**

  • 根据event计算多指中心点

  • @param event

  • @return

*/

private PointF caculateFocalPointer(MotionEvent event)

{

final int count = event.getPointerCount();

float x = 0, y = 0;

for (int i = 0; i < count; i++)

{

x += event.getX(i);

y += event.getY(i);

}

x /= count;

y /= count;

return new PointF(x, y);

}

public float getMoveX()

{

return mExtenalPointer.x;

}

public float getMoveY()

{

return mExtenalPointer.y;

}

public interface OnMoveGestureListener

{

public boolean onMoveBegin(MoveGestureDetector detector);

public boolean onMove(MoveGestureDetector detector);

public void onMoveEnd(MoveGestureDetector detector);

}

public static class SimpleMoveGestureDetector implements OnMoveGestureListener

{

@Override

public boolean onMoveBegin(MoveGestureDetector detector)

{

return true;

}

@Override

public boolean onMove(MoveGestureDetector detector)

{

return false;

}

@Override

public void onMoveEnd(MoveGestureDetector detector)

{

}

}

}

  • BaseGestureDetector

package com.zhy.blogcodes.largeImage.view;

import android.content.Context;

import android.view.MotionEvent;

public abstract class BaseGestureDetector

{

protected boolean mGestureInProgress;

protected MotionEvent mPreMotionEvent;

protected MotionEvent mCurrentMotionEvent;

protected Context mContext;

public BaseGestureDetector(Context context)

{

mContext = context;

}

public boolean onToucEvent(MotionEvent event)

{

if (!mGestureInProgress)

{

handleStartProgressEvent(event);

} else

{

handleInProgressEvent(event);

}

return true;

}

protected abstract void handleInProgressEvent(MotionEvent event);

protected abstract void handleStartProgressEvent(MotionEvent event);

protected abstract void updateStateByEvent(MotionEvent event);

protected void resetState()

{

if (mPreMotionEvent != null)

{

mPreMotionEvent.recycle();

mPreMotionEvent = null;

}

if (mCurrentMotionEvent != null)

{

mCurrentMotionEvent.recycle();

mCurrentMotionEvent = null;

}

mGestureInProgress = false;

}

}

你可能会说,一个move手势搞这么多代码,太麻烦了。的确是的,move手势的检测非常简单,那么之所以这么写呢,主要是为了可以复用,比如现在有一堆的XXXGestureDetector,当我们需要监听什么手势,就直接拿个detector来检测多方便。我相信大家肯定也郁闷过Google,为什么只有ScaleGestureDetector而没有RotateGestureDetector呢。

根据上述,大家应该理解了为什么要这么做,当时不强制,每个人都有个性。

不过值得一提的是:上面这个手势检测的写法,不是我想的,而是一个开源的项目https://github.com/rharter/android-gesture-detectors,里面包含很多的手势检测。对应的博文是:http://code.almeros.com/android-multitouch-gesture-detectors#.VibzzhArJXg那面上面两个类就是我偷学了的~ 哈

四、测试


测试其实没撒好说的了,就是把我们的LargeImageView放入布局文件,然后Activity里面去设置inputstream了。

<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.zhy.blogcodes.largeImage.view.LargeImageView

android:id=“@+id/id_largetImageview”

android:layout_width=“match_parent”

android:layout_height=“match_parent”/>

然后在Activity里面去设置图片:

package com.zhy.blogcodes.largeImage;

import android.os.Bundle;

import android.support.v7.app.AppCompatActivity;

import com.zhy.blogcodes.R;

import com.zhy.blogcodes.largeImage.view.LargeImageView;

import java.io.IOException;

import java.io.InputStream;

public class LargeImageViewActivity extends AppCompatActivity

{

private LargeImageView mLargeImageView;

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_large_image_view);

mLargeImageView = (LargeImageView) findViewById(R.id.id_largetImageview);

try

{

InputStream inputStream = getAssets().open(“world.jpg”);

mLargeImageView.setInputStream(inputStream);

} catch (IOException e)

{

e.printStackTrace();

}

}

}

效果图:

ok,那么到此,显示巨图的方案以及详细的代码就描述完成了,总体还是非常简单的。

但是,在实际的项目中,可能会有更多的需求,比如增加放大、缩小;增加快滑手势等等,那么大家可以去参考这个库:https://github.com/johnnylambada/WorldMap,该库基本实现了绝大多数的需求,大家根据本文这个思路再去看这个库,也会简单很多,定制起来也容易。我这个地图的图就是该库里面提供的。

哈,掌握了这个,以后面试过程中也可以悄悄的装一把了,当你优雅的答完android加载图片的方案以后,然后接一句,其实还有一种情况,就是高清显示巨图,那么我们应该…相信面试官对你的印象会好很多~ have a nice day ~

源码点击下载


欢迎关注我的微博:

http://weibo.com/u/3165018720


群号:463081660,欢迎入群

微信公众号:hongyangAndroid

(欢迎关注,第一时间推送博文信息)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

**其实上面说了这么多,钱是永远赚不完的,在这个知识付费的时代,知识技能提升才是是根本!我作为一名8年的高级工程师,知识技能已经学习的差不多。**在看这篇文章的可能有刚刚入门,刚刚开始工作,或者大佬级人物。

像刚刚开始学Android开发小白想要快速提升自己,最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以这里分享一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

这么重要的事情说三遍啦!点赞+点赞+点赞!

【Android高级架构师系统学习资料】高级架构师进阶必备——设计思想解读开源框架

第一章、热修复设计
第二章、插件化框架设计
第三章、组件化框架设计
第四章、图片加载框架
第五章、网络访问框架设计
第六章、RXJava 响应式编程框架设计
第七章、IOC 架构设计
第八章、Android 架构组件 Jetpack

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

0Vx8Fzpu-1713205644355)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

**其实上面说了这么多,钱是永远赚不完的,在这个知识付费的时代,知识技能提升才是是根本!我作为一名8年的高级工程师,知识技能已经学习的差不多。**在看这篇文章的可能有刚刚入门,刚刚开始工作,或者大佬级人物。

像刚刚开始学Android开发小白想要快速提升自己,最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以这里分享一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

这么重要的事情说三遍啦!点赞+点赞+点赞!
[外链图片转存中…(img-bkZHukYu-1713205644357)]

【Android高级架构师系统学习资料】高级架构师进阶必备——设计思想解读开源框架

第一章、热修复设计
第二章、插件化框架设计
第三章、组件化框架设计
第四章、图片加载框架
第五章、网络访问框架设计
第六章、RXJava 响应式编程框架设计
第七章、IOC 架构设计
第八章、Android 架构组件 Jetpack

[外链图片转存中…(img-A48qiZda-1713205644358)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值