Android HD Load Long chart or big picture scheme

Source: Internet
Author: User

I. Overview

For loading pictures, we are not unfamiliar, generally in order to avoid oom as much as possible according to the following practices:

    1. For picture display: Compress the picture to show the size of the picture control as needed.
    2. If you have a very large number of pictures: You will use a caching mechanism such as lrucache to keep all of your pictures occupied in one range.

In fact, for picture loading there is a situation, is a single picture is very large, and also does not allow compression. such as display: The world map, Qingming River map, micro-bo long map.

So what should be done about this demand?

First do not compress, according to the original size of the load, then the screen is certainly not large enough, and considering the memory situation, it is not possible to load the whole picture into memory, so it must be a partial load, then you need to use a class:

    • BitmapRegionDecoder

Second, since the screen display is not complete, then at least to add a left and right to drag the gesture, so that users can drag to view.

So in general, the purpose of this blog is to customize a display of the macro view, support users to drag the view, probably the following:

Well, this Qingming river map is too long, want to see the full map, text download, pictures in the assets directory. Of course, if your figure, the height is also very large, must also be able to drag up and down.

Second, the first knowledge bitmapregiondecoder

BitmapRegionDecoderUsed primarily to display a rectangular area of a picture, which is appropriate if you need to display a specific area of a picture.

For the use of this class, it is very simple, since it is to display an area of the picture, then at least one method to set up the picture, a method to pass in the displayed area;

    • Bitmapregiondecoder provides a series of newinstance methods to construct objects, support incoming file paths, file descriptors, file Inputstrem, and so on.

      For example:

      Bitmapregiondecoder Bitmapregiondecoder =  bitmapregiondecoder.newinstance (InputStream, false); <code class= " Language-java hljs  has-numbering "></code>

    • The above solves the incoming image we need to process, then the next is to display the specified area.

      Bitmapregiondecoder.decoderegion (rect, options);

      Parameter one is obviously a rect, parameter two is bitmapfactory.options, you can control the picture inSampleSize , and inPreferredConfig so on.

So here's a super simple example:

Package Com.zhy.blogcodes.largeimage;import Android.graphics.bitmap;import Android.graphics.bitmapfactory;import Android.graphics.bitmapregiondecoder;import Android.graphics.rect;import Android.os.bundle;import Android.support.v7.app.appcompatactivity;import Android.widget.imageview;import Com.zhy.blogcodes.R;import    Java.io.ioexception;import Java.io.inputstream;public class Largeimageviewactivity extends appcompatactivity{    Private ImageView Mimageview;        @Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);        Setcontentview (R.layout.activity_large_image_view);        Mimageview = (ImageView) Findviewbyid (R.id.id_imageview);            try {inputstream InputStream = Getassets (). Open ("tangyan.jpg");            Get the picture's width, height bitmapfactory.options tmpoptions = new Bitmapfactory.options ();            Tmpoptions.injustdecodebounds = true; Bitmapfactory.decodestream (InputStream, NULL, TMPOPTIONS);            int width = tmpoptions.outwidth;            int height = tmpoptions.outheight;            Set the center area of the display picture Bitmapregiondecoder Bitmapregiondecoder = Bitmapregiondecoder.newinstance (InputStream, false);            Bitmapfactory.options Options = new Bitmapfactory.options ();            Options.inpreferredconfig = Bitmap.Config.RGB_565;  Bitmap Bitmap = bitmapregiondecoder.decoderegion (New Rect (WIDTH/2-$, HEIGHT/2-$, WIDTH/2 +, HEIGHT/2 +            (), options);        Mimageview.setimagebitmap (bitmap);        } catch (IOException e) {e.printstacktrace (); }    }}

The above code, is to use Bitmapregiondecoder to load the picture in the assets, call to bitmapRegionDecoder.decodeRegion parse the middle rectangular area of the picture, return to bitmap, the final display on the ImageView.

The small figure above shows the middle area of the larger image below.

OK, so at present we have understood BitmapRegionDecoder the basic user, then the outward diffusion, we need to customize a control to show the giant map is very simple, first the scope of the RECT is the size of our view, and then according to the user's mobile gesture, constantly to update the parameters of our rect.

Three, custom display large map control

According to the above analysis, the idea of our custom control is very clear:

    • Provides a portal for setting up a picture
    • Rewrite the ontouchevent to update the parameters of the display area based on user-moved gestures
    • After each update of the zone parameters, call Invalidate,ondraw inside to regiondecoder.decoderegion get bitmap, go to draw

Cleared up, found so easy, the following code:

Package Com.zhy.blogcodes.largeimage.view;import Android.content.context;import Android.graphics.bitmap;import Android.graphics.bitmapfactory;import Android.graphics.bitmapregiondecoder;import Android.graphics.Canvas;import Android.graphics.rect;import Android.util.attributeset;import Android.view.motionevent;import Android.view.View; Import Java.io.ioexception;import java.io.inputstream;/** * Created by Zhy on 15/5/16.    */public class Largeimageview extends view{private Bitmapregiondecoder mdecoder;    /** * Picture width and Height */private int mimagewidth, mimageheight;    /** * Area Drawn * * Private volatile Rect mrect = new rect ();    Private Movegesturedetector Mdetector;    private static final Bitmapfactory.options Options = new Bitmapfactory.options ();    static {options.inpreferredconfig = Bitmap.Config.RGB_565; } public void Setinputstream (InputStream) {try {Mdecoder = Bitmapregiondecoder.newinst         Ance (is, false);   Bitmapfactory.options tmpoptions = new Bitmapfactory.options ();            Grab the bounds for the scene dimensions tmpoptions.injustdecodebounds = true;            Bitmapfactory.decodestream (IS, null, tmpoptions);            Mimagewidth = Tmpoptions.outwidth;            Mimageheight = Tmpoptions.outheight;            Requestlayout ();        Invalidate ();        } catch (IOException e) {e.printstacktrace ();            } finally {try {if (is! = null) is.close (); } catch (Exception e) {}}} public void Init () {mdetector = new Movegestur Edetector (GetContext (), new Movegesturedetector.simplemovegesturedetector () {@Override publi                C Boolean OnMove (Movegesturedetector detector) {int moveX = (int) Detector.getmovex ();                int movey = (int) detector.getmovey (); if (MimagewidtH > GetWidth ()) {Mrect.offset (-movex, 0);                    Checkwidth ();                Invalidate ();                    } if (Mimageheight > GetHeight ()) {mrect.offset (0,-movey);                    Checkheight ();                Invalidate ();            } return true;    }        });        } private void Checkwidth () {rect rect = mrect;        int imagewidth = Mimagewidth;        int imageheight = Mimageheight;            if (Rect.right > ImageWidth) {rect.right = ImageWidth;        Rect.left = Imagewidth-getwidth ();            } if (Rect.left < 0) {rect.left = 0;        Rect.right = GetWidth ();        }} private void Checkheight () {rect rect = mrect;        int imagewidth = Mimagewidth;        int imageheight = Mimageheight;        if (Rect.bottom > ImageHeight) {    Rect.bottom = ImageHeight;        Rect.top = Imageheight-getheight ();            } if (Rect.top < 0) {rect.top = 0;        Rect.bottom = GetHeight ();        }} public Largeimageview (context context, AttributeSet Attrs) {Super (context, attrs);    Init ();        } @Override public boolean ontouchevent (Motionevent event) {mdetector.ontoucevent (event);    return true;        } @Override protected void OnDraw (canvas canvas) {Bitmap BM = mdecoder.decoderegion (mrect, Options);    Canvas.drawbitmap (BM, 0, 0, NULL); } @Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {super.onmeasure (widthm        Easurespec, Heightmeasurespec);        int width = getmeasuredwidth ();        int height = getmeasuredheight ();        int imagewidth = Mimagewidth;         int imageheight = Mimageheight; The default direct display image of the central area, you can adjust Mrect.left = IMAGEWIDTH/2-WIDTH/2;        Mrect.top = IMAGEHEIGHT/2-HEIGHT/2;        Mrect.right = mrect.left + width;    Mrect.bottom = mrect.top + height; }}

According to the above source code:

    1. Setinputstream inside to get the true width and height of the picture, as well as initialize our Mdecoder
    2. Onmeasure inside the rect of our display area, size is the size of the view
    3. Ontouchevent inside we listen to the gesture of move, in the listener callback to change the parameters of rect, and do the boundary check, and finally invalidate
    4. In OnDraw, the bitmap is based on Rect, and then draw.

OK, the above is not complicated, but we did not notice that the listener user move gesture code to write a little strange, well, here imitate the system ScaleGestureDetector , wrote MoveGestureDetector , the code is as follows:

  • Movegesturedetector

    Package Com.zhy.blogcodes.largeimage.view;import Android.content.context;import Android.graphics.pointf;import Android.view.motionevent;public class Movegesturedetector extends basegesturedetector{private PointF Mcurrentpointer    ;    Private PointF Mprepointer;    Just to reduce the creation of memory private PointF Mdeltapointer = new PointF ();    Used to record the final result and return private PointF Mextenalpointer = new PointF ();    Private Onmovegesturelistener Mlistenter;        Public Movegesturedetector (context context, Onmovegesturelistener Listener) {super (context);    Mlistenter = listener; } @Override protected void Handleinprogressevent (Motionevent event) {int actioncode = Event.getaction () & Amp        Motionevent.action_mask;                Switch (actioncode) {case MotionEvent.ACTION_CANCEL:case motionevent.action_up:                Mlistenter.onmoveend (this);                ResetState ();            Break               Case Motionevent.action_move: Updatestatebyevent (event);                Boolean update = Mlistenter.onmove (this);                    if (update) {mpremotionevent.recycle ();                Mpremotionevent = Motionevent.obtain (event);        } break; }} @Override protected void Handlestartprogressevent (Motionevent event) {int actioncode = Event.getac        tion () & motionevent.action_mask; Switch (actioncode) {case MotionEvent.ACTION_DOWN:resetState ();//prevent receiving cancel or up,                Risk mpremotionevent = Motionevent.obtain (event);                Updatestatebyevent (event);            Break                Case MotionEvent.ACTION_MOVE:mGestureInProgress = Mlistenter.onmovebegin (this);        Break        }} protected void Updatestatebyevent (Motionevent event) {final motionevent prev = mpremotionevent; Mprepointer = Caculatefocalpointer (preV);        Mcurrentpointer = Caculatefocalpointer (event);        LOG.E ("TAG", mprepointer.tostring () + "," + mcurrentpointer);        Boolean mskipthismoveevent = Prev.getpointercount ()! = Event.getpointercount ();        LOG.E ("TAG", "mskipthismoveevent =" + mskipthismoveevent); Mextenalpointer.x = mskipthismoveevent?        0:mcurrentpointer.x-mprepointer.x; Mextenalpointer.y = mskipthismoveevent?    0:MCURRENTPOINTER.Y-MPREPOINTER.Y; /** * Calculates a multi-fingered center point based on event * * @param event * @return */private PointF Caculatefocalpointer (motione        Vent event) {Final int count = Event.getpointercount ();        float x = 0, y = 0;            for (int i = 0; i < count; i++) {x + = Event.getx (i);        Y + = event.gety (i);        } x/= count;        Y/= count;    return new PointF (x, y);    } public float Getmovex () {return mextenalpointer.x; } public float Getmovey () {return MextenALPOINTER.Y;        } public interface Onmovegesturelistener {public boolean onmovebegin (Movegesturedetector detector);        public boolean onMove (Movegesturedetector detector);    public void Onmoveend (Movegesturedetector detector); } public static class Simplemovegesturedetector implements Onmovegesturelistener {@Override public bo        Olean Onmovebegin (Movegesturedetector detector) {return true;        } @Override public Boolean onMove (Movegesturedetector detector) {return false; } @Override public void Onmoveend (Movegesturedetector detector) {}}}

  • Basegesturedetector

    Package Com.zhy.blogcodes.largeimage.view;import Android.content.context;import Android.view.motionevent;public    Abstract class basegesturedetector{protected Boolean mgestureinprogress;    protected Motionevent mpremotionevent;    protected Motionevent mcurrentmotionevent;    protected Context Mcontext;    Public Basegesturedetector (Context context) {Mcontext = context; } public boolean ontoucevent (Motionevent event) {if (!mgestureinprogress) {Handlestartpro        Gressevent (event);        } else {handleinprogressevent (event);    } return true;    } protected abstract void Handleinprogressevent (Motionevent event);    protected abstract void Handlestartprogressevent (Motionevent event);    protected abstract void Updatestatebyevent (Motionevent event);            protected void ResetState () {if (mpremotionevent! = null) {mpremotionevent.recycle ();        Mpremotionevent = null;  }      if (mcurrentmotionevent! = null) {mcurrentmotionevent.recycle ();        Mcurrentmotionevent = null;    } mgestureinprogress = false; }}

    You might say that a move gesture makes so much code, too cumbersome. Yes, the move gesture is very simple to detect, then the reason is so written, mainly to be reusable, such as now have a bunch of XXXGestureDetector , when we need to listen to what gesture, directly to take a detector to detect how convenient. I believe we must have been depressed about Google, why only ScaleGestureDetector and not RotateGestureDetector .

According to the above, we should understand why to do so, when not mandatory, everyone has a personality.

But it is worth mentioning that: the above gesture detection of the wording, not what I think, but an open source project Https://github.com/rharter/android-gesture-detectors, which contains a lot of gesture detection. The corresponding blog post is: Http://code.almeros.com/android-multitouch-gesture-detectors#.VibzzhArJXg that side of the above two classes is I stole learning ~ ha

Iv. Testing

The test actually did not say, is to put our Largeimageview into the layout file, and then the activity inside to set up InputStream.

<relativelayout xmlns:android= "http://schemas.android.com/apk/res/android"                xmlns:tools= "http// Schemas.android.com/tools "                android:layout_width=" match_parent "                android:layout_height=" Match_parent " >    <com.zhy.blogcodes.largeimage.view.largeimageview        android:id= "@+id/id_largetimageview"        Android:layout_width= "Match_parent"        android:layout_height= "Match_parent"/></relativelayout>

Then in the activity to set the picture:

Package Com.zhy.blogcodes.largeimage;import Android.os.bundle;import android.support.v7.app.AppCompatActivity; Import Com.zhy.blogcodes.r;import Com.zhy.blogcodes.largeimage.view.largeimageview;import java.io.IOException; Import Java.io.inputstream;public class Largeimageviewactivity extends appcompatactivity{    private Largeimageview Mlargeimageview;    @Override    protected void onCreate (Bundle savedinstancestate)    {        super.oncreate (savedinstancestate);        Setcontentview (R.layout.activity_large_image_view);        Mlargeimageview = (Largeimageview) Findviewbyid (R.id.id_largetimageview);        Try        {            InputStream InputStream = Getassets (). Open ("world.jpg");            Mlargeimageview.setinputstream (InputStream);        } catch (IOException e)        {            e.printstacktrace ();}}    }

OK, so here, the scheme of displaying the macro map and the detailed code are finished, and the overall is very simple.
However, in the actual project, there may be more needs, such as increasing magnification, shrinking, adding quick-slip gestures and so on, then you can refer to this library: Https://github.com/johnnylambada/WorldMap, the library basically to achieve the majority of the requirements, Everyone according to the idea of this article to see this library, it will be much simpler, custom-made also easy. This map of mine is provided in this library.

Android HD Load Long chart or big picture scheme

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.