利用图片局部解析技术,加载高清图片,拒绝压缩!!!

    xiaoxiao2021-03-25  83

    本篇博客主要介绍如何使用BitmapRegionDecoder来加载大型图片,这样的好处就是不需要对图片进行压缩,不会降低图片显示的效果,缺点也很明显,就是不能一次完全显示出来,一次只能显示一幅图片的部分。 先看一下动态效果图吧:

    主布局文件:

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.example.navigationview.CustomNavigationView android:id="@+id/cnv" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <com.example.navigationview.DecoderView android:id="@+id/dv" android:layout_width="match_parent" android:layout_height="0dp" android:layout_below="@id/cnv" android:layout_weight="2" /> </LinearLayout>

    导航栏对应的View:

    package com.example.navigationview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; /** * Created by zhuyuqiang on 2017/3/15. */ public class CustomNavigationView extends View { private Context mContext; private int mViewWidth, mViewHeight; private int mSourceWidth, mSourceHeight; private int BOUNDS_COLOR = Color.RED; private int VIEW_DEFAULT_WIDTH = 400; private int VIEW_DEFAULT_HEIGHT = 400; private int sample = 1; private Bitmap mNavigationMap; private Paint mBitmapPaint, mBoundsPaint; private int mSourceId; private int mImageWidth, mImageHeight; private float density = 1f; private Point mViewPortPosition = new Point(); private Rect mBoundRect = new Rect(); private Rect mViewPortRect = null; private int mBoundWidth, mBoundHeight; private onBoundsChangedListener mListener; public interface onBoundsChangedListener { void deliveryViewAndBoundsRect(int width, int height, Rect mBoundsRect); } public CustomNavigationView(Context context) { this(context, null); } public CustomNavigationView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public CustomNavigationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public CustomNavigationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); Log.i("zyq", "CustomNavigationView:construction"); initBitmapPaint(); mContext = context; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); Log.i("zyq", "CustomNavigationView:onMeasure"); setMeasuredDimension(getCustomMeasureWidth(widthMeasureSpec), getCustomMeasureHeight(heightMeasureSpec)); initNavigationMap(); calculateBoundRect(mViewPortRect); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mNavigationMap != null) { Log.i("zyq", "onDraw:mNavigationMap"); canvas.drawBitmap(mNavigationMap, mViewPortPosition.x, mViewPortPosition.y, mBitmapPaint); drawBounds(canvas); } } @Override public boolean onTouchEvent(MotionEvent event) { mDetector.onTouchEvent(event); return true; } public void setBoundsRect(Rect boundsRect) { mViewPortRect = boundsRect; calculateBoundRect(boundsRect); } public void setOnBoundsRectChangeListener(onBoundsChangedListener listener) { this.mListener = listener; } private void calculateBoundRect(Rect mViewPortRect) { if (mViewWidth > 0 && mViewPortRect != null) { mBoundRect.left = (mViewPortRect.left * mImageWidth / mSourceWidth); mBoundRect.right = (mViewPortRect.right * mImageWidth / mSourceWidth); mBoundRect.top = (mViewPortRect.top * mImageHeight / mSourceHeight); mBoundRect.bottom = (mViewPortRect.bottom * mImageHeight / mSourceHeight); mBoundWidth = mBoundRect.width(); mBoundHeight = mBoundRect.height(); Log.i("zyq_bound", "reset rect:left" + mBoundRect.left + " top=" + mBoundRect.top + " right=" + mBoundRect.right + " bottom=" + mBoundRect.bottom + "\n" + "viewPort:left=" + mViewPortRect.left + " top=" + mViewPortRect.top + " right=" + mViewPortRect.right + " bottom=" + mViewPortRect.bottom); } invalidate(); } public void setDrawableId(int sourceId) { this.mSourceId = sourceId; } private void initNavigationMap() { density = getResources().getDisplayMetrics().density; BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), mSourceId, options); mSourceWidth = options.outWidth; mSourceHeight = options.outHeight; while (mSourceWidth / sample > (mViewWidth / density) || mSourceHeight / sample > (mViewHeight / density)) { sample *= 2; } Log.i("zyq", "sample =" + sample); options.inJustDecodeBounds = false; options.inSampleSize = sample; mNavigationMap = BitmapFactory.decodeResource(getResources(), mSourceId, options); mImageWidth = mNavigationMap.getWidth(); mImageHeight = mNavigationMap.getHeight(); mViewPortPosition.x = getPaddingLeft() + (mViewWidth - mImageWidth) / 2; mViewPortPosition.y = getPaddingTop() + (mViewHeight - mImageHeight) / 2; Log.i("zyq", "mViewWidth=" + mViewWidth + " mViewHeight=" + mViewHeight + " mImageWidth=" + mImageWidth + " mImageHeight=" + mImageHeight); } private void initBitmapPaint() { mBitmapPaint = new Paint(); mBitmapPaint.setAntiAlias(true); mBitmapPaint.setDither(true); mBitmapPaint.setFilterBitmap(true); mBoundsPaint = new Paint(); mBoundsPaint.setAntiAlias(true); mBoundsPaint.setDither(true); mBoundsPaint.setColor(BOUNDS_COLOR); mBoundsPaint.setStrokeWidth(2.0f); mBoundsPaint.setStyle(Paint.Style.STROKE); } private int getCustomMeasureWidth(int measureWidth) { int mode = MeasureSpec.getMode(measureWidth); int size = MeasureSpec.getSize(measureWidth); Log.i("zyq", "mode=" + mode + " EXACTLY=" + MeasureSpec.EXACTLY + " UNSPECIFIED=" + MeasureSpec.UNSPECIFIED + " AT_MOST=" + MeasureSpec.AT_MOST); if (mode == MeasureSpec.EXACTLY) { mViewWidth = size - getPaddingLeft() - getPaddingRight(); return size; } else { mViewWidth = VIEW_DEFAULT_WIDTH; return VIEW_DEFAULT_WIDTH + getPaddingRight() + getPaddingLeft(); } } private int getCustomMeasureHeight(int measureHeight) { int mode = MeasureSpec.getMode(measureHeight); int size = MeasureSpec.getSize(measureHeight); Log.i("zyq", "mode=" + mode + " EXACTLY=" + MeasureSpec.EXACTLY + " UNSPECIFIED=" + MeasureSpec.UNSPECIFIED + " AT_MOST=" + MeasureSpec.AT_MOST); if (mode == MeasureSpec.EXACTLY) { mViewHeight = size - getPaddingTop() - getPaddingBottom(); return size; } else { mViewHeight = VIEW_DEFAULT_HEIGHT; return VIEW_DEFAULT_HEIGHT + getPaddingTop() + getPaddingBottom(); } } private void drawBounds(Canvas canvas) { canvas.save(); if (mBoundRect != null) { Log.i("zyq_bound", "drawBounds"); // canvas.drawRect(mBoundRect,mBoundsPaint); canvas.drawRect(mBoundRect.left + mViewPortPosition.x, mBoundRect.top + mViewPortPosition.y, mBoundRect.right + mViewPortPosition.x, mBoundRect.bottom + mViewPortPosition.y, mBoundsPaint); } canvas.restore(); } private GestureDetector mDetector = new GestureDetector(mContext, new GestureDetector.OnGestureListener() { @Override public boolean onDown(MotionEvent e) { return false; } @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) { updateBoundRect(-distanceX, -distanceY); // postInvalidateDelayed(50); invalidate(); return true; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; } }); private void updateBoundRect(float width, float height) { Log.i("zyq", "width=" + width + " height=" + height); mBoundRect.left = mBoundRect.left + (int) width; mBoundRect.right = mBoundRect.right + (int) width; mBoundRect.top = mBoundRect.top + (int) height; mBoundRect.bottom = mBoundRect.bottom + (int) height; if (mBoundRect.left < 0) { mBoundRect.left = 0; mBoundRect.right = mBoundWidth; } if (mBoundRect.top < 0) { mBoundRect.top = 0; mBoundRect.bottom = mBoundHeight; } if (mBoundRect.right > (mImageWidth)) { mBoundRect.right = (mImageWidth); mBoundRect.left = (mImageWidth) - mBoundWidth; } if (mBoundRect.bottom > (mImageHeight)) { mBoundRect.bottom = (mImageHeight); mBoundRect.top = (mImageHeight) - mBoundHeight; } if (mListener != null) { mListener.deliveryViewAndBoundsRect(mImageWidth, mImageHeight, mBoundRect); } Log.i("zyq_bound", "reset rect:left" + mBoundRect.left + " top=" + mBoundRect.top + " right=" + mBoundRect.right + " bottom=" + mBoundRect.bottom + "\n" + "mViewPortPosition.y+mImageHeight=" + (mViewPortPosition.y + mImageHeight) + " mViewPortPosition.x+mImageWidth" + (mViewPortPosition.x + mImageWidth)); } }

    图片显示View:

    package com.example.navigationview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import java.io.IOException; import java.io.InputStream; /** * Created by zhuyuqiang on 2017/3/14. */ public class DecoderView extends View { private Context mContext; private Paint mBitmapPaint, mBoundPaint; private int DEFAULT_BOUND_COLOR = Color.RED; private final int DEFAULT_WIDTH = 600; private final int DEFAULT_HEIGHT = 600; private Rect mViewPort = new Rect(); private int mOriginWidth, mOriginHeight; private BitmapFactory.Options mDecoderOption = new BitmapFactory.Options(); private InputStream mSourceInputStream = null; private String mSourcePath; private float mDownX, mDownY; private BitmapRegionDecoder mDecoder = null; private int mCurrentWidth, mCurrentHeight; private onViewPortPositionChangeListener mListener; public interface onViewPortPositionChangeListener { void getViewPortRect(Rect mViewPortRect); } public DecoderView(Context context) { this(context, null); } public DecoderView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public DecoderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public DecoderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mContext = context; initPaints(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(getCustomMeasuteWidth(widthMeasureSpec), getCustomMeasuteHeight(heightMeasureSpec)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawPartImage(canvas); Log.i("zyq", "width =" + getWidth() + " height=" + getHeight()); } private void drawPartImage(Canvas canvas) { Log.i("zyq", "drawPartImage = " + (mSourceInputStream != null)); if (mSourceInputStream != null) { try { Log.i("zyq", "mSourceInputStream != null"); if (mDecoder == null) { mDecoder = BitmapRegionDecoder.newInstance(mSourceInputStream, true); } } catch (IOException e) { Log.i("zyq", "e=" + e.toString()); } } if (mSourcePath != null) { try { Log.i("zyq", "mSourceInputStream != null"); if (mDecoder == null) { mDecoder = BitmapRegionDecoder.newInstance(mSourceInputStream, true); } } catch (IOException e) { Log.i("zyq", "e=" + e.toString()); } } if (mDecoder != null) { Bitmap b = mDecoder.decodeRegion(mViewPort, mDecoderOption); canvas.drawBitmap(b, getPaddingLeft(), getPaddingTop(), mBitmapPaint); } } @Override public boolean onTouchEvent(MotionEvent event) { // switch (event.getAction()) { // case MotionEvent.ACTION_DOWN: // Log.i("zyq", "MotionEvent.ACTION_DOWN: mDownX="+mDownX+" mDownY="+mDownY); // mDownX = event.getX(); // mDownY = event.getY(); break; // case MotionEvent.ACTION_MOVE: // float mMoveX = event.getX(); // float mMoveY = event.getY(); // Log.i("zyq", "MotionEvent.ACTION_MOVE mMoveX="+mMoveX+" mMoveY="+mMoveY); // Log.i("zyq", "mMoveX - mDownX="+(mMoveX - mDownX)+" mMoveY - mDownY="+(mMoveY - mDownY)); // updateViewPort(-(mMoveX - mDownX), -(mMoveY - mDownY)); // invalidate(); break; // case MotionEvent.ACTION_UP: // Log.i("zyq", "MotionEvent.ACTION_UP"); break; // } mDetector.onTouchEvent(event); return true; } private void updateViewPort(float width, float height) { // Log.i("zyq", "width=" + width + " height=" + height); mViewPort.left = mViewPort.left + (int) width; mViewPort.right = mViewPort.right + (int) width; mViewPort.top = mViewPort.top + (int) height; mViewPort.bottom = mViewPort.bottom + (int) height; if (mViewPort.left < 0) { mViewPort.left = 0; mViewPort.right = mCurrentWidth; } if (mViewPort.top < 0) { mViewPort.top = 0; mViewPort.bottom = mCurrentHeight; } if (mViewPort.right > mOriginWidth) { mViewPort.right = mOriginWidth; mViewPort.left = mOriginWidth - mCurrentWidth; } if (mViewPort.bottom > mOriginHeight) { mViewPort.bottom = mOriginHeight; mViewPort.top = mOriginHeight - mCurrentHeight; } if (mListener != null) { mListener.getViewPortRect(mViewPort); } } public void setViewPortChangeListener(onViewPortPositionChangeListener listener) { this.mListener = listener; } public void setImageResource(String filePath) { if (mSourceInputStream != null) { throw new SecurityException("已经设置过图片资源"); } mDecoderOption.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, mDecoderOption); mOriginHeight = mDecoderOption.outHeight; mOriginWidth = mDecoderOption.outWidth; Log.i("qqq","width = "+mOriginWidth+" height = "+mOriginHeight); mSourcePath = filePath; mDecoderOption.inJustDecodeBounds = false; mDecoderOption.inPreferredConfig = Bitmap.Config.ARGB_8888; invalidate(); } public void setImageResource(InputStream is) { if (mSourcePath != null) { throw new SecurityException("已经设置过图片资源"); } mDecoderOption.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, mDecoderOption); mOriginHeight = mDecoderOption.outHeight; mOriginWidth = mDecoderOption.outWidth; Log.i("qqq","width = "+mOriginWidth+" height = "+mOriginHeight); mSourceInputStream = is; mDecoderOption.inJustDecodeBounds = false; mDecoderOption.inPreferredConfig = Bitmap.Config.ARGB_8888; Log.i("zyq", "set Image source"); invalidate(); } public Rect getViewPortRect() { return mViewPort; } public void setViewPortRectPosition(float x, float y) { int positionX = (int) (x * mOriginWidth); int positionY = (int) (y * mOriginHeight); int tran_X = positionX - mViewPort.left; int tran_y = positionY - mViewPort.top; updateViewPort(tran_X, tran_y); invalidate(); } private void initPaints() { Log.i("zyq", "init Paints"); mBitmapPaint = new Paint(); mBitmapPaint.setAntiAlias(true); mBitmapPaint.setDither(true); mBitmapPaint.setFilterBitmap(true); mBoundPaint = new Paint(); mBoundPaint.setAntiAlias(true); mBoundPaint.setDither(true); mBoundPaint.setColor(DEFAULT_BOUND_COLOR); } private int getCustomMeasuteWidth(int widthMeasureSpec) { int mode = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec); if (mode == MeasureSpec.EXACTLY) { mViewPort.left = getPaddingLeft(); mViewPort.right = size - getPaddingRight(); mCurrentWidth = size - getPaddingRight() - getPaddingLeft(); Log.i("zyq", "getCustomMeasuteHeight: MeasureSpec.EXACTLY"); return size; } else { Log.i("zyq", "getCustomMeasuteHeight: MeasureSpec.*"); mViewPort.left = getPaddingLeft(); mViewPort.right = DEFAULT_WIDTH + getPaddingLeft(); return DEFAULT_WIDTH + getPaddingRight() + getPaddingRight(); } } private int getCustomMeasuteHeight(int heightMeasureSpec) { int mode = MeasureSpec.getMode(heightMeasureSpec); int size = MeasureSpec.getSize(heightMeasureSpec); if (mode == MeasureSpec.EXACTLY) { Log.i("zyq", "getCustomMeasuteHeight: MeasureSpec.EXACTLY"); mViewPort.top = getPaddingTop(); mViewPort.bottom = size - getPaddingBottom(); mCurrentHeight = size - getPaddingBottom() - getPaddingTop(); return size; } else { Log.i("zyq", "getCustomMeasuteHeight: MeasureSpec.*"); mViewPort.top = getPaddingTop(); mViewPort.bottom = DEFAULT_HEIGHT + getPaddingTop(); return DEFAULT_HEIGHT + getPaddingTop() + getPaddingBottom(); } } private GestureDetector mDetector = new GestureDetector(mContext, new GestureDetector.OnGestureListener() { @Override public boolean onDown(MotionEvent e) { return false; } @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) { updateViewPort(distanceX, distanceY); // postInvalidateDelayed(50); invalidate(); return true; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; } }); }

    activity文件:

    package com.example.navigationview; import android.graphics.Rect; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import java.io.IOException; import java.io.InputStream; public class MainActivity extends AppCompatActivity { private DecoderView dv; private CustomNavigationView cnv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dv = (DecoderView) findViewById(R.id.dv); cnv = (CustomNavigationView) findViewById(R.id.cnv); InputStream is = null; try { is = getAssets().open("lol.jpg"); Log.i("zyq", "(is != null) =" + (is != null)); } catch (IOException e) { Log.i("zyq", "exception = " + e.toString()); e.printStackTrace(); } dv.setImageResource(is); dv.setViewPortChangeListener(new DecoderView.onViewPortPositionChangeListener() { @Override public void getViewPortRect(Rect mViewPortRect) { cnv.setBoundsRect(mViewPortRect); } }); cnv.setDrawableId(R.drawable.lol); cnv.setOnBoundsRectChangeListener(new CustomNavigationView.onBoundsChangedListener() { @Override public void deliveryViewAndBoundsRect(int width, int height, Rect mBoundsRect) { dv.setViewPortRectPosition(mBoundsRect.left / (float) width, mBoundsRect.top / (float) height); } }); } @Override protected void onResume() { super.onResume(); cnv.setBoundsRect(dv.getViewPortRect()); } }

    有几个知识点需要稍微提及一下: 1、一般大的文件资源都在存放在assets目录下的,在AndroidStudio中新建资源目录,需要注意目录的层级,如果目录层级不对,可能出现无法加载资源情况!! 目录所在层级如下图所示:

    2、在本Demo中可以通过拉动导航View中的红框或者直接滑动图片显示View都可以更新显示图片,这一点可能在上面的效果上看的不是很明显,有兴趣的朋友可以自行下载的demo试一下!!!

    3、BitmapRegionDecoder对象可以通过newInstance方法获取,其中第一个参数为图片资源来源!!至于第二个参数直接设置true就可以了!!其中图片的资源可以设置为输入流、路径等,详细的还是看一下源码吧,在这里就不解释了!!

    4、导航View的图片加载需要使用压缩技术,不然肯定会抛异常的,解析图片需要使用BitmapFactory,使用起来也很简单,在这里就不做更多的介绍了!!!

    5、由于可以更过导航栏中的View和图片显示的View更新BitmapRegionDecoder解析的区域,需要对两个view的显示进行同步,具体的可以看一下代码,使用过使用回调函数实现的,实现过程比较简单,不详细介绍来了!!!

    6、局部解析的方法decodeRegion,第一个参数为一个rect参数,代表的是解析的区域,更新的时候改变的就是这个rect的四个顶点的值,详细的还是看一下代码吧,主要还是思路,真正实现起来其实没有什么难度的!!!!

    好了,关于图片的局部解析就介绍到这里,谢谢大家的关注!!!!

    这是我的微信公众号,如果可以的话,希望您可以帮忙关注一下,这将是对我最大的鼓励了,谢谢!!

    代码地址:

    https://github.com/zhuyuqiang2017/CustomView

    转载请注明原文地址: https://ju.6miu.com/read-17287.html

    最新回复(0)