前言
講解流程:
1.業(yè)務(wù)需求
2.完整顯示,精度要求不高處理辦法
3.精度要求高妻顶,不需要完成顯示處理
4.總結(jié)
1.業(yè)務(wù)需求
1.1完整顯示,對精度要求不高滞造,圖片本身就很大
1.2對精度需求比較高,不需要完整顯示
2.完整顯示栋烤,精度要求不高處理辦法
1.根據(jù)顯示設(shè)備本身大小進行縮放
2.降低精度加載(改變圖片模式RGB565)
3.修改圖片格式(png改成webp谒养,jpg)
3.精度要求高,不需要完成顯示處理(區(qū)域加載)
3.1BitmapRegionDecoder
其實從名字我們也可以看出:指定Bitmap區(qū)域進行解碼班缎,沒錯它主要用于顯示圖片的某一塊矩形區(qū)域蝴光,如果需要顯示某個圖片的指定區(qū)域,那么這個類非常合適达址。
它的用法也非常簡單蔑祟,既然是顯示圖片的某一塊區(qū)域,那么至少需要兩個方法:1沉唠、設(shè)置圖片疆虚,2、設(shè)置顯示區(qū)域满葛。
接下來通過自定義一個可以加載巨圖的View展開說明:
3.2径簿、設(shè)置圖片
/**
* 由使用者輸入一張圖片
*/
public void setImage(InputStream is){
//先讀取原圖片的信息 高,寬
mOptions.inJustDecodeBounds=true;
BitmapFactory.decodeStream(is,null,mOptions);
mImageWidth=mOptions.outWidth;
mImageHeight=mOptions.outHeight;
//開啟復(fù)用
mOptions.inMutable=true;
//設(shè)置格式成RGB_565
mOptions.inPreferredConfig=Bitmap.Config.RGB_565;
mOptions.inJustDecodeBounds=false;
//創(chuàng)建一個區(qū)域解碼器
try {
mDecoder=BitmapRegionDecoder.newInstance(is,false);
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();
}
設(shè)置inJustDecodeBounds為ture(只解碼圖片的尺寸)嘀韧。然后得到圖片寬高篇亭,并且設(shè)置Bitmap是可以被復(fù)用的。然后創(chuàng)建BitmapRegionDecoder的實例對象锄贷。最后調(diào)用requestLayout()方法译蒂,reqeustLayout會重新測量我們的布局也就是會執(zhí)行View的onMeasure()。
3.3指定顯示區(qū)域
/**
* 在測量的時候把我們需要的內(nèi)存區(qū)域獲取到 存入到mRect中
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取測量的view的大小
mViewWidth=getMeasuredWidth();
mViewHeight=getMeasuredHeight();
//確定要加載的圖片的區(qū)域
mRect.left=0;
mRect.top=0;
mRect.right=mImageWidth;
//獲取一個縮放因子
mScale=mViewWidth/(float)mImageWidth;
//高度就根據(jù)縮放比進行獲取
mRect.bottom=(int)(mViewHeight/mScale);
}
/**
* 畫出內(nèi)容
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//如果解碼器拿不到谊却,表示沒有設(shè)置過要顯示的圖片
if(null==mDecoder){
return;
}
//復(fù)用上一張bitmap
mOptions.inBitmap=bitmap;
//解碼指定的區(qū)域
bitmap=mDecoder.decodeRegion(mRect,mOptions);
//把得到的矩陣大小的內(nèi)存進行縮放 得到view的大小
Matrix matrix=new Matrix();
matrix.setScale(mScale,mScale);
//畫出來
canvas.drawBitmap(bitmap,matrix,null);
}
3.4改變區(qū)域完成巨圖加載
Scroller + GestureDetector借助手勢GestureDetector與Scroller(滑動幫助)來完成這一功能柔昼。
附上完整代碼
package com.example.administrator.lsn_8_demo;
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.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;
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);
//指定要加載的區(qū)域
mRect =new Rect();
//需要復(fù)用
mOptions=new BitmapFactory.Options();
//手勢識別類
mGestureDetector=new GestureDetector(context,this);
//設(shè)置onTouchListener
setOnTouchListener(this);
//滑動幫助
mScroller=new Scroller(context);
}
/**
* 由使用者輸入一張圖片
*/
public void setImage(InputStream is){
//先讀取原圖片的信息 高,寬
mOptions.inJustDecodeBounds=true;
BitmapFactory.decodeStream(is,null,mOptions);
mImageWidth=mOptions.outWidth;
mImageHeight=mOptions.outHeight;
//開啟復(fù)用
mOptions.inMutable=true;
//設(shè)置格式成RGB_565
mOptions.inPreferredConfig=Bitmap.Config.RGB_565;
mOptions.inJustDecodeBounds=false;
//創(chuàng)建一個區(qū)域解碼器
try {
mDecoder=BitmapRegionDecoder.newInstance(is,false);
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();
}
/**
* 在測量的時候把我們需要的內(nèi)存區(qū)域獲取到 存入到mRect中
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取測量的view的大小
mViewWidth=getMeasuredWidth();
mViewHeight=getMeasuredHeight();
//確定要加載的圖片的區(qū)域
mRect.left=0;
mRect.top=0;
mRect.right=mImageWidth;
//獲取一個縮放因子
mScale=mViewWidth/(float)mImageWidth;
//高度就根據(jù)縮放比進行獲取
mRect.bottom=(int)(mViewHeight/mScale);
}
/**
* 畫出內(nèi)容
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//如果解碼器拿不到炎辨,表示沒有設(shè)置過要顯示的圖片
if(null==mDecoder){
return;
}
//復(fù)用上一張bitmap
mOptions.inBitmap=bitmap;
//解碼指定的區(qū)域
bitmap=mDecoder.decodeRegion(mRect,mOptions);
//把得到的矩陣大小的內(nèi)存進行縮放 得到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);
}
/**
* 手按下的回調(diào)
* @param e
* @return
*/
@Override
public boolean onDown(MotionEvent e) {
//如果移動還沒有停止捕透,強制停止
if(!mScroller.isFinished()){
mScroller.forceFinished(true);
}
//繼續(xù)接收后續(xù)事件
return true;
}
/**
*
* @param e1 接下
* @param e2 移動
* @param distanceX 左右移動時的距離
* @param distanceY 上下移動時的距離
* @return
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//上下移動的時候,需要改變顯示區(qū)域 改mRect
mRect.offset(0,(int)distanceY);
//處理移動時已經(jīng)移到了兩個頂端的問題
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) {
//做計算
mScroller.fling(0,mRect.top,
0,(int)-velocityY,
0,0,
0,mImageHeight-(int)(mViewHeight/mScale));
return false;
}
/*
使用上一個接口的計算結(jié)果
*/
@Override
public void computeScroll() {
if(mScroller.isFinished()){
return;
}
//true 表示當(dāng)前滑動還沒有結(jié)束
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) {
}
}
4.總結(jié)
1碴萧、首先確定大圖的用途乙嘀,精度需求:
a)完整顯示,對精度要求不高破喻,圖片本身就很大
b)對精度需求比較高乒躺,不需要完整顯示
2、解決方案
a)針對第一種的處理圖片本身低缩,按需加載(根據(jù)顯示設(shè)備本身大小進行縮放)嘉冒,降低精度加載(改變圖片模式,如將ARGB8888改成RGB565,ARGB4444),修改圖片格式(png改成webp咆繁,jpg)
b)第二種的一般采用局部加載讳推,主要要用到的是BitmapRegionDecoder這個類decodeRegion的方法,讀取圖片指定大小的數(shù)據(jù)玩般,然后通過移動來動態(tài)改變顯示區(qū)域的圖片