First, let's take a look at the example.
I will know if I have used multi-meter music. This UI can be slide up and down, so I can draw a line when I'm bored. This is called "feelings" by the hammer company Lao Luo ", in fact, "fun" is more appropriate. Hey hey. nowadays, mobile Internet is developing so fast that the market is no longer in the initial stage where you can have hundreds of thousands of users by simply hitting an app and putting it on shelves. recently, apple, for fear that android downloads will catch up with Apple's stores, claims that:
50 billion users who download the app can get a $10,000 iTunes gift card. In addition, the top 50 users that follow the 500th million will also get a $500 gift card. as for the mobile development trend, I think people who want to engage in Mobile it are quite clear and far away ). in fact, the application UI special effects are a huge part of the application. If the two software with the same function have a better function, such as "Netease news", and the other is a little close like "Sina news ", if you are a user, you will certainly choose the Netease client. the conclusion is that "operability" plays a crucial role in the product.
Next, let's take a look at how to implement it. First, we declare that this implementation method is not very good. Here I am just proposing a solution for everyone to innovate based on their own ideas.
Principle:Relativelayout + custom scrollview.
Let's look at the layout structure.
In fact, there is no technical content. I will briefly introduce: Red represents the background photo, green represents the custom scrollview, and pink represents the transparent area you want to edit. I can't explain it too much. You must understand it. Let's take a look at the code.
As it is a special effect of feelings (no specific callback event requirements), there is no need for Custom listening and callback processing. I directly inject the UI to be processed into the custom control, in this way, it is convenient for me.
The preceding implementation is incorrect, but I hope you can read it carefully. I believe you will learn some knowledge.
First, inject the background image and top line into the control. Next, let's take a look at the ontouchevent event, because at the beginning and end, she was working.
/***** Touch event ** @ Param EV */Public void commontouchevent (motionevent eV) {int action = eV. getaction (); Switch (Action) {Case motionevent. action_down: inittouchy = eV. gety (); current_top = inittop = imageview. gettop (); current_bottom = initbottom = imageview. getbottom (); lineup_current_top = line_up_top = line_up.gettop (); lineup_current_bottom = line_up_bottom = line_up.getbottom (); break; Case motionevent. action_up:/** contraction animation **/If (isneedanimation () {animation ();} ismoveing = false; touchy = 0; // 0 is returned when you release your finger. break;/***** exclude the first mobile computing, because the height of deltay cannot be known for the first time. However, we also need to initialize the system so that the sliding distance is 0 when the first mobile operation is performed. * After the record is accurate, it will be executed normally. */case motionevent. action_move: log. E (TAG, "ismoveing =" + ismoveing); touchy = eV. gety (); float deltay = touchy-inittouchy; // The sliding distance from log. E (TAG, "deltay =" + deltay);/** filter: **/If (deltay <0 & inner. gettop () <= 0) {return;} // The isneedmove (); If (ismoveing) {// initialize the header rectangle if (normal. isempty () {// Save the normal layout position normal. set (inner. getleft (), inner. gettop (), inner. getright (), inner. getbottom ();} // move the layout (1/3 of gesture movement) float inner_move_h = deltay/5; inner. layout (normal. left, (INT) (normal. top + inner_move_h), normal. right, (INT) (normal. bottom + inner_move_h);/** image_bg **/float image_move_h = deltay/10; current_top = (INT) (inittop + image_move_h); current_bottom = (INT) (initbottom + image_move_h); imageview. layout (imageview. getleft (), current_top, imageview. getright (), current_bottom);/** line_up **/float line_up_h = success; lineup_current_top = (INT) (line_up_top + inner_move_h); lineup_current_bottom = (INT) (line_up_bottom + inner_move_h); line_up.layout (line_up.getleft (), lineup_current_top, line_up.getright (), lineup_current_bottom);} break; default: break ;}}
Simple Description:
Motionevent. action_down: obtain the corresponding coordinates with a touch.
Motionevent. action_move:
There is an isneedmove method in it. Purpose: we slide the scrollview itself, or the slide we simulate.
/***** Whether to move the layout inner. getmeasuredheight (): Get the total height of the control ** getheight (): Get the screen height ** @ return */Public void isneedmove () {int offset = inner. getmeasuredheight ()-getheight (); int scrolly = getscrolly (); // If the subviews of scrollview do not exceed one screen, scrolly = 0, true is returned directly, // If the subviews of scrollview exceed one screen, getscrolly () = offset indicates that the subview slides to the lower end of scrollview. in this case, true is returned. if (scrolly = 0 | scrolly = offset) {ismoveing = true ;}}
The most used is:View. layout (L, t, R, B );The function is simple and not explained. For more information, see the source code.
Motionevent. action_up: it is to do some aftercare operations, mainly look at the animation method.
/***** Thumbnail animation */Public void animation () {translateanimation image_anim = new translateanimation (0, 0, math. ABS (inittop-current_top), 0); image_anim.setduration (200); imageview. startanimation (image_anim); imageview. layout (imageview. getleft (), (INT) inittop, imageview. getright (), (INT) initbottom); // enable the mobile animation translateanimation inner_anim = new translateanimation (0, 0, inner. gettop (), normal. top); inner_anim.s Etduration (1, 200); inner. startanimation (inner_anim); inner. layout (normal. left, normal. top, normal. right, normal. bottom);/** line_up **/translateanimation line_up_anim = new translateanimation (0, 0, math. ABS (line_up_top-lineup_current_top), 0); values (200); values (rows); line_up.layout (line_up.getleft (), line_up_top, line_up.getright (), line_up_bottom); normal. sete Mpty ();/** animation execution **/If (current_top> inittop + 50 & turnlistener! = NULL) turnlistener. onturn ();}
Here, I would like to briefly describe it because I have planted some time here.
For example, the original coordinates of the background image are:(0,-1, 190,800,300)As the gesture moves to (0,-100,800,390) 90 pixels, how should we write the translateanimation? I used to think that it would not be enough to point the coordinates at the end to the initial coordinates. As a result, you will find that the animation does not work at all, but is a flash. The reason is that the animation parameters cannot be negative. Maybe because the animation uses () as the reference object, you must write the animation as translateanimation.
Line_up_anim = new translateanimation (0, 0, math. Abs (-190-(-100), 0); then the animation effect we need is realized.
However, new problems have emerged:
When you pull down to a certain State and move up slowly, you will find that the movement is very fast (no contraction reaction), and when you move to the top, the rebound effect suddenly appears. This effect is certainly not what we need. The effect we need is: Pull down to a certain extent, and then slowly move back to the origin (center position) when pulling up. If it is pulled up, do not show a rebound effect. If it is pulled down and released, the rebound effect will appear.
The description is a bit messy. If you want to know the specific effect, I suggest you use papa. In fact, these excellent application UIS in China are copied from other countries. If you use Facebook, you will find that the personal page is like Facebook. See:
Hey, sorry, I ran the question. I will briefly explain the problem above.
First of all, for example, if we pull down 50 pixels with a gesture, the top of the control is set to 50 for the child of the custom scrollview, and the value of getscrolly () is still 0 at this time,However, if you stop the drop-down and pull it up, the getscrolly () will gradually increase from 0. When we move it to the top, that is, move the scrollview to the bottom, at this time, ismoveing is true, so if you continue to pull it up, it will rebound.
It is not difficult to solve this problem, but I have struggled for a long time and made many detours. Here, I want to explain my blind road section and my questions: At that time, I thought that getscrolly () was so disobedient that I had to perform operations on scrollview's children, why didn't I execute layout (L, t, R, B) on the control directly? Later, I updated the control based on the logic and finally changed it. I had to solve the problem again,Execute layout (L, t, R, B) ON THE scrollview itself in the drop-down list, but you cannot slide at this time, the slide of the scrollview is disabled for no reason.I suspect that the parameter was incorrect during layout ., Later, after careful modification, I found that I still could not slide, and Google had no news for a long time. Finally, I gave up and returned to the origin. Then I thought about it... It's a hard task. I finally thought of the solution and hope it will help you.
Taking the short talk mentioned above,For example, if our gesture pulls down 50 pixels, then the touch distance is also 50 pixels. If we pull up at this time, we also need 50 pixels to return to the original position. I think everyone understands this. (First, we need to separate the operations into up and down. If it is down, we will disable the sliding of the custom control when pulling the custom control from the drop-down menu, instead, execute layout through gestures to execute these 50 pixels .)
See some code below:
/** Determine the orientation of the first touch operation: Up or down **/If (deltay <0 & State = state. nomal) {state = state. up;} else if (deltay> 0 & State = state. nomal) {state = state. down;} If (State = state. up) {deltay = deltay <0? Deltay: 0; ismoveing = false; shuttouch = false;} else if (State = state. down) {If (getscrolly () <= deltay) {shuttouch = true; ismoveing = true;} deltay = deltay <0? 0: deltay ;}
The code is very simple, but I have explained it more. If you don't understand it, you will certainly understand it after carefully reading the source code.
Touch event processing:
/** Touch event processing **/@ overridepublic Boolean ontouchevent (motionevent eV) {If (inner! = NULL) {commontouchevent (EV);} // Ture: do not slide the control itself. If (shuttouch) return true; elsereturn super. ontouchevent (EV );}
Note:If the returned value is true, the function is to disable scrollview sliding. The touch event is still stored !!! If you are familiar with the touch event, I believe it is a bit nonsense. Haha, I am also a little cainiao and I am stuck here.
Finally, there is a small bug, that is, the top draw line. If you make scrollview inertial slide, you will find that the top line does not follow the movement, in fact, it is because we cannot get the getscrolly () value during inertial sliding. We haven't found the relevant information after a long time of investigation. This problem will be left here for the time being, it's time to continue.
Here I will post the source code:
Package COM. example. scrollviewdemo; import android. content. context; import android. graphics. rect; import android. util. attributeset; import android. util. log; import android. view. motionevent; import android. view. view; import android. view. animation. translateanimation; import android. widget. imageview; import android. widget. scrollview;/*** custom scrollview ** @ author Jia **/public class personalscrollview extend S scrollview {private final string tag = personalscrollview. class. getsimplename (); private view inner; // child viewprivate float touchy; // when clicked, the Y coordinate private float deltay; // The distance between the Y axis and the private float inittouchy; // The Y coordinate private Boolean shuttouch = false when you click it for the first time; // whether to close the slide of scrollview. private rect normal = new rect (); // rectangle (this is only a form, only used to determine whether an animation is required .) private Boolean ismoveing = false; // whether to start moving. private imageview; // background Widget. private view line_up; // Private int line_up_top; // topprivate int line_up_bottom; // bottomprivate int inittop, initbottom; // The initial height is private int current_top, current_bottom; // drag the time height. Private int lineup_current_top, lineup_current_bottom; // Private onturnlistener turnlistener; private imageview imageheader; Public void setimageheader (imageview imageheader) {This. imageheader = imageheader;} // status: upper part and lower part. Default private Enum state {up, down, nomal}; // default state private State state = state. nomal; Public void setturnlistener (onturnlistener turnlistener) {This. turnlistener = turnlistener;} public Vo Id setline_up (view line_up) {This. line_up = line_up;} // inject the background image public void setimageview (imageview) {This. imageview = imageview;}/***** constructor ** @ Param context * @ Param attrs */Public personalscrollview (context, attributeset attrs) {super (context, attrs);}/***** generate a view based on XML. this function is called at the end of the view generation. After all the child views are added. even if the subclass overwrites the onfinishinflate * method, the method of the parent class should be called to execute the method. * // @ overrideprotected void onf Inishinflate () {If (getchildcount ()> 0) {inner = getchildat (0) ;}/ ** touch event processing **/@ overridepublic Boolean ontouchevent (motionevent eV) {If (inner! = NULL) {commontouchevent (EV);} // Ture: do not slide the control itself. if (shuttouch) return true; elsereturn super. ontouchevent (EV);}/*** touch event *** @ Param EV */Public void commontouchevent (motionevent eV) {int action = eV. getaction (); Switch (Action) {Case motionevent. action_down: inittouchy = eV. gety (); current_top = inittop = imageview. gettop (); current_bottom = initbottom = imageview. getbottom (); If (line_up_top = 0) {Li Neup_current_top = line_up_top = line_up.gettop (); lineup_current_bottom = line_up_bottom = line_up.getbottom ();} break; Case motionevent. action_up:/** thumbnail animation **/If (isneedanimation () {animation ();} If (getscrolly () = 0) {state = state. nomal;} ismoveing = false; touchy = 0; shuttouch = false; break;/*** exclude the first mobile computing, because the height of deltay cannot be known for the first time, however, we also need to initialize the sliding distance to 0 when moving for the first time. * After the record is accurate, it will be executed normally. */case motionevent. acti On_move: Touchy = eV. gety (); deltay = touchy-inittouchy; // sliding distance/** determine the orientation of the first touch operation: up or down **/If (deltay <0 & State = state. nomal) {state = state. up;} else if (deltay> 0 & State = state. nomal) {state = state. down;} If (State = state. up) {deltay = deltay <0? Deltay: 0; ismoveing = false; shuttouch = false;/** line_up **/lineup_current_top = (INT) (line_up_top-getscrolly (); lineup_current_bottom = (INT) (line_up_bottom-getscrolly (); log. E (TAG, "Top =" + getscrolly (); line_up.layout (line_up.getleft (), lineup_current_top, line_up.getright (), lineup_current_bottom);} else if (State = state. down) {If (getscrolly () <= deltay) {shuttouch = true; ismoveing = True;} deltay = deltay <0? 0: deltay;} If (ismoveing) {// initialize the header rectangle if (normal. isempty () {// Save the normal layout position normal. set (inner. getleft (), inner. gettop (), inner. getright (), inner. getbottom ();} // move the layout (1/3 of gesture movement) float inner_move_h = deltay/5; inner. layout (normal. left, (INT) (normal. top + inner_move_h), normal. right, (INT) (normal. bottom + inner_move_h);/** image_bg **/float image_move_h = deltay/10; current_top = (INT) (inittop + image _ Move_h); current_bottom = (INT) (initbottom + image_move_h); imageview. layout (imageview. getleft (), current_top, imageview. getright (), current_bottom);/** line_up **/lineup_current_top = (INT) (line_up_top + timeout); lineup_current_bottom = (INT) (line_up_bottom + inner_move_h ); line_up.layout (line_up.getleft (), lineup_current_top, line_up.getright (), lineup_current_bottom);} break; default: break ;}} /***** Thumbnail animation */Public void animation () {translateanimation image_anim = new translateanimation (0, 0, math. ABS (inittop-current_top), 0); image_anim.setduration (200); imageview. startanimation (image_anim); imageview. layout (imageview. getleft (), (INT) inittop, imageview. getright (), (INT) initbottom); // enable the mobile animation translateanimation inner_anim = new translateanimation (0, 0, inner. gettop (), normal. top); inner_anim. Setduration (1, 200); inner. startanimation (inner_anim); inner. layout (normal. left, normal. top, normal. right, normal. bottom);/** line_up **/translateanimation line_up_anim = new translateanimation (0, 0, math. ABS (line_up_top-lineup_current_top), 0); values (200); values (rows); line_up.layout (line_up.getleft (), line_up_top, line_up.getright (), line_up_bottom); normal. set Empty ();/** animation execution **/If (current_top> inittop + 50 & turnlistener! = NULL) turnlistener. onturn ();}/** whether to enable the animation **/Public Boolean isneedanimation () {return! Normal. isempty ();} /***** run the flip command ** @ author Jia **/public interface onturnlistener {/** to a certain extent, run the command **/void onturn ();}}
:
The interface is a bit ugly, but the UI can be adjusted as needed.
Finally, let me have a little more fun. Here I use the tablelayout layout, not the listview, because the listview conflicts with the scrollview itself. Although there are solutions, I still like to use tablelayout. The Code simulates the 3D Rotation effect. I will not explain it here, and there are many articles related to the Internet. Here we will supply the source code. If you have any questions, please leave a message.
Source code download
Sorry, the source code uploaded above is incorrect, but it is also the initial idea. The code is messy. You can download the following version directly.
Source code download