丢,这个问题纠结老长时间了,后面还是没百度,自己瞎琢磨弄出来,感觉自己棒棒的
1.自定义View的方法,详情查看弘扬的博客,百度上广为流传的方法,额,这块得要在滑动这块处理下,要不然给人感觉是上下滑动时整体特别卡顿的,反正我比较渣,这块我没搞好。哈哈,贴出这份百度上广为流传的代码
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 BigView extends View implements GestureDetector.OnGestureListener, View.OnTouchListener {
private Rect mRect;//可显示区域的位置,当前可显示多少图片内容
private BitmapFactory.Options mOptions;
private GestureDetector mGestureDetector;
private Scroller mScroller;
private int mImageWidth;
private int mImageHeight;
private BitmapRegionDecoder mDecoder;//核心类
private int mViewWidth;
private int mViewHeight;
private float mScale;
private Bitmap bitmap;
public BigView(Context context) {
this(context, null, 0);
}
public BigView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//指定要加载的区域
mRect = new Rect();
//需要复用
mOptions = new BitmapFactory.Options();
//手势识别类
mGestureDetector = new GestureDetector(context, this);
//设置onTouchListener
setOnTouchListener(this);
//滑动帮助
mScroller = new Scroller(context);
}
/**
* 由使用者输入一张图片
*/
public void setImage(InputStream is,int type) {
//先读取原图片的信息 高,宽
mOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, mOptions);
mImageWidth = mOptions.outWidth;
mImageHeight = mOptions.outHeight;
if(mImageWidth==-1||mImageHeight==-1){
//暂时不知道什么鬼,先写死为好,丢,反正目前需求就2张本地图
switch (type){
case 0:
mImageWidth=750;
mImageHeight=2200;
break;
case 1:
mImageWidth=750;
mImageHeight=3569;
break;
}
}
//开启复用
mOptions.inMutable = true;
//设置格式成RGB_565,因为565 存储像素点占用内存小,一个像素点只需要两个字节
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
mOptions.inJustDecodeBounds = false;
//创建一个区域解码器
try {
mDecoder = BitmapRegionDecoder.newInstance(is, false);
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();
}
/**
* 在测量的时候把我们需要的内存区域获取到 存入到mRect中
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取测量的view的大小
mViewWidth = getMeasuredWidth();
mViewHeight = getMeasuredHeight();
//确定要加载的图片的区域
mRect.left = 0;
mRect.top = 0;
mRect.right = mImageWidth;
//获取一个缩放因子
mScale = mViewWidth / (float) mImageWidth;
//高度就根据缩放比进行获取
mRect.bottom = (int) (mViewHeight / mScale);
}
/**
* 画出内容
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//如果解码器拿不到,表示没有设置过要显示的图片
if (null == mDecoder) {
return;
}
//复用上一张bitmap
mOptions.inBitmap = bitmap;
//解码指定的区域
bitmap = mDecoder.decodeRegion(mRect, mOptions);
//把得到的矩阵大小的内存进行缩放 得到view的大小
Matrix matrix = new Matrix();
matrix.setScale(mScale, mScale);
//画出来
canvas.drawBitmap(bitmap, matrix, null);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
//交给手势处理
return mGestureDetector.onTouchEvent(event);
}
/**
* 手按下的回调
*
* @param e
* @return
*/
@Override
public boolean onDown(MotionEvent e) {
//如果移动还没有停止,强制停止
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
//继续接收后续事件
return true;
}
/**
* @param e1 接下
* @param e2 移动
* @param distanceX 左右移动时的距离
* @param distanceY 上下移动时的距离
* @return
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//上下移动的时候,需要改变显示区域 改mRect
mRect.offset(0, (int) distanceY);
//处理移动时已经移到了两个顶端的问题
if (mRect.bottom > mImageHeight) {
mRect.bottom = mImageHeight;
mRect.top = mImageHeight - (int) (mViewHeight / mScale);
}
if (mRect.top < 0) {
mRect.top = 0;
mRect.bottom = (int) (mViewHeight / mScale);
}
invalidate();
return false;
}
/**
* 处理惯性问题
*
* @param e1
* @param e2
* @param velocityX 每秒移动的x点
* @param velocityY
* @return
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
//做计算 -velocityY 正负号问题,相反的
// ( 按下手指不拿开,屏幕跟着手势方向,若松开,方向则向相反方向滑动 ) 故 使用 负值才能正常使用
mScroller.fling(0, mRect.top,
0, (int) -velocityY,
0, 0,
0, mImageHeight - (int) (mViewHeight / mScale));
return false;
}
/*
使用上一个接口的计算结果
*/
@Override
public void computeScroll() {
if (mScroller.isFinished()) {
return;
}
//true 表示当前滑动还没有结束
if (mScroller.computeScrollOffset()) {
mRect.top = mScroller.getCurrY();
mRect.bottom = mRect.top + (int) (mViewHeight / mScale);
invalidate();
}
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
}
2.额,百度上还有个说法是用WebView的方法,这个是动态获取的,正常图片如果后台处理是富文本编辑下的话,则可以使用此方法全屏显示
String webs = "<!DOCTYPE html>\n"
+ "<html><meta name=\"viewport\" content=\"initial-scale=1.0, minimum-scale=0.0, maximum-scale=1.0, user-scalable=no,vertical-align: bottom\"/>\n"
+ "<head><meta charset=\"utf-8\"> <head>\n" + "<body><style>*{ width: 100%; margin: 0; padding: 0; box-sizing: border-box;} img{ width: 100%,vertical-align: bottom;display: block;}</style>"
+ 图片标签+ "</body>\n</html>";
webView.loadDataWithBaseURL(null, webs, "text/html", "UTF-8", null);
3.这种我是挺推荐的,我是这么实现的,不存在OOM的问题,滑动也贼流畅,原理也简单,就是对bitmap进行压缩,然后计算图片宽高比例与屏幕比例得出来的值重新绘制一个新的bitmap出来,嗯,就这么简单,直接贴代码
private void compressBitmap() {
//RGB565压缩
BitmapFactory.Options mOptions = new BitmapFactory.Options();
mOptions.inJustDecodeBounds = true;//表示解析图片的时候,只解析长度和宽度,不载入图片,这样就节省内存开支。
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;//前文提到的表格一目了然,这样会节省一半的内存。
mOptions.inSampleSize = calculateInSampleSize(mOptions, MyApplication.defaultWidth, MyApplication.defaultHidth);//计算缩放的比例,inSampleSize只能是2的整数次幂,如果不是的话,向下取得最大的2的整数次幂,
// 比如比例为7,向下寻找2的整数次幂,就是4。如果缩放比例是4的话,7.9M的那张图片最后占用的内存会是7.9/16=0.49M,完全不用担心OOM的发生。
mOptions.inJustDecodeBounds = false;//计算好压缩比例后,去加载解析原图。
switch (type) {
case 0:
bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.donate_fate_strategy, mOptions);//解析文件得到Bitmap。
// getBinding().ivLargeImg.setBackgroundResource(图片);
break;
case 1:
bitmap = BitmapFactory.decodeResource(getResources(), 图片, mOptions);//解析文件得到Bitmap。
// getBinding().ivLargeImg.setBackgroundResource(R.mipmap.donate_fate_use);
break;
}
float imgWhite = mOptions.outWidth;
float imgHeight = mOptions.outHeight;
//
// //根据屏幕宽高比算出当前ImagView的宽高比
// RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getBinding().ivLargeImg.getLayoutParams();
// layoutParams.width = MyApplication.defaultWidth;
// layoutParams.height = Float.valueOf((MyApplication.defaultWidth * imgHeight) / imgWhite).intValue();
// getBinding().ivLargeImg.setLayoutParams(layoutParams);
ivBitmap = BitmapUtil.changeBitmapSize(bitmap, MyApplication.defaultWidth, Float.valueOf((MyApplication.defaultWidth * imgHeight) / imgWhite).intValue());
runOnUiThread(new Runnable() {
@Override
public void run() {
if (ValidateUtils.isValidate(ivBitmap)) {
getBinding().ivLargeImg.setImageBitmap(ivBitmap);
}
}
});
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio > widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
使用完后记得在onDestroy中释放掉,额,不推荐在onPause中释放,因为容易触发
java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@84709c2这个错误,
释放代码
if (ValidateUtils.isValidate(bitmap) && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
if (ValidateUtils.isValidate(ivBitmap) && !ivBitmap.isRecycled()) {
ivBitmap.recycle();
ivBitmap = null;
}
额,这个ValidateUtils.isValidate,就是一个判空操作来的,不要管
然后布局文件代码是这样搞得:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_large_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
<ImageView
android:id="@+id/iv_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="11.5dp"
android:paddingTop="16.5dp"
android:paddingRight="11.5dp"
android:paddingBottom="16.5dp"
android:src="@mipmap/img_donate_back_black" />
</RelativeLayout>
</ScrollView>
</layout>
嗯,大概就这样子,丢,继续加班去,最近都没时间学kotlin了,难受