The implementation of the No-trace transition Pull-down Control for Android Development detailed _android

Source: Internet
Author: User
Tags flush gety throw exception touch

I believe everyone has been familiar with the Drop-down refresh can not be familiar with the market on the Drop-down refresh dazzling, but there are many in my opinion slightly flawed, then I will explain the existence of defects, and then provide a way of thinking to solve this flaw, nonsense not to say more! Look down!

1. Some of the market Drop-down Refresh Control Universal Defect Demo

Take the live Bar app for example:

The 1th situation:

Slide control in the initial 0 position, the gesture slide down and then slide up, you can see sliding to the original position when the sliding control can not slide.

Reason:

The Drop-down refresh control responds to the touch event, followed by a series of events that are handled by it, and when sliding the control to the top, the slide event is consumed by the Drop-down refresh control, and the sliding control cannot be passed through its child control, which is the sliding control.

The 2nd situation:

Slide the control to a location that is not 0, and then pull back to the 0 position, you can see that the dropdown flush head is not pulled out.

Reason:

The sliding control responds to the touch event, and subsequent events are handled by it, and when sliding the control to the top, the slide event is consumed by the sliding control, and the parent control, the Drop-down refresh control, cannot consume the sliding event, so the Drop-down flush head is not pulled out.


Perhaps most people feel irrelevant, put the finger up and then pull down on it, but for obsessive-compulsive disorder I, can provide a seamless transition is the most consistent with operational logic, so next I will explain the realization of the idea.

2. The realization of the way to explain

2.1. Introduction to the event distribution mechanism (from Android development art exploration)

The relationship pseudocode of Dispatchtouchevent, Onintercepttouchevent and Ontouchevent methods

public boolean dispatchtouchevent (motionevent ev) { 
Boolean consume = false;
if (onintercepttouchevent (EV)) { 
consume = ontouchevent (EV);
} else { 
consume = child.dispatchtouchevent ( EV); 
}
return consume; 
}

1. From the code to know if the current view interception event, to their own ontouchevent to deal with, or throw to the child view continue to go the same process.

2. Sequence of events: Activity-> Window-> View, if the view is not dealt with, will eventually be ontouchevent
Processing, is a kind of responsibility chain pattern realization.

3. Normally, a sequence of events can only be intercepted and consumed by one view.

4. Once a view has been decided to intercept, this sequence of events can only be handled by it, and its onintercepttouchevent will no longer be invoked

5. Without consuming Action_down, the sequence of events is handled by its parent element.

2.2. General down-pull refresh of the realization of ideas conjecture

First, the Drop-down refresh control, as a container, needs to rewrite both the Onintercepttouchevent and Ontouchevent methods, and then judge the Action_down event in Onintercepttouchevent. Judging by the sliding distance of the child control, if it has not yet slipped, then Onintercepttouchevent returns true to indicate that it intercepts the event, and then displays the hidden logic in the head of the Drop-down flush in ontouchevent, and if the child controls slide over and do not intercept the event, Onintercepttouchevent returns FALSE, and subsequent drop-down refreshes of the header display hidden logical processing cannot be invoked.

2.3. The realization idea of the non-trace transition Drop-down Refresh control

From 2.2 can be seen, to the no trace transition, Drop-down refresh control can not intercept events, at this time you may ask, since the event to the child control, subsequent pull refresh head logic How to achieve it?

This is when you use the event distribution method that is generally ignored dispatchtouchevent, this method returns true in ViewGroup by default, and even if the child control intercepts the event, the dispatchtouchevent of the parent layout is still invoked. Because the event is passed down, this method must be invoked.

So we can make a judgment on the sliding distance of the control in Dispatchtouchevent, and here we'll handle the logic of the Drop-down flush head, while the function call return Super.dispatchtouchevent (event) Before the action of the event is set to Action_cancel, the child control does not respond to the sliding operation.

3. Code implementation

3.1. Identifying Requirements

You need to fit arbitrary controls, such as Recyclerview, ListView, Viewpager, WebView, and plain, sliding view

Cannot affect the original event logic of a child control

Exposure method provides manual call Refresh feature

You can set the Disable dropdown refresh feature

3.2. Code explanation

Required variables

public class Refreshlayout extends LinearLayout {//Hidden state private static final int hide = 0;//dropdown refresh status private static F
inal int pull_to_refresh = 1;
Release the refreshed state private static final int release_to_refresh = 2;
The state being refreshed private static final int refreshing = 3;
The state being hidden private static final int hiding = 4;
Current state private int mcurrentstate = HIDE;
Default time for Head animation (in milliseconds) public static final int default_duration = 200;
Head height private int mheaderheight;
The sliding distance of the content control is private int mcontentviewoffset;
Record the last y-coordinate private int mlasty;
Minimum sliding response distance private int mscaledtouchslop;
Sliding offset private int mtotaldeltay;
Whether to handle the head private Boolean misheaderhandling;
Whether you can drop the flush private Boolean misrefreshable = true;
Whether a content control can be slid, a control that cannot be slid does the optimization of the touch event private Boolean mcontentviewscrollable = true;
The head, in order to facilitate the demonstration selected TextView private TextView mheader;
Container to host the content control, in the XML to place a good private View mcontentview;
Value animation, as the head shows hidden private valueanimator mheaderanimator; Refreshing listener private Onrefreshlistener monrefreshlistener;

Create headers when initialized perform display hidden value animation, add head to layout, and hide head by setting paddingtop

Public Refreshlayout (context, AttributeSet attrs, int defstyleattr) {Super (context, attrs, defstyleattr); Init ()
;
AddHeader (context); private void Init () {mscaledtouchslop = Viewconfiguration.get (GetContext ()). Getscaledtouchslop (); mheaderanimator =
Valueanimator.ofint (0). Setduration (default_duration); Mheaderanimator.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Override public void Onanimationupdate (Valueanimator valueanimator) {if (getcontext () = null) {//If exiting the activity, the animation end does not need to perform a head action return;}//Through
Set the paddingtop implementation to show or hide the head int offset = (Integer) valueanimator.getanimatedvalue ();
Mheader.setpadding (0, offset, 0, 0);
}
});
Mheaderanimator.addlistener (New Animatorlisteneradapter () {@Override public void Onanimationend (animator animation) { if (getcontext () = null) {//If exiting the activity, the animation end does not need to perform a head action return;} if (mcurrentstate = = Release_to_refresh) {//Release refresh State execution
The end of the animation, meaning that the next is refreshed, changed state and invoke the refreshed listening mheader.settext ("refreshing ...");
Mcurrentstate = refreshing; if (monrefReshlistener!= null) {Monrefreshlistener.onrefresh ();}}
else if (mcurrentstate = = hiding) {//dropdown state executes animation end, hide head, change state mheader.settext ("I am Head"); mcurrentstate = hide;}
}); }//Head creation private void AddHeader (context context) {//Forced vertical method SetOrientation (linearlayout.vertical); mheader = new TEXTVI
EW (context);
Mheader.setbackgroundcolor (Color.gray);
Mheader.settextcolor (Color.White);
Mheader.settext ("I Am the Head");
Mheader.settextsize (TYPEDVALUE.COMPLEX_UNIT_SP, 25);
Mheader.setgravity (Gravity.center);
AddView (Mheader, layoutparams.match_parent, layoutparams.wrap_content);
Mheader.getviewtreeobserver (). Addongloballayoutlistener (New Viewtreeobserver.ongloballayoutlistener () {@Override public void Ongloballayout () {//figure out head Height mheaderheight = mheader.getmeasuredheight ();//Remove listener if (Build.VERSION.SDK_INT &G T;= Build.version_codes. Jelly_bean) {mheader.getviewtreeobserver (). Removeongloballayoutlistener (this);} else {Mheader.getviewtreeobserver
(). Removeglobalonlayoutlistener (this); }//Set paddingTop for-mheaderheight, just to hide the head off mheader.setpadding (0,-mheaderheight, 0, 0);
}
}); }

To remove a content control after the layout has finished filling

@Override
protected void Onfinishinflate () {
super.onfinishinflate ();
Set a long click or a short click can consume events, otherwise, if the child does not consume, the final click event will be consumed by its superiors, followed by a series of events only to its superiors to deal with
setlongclickable (true);
Gets the content control
Mcontentview = Getchildat (1);
if (Mcontentview = = null) {
//is an empty throw exception, forces the requirement to set the content control in the XML
throw new IllegalArgumentException ("You must add a content vie W! ");
}
if (!) ( Mcontentview instanceof Scrollingview 
| | mcontentview instanceof WebView 
| | Mcontentview instanceof ScrollView 
| | mcontentview instanceof abslistview) {
//Not with scrolling control, set flag bit
mcontentviewscrollable = false;
}
}

The play is coming, distributing special handling for Drop-down refreshes:

1.mContentViewOffset is used to discriminate the sliding distance of the content page and to handle the operation of the Drop-down refresh when there is no offset value;

2. In mcontentviewoffset! =0 is the first moment that the content page slides, forcing the move event to down, because before move was intercepted, if do not give a down let the content page again set the sliding starting point, there will be a moment sliding a large distance of the pit dad effect.

@Override public boolean dispatchtouchevent (final Motionevent event) {if (!misrefreshable) {//disable drop-down refresh, distribute the event directly to Su
Per.dispatchtouchevent (event); if (mcurrentstate = refreshing | | mcurrentstate = Release_to_refresh | | mcurrentstate = hiding) && mheade
Ranimator.isrunning ()) {//is refreshing, is being released, the header is being hidden does not handle the event, and is not distributed to return true;} int y = (int) event.gety (); 
Switch (event.getaction ()) {case MotionEvent.ACTION_DOWN:break, case motionevent.action_move: {int deltay = Y-mlasty; if (mcontentviewoffset = 0 && (DeltaY > 0 | | (DeltaY < 0 && isheadershowing ())) {//Offset value 0 o'clock, drop down or slide when the head is still displayed, handle the sliding event yourself mtotaldeltay + = DeltaY; if (Mtotaldeltay > 0 && mtotaldeltay <= MS Caledtouchslop &&!isheadershowing ()) {//optimized dropdown head, do not respond with a little bit of displacement mlasty = y; return super.dispatchtouchevent (event)
;
}//handling event onhandletouchevent;
Processing event misheaderhandling = true; if (mcurrentstate = = refreshing) {//is refreshing and does not allow the Contentview response to slide event.setaction (MoTionevent.action_cancel); } else if (misheaderhandling) {//event at the moment hidden in the head special handling if (mcontentviewscrollable) {//1. Sliding view, the previous move event was not passed to the Content page, here//need to Action_down to re-inform the starting point of the slide, or you will be able to slide a short distance//2. For a view that does not slide, set the Click event, and if this gives it a Action_down event, the finger//lift is Action_
The UP event triggers the click, so there is a processing event.setaction (Motionevent.action_down);
} misheaderhandling = false;
} break;  Case MotionEvent.ACTION_CANCEL:case motionevent.action_up: {if (mcontentviewoffset = 0 && isheadershowing ())
{//Handle finger lift or cancel event onhandletouchevent (events);} mtotaldeltay = 0;
Break
} Default:break;
} mlasty = y;  if (mcurrentstate!= refreshing && isheadershowing () && event.getaction ()!= motionevent.action_up) {//
Not in the refresh time, and the head is displayed, do not let Contentview response event event.setaction (Motionevent.action_cancel);
Return Super.dispatchtouchevent (event); }

Handling the logic of the event: Get the drop offset, and then dynamically set the head of the Paddingtop value, you can achieve display hidden; Depending on the status of the finger to show whether the refresh or directly hide the head

Handle Events Yourself Public boolean onhandletouchevent (Motionevent event) {int y = (int) event.gety (); switch (Event.getaction ()) { Case Motionevent.action_move: {//Get y-direction displacement int deltay = y-mlasty;//divided by 3 equivalent to damping value DeltaY/= 3; Calculate the moving head position int top = de
Ltay + mheader.getpaddingtop (); Control the head position to no more than-mheaderheight if (top <-mheaderheight) {mheader.setpadding (0,-mheaderheight, 0, 0);} else {Mheader.
Setpadding (0, top, 0, 0);
} if (mcurrentstate = = refreshing) {//is still in the refresh State, continue to maintain the refresh state mheader.settext ("refreshing ...");
Break
} if (Mheader.getpaddingtop () > MHEADERHEIGHT/2) {//greater than MHEADERHEIGHT/2 can refresh Mheader.settext ("Can release refresh ...");
Mcurrentstate = Release_to_refresh;
else {//dropdown state Mheader.settext ("Drop-down ...");
Mcurrentstate = Pull_to_refresh;
} break; Case MOTIONEVENT.ACTION_UP: {if (mcurrentstate = = Release_to_refresh) {//Release refresh State, finger lift, animate head back (0,0) position Mheaderanimat
Or.setintvalues (Mheader.getpaddingtop (), 0);
Mheaderanimator.setduration (default_duration);
Mheaderanimator.start (); Mheader. SetText ("is releasing ...");} else if (mcurrentstate = = Pull_to_refresh | | mcurrentstate = = refreshing) {//dropdown state or refreshing state, hide head by animation Mheaderanimator.setin
Tvalues (Mheader.getpaddingtop (),-mheaderheight); if (mheader.getpaddingtop () <= 0) {mheaderanimator.setduration (long) (Default_duration * 1.0/mheaderheight * (MHea
Der.getpaddingtop () + mheaderheight));
else {mheaderanimator.setduration (default_duration);} mheaderanimator.start ();
if (mcurrentstate = = Pull_to_refresh) {//dropdown state, change state to hidden head state mcurrentstate = hiding; Mheader.settext ("Retract head ...");}
Break
} Default:break;
} mlasty = y;
Return Super.ontouchevent (event); }

You may ask, how does this mcontentviewoffset know? Next is the processing method, I will be for different sliding control, to set their sliding distance monitoring, various methods, through Handletargetoffset to identify the type of view to adopt a different strategy; and then you might think that if I had that control, I'd have to implement the monitor. This simple, inherited I have implemented the listener, and then add the function you want to, this time can not be adjusted Handletargetoffset this method Bai.

Set content page sliding distance public void setcontentviewoffset (int offset) {mcontentviewoffset = offset;}/** * Use different types of policies to calculate sliding based on different types of view Distance * * @param view content View/public void Handletargetoffset (view view) {if (view instanceof Recyclerview) {(Recyclervie
W) view). Addonscrolllistener (New Recyclerviewonscrolllistener ()); else if (view instanceof Nestedscrollview) {(Nestedscrollview) view). Setonscrollchangelistener (New
Nestedscrollviewonscrollchangelistener ()); else if (view instanceof WebView) {View.setontouchlistener (New Webviewontouchlistener ());} else if (view instanceof Sc Rollview) {View.setontouchlistener (New Scrollviewontouchlistener ());} else if (view instanceof ListView) {(ListView) v
iew). Setonscrolllistener (New Listviewonscrolllistener ()); }/** * Applicable to Recyclerview sliding distance monitoring/public class Recyclerviewonscrolllistener extends Recyclerview.onscrolllistener {int
offset = 0; @Override public void onscrolled (recyclerview recyclerview, int dx, int dy) {super.onscrolled (Recyclerview, DX, DY);
offset + dy;
Setcontentviewoffset (offset); }/** * Applicable to Nestedscrollview sliding distance monitoring/public class Nestedscrollviewonscrollchangelistener implements Nestedscrollview.onscrollchangelistener {@Override public void Onscrollchange (Nestedscrollview v, int scrollx, int
scrolly, int oldscrollx, int oldscrolly) {setcontentviewoffset (scrolly);}} /** * Applicable to WebView sliding distance monitoring/public class Webviewontouchlistener implements View.ontouchlistener {@Override public boolean
Ontouch (view view, Motionevent motionevent) {Setcontentviewoffset (view.getscrolly ()); return false;}} /** * Applicable to ScrollView sliding distance monitoring/public class Scrollviewontouchlistener extends Webviewontouchlistener {}/** * applicable to ListView Sliding distance monitoring/public class Listviewonscrolllistener implements Abslistview.onscrolllistener {@Override public void Onscrolls  Tatechanged (Abslistview abslistview, int i) {} @Override public void onscroll (Abslistview view, int firstvisibleitem, int VisibleItemCount, int totalitemcount) {if (firstvisibleItem = = 0 {View c = view.getchildat (0); if (c = = null) {return;} int firstvisibleposition = View.getfirstvisiblepositi
On ();
int top = C.gettop ();
int scrolledy =-top + firstvisibleposition * c.getheight ();
Setcontentviewoffset (Scrolledy);
else {setcontentviewoffset (1);}} }

Finally refer to Google's large swiperefreshlayout to provide setrefreshing to turn on or off the refresh animation, as to Openheader why post (Runnable)? Believe that the use of swiperefreshlayout in OnCreate direct call setrefreshing (true) no small circle out of all know this pit!

public void Setrefreshing (Boolean refreshing) {
if (refreshing && mcurrentstate!= refreshing) {
//strong Open Flush Header Part
Openheader ();
} else if (!refreshing) {
closeheader ();
}
}
private void Openheader () {
post (new Runnable () {
@Override public
void Run () {
mcurrentstate = Release_to_refresh;
Mheaderanimator.setduration ((Long) (default_duration * 2.5));
Mheaderanimator.setintvalues (Mheader.getpaddingtop (), 0);
Mheaderanimator.start ();}}
);
private void Closeheader () {
mheader.settext ("Refresh complete, retract head ...");
Mcurrentstate = hiding;
Mheaderanimator.setintvalues (Mheader.getpaddingtop (),-mheaderheight);
0~-mheaderheight spents default_duration
mheaderanimator.setduration (default_duration);
Mheaderanimator.start ();
}

3.3. Effect display

In addition to the above three also in the demo to achieve the ListView, Viewpager, ScrollView, Nestedscrollview, the specific look at the code can

Demo Address: Github:refreshlayoutdemo, think it's a good word to a star oh.

The above is a small set to introduce the Android development of the seamless transition of the implementation of the control to refresh the realization of ideas, I hope to help you, if you have any questions please give me a message, small series will promptly reply to everyone. Here also thank you very much for the cloud Habitat Community website support!

Related Article

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.