An attempt to turn pages on the Android page effect

Source: Internet
Author: User
Tags drawtext polyline

Respect the original reprint please specify: from Aigestudio (Http://blog.csdn.net/aigestudio) Power by aige infringement must investigate!

Artillery Zhen Lou

In the first half of the "custom control is very simple" series, we used a full six knots nearly 20,000 words, more than 200 pictures of the graphics of Android, although the space is very large but still only a graphical drawing of the tip of the iceberg, in order to lead everyone to get started, as for the results of the practice to see you ... So these columns are mainly about trying to finish a page turn by using some of the methods learned earlier.

For me personally, I am not very suggesting that you do not have to try to read this article before, because you see other people's thinking will have a inertial thinking towards others ' thinking to rely on, in fact, if you try to analyze to do the words can not do it, or even more simple and efficient method. When I analyzed this effect, I asked my sister for a new notebook for two or three hours, and then I simulated it in PS, and then we finally got a short idea through the drawing calculation.

Page flipping Although almost everyone has tried, unless you have not read the book ... Never touched a book ... Didn't even touch the paper ... A seemingly simple action actually hides a huge amount of information, if we want to turn the process of simulation to real, involving the surface will be quite wide, here I assume that we do the page turn effect is similar to the text page, from the lower right corner to flip the paper to the left, Our control is assumed to be the right part of the textbook and the left part is not visible outside the left side of our control, then the left end of the control can be regarded as the binding line of our textbook, so that we can simplify the problem, as I said before, the control must be imperfect, if there is a perfect control does not need our custom ~ So what is the effect of this control? The effect is simple, to pass in multiple pictures in the control, we display the pictures in the form of a page turn. The whole page of the principle is figured, although this effect I simulated very simple, but you can completely according to my thinking definition viewgroup or valueanimation and so on ...

In order to further simplify the problem, we will be divided into four parts of the entire page, the first part of the implementation of the page, the second part is the realization of the zigzag page, the third part of our attempt to introduce the Curve page, the fourth part of the subsequent effects of the processing and optimization of efficiency, If necessary, some chapters will be added to improve the effect. So our process is very clear, this section we first to try to achieve the page, if you can which book or books to follow my ideas to go better, was intended to take some photo as a display, but I found that the reality of the page is not control, forget, some theoretical things have to rely on your own hands + brain repair.

First of all, we have to make some conventions, said that our simulation of the page turn effect is to the left of the control of the paper binding line so that it can be achieved from the lower right corner of the effect, which is one of the Convention, and secondly, we stipulate to ignore the set of the arc relative to this page so that it is always parallel with this page Again, the point of view and the direction of the light source is located directly above the paper and the light source is a single light spot light cone just cover the paper, these conventions do not understand it does not matter, I will specify when involved.

We know that view is not a container that can hold other controls like ViewGroup, so let's put some pictures in the view and show them in turn. Through the front-facing canvas learning, we can try to use the canvas's "layer" function to achieve this effect, will bitmap in order to different "layer" and then through the canvas of the Clipxxx method clipping rendering different bitmap, theoretically feasible, then what is the actual? Let's try, create a new view subclass Pagecurlview:

public class Pageturnview extends View {private list<bitmap> mbitmaps;//bitmap data List public Pageturnview (context context , AttributeSet Attrs) {Super (context, attrs);}}
Similarly, Pagecurlview is data-related, and we provide a way to set the data for Pagecurlview:

/** * Set BITMAP data *  * @param mbitmaps *            bitmap data list */public synchronized void Setbitmaps (list<bitmap> mbitmaps) {/* * Throws an exception if the data is empty */if (null = = Mbitmaps | | mbitmaps.size () = = 0) throw new IllegalArgumentException ("No bitmap to display");/* * If the data length is less than 2 GG */if (Mbitmaps.size () < 2) throw new IllegalArgumentException ("Fuck you and fuck to use ImageView"); th Is.mbitmaps = Mbitmaps;invalidate ();}
Here to note, if the picture is less than two, then there is no need to do page flipping effect, of course you can also draw it out and then in the user "page" when the Prompt "is the last page" also can, here I directly do not allow the number of pictures less than 2 Zhang.

In the custom control is actually very simple 5/12 we have customized a polyline view, in which we set up an initialization data for Polylineview, which shows a set of random value data by default when the user does not set the data. That's right here. I don't do initialization data, but when I draw, if the data is empty then we display a set of text messages prompting the user to set the data:

/** * Default display *  * @param canvas *            Canvas object */private void Defaultdisplay (canvas canvas) {//Draw background Canvas.drawcolor (Colo R.white);//Draw title text mtextpaint.settextsize (Mtextsizelarger); Mtextpaint.setcolor (color.red); Canvas.drawtext ("FBI WARNING ", MVIEWWIDTH/2, MVIEWHEIGHT/4, mtextpaint);//Draw hint text mtextpaint.settextsize (mtextsizenormal); Mtextpaint.setcolor (Color.Black); Canvas.drawtext ("Please set data use Setbitmaps method", MVIEWWIDTH/2, MVIEWHEIGHT/3 , mtextpaint);}

If no data is set, the default display for Pagecurlview is as follows:


If there is data, then we have to adjust the size of the bitmap before we draw them, here I will directly correct the size of the bitmap with the control, of course, in practical applications you can scale the picture to maintain the aspect ratio, here I directly discard the aspect ratio:

/** * Initialize bitmap data * Scaled bitmap size matches screen */private void initbitmaps () {list<bitmap> temp = new arraylist<bitmap> (); for (int i = 0; I < mbitmaps.size (); i++) {Bitmap Bitmap = Bitmap.createscaledbitmap (Mbitmaps.get (i), Mviewwidth, Mviewheight, True); Temp.add (Bitmap);} Mbitmaps = temp;}
So the data is there, and we try to draw it out to see:

/** * Draw Bitmap *  * @param canvas *            Canvas object */private void drawbtimaps (canvas canvas) {for (int i = 0; i < mbitmaps . Size (); i++) {canvas.save (); Canvas.drawbitmap (Mbitmaps.get (i), 0, 0, null); Canvas.restore ();}}
As shown in the code above, every time we draw a bitmap we lock and restore the canvas so that each bitmap is drawn independently of each other to facilitate our operation:


Very spectacular building ~ Although we have bitmap drawn out, but careful friends will find that the drawing order is reversed, located at the end of the list of the bitmap is drawn at the top, very simple, we in the initbitmaps when the head is not it:

private void Initbitmaps () {list<bitmap> temp = new arraylist<bitmap> (); for (int i = Mbitmaps.size ()-1; I &G t;= 0; i--) {Bitmap Bitmap = Bitmap.createscaledbitmap (Mbitmaps.get (i), Mviewwidth, Mviewheight, True); Temp.add (Bitmap);} Mbitmaps = temp;}
The first picture is displayed when you run it:


Classical European Architecture ~ ~

Many attentive friends may have such a question to ask what is not in the drawbtimaps inside the flip order? There are two reasons, one is that since it is initialized data then we want to be able to get the data after Initbitmaps is directly can be used instead of at the time of draw do not need to calculate the impact efficiency, the other is we will perform some calculations in Drawbtimaps, Some of the parameters required include some parameters of the loop, and if our loops have to be computed by ourselves, it will necessarily increase the complexity of the logic.

The picture is drawn, but how do you "cover" the previous one and display the next picture? We can use the "custom control is actually very simple 5/12" described in the Clipxxx clipping method to do, by controlling the cliprect right coordinates to display the picture, good we modify the Drawbtimaps method to add clip:

/** * Draw Bitmap *  * @param canvas *            Canvas object */private void drawbtimaps (canvas canvas) {for (int i = 0; i < mbitmaps . Size (); i++) {canvas.save (); canvas.cliprect (0, 0, mclipx, mviewheight); Canvas.drawbitmap (Mbitmaps.get (i), 0, 0, NULL); Canvas.restore ();}}
MCLIPX is the coordinate value of the right end of the cropping area, and we assign it to the width of the control in onsizechanged:

@Overrideprotected void onsizechanged (int w, int h, int oldw, int oldh) {//Omit some code ...//Initialize crop right end coordinates mclipx = mviewwidth;}
and override the view's Ontouchevent method to get the event, assign the x-coordinate of the current touch point to mclipx and redraw the view:

@Overridepublic boolean ontouchevent (Motionevent event) {//Gets the x-coordinate of the touch point mclipx = Event.getx (); invalidate (); return true;}
At this point the effect is as follows:


You can see that although we can clip the picture by swiping the finger, but the visual does not reach our desired effect, clip cuts all the pictures and we actually just want to crop the first one and make the second one appear ... OK, then we'll change the Drawbtimaps method:

/** * Draw Bitmap *  * @param canvas *            Canvas object */private void drawbtimaps (canvas canvas) {for (int i = 0; i < mbitmaps . Size (); i++) {canvas.save ();/* * Crop only the canvas area at the top level */if (i = = Mbitmaps.size ()-1) {canvas.cliprect (0, 0, mclipx, mviewheight);} Canvas.drawbitmap (Mbitmaps.get (i), 0, 0, null); Canvas.restore ();}}
We crop only on the top-most canvas and the rest remains the same, so we can get a "flip" effect:


Now think about every time we go to the swipe from the right to the left, but our fingers are width, want to accurately slide from right to left too troublesome, we can set an area at both ends, when the current touch point in the area to let our pictures automatically slide to or to the left or to the right:

@Overridepublic boolean ontouchevent (Motionevent event) {switch (Event.getaction () & Motionevent.action_mask) { DEFAULT://gets the x-coordinate of the touch point mclipx = Event.getx (); invalidate (); break;case motionevent.action_up://Contact Lift// Determine if you need to automatically slide Judgeslideauto (); return true;}
So this event is triggered when the finger is lifted, and when the finger is lifted, the position of the current point is determined:

/** * Determine if automatic sliding is required * Draw */private void Judgeslideauto () according to the current value of the parameter, if the right endpoint coordinates of the crop are within the area of the left end of the control one-fifth, then we will let it slide directly to the left side of the control */if (mclip X < Mviewwidth * 1/5f) {while (mclipx > 0) {mclipx--;invalidate ();}} /* If the right endpoint coordinates of the crop are within the area of the right end of the control one-fifth, then we'll let it slide directly to the right end of the control */if (mclipx > Mviewwidth * 4/5f) {while (Mclipx < mviewwidth) {MCLI Px++;invalidate ();}}}
, when our touch points are lifted up in the area 1/5 from the left end of the control, the image is automatically adsorbed to the left end, and the image is automatically adsorbed to the right when our touch points are lifted up in the area 4/5-5/5 the right end of the control:


OK, looks like there's no problem, is there? hahaha haha ha ha haha haha haha if you really want to think so you are fooled, we can seriously see if it is really no problem, here in fact I give you dug a hole, seemingly no problem, in fact, involves a very important information, the next section we will talk about. It is important to note that because we will constantly trigger touch events, which means that ontouchevent is constantly being called, and in Ontouchevent we will repeatedly calculate the values of Mviewwidth * 1/5f and Mviewwidth * 4/5f, This is quite detrimental to the efficiency of our control, and we consider encapsulating it as a member variable and giving the initial value in onsizechanged:

@Overrideprotected void onsizechanged (int w, int h, int oldw, int oldh) {//Omit some code ...//Calculate the area of the left and right of the control automatically adsorbed autoarealeft = Mvie Wwidth * 1/5f;autoarearight = Mviewwidth * 4/5f;}
Again in the Judgeslideauto call:

private void Judgeslideauto () {/* * If the right endpoint coordinates of the crop are within the area of the left end of the control one-fifth, then we directly let it slide automatically to the left side of the control */if (Mclipx < Autoarealeft) {while (mcli PX > 0) {mclipx--;invalidate ();}} /* If the right endpoint coordinates of the crop are within the area of the right end of the control one-fifth, then we'll let it slide directly to the right end of the control */if (Mclipx > Autoarearight) {while (Mclipx < mviewwidth) {mclipx++; Invalidate ();}}}
The reuse of objects and parameters must be perfected, especially in methods such as OnDraw, Ontouchevent, which are likely to be repeatedly called, avoiding unnecessary computations (especially floating-point calculation) and object generation, which is of great significance to improve the efficiency of view operation.

So far we have succeeded in turning up the first picture, but how can we continually turn over the remaining pictures? Before we do that, let's start by looking at a waste of resources affecting efficiency. I passed five pictures in the setbitmaps, which means that the size of the Mbitmaps data list is 5, and in Drawbtimaps we directly loop through the five pictures in the canvas:

for (int i = 0; i < mbitmaps.size (); i++) {canvas.save ();//Omit some code ... canvas.drawbitmap (Mbitmaps.get (i), 0, 0, null); Canv As.restore ();}
Now think about whether we have such a need? Here are five pictures, if it is 10, 100, 1000, 10000 Zhang ... It? So direct one-time all draw absolutely forced to collapse ~ and in fact we do not have to go to draw so many pictures at once, because we will only show a maximum of two: the last one and the next display, that is, we only need to display the current topmost two pictures can ~ This can greatly improve the efficiency of our drawing when there are a large number of pictures, and then through the judgment of some parameters to constantly draw the next two pictures in the sliding process until the last picture is finished, so~ we change the drawbtimaps:

/** * Draw Bitmap * * @param canvas * Canvas object */private void drawbtimaps (canvas canvas  {//Draw bitmap before resetting islastpage to Falseislastpage = false;//Limit pageIndex value range PageIndex = PageIndex < 0? 0:pageindex;pageindex = PageIndex > Mbitmaps.size ()? Mbitmaps.size (): pageindex;//calculate data start position int start = Mbitmaps.size ()-2-pageindex;int end = Mbitmaps.size ()-pageindex;/ * * If the data start position is less than 0 indicates that the last picture is currently */if (Start < 0) {//At this time set Islastpage to Trueislastpage = true;//and display the message Showtoast ("This is F Ucking Lastest Page ");//force reset start Position start = 0;end = 1;} for (int i = start; i < end; i++) {canvas.save ();/* * Only the top-most canvas area is cropped * If the last page is not executed, the cropping */if (!islastpage && i = = E nd-1) {canvas.cliprect (0, 0, mclipx, mviewheight);} Canvas.drawbitmap (Mbitmaps.get (i), 0, 0, null); Canvas.restore ();}} 
We have added a member variable of type int pageindex, which is used as the reference value for calculating the read list of data. What do you mean by agreeing that the area on the left side of the control is less than Autoarealeft? If our fingers touch the point in that area, then we think the user's action is "go back to the previous page" (Of course you can also calculate the difference between the slip starting point of the positive and negative to determine the user's behavior, the event is not the focus of this series is not said). The function of pageindex can be expressed in simple use:


The five colors represent five pictures, the upper-left ordinal indicates its subscript position in the list, when pageindex is 0 o'clock start is 3 and end is 5, then it means that the final picture of the list is drawn at the top, and then the penultimate one is drawn. If pageindex is 1 o'clock start and end is 4, it means that the second-to-last picture of the list is drawn at the top, and then the third from the bottom. And so on, how do we control the value of the pageindex? From the above, pageindex++ means that the next page is displayed and pageindex--represents the previous page, then the story is very simple, when our mclipx value of 0 o'clock means that the picture has been cropped, then we can make pageindex++, When the user's finger touches the rollback area, let pageindex--show back to the previous page, since the need to judge the event, then we have to modify the ontouchevent slightly:

@Overridepublic boolean ontouchevent (Motionevent event) {//per trigger TouchEvent reset Isnextpage to Trueisnextpage = true;/* * Determine the current event type */switch (Event.getaction () & Motionevent.action_mask) {case motionevent.action_down://touch screen// Gets the current event point x-coordinate mcurpointx = Event.getx ();/* If the event point is located in the rollback area */if (Mcurpointx < Mautoarealeft) {//Then the next page is not turned, but the previous page isnextpage = FA Lse;pageindex--;mclipx = Mcurpointx;invalidate ();} Break;case motionevent.action_move://sliding float Slidedis = mcurpointx-event.getx (); if (Math.Abs) > Mmovevalid) {//Gets the x-coordinate of the touch point mclipx = Event.getx (); invalidate ();} Break;case motionevent.action_up://Contact Lift//Determine if the need to automatically slide Judgeslideauto ();/* If the current page is not the last page * if it is necessary to turn the next page * and the previous page has been clip off */if ( !islastpage && isnextpage && mclipx <= 0) {pageindex++;mclipx = Mviewwidth;invalidate ();} break;} return true;}
In the Action_move event, we redefined the execution criteria of the event, if the move is less than mmovevalid = Mviewwidth * 1/100f that is 1% of the width of the control is not valid, the comments on the above code and the above analysis of the same process is not much to pull, The specific results are as follows:


Here's all the code for this part of Pageturnview, and the next section we'll try to introduce a polyline that will simply switch from right to left into a folding flip effect.

public class Pageturnview extends View {private static final float Text_size_normal = 1/40f, Text_size_larger = 1/20f; The standard text size and the large text size account for the private textpaint mtextpaint;//text Brush Private Context mcontext;//Context environment Reference private list<bitmap> mbitmaps;//bitmap data List private int pageindex;//current display mbitmaps data subscript private int mviewwidth, mviewheight;//control wide height private float Mtextsizenormal, mtextsizelarger;//standard text size and large text size private float mclipx;//cropped right endpoint coordinates private float mautoarealeft, mautoarearight;//Control Auto-adsorbed area on left and right of private float mcurpointx;//fingertip touch screen point x coordinate value private float mmovevalid;//effective distance to move event private Boolean isnextpage, islastpage;//whether to display the next page, whether the last page identifies the value of public pageturnview (context context, AttributeSet Attrs) {super ( context, attrs); mcontext = context;/* * Instantiate a text brush and set parameters */mtextpaint = new Textpaint (Paint.anti_alias_flag | Paint.dither_flag | Paint.linear_text_flag); mtextpaint.settextalign (Paint.Align.CENTER);} @Overridepublic boolean ontouchevent (Motionevent event) {//per trigger TouchEvent reset Isnextpage to Trueisnextpage = true;/* * Determine current event type */switch (Event.getaction () & Motionevent.action_mask) {case motionevent.action_down://touch screen// Gets the current event point x-coordinate mcurpointx = Event.getx ();/* If the event point is located in the rollback area */if (Mcurpointx < Mautoarealeft) {//Then the next page is not turned, but the previous page isnextpage = FA Lse;pageindex--;mclipx = Mcurpointx;invalidate ();} Break;case motionevent.action_move://sliding float Slidedis = mcurpointx-event.getx (); if (Math.Abs) > Mmovevalid) {//Gets the x-coordinate of the touch point mclipx = Event.getx (); invalidate ();} Break;case motionevent.action_up://Contact Lift//Determine if the need to automatically slide Judgeslideauto ();/* If the current page is not the last page * if it is necessary to turn the next page * and the previous page has been clip off */if ( !islastpage && isnextpage && mclipx <= 0) {pageindex++;mclipx = Mviewwidth;invalidate ();} break;} return true;} /** * Determine if automatic sliding is required * Based on the current value of the parameter, automatically slide */private void Judgeslideauto () {/* * If the right endpoint coordinates of the crop are within the area of the left end of the control one-tenth, then we will let it slide directly to the left side of the control */if (mCl IpX < Mautoarealeft) {while (mclipx > 0) {mclipx--;invalidate ();}} /* If the right endpoint coordinates of the crop are within the area of the right end of the control one-tenth, then we'll let it slide directly to the right end of the control */if (Mclipx > Mautoarearight) {while (mclipx < Mviewwidth) {mclipx++;invalidate ();}}} @Overrideprotected void onsizechanged (int w, int h, int oldw, int oldh) {//Get control wide height mviewwidth = w;mviewheight = h;//Initialize number of bitmaps According to Initbitmaps ();//Calculate text Size Mtextsizenormal = text_size_normal * Mviewheight;mtextsizelarger = Text_size_larger * mviewheight;//Initialize crop Right endpoint coordinates mclipx = mviewwidth;//calculates the area to the left and right of the control automatically adsorbed mautoarealeft = mviewwidth * 1/5f;mautoarearight = MView Width * 4/5f;//calculates a once effective distance mmovevalid = Mviewwidth * 1/100F;}  /** * Initialize bitmap data * Scaled bitmap size matches screen */private void initbitmaps () {list<bitmap> temp = new arraylist<bitmap> (); for (int i = Mbitmaps.size ()-1; I >= 0; i--) {Bitmap Bitmap = Bitmap.createscaledbitmap (Mbitmaps.get (i), Mviewwidth, Mviewheight, True); Temp.add (Bitmap);} Mbitmaps = temp;} @Overrideprotected void OnDraw (canvas canvas) {/* * Displays the default prompt text */if if the data is empty (null = = Mbitmaps | | mbitmaps.size () = = 0) {Defau Ltdisplay (canvas); return;} Draw Bitmap Drawbtimaps (canvas);} /** * Default Display * * @param canvas * Canvas object */private void DefaulTdisplay (canvas canvas) {//Draw Canvas.drawcolor (Color.White);//Draw title text mtextpaint.settextsize (Mtextsizelarger); Mtextpaint.setcolor (color.red); Canvas.drawtext ("FBI WARNING", MVIEWWIDTH/2, MVIEWHEIGHT/4, mtextpaint);// Draw hint text mtextpaint.settextsize (mtextsizenormal); Mtextpaint.setcolor (Color.Black); Canvas.drawtext ("Please set data Use Setbitmaps method ", MVIEWWIDTH/2, MVIEWHEIGHT/3, mtextpaint);} /** * Draw Bitmap * * @param canvas * Canvas object */private void drawbtimaps (canvas canvas) {//Draw bitmap before reset Islastpage to Falsei Slastpage = false;//Limit the value range of PageIndex PageIndex = PageIndex < 0? 0:pageindex;pageindex = pageIndex > mbitmaps.size ()? Mbitmaps.size (): pageindex;//calculate data start position int start = Mbitmaps.size ()-2-pageindex;int end = Mbitmaps.size ()-pageindex;/ * * If the data start position is less than 0 indicates that the last picture is currently */if (Start < 0) {//At this time set Islastpage to Trueislastpage = true;//and display the message Showtoast ("This is F Ucking Lastest Page ");//force reset start Position start = 0;end = 1;} for (int i = start; i < end; i++) {canvas.save ();* * Crop only in the top-most canvas area * If the last page is not executed crop */if (!islastpage && i = = end-1) {canvas.cliprect (0, 0, mclipx, mviewheight);} Canvas.drawbitmap (Mbitmaps.get (i), 0, 0, null); Canvas.restore ();}}  /** * Set BITMAP data * * @param bitmaps * Bitmap data list */public synchronized void Setbitmaps (list<bitmap> bitmaps) {/* * Throws exception if data is empty */if (null = = Bitmaps | | bitmaps.size () = = 0) throw new IllegalArgumentException ("No bitmap to display");/* * If the data length is less than 2 GG */if (Bitmaps.size () < 2) throw new IllegalArgumentException ("Fuck you and fuck to use ImageView"); mBi Tmaps = Bitmaps;invalidate ();} /** * Toast Display * * @param msg * Toast display text */private void Showtoast (Object msg) {Toast.maketext (Mcontext, msg.to String (), Toast.length_short). Show ();}}
This part of the source code download: Portal

An attempt to turn pages on the Android page effect

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.