A ListView with a Pac-Man delete animation
This is a boring effect, written by a boring program ape, in the case of boredom.
Although this effect is not used, but as a study.
First
Results at a glance, mainly:
(1) Perform Pac-Man animation when item is removed
(2) When scrolling, the bean-eater also moves accordingly.
(3) Dealing with transitions between visible and invisible states
Simple principle Analysis:
(1) Eat beans, beans, and the left side of the white rectangle (of course, all colors can be changed, you want to change the picture) are painted with canvas.
(2) Q: Where does the canvas come from? Answer: The canvas of the ListView. This method of rewriting the ListView is specific:
protected void Dispatchdraw (Android.graphics.Canvas Canvas)
Q: Why not rewrite the OnDraw method?
A: For a container control, the OnDraw method is not necessarily called when it is drawn. Because the container itself is nothing to draw (unless you set up a background or something), you just need to call Dispatchdraw to distribute the drawing request to the child view, and let the child view draw itself out. So when overriding ViewGroup and its subclasses, overriding the Dispatchdraw method is more insurance.
(3) Use handler to send a redraw request every once in a while, and overlay the coordinates of the bean, bean, and left white rectangles until they reach the border
(4) Monitor the scroll event, which is to update the coordinates of the Pac Man, the Bean, and the left white rectangle when scrolling
(5) Write an interface, you can notify the caller when the Bean eater finishes eating.
Start implementing:
(1) Perform some initialization including brushes, etc.:
/** * Initialize */private void init () {paintrect = new Paint ();p aintrect.setcolor (color.white);p Aintrect.setantialias (true); PAINTC = new Paint ();p aintc.setcolor (color.yellow);p Aintc.setantialias (true); rectdrawrect = new Rect (); Rectfdrawarc = New RECTF (); Super.setonscrolllistener (Mscrolllistener);}
Two pens, one painted white rectangle, one painting to eat bean man and beans.
Two rectangles, one is the area of the white rectangle, and the other is the area where the bean-eater is painted.
The last Call to Super.setonscrolllistener (Mscrolllistener) is to set up the scrolling listener, for the external can also be monitored, using a member variable to save the external settings of the Onscrolllistener, and synchronously notifies the listener outside when a scrolling event occurs.
/** * Because you can only set a Onscrolllistener to the ListView This is used to save user settings Onscrolllistener * and in the scrolling event to notify */private Onscrolllistener Userscrolllistener;
Private Onscrolllistener Mscrolllistener = new Onscrolllistener () {@Overridepublic void onscrollstatechanged ( Abslistview view, int scrollstate) {if (Userscrolllistener! = NULL)//notifies the external listener userscrolllistener.onscrollstatechanged ( View, scrollstate);} @Overridepublic void Onscroll (abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) {//scroll to be more New position and determine if the state is still visible if (Removep >-1) {Removeview = Getchildat (Removep-firstvisibleitem); if (rectdrawrect! = null &&am P Removeview = null) {rectdrawrect.top = Removeview.gettop (); rectdrawrect.bottom = Removeview.getbottom ();} Show = ((Removep >= firstvisibleitem) && (Removep <= (Firstvisibleitem + VisibleItemCount));} if (Userscrolllistener! = null) {//Notifies the external listener userscrolllistener.onscroll (view, Firstvisibleitem, VisibleItemCount, Totalitemcount);}}
(2) Establish calculation logic:
At first glance to calculate the coordinates of each part is very complex, but actually fried chicken simple.
My idea is to first think about what is known or can be easily obtained.
For example, here we have to remove an item, so we must know the position of the item. With this position, we can get the item's view (How to get it later), including the information contained in the view, such as coordinates in four directions (left,top,right,bottom), height and width, etc. (others we don't need to say).
After getting this information, look at what we need.
First white rectangle, it is easy to see the white rectangle's upper left and left bottom vertices are coincident with the item view, so the white rectangle left,top,bottom should be equal to the item's view. As for right, it should be controlled by us to dynamically change the range should be from 0 to item right.
Then the bean man is more simple, first the center should be in the white rectangle to the right of the midpoint, this is easy to calculate. The radius should be half the height of item, this is much easier.
Finally, those beans. Similar to PAC people, just a small radius, and then loop the painting until the border.
OK, first look at the method of providing an external call to start the Pac-Man animation, and of course our data will be initialized here:
public void starteat (int position) throws Throwable {if (Removep! =-1) {//prev has not finished eating throw new Throwable ("You can not eat an Other item before last one done! ");} if (position >= getfirstvisibleposition () && position <= getlastvisibleposition ()) {//If the item status is visible show = t rue;//gets the item to be removed Viewremoveview = Getchildat (Position-getfirstvisibleposition ());//initializes the left white rectangle's coordinate rectdrawrect.set ( Removeview.getleft (), Removeview.gettop (), 0, Removeview.getbottom ());//record the right coordinate of item itemright = Removeview.getright ( );//Record the height of item itemheight = rectdrawrect.bottom-rectdrawrect.top;//Record the position of item in the data source REMOVEP = position;//Send Message, began to eat mhandler.sendemptymessage (JUST_EAT_IT);} else {//Item not visible, no need to perform animation, direct notification finished show = FALSE;REMOVEP = -1;if (Meatfinishlintener! = null) { Meatfinishlintener.oneatfinish (position);}}}
one thing to say is how to get the corresponding view through position.
We all know that there is an array of view inside the ViewGroup that stores the child view (if you don't know it, you now know), you can use the Getchildat (index) method to remove the view of the corresponding index.
So now the question is, how do we use the excavator to dig up the item's view?
Good! The front row Xiao Ming raised his hand, he raised his hand, now Einstein, Hua, Newton Soul possessed, he is not a person in the answer, he is not a person ...
I saw him stand up proudly, quite he was proud of the chest, confidently replied: View Childview = getchildat (position);
Immediately the teacher smiled happily, you did not let me disappointed, really answered wrong, now you can get out! (Feel the world's deep hatred for xiaoming)
Xiao Ming's answer is wrong, the reason is my little ape to say to everyone:
Because the view array in the ListView holds not all of the item's view, only the view of the visible item is saved. That is, the number of elements in the array is the number of items visible in the ListView, and the view of the first visible item in the ListView has an index of 0 in the array.
And here the position is relative to the entire ListView of all item (including visible and invisible), so we will first convert the position into the sub-view array position. In other words, subtract the first visible item from the position, The ListView also provides a way to get the position of the first visible item (note: The position here refers to the getfirstvisibleposition () relative to all of the item).
So the correct answer should be: View child = Getchildat (Position-getfirstvisibleposition ()); (Don't understand yourself assuming a few specific examples to figure it out)
Xiao Ming, you can go to the peace of mind, told me to rob Xiao Red! Little Red before the age of 35 is mine, after 35 years old you want to be able to give you.
From then on, I and Little Red live a happy life.
Let me first revel and then continue ...
Well, back to the point, we got the item's view and sat down on the position of the white rectangle.
Next we're going to use handler to continue sending the message overlay the white rectangle right, and the white rectangle right is the basis for our calculation of the bean and Bean's coordinate information.
(3) Start data overlay and continuous request redraw
Private Handler Mhandler = new Handler (new Handler.callback () {@Overridepublic Boolean handlemessage (Message msg) {//TODO Auto-generated method stub switch (msg.what) {case just_eat_it://request redraw invalidate (Rectdrawrect.left, Rectdrawrect.top, Itemright, Rectdrawrect.bottom);//If you have finished eating if (rectdrawrect.right >= itemright) {show = False;if (Meatfinishlintener! = null) {//Pass The requestor has finished meatfinishlintener.oneatfinish (REMOVEP);} REMOVEP =-1;} If you have not finished eating else {//judging the SweepAngle >=) {csangle = 10;cangle = -20;} else if (SweepAngle <=) {Csangle = -10;cangle = 20;} Calculates a new mouth angle startangle + = Csangle;sweepangle + = cangle;//Left rectangle to the right to grow rectdrawrect.right + = 20;// Send a message to draw the next frame mhandler.sendemptymessagedelayed (Just_eat_it, 40);} break;} return false;}});
In the same way, the mouth action of the Bean Eater is achieved by dynamically changing the startangle (the angle of the drawing) and the Sweepangle (the degree to be drawn) in the handler, and the concrete logic is not difficult to scrutinize.
Finally, the data are basically calculated, and began to draw.
(4) I draw and draw:
protected void Dispatchdraw (Android.graphics.Canvas canvas) {super.dispatchdraw (canvas); if (show) {// Draw the left rectangle Canvas.drawrect (rectdrawrect, paintrect);//Calculate the Rectangle Rectfdrawarc.set (rectdrawrect.right-itemheight > > 1), Rectdrawrect.top, rectdrawrect.right+ (itemheight >> 1), rectdrawrect.bottom);//Painting Pac Man Canvas.drawarc ( Rectfdrawarc, StartAngle, SweepAngle, True, PAINTC);//Calculate the radius and position of the beans int radius = itemheight >> 3;//radius int offset = radius * 4;//interval float cy = rectdrawrect.top + (itemheight >> 1);//y-coordinate of the center of int circlesleft = Rectdrawrect.right + (itemheight >> 1); for (int i = itemright-radius-10; I >= circlesleft; I-= offset) {//right-to-left-draw peas canvas.drawcircle (i, CY, RA Dius, PAINTC);}};
Show is to identify whether the item is currently visible, if the current item is not visible, the calculation will continue, but does not request the drawing, the painting is not seen.
The calculation of the Pac-Man's rectangular coordinates here and not in the handler is because in the rolling handler will have a short delay, will lead to the short-term situation of people eating beans.
Some bit operations are used here purely to improve the force lattice.
This is purely I am bored to write to play, in fact there are many places need to optimize, so welcome to spit Groove!
The principle of this boring effect can actually be used to implement some other ListView effects, such as a ListView that can fix a view of an item on top of it. Simply say the idea, and later have the opportunity to elaborate.
(1) Listen to scrolling events, the main judge Firstvisibleitem, according to the external logic to determine the item to dock at the top of
(2) Gets the view of the item to be docked (followed by Mheadview) (I do not know how to get to see again I and Xiao Ming and Little red between those who have to say the story)
(3) Calculate the position of the mheadview (in fact, in addition to the top, you want to show in other places as long as the screen is also completely possible)
(4) Use Drawchild (canvas, Mheadview, Getdrawingtime ()) in the Dispatchdraw method to draw it out.
This article is so long, but also buy one to get one, so, quickly raise your praise of the mouse hand
Demo download
Android Curious baby _ watch face World _02