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

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

Android高级架构师

由于篇幅问题,我呢也将自己当前所在技术领域的各项知识点、工具、框架等汇总成一份技术路线图,还有一些架构进阶视频、全套学习PDF文件、面试文档、源码笔记。

  • 330页PDF Android学习核心笔记(内含上面8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT部分大厂面试题(有解析)

好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
很多,定制起来也容易。我这个地图的图就是该库里面提供的。

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

源码点击下载


欢迎关注我的微博:

http://weibo.com/u/3165018720


群号:463081660,欢迎入群

微信公众号:hongyangAndroid

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

Android高级架构师

由于篇幅问题,我呢也将自己当前所在技术领域的各项知识点、工具、框架等汇总成一份技术路线图,还有一些架构进阶视频、全套学习PDF文件、面试文档、源码笔记。

  • 330页PDF Android学习核心笔记(内含上面8大板块)

[外链图片转存中…(img-mgnykm6g-1714695798293)]

[外链图片转存中…(img-uhdP0b04-1714695798294)]

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

[外链图片转存中…(img-m1SerSWF-1714695798295)]

  • Android BAT部分大厂面试题(有解析)

[外链图片转存中…(img-ys9wYzij-1714695798295)]

好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值