I still remember that an interviewer in the interview asked me how to load the giant map so as not to burst the memory. I did not answer. He said that the fragmentation is displayed. I wonder if fragmentation can reduce memory usage? ? Now you can slap him in the face!
Content expansion
1. In the three-level cache of the picture, the picture is loaded into the memory. If the memory is about to burst, what will happen? How to deal with it?
2. If a 500*500 png high-definition picture is loaded in the memory, how much memory should it occupy?
3. How does Bitmap handle a large picture, such as a 30M large picture, and how to prevent OOM?
In Android development, sometimes there is a need to load a giant image. How to load a large image without generating OOM? It BitmapRegionDecoder
can be done easily using this class provided by the system .
Effect picture:
BitmapRegionDecoder
: Area decoder, can be used to decode a rectangular area image, with this we can customize a rectangular area, and then move the position of the rectangular area according to the gesture to slowly see the whole picture.
The core principle of OK is that simple, but there are still some details to deal with. The following is a step by step to complete a loaded large image, support drag view, double click to zoom, and gesture zoom.
The first step is to initialize variables
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 familiar with it, used to configure Bitmap-related parameters, such as getting the width and height of Bitmap, memory reuse and other parameters.
GestureDetector
Used to identify double-click events and ScaleGestureDetector
to monitor finger zooming events, which are all classes provided by the system, which are more convenient to use.
The second step is to set the picture to be loaded
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();
}
Set the picture that needs to be loaded, and you can get an input stream of the picture no matter where the picture is placed, so the input stream is used as the parameter, and BitmapFactory.Options
the real width and height of the picture are obtained.
inPreferredConfig
By default Bitmap.Config.ARGB_8888
, this parameter is changed to here Bitmap.Config.RGB_565
, removing the transparent channel can reduce the memory usage by half. Finally, initialize the region decoder BitmapRegionDecoder
.
ARGB_8888
It is composed of 4 8 bits that is 32 bits, RGB_565 means R is 5 bits, G is 6 bits, and B is 5 bits, a total of 16 bits
The third step is to obtain the width and height of the View and calculate the zoom 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 of the method, when the size of this view changes, this method will be called, the first time onMeasure
it is called later, you can easily get the width and height of the View.
Then mRect
assign values to the top, bottom, left, and right boundaries of our custom rectangle . Under normal circumstances, we use this custom View to display large images, which are all occupying this View, so the initial size of the rectangle here is the same size as the View.
mScale
It is used to record the original to-to-square ratio and mCurrentScale
to record the current to-to-square ratio, because there are double-tap to zoom in and gesture zoom, which mCurrentScale
change with the gesture.
The fourth step is to 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);
}
The drawing is also very simple, decode a rectangular area through the area decoder, return a Bitmap object, and then draw the Bitmap through the canvas. Need to pay attention mOptions.inBitmap = mBitmap
; this configuration can reuse memory to ensure that the use of memory is always only a rectangular area.
Run here and you can draw a part of the picture. If you want to see all the pictures, you need to drag your finger to see it, which requires processing various events.
The fifth step is to distribute the event
@Override
public boolean onTouchEvent(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
mScaleGestureDetector.onTouchEvent(event);
return true;
}
onTouchEvent
The middle is very simple, the events are all handled by the two gesture detectors.
The sixth step, GestureDetector
the event being processed
@Override
public boolean onDown(MotionEvent e) {
//如果正在滑动,先停止
if(!mScroller.isFinished()){
mScroller.forceFinished(true);
}
return true;
}
When the finger is pressed, if the picture is sliding fast, then 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
In the middle processing slide, move the rectangle drawing area according to the parameters of the finger movement. Here you need to deal with each boundary point. For example, the minimum on the left is 0, and the maximum on the right is the width of the picture. It cannot exceed the boundary or an error will be reported.
@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. The distance of inertial movement computeScroll()
is calculated in the View method, and you also need to pay attention to boundary issues and do not slide out of the boundary.
The seventh step is to handle the double-click event
@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
To zoom in several times after double-clicking, set 3 times here. Double-click the first time to zoom in 3 times, and double-click the second time to return to the original state. After the zoom is completed, the boundary of the drawing area needs to be reset according to the current zoom ratio. Finally, you need to reposition the border, because if you use two fingers to zoom in, then double-click to return to the original state. If the border is not processed, the position will be wrong. The code for processing boundaries can be extracted.
The eighth step is to handle the finger zoom event
@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 the gesture zoom cannot be detected. onScale
Get the zoom factor in the method, this zoom factor is compared with the last event. Therefore, *= is used here, and mRect
the boundary of the drawing area needs to be reset after completion .
The various functions are completed here~
At last
Finally, I want to say: For programmers, there are too many knowledge content and technologies to learn. If you don’t want to be eliminated by the environment, you have to constantly improve yourself. We always adapt to the environment, not the environment to adapt to us. !
Here the big cattle sorted out Android studying architecture PDF + Video + source notes , as well as advanced technical architecture Advanced Brain Mapping, Android interview with thematic development, senior advanced information architecture to share out, help you learn advanced upgrade, we also save Learn when you search for information online, or you can share it with your friends
If you want the above information, you can follow me [Homepage Introduction] or [Short Letter] Get it for free
It’s easy to be a programmer. Being a good programmer requires continuous learning. From junior programmer to senior programmer, from junior architect to senior architect, or to management, from technical manager to technical director, every stage Need to master different abilities. Determine your career direction early to get rid of your peers in your work and ability improvement.