android 加載長圖

丟,這個問題糾結老長時間了,後面還是沒百度,自己瞎琢磨弄出來,感覺自己棒棒的

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的方法,額,可能我百度的方式不對,這個我試了不行,,額

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了,難受

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章