Remember the interview the interviewer asked me how to load a giant map memory in order not to explode, I did not answer it, he said, slicing show, I find it Fest fragmentation can reduce memory usage? ? He may now face a fight!
Extend
1. The three-level cache image, the image is loaded into memory, if the memory is fast burst, what will happen? How to deal with?
2. If memory is loaded png HD picture a 500 * 500. Should be the number of memory footprint?
How 3.Bitmap handle big picture, the big picture as a 30M, how to prevent OOM?
Android development, and sometimes there will be huge demand load diagram of how to load a large image without creating OOM
it, use the system provided by BitmapRegionDecoder
this class can easily complete.
FIG Effect:
BitmapRegionDecoder
: decoder area, a rectangular area may be used to decode the image with this area we can customize a rectangle, and then to move the position of the rectangular area according to the gesture of the whole picture can be seen slowly .
OK core principle is so simple, but doing it there are some details of the deal, step by step following the completion of a large image loading, support drag view, double-click zoom in, zoom gestures Custom View.
The first step, variable initialization
private void init(){
mOptions = new BitmapFactory.Options();
//滑动器
mScroller = new Scroller(getContext());
//所放器
mMatrix = new Matrix();
//手势识别
mGestureDetector = new GestureDetector(getContext(),this);
mScaleGestureDetector = new ScaleGestureDetector(getContext(),this);
}
BitmapFactory.Options
We are very familiar with, to configure Bitmap-related parameters, such as obtaining Bitmap width and height memory multiplexing parameters.
GestureDetector
Double-click to identify events ScaleGestureDetector
that listens finger zoom events, classes are provided by the system, more convenient to use.
The second step, set to load images
public void setImage(InputStream is){
mOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is,null,mOptions);
mImageWidth = mOptions.outWidth;
mImageHeight = mOptions.outHeight;
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
mOptions.inJustDecodeBounds = false;
try {
//区域解码器
mRegionDecoder = BitmapRegionDecoder.newInstance(is,false);
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();
}
Settings need to load a picture, regardless of where the picture can get into an input stream of the picture, so the parameter input stream through BitmapFactory.Options
to get the real picture width and height.
inPreferredConfig
This parameter defaults Bitmap.Config.ARGB_8888
, where it is changed Bitmap.Config.RGB_565
, remove the transparent channel, memory usage can be reduced by half. Finally, the decoder initialization area BitmapRegionDecoder
.
ARGB_8888
Is composed of four 8-bit 32-bit i.e., RGB_565 is R is 5, G is 6, B is a total of five 16-bit
A third step of obtaining View, width and height, calculated scaling value
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mViewWidth = w;
mViewHeight = h;
mRect.top = 0;
mRect.left = 0;
mRect.right = (int) mViewWidth;
mRect.bottom = (int) mViewHeight;
mScale = mViewWidth/mImageWidth;
mCurrentScale = mScale;
}
onSizeChanged
During the layout method, when the size is changed this view, this method is called, the first time onMeasure
after the call, can easily get View, width and height.
Then give us a custom rectangle mRect
around the upper and lower boundaries of the assignment. Normally we use this custom View larger image display, the View are filled, so here it is the initial size of the rectangle as big as it View.
mScale
For recording the original side than mCurrentScale
to the side than the current record, and because of the double-click gestures enlarged scale, mCurrentScale
with a gesture change.
The fourth step, draw
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mRegionDecoder == null){
return;
}
//复用内存
mOptions.inBitmap = mBitmap;
mBitmap = mRegionDecoder.decodeRegion(mRect,mOptions);
mMatrix.setScale(mCurrentScale,mCurrentScale);
canvas.drawBitmap(mBitmap,mMatrix,null);
}
Drawing is also very simple, a decoder decodes the region by the rectangular area, returns a Bitmap object, and then drawn through Bitmap canvas. Note mOptions.inBitmap = mBitmap
; this configuration can be reused memory, use memory to ensure that this area has been the only rectangle.
Here you can run to map out part of the picture, and you want to see the whole picture, you need to drag your finger to see, which need to handle a variety of events.
The fifth step, the distribution event
@Override
public boolean onTouchEvent(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
mScaleGestureDetector.onTouchEvent(event);
return true;
}
onTouchEvent
A very simple gesture events to two detectors own to deal with.
The sixth step, handling GestureDetector
events
@Override
public boolean onDown(MotionEvent e) {
//如果正在滑动,先停止
if(!mScroller.isFinished()){
mScroller.forceFinished(true);
}
return true;
}
When a finger pressed, if the picture is the rapid slide, stop
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//滑动的时候,改变mRect显示区域的位置
mRect.offset((int)distanceX,(int)distanceY);
//处理上下左右的边界
if(mRect.left<0){
mRect.left = 0;
mRect.right = (int) (mViewWidth/mCurrentScale);
}
if(mRect.right>mImageWidth){
mRect.right = (int) mImageWidth;
mRect.left = (int) (mImageWidth-mViewWidth/mCurrentScale);
}
if(mRect.top<0){
mRect.top = 0;
mRect.bottom = (int) (mViewHeight/mCurrentScale);
}
if(mRect.bottom>mImageHeight){
mRect.bottom = (int) mImageHeight;
mRect.top = (int) (mImageHeight-mViewHeight/mCurrentScale);
}
invalidate();
return false;
}
onScroll
Slide processing, according to the parameters of the finger movement, to move the rectangular drawing area, where each boundary point to deal with, such as the minimum on the left is 0, the maximum width of the picture to the right, beyond the boundary or not on the error.
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
mScroller.fling(mRect.left,mRect.top,-(int)velocityX,-(int)velocityY,0,(int)mImageWidth
,0,(int)mImageHeight);
return false;
}
@Override
public void computeScroll() {
super.computeScroll();
if(!mScroller.isFinished()&&mScroller.computeScrollOffset()){
if(mRect.top+mViewHeight/mCurrentScale<mImageHeight){
mRect.top = mScroller.getCurrY();
mRect.bottom = (int) (mRect.top + mViewHeight/mCurrentScale);
}
if(mRect.bottom>mImageHeight) {
mRect.top = (int) (mImageHeight - mViewHeight/mCurrentScale);
mRect.bottom = (int) mImageHeight;
}
invalidate();
}
}
In the onFling
calling method slider Scroller
of fling method inertial sliding finger off after treatment. View inertia moving distance of computeScroll()
methods to calculate, but also need to pay attention to the border issue, do not slide out of bounds.
The seventh step, double-click the event processing
@Override
public boolean onDoubleTap(MotionEvent e) {
//处理双击事件
if (mCurrentScale>mScale){
mCurrentScale = mScale;
} else {
mCurrentScale = mScale*mMultiple;
}
mRect.right = mRect.left+(int)(mViewWidth/mCurrentScale);
mRect.bottom = mRect.top+(int)(mViewHeight/mCurrentScale);
//处理边界
if(mRect.left<0){
mRect.left = 0;
mRect.right = (int) (mViewWidth/mCurrentScale);
}
if(mRect.right>mImageWidth){
mRect.right = (int) mImageWidth;
mRect.left = (int) (mImageWidth-mViewWidth/mCurrentScale);
}
if(mRect.top<0){
mRect.top = 0;
mRect.bottom = (int) (mViewHeight/mCurrentScale);
}
if(mRect.bottom>mImageHeight){
mRect.bottom = (int) mImageHeight;
mRect.top = (int) (mImageHeight-mViewHeight/mCurrentScale);
}
invalidate();
return true;
}
mMultiple
Double-click is enlarged several times, 3 times provided herein. Double-first amplifying 3 times, the second double-click to return undisturbed. After scaling, we need to re-set the boundaries of the drawing area according to the current zoom ratio. Finally, it also needs to be redefined boundaries, because after you use two fingers to enlarge, this time double-click return to the status quo, if not addressed border, position error. Processing the code boundary can be extracted.
The eighth step, processing finger zoom events
@Override
public boolean onScale(ScaleGestureDetector detector) {
//处理手指缩放事件
//获取与上次事件相比,得到的比例因子
float scaleFactor = detector.getScaleFactor();
// mCurrentScale+=scaleFactor-1;
mCurrentScale*=scaleFactor;
if(mCurrentScale>mScale*mMultiple){
mCurrentScale = mScale*mMultiple;
}else if(mCurrentScale<=mScale){
mCurrentScale = mScale;
}
mRect.right = mRect.left+(int)(mViewWidth/mCurrentScale);
mRect.bottom = mRect.top+(int)(mViewHeight/mCurrentScale);
invalidate();
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
//当 >= 2 个手指碰触屏幕时调用,若返回 false 则忽略改事件调用
return true;
}
onScaleBegin
The method needs to return true, otherwise it is impossible to detect the zoom gesture. onScale
The method of obtaining the scaling factor, the scaling factor is out of the event with the last match. * = So used herein, also need to be reset after completion of the drawing area mRect
boundary.
Here the various functions to complete friends ~
Source
More content of the interview, the interview topic, flutter full range of video, audio and video experts from 0 to development.
Focus on GitHub: https://github.com/xiangjiana/Android-MS
free access
More complete project download. To be continued. Source. Graphic knowledge subsequent upload github.
You can click on my link I get