如果是高清大圖,那就說明不允許進行圖片壓縮,比如微博長圖,清明上河圖。
所以我們就要對圖片進行局部顯示,這就用到BitmapRegionDecoder屬性,主要用於顯示圖片的某一塊矩形區域。實際項目使用中,我們可以根據手勢滑動,然後不斷更新我們的Rect參數來實現具體的功能即可。
效果如下:
BitmapRegionDecoder類:
public final class BitmapRegionDecoder {
}
BitmapRegionDecoder構造方法:
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private BitmapRegionDecoder(long decoder) {
mNativeBitmapRegionDecoder = decoder;
mRecycled = false;
}
BitmapRegionDecoder 類被final進行修飾,因此無法被繼承。構造方法被私有化,無法直接new對象。
BitmapRegionDecoder 提供了一系列newInstance,最終都是通過數據流來得到對象。很好理解,加載圖片肯定要有圖片文件纔行。
// 創建 BitmapRegionDecoder 對象方法
static BitmapRegionDecoder newInstance(InputStream is, boolean isShareable)
static BitmapRegionDecoder newInstance(FileDescriptor fd, boolean isShareable)
static BitmapRegionDecoder newInstance(String pathName, boolean isShareable)
static BitmapRegionDecoder newInstance(byte[] data, int offset, int length, boolean isShareable)
使用decodeRegion方法解碼一個矩形區域來返回bitmap,矩形區域可以根據手勢拖動或者縮放來獲取。
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
BitmapFactory.Options.validate(options);
synchronized (mNativeLock) {
checkRecycled("decodeRegion called on recycled region decoder");
if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= getWidth()
|| rect.top >= getHeight())
throw new IllegalArgumentException("rectangle is outside the image");
return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top, options,
BitmapFactory.Options.nativeInBitmap(options),
BitmapFactory.Options.nativeColorSpace(options));
}
}
通過onTouchEvent對滑動手勢修改decodeRegion的區域,進行區域加載改變:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
dx = downX - event.getRawX();
dy = downY - event.getRawY();
updateRegion((int)dx, (int)dy);
break;
}
return true;
}
然後在onDraw方法中進行繪製bitmap:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mOptions.inBitmap = mBitmap;
Rect rect = new Rect(rectLeft, rectTop, rectRight, rectBottom);
mMatrix.setScale(1, 1);
mBitmap = mRegionDecoder.decodeRegion(rect, mOptions);
canvas.drawBitmap(mBitmap, mMatrix, null);
}
支持的圖像格式 : 目前圖像區域解碼對象只支持 JPEG 和 PNG 兩種圖像格式。
這裏寫了一個自定義ImageView來加載區域圖片,完整代碼:
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.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.widget.AppCompatImageView;
import java.io.IOException;
import java.io.InputStream;
public class RegionLoadImageView extends AppCompatImageView {
private BitmapRegionDecoder mRegionDecoder;
private BitmapFactory.Options mOptions = new BitmapFactory.Options();
private Bitmap mBitmap;
private Matrix mMatrix = new Matrix();
private InputStream inputStream;
private int rectLeft, rectTop, rectRight, rectBottom;
private int measuredWidht, measuredHeight;
private float dx, dy, downX, downY;
public RegionLoadImageView(Context context) {
super(context);
}
public RegionLoadImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public RegionLoadImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.S)
public void setImageRes(int imageRes) {
inputStream = getResources().openRawResource(imageRes);
setRegionDecoder(inputStream);
}
/**
* 設置區域解碼器
*/
@RequiresApi(api = Build.VERSION_CODES.S)
public void setRegionDecoder(InputStream inputStream) {
try {
mRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measuredWidht = getMeasuredWidth();
measuredHeight = getMeasuredHeight();
updateRegion(0, 0);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
dx = downX - event.getRawX();
dy = downY - event.getRawY();
updateRegion((int)dx, (int)dy);
break;
}
return true;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mOptions.inBitmap = mBitmap;
Rect rect = new Rect(rectLeft, rectTop, rectRight, rectBottom);
mMatrix.setScale(1, 1);
mBitmap = mRegionDecoder.decodeRegion(rect, mOptions);
canvas.drawBitmap(mBitmap, mMatrix, null);
}
public void updateRegion(int x, int y) {
rectLeft = x;
rectTop = y;
rectRight = x + measuredWidht;
rectBottom = y + measuredHeight;
invalidate();
}
}
最後,給該自定義view設置加載大圖資源,資源放在res/raw文件夾下:
regionLoadImageView?.setImageRes(R.raw.super_long)
使用的大圖資源: