android 高清图片,Android加载高清大图

前两天在面试当中被问到有没有做过加载高清大图,当时确实没有做过,听面试官提到可以动态加载图片的显示区域。回来后在网上找到了一篇鸿洋大神的博文悔啊-_-!为什么早点没有看到。废话不多说代码如下:

一、BitmapRegionDecoder

BitmapRegionDecoder主要用于显示图片的某一块矩形区域,所以可以利用它来完成大图片的动态区域显示。

简单用法:

BitmapRegionDecoder提供了一系列的newInstance方法来构造对象,支持传入文件路径,文件描述符,文件的inputstrem等。

例如:

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder

.newInstance(is, false);

BitmapRegionDecoder.decodeRegion()方法,通过传入矩形区域即可显示图片的指定区域。

Bitmap bitmap = bitmapRegionDecoder.decodeRegion(rect, options);

参数一为矩形区域,参数二为BitmapFactory.Options。

一个简单的使用例子:

// load Assets image

try {

InputStream is = getAssets().open("qm.jpg");

BitmapFactory.Options tmpOptions = new BitmapFactory.Options();

tmpOptions.inJustDecodeBounds = true;

BitmapFactory.decodeStream(is, null, tmpOptions);

int width = tmpOptions.outWidth;

int height = tmpOptions.outHeight;

// 设置显示图片的中心区域

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder

.newInstance(is, false);

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

// JPG图片没有Aphla通道使用该颜色模式更加节省内存

options.inPreferredConfig = Bitmap.Config.RGB_565;

// 设置显示区域的矩形大小

Rect rect = new Rect(

width / 2 - 100, height / 2 - 100,

width / 2 + 100, height / 2 + 100);

// 通过BitmapRegionDecoder来解析显示区域的图像

Bitmap bitmap = bitmapRegionDecoder.decodeRegion(rect, options);

mShow.setImageBitmap(bitmap);

} catch (IOException e) {

e.printStackTrace();

}

一个简单的使用例子,整体自定义View的设计思想,即是通过滑动时获得的坐标不断设置矩形区域的偏移坐标,然后不断绘制View来完成动态显示大图的效果。具体代码大家可以自行参考鸿洋大神的博客,我这里将我注释的代码分享给大家。

二、LargeImageView

首先手势检测的两个类:BaseGestureDetector、MoveGestureDetector。

public abstract class BaseGestureDetector {

protected boolean mGestureInProgress;

protected MotionEvent mPreMotionEvent;

protected MotionEvent mCurrentMotionEvent;

protected Context mContext;

public BaseGestureDetector(Context context) {

mContext = context;

}

public boolean onTouchEvent(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;

}

}

这里有一个关于抽象类的构造方法需要提一下,具体说明大家可以自行移至这里,结论即是抽象类的子类在创建对象时,也会跟非抽象类的子类一样,都会默认调用父类的无参构造方法。

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)

{

}

}

}

关于这两个检测手势移动的类我还没有研究,各位同学可以自行研究。

最后自定义View的代码如下:

public class LargeImageView extends View {

private BitmapRegionDecoder mDecoder;

/**

* 图片的宽高

*/

private int mImageWidth,mImageHeight;

/**

* 矩形显示的区域(volatile修饰的对象线程安全)

*/

private volatile Rect mRect = new Rect();

/**

* 手势检测

*/

private MoveGestureDetector mDetector;

private static final BitmapFactory.Options options =

new BitmapFactory.Options();

static {

options.inPreferredConfig = Bitmap.Config.RGB_565;

}

public LargeImageView(Context context, AttributeSet attrs) {

super(context, attrs);

init();

}

public void setInputStream(InputStream is) {

try {

mDecoder = BitmapRegionDecoder.newInstance(is, false);

BitmapFactory.Options tmpOptions = new BitmapFactory.Options();

tmpOptions.inJustDecodeBounds = true;

BitmapFactory.decodeStream(is, null, tmpOptions);

mImageWidth = tmpOptions.outWidth;

mImageHeight = tmpOptions.outHeight;

// Log.d("xns", "width:" + mImageWidth + " ,height:" + mImageHeight);

// 重新布局View

requestLayout();

invalidate();

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (is != null) {

is.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

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();

// 只要图片在X或Y方向上大于View的宽高才进行矩形区域的偏移

if (mImageWidth > getWidth()) {

mRect.offset(-moveX, 0);

checkWidth();

// 偏移后重新绘制显示图片

invalidate();

}

if (mImageHeight > getHeight())

{

mRect.offset(0, -moveY);

checkHeight();

invalidate();

}

return true;

}

});

}

private void checkHeight() {

Rect rect = mRect;

int imageHeight = mImageHeight;

if (rect.bottom > imageHeight)

{

rect.bottom = imageHeight;

rect.top = imageHeight - getHeight();

}

if (rect.top < 0)

{

rect.top = 0;

rect.bottom = getHeight();

}

}

private void checkWidth() {

Rect rect = mRect;

int imageWidth = mImageWidth;

if (rect.right > imageWidth) {

rect.right = imageWidth;

rect.left = imageWidth - getWidth();

}

if (rect.left < 0) {

rect.left = 0;

rect.right = getWidth();

}

}

@Override

public boolean onTouchEvent(MotionEvent event) {

//Log.d("xns", "x:" + event.getX() + " ,y:" + event.getY());

mDetector.onTouchEvent(event);

return true;

}

@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;

}

@Override

protected void onDraw(Canvas canvas) {

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

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

}

}

使用代码如下:

public class MainActivity extends AppCompatActivity {

private static final String TAG = "MainActivity";

private LargeImageView mLIv;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mLIv = (LargeImageView) findViewById(R.id.iv_large);

// load Assets image

try {

InputStream is = getAssets().open("qm.jpg");

// 调用自定义大图加载View

mLIv.setInputStream(is);

} catch (IOException e) {

e.printStackTrace();

}

}

}

写在最后

最近找工作找的很烦躁,最后写给自己的寄语,以此激励自己一下,静心思考,勇往直前。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值