如果自己没有大图,那请自己手机截长屏,这就是大图!!!
[项目github地址](https://github.com/fenchen31/bigImageView)
**效果:**话不多说,先上效果图(上视频太麻烦,干脆截两张图好了)注意第二张是滑动之后的效果哈:
**概念:**大图无外乎两个概念,1.图片体积大,2.图片像素点多,如果将大图直接加载进内存,可能会直接引起oom,这是对大图加载进行优化的直接原因。而优化方案无外乎两个方面:内存复用和分块加载。
分块加载:假设一张大图有100M是我们在屏幕上看到的只有10M,那对于我们来说只要这10M就可以,这时候我们只需要截取原图中的一部分进行加载即可
内存复用:开启内存复用是指在该图片的加载过程中,不管我们怎么滑动图片,使用的始终是内存中的同一块地方。
大致流程:
1.用options将图片按照安卓屏幕比例进行压缩(其中包括确定图片加载格式以及开启内存复用)
2.用rect确定解码区域,然后传入decoder进行解码
3.用canvas进行绘制
优化: 1.在格式方面采用RGB_565而不适用ARGB_8888本身就是一种优化
2.应该避免在onDraw方法里面创建临时变量,因为ondraw是一个会被频繁调用的方法,创建的临时变量会迅速占据内存,引起频繁gc,甚至是内存抖动(内存抖动:内存频繁回收与分配)
**最后贴上全部代码:
MyBigImageView文件
package com.test.bigimageview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;
import androidx.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
public class MyBigImageView extends View implements GestureDetector.OnGestureListener, View.OnTouchListener {
private Scroller mScroll;
private Rect mRect;
private int pictureWidth,pictureHeight;//图片宽高
private int viewWidth,viewHeight;//控件宽高
private BitmapFactory.Options mOptions;
private GestureDetector mGestureDetector;
private BitmapRegionDecoder mDecoder;//解码器
private float mScale;//压缩比例
private Bitmap mBitmap;
private Matrix matrix;//图片压缩类
public MyBigImageView(Context context) {
this(context,null);
if (isInEditMode())
return;
}
/*
* 注意:因为在xml文件中使用自定义控件的时候调用的是第二个构造方法,所以该构造方法
* 必须被重写,否则会直接报错
* */
public MyBigImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public MyBigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mRect = new Rect();
mOptions = new BitmapFactory.Options();
mScroll = new Scroller(context);
matrix = new Matrix();
mGestureDetector = new GestureDetector(this);
setOnTouchListener(this);
}
public void setPicture(InputStream inputStream){
// 在不把图片加载进内存的情况下获取图片的宽和高
mOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream,null,mOptions);
pictureWidth = mOptions.outWidth;
pictureHeight = mOptions.outHeight;
// 开启内存复用
mOptions.inMutable = true;
/*
*注:在计算机中,图片是由像素点组成,而像素点又是由三原色(红绿蓝,即RGB)组成,
* 565代表的是每个颜色的像素点占多少位,而565格式表示每个像素点占据16位,即两个字节。
* ARGB中的A表示透明度。
*内存优化:从内存优化的角度上来说,采用RGB_565替代ARGB_8888节省了一半的存储空间,
* 从一定角度上来说本身就是一种内存优化,同时也提高了性能
* */
// 设置图片格式
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
// 注意:这句话必须是和mOptions.inJustDecodeBounds=true代码成对出现,可以理解为将其改为true之后在使用完又复原
mOptions.inJustDecodeBounds = false;
// 创建解码区域
try {
mDecoder = BitmapRegionDecoder.newInstance(inputStream,false);
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 1.获取控件宽高
viewHeight = getMeasuredHeight();
viewWidth = getMeasuredWidth();
// 2.确定加载区域
mRect.top = 0;
mRect.left = 0;
mRect.right = viewWidth;
mScale = viewWidth/(float)pictureWidth;
mRect.bottom = (int) (viewHeight/mScale);
// 3.按照图片和屏幕比例,对图片开始压缩
matrix.setScale(mScale,mScale);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDecoder == null)
return;
// 解析加载区域
mBitmap = mDecoder.decodeRegion(mRect,mOptions);
mOptions.inBitmap = mBitmap;
// 绘制
canvas.drawBitmap(mBitmap,matrix,null);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
@Override
public boolean onDown(MotionEvent e) {
// 如果图片由于惯性仍然在滑动,当手指又放到屏幕上时,上一次的滑动应该被停止
if (!mScroll.isFinished())
mScroll.forceFinished(true);
// 在由于惯性任然在滑动,我们又将手指放了上去,这时又摆动手指滑动,所以应该返回true
return true;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
mRect.offset(0, (int) distanceY);
if (mRect.bottom > pictureHeight) {
mRect.bottom = pictureHeight;
mRect.top = (int) (pictureHeight - viewHeight/mScale);
}
if (mRect.top < 0){
mRect.top = 0;
mRect.bottom = (int) (viewHeight/mScale);
}
invalidate();//通过调用该方法去触发onDraw,对区域进行重新绘制
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
}
xml文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.test.bigimageview.MyBigImageView
android:id="@+id/bigView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/image"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity文件
package com.test.bigimageview;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import java.io.IOException;
import java.io.InputStream;
public class MainActivity extends AppCompatActivity {
private MyBigImageView bigView;
private ImageView image;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bigView = findViewById(R.id.bigView);
image = findViewById(R.id.image);
// 内存复用模式加载大图
try {
InputStream inputStream = getAssets().open("bigpicture.png");
bigView.setPicture(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
/*// 传统方式加载大图,注意:因为本项目中图片尺寸较大,可能会引起oom,闪退请查看log日志
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bigpicture, null);
image.setImageBitmap(bitmap);*/
}
}