Maxwin-z/xlistview-android (Pull-up reload) source parsing (i)

Source: Internet
Author: User

The content of this resolution is GitHub's previous control Xlistview for pull-up refresh, which is believed to be used frequently in the development process.

The source address of the control is https://github.com/Maxwin-z/XListView-Android

Before this control, I saw some of the same functions of the control, after the selection of Xlistview function is more complete, and easy to understand. In the android-open-project , there is a mention of a dropdownlistview, personal use, feel that the function is equipped, but the operation experience is not good, the reason is not used to scroller to deal with sliding problems, The speed of the pull-down and rollback is the same (soon), in principle, the rollback should be fast and slow, and the pull-pull force (feeling).

These are the reasons why I have not selected Dropdownlistview, let us take a specific look at Xlistview.


We know that pull-up reload loading this feature, most often is used on the ListView, so we need to inherit the ListView, add the head and tail to it


For pull-up refresh pull-up loading, we discuss it separately (although the principle is similar)

Drop-down refresh:

It is natural for us to think of adding a head to the ListView that is always in the first place, first customizing a head and then add it to the ListView, which solves the problem of drawing.

How do you make sure the header is always on the head? The ListView provides us with a way to Addheaderview ()

Again to consider the problem of animation, we know that the drop down when the arrow down (there is a rotation), let go, the arrow will change direction (there is a rotation animation)

How do we rotate the arrows, the arrows are obviously a imageview, then we just set two animated rotateanimation, one clockwise 180, one counterclockwise 180

Then the first one is called at the drop-down (Action_mov), and the second is called after Letting Go (action_up).

To consider the problem of pulling, xlistview to us is that the header is a layout, and there is a layout of all the layouts (called container), We set this container to Gravity.bottom, which is to keep it at the bottom of the header forever. In addition we record the height of the header, and then set the header height to 0 to hide the header.

Each time you drag, calculate offset in the Y direction (take advantage of the Action_down and Action_up events), and then record this offset (very important, and then handle the various cases based on offset). Because the header is added to the ListView, you don't have to worry about the pull-down effect.

Next consider the various scenarios of the drop-down process:

1, first we recorded the offset, each move, there is an offset, and then according to this offset we can increase the height of the header, thus the header is displayed.

When Offset

2, when Offset>height, let go after, should return to the loading state, such as. Then the height of the header narrowing is offset-height.

Finally, when the data is loaded, it is not retracted until it is visible.

The retraction of the above process occurs after the finger leaves the screen, and obviously we have to use scroller to handle it.


The drop-down refresh principle here, the pull load more original is the same, only the header changed the height, and footer change is Margin-bottom


Now let's look at the source

First look at Xlistviewheader, the custom head.

Head layout

<?xml version= "1.0" encoding= "Utf-8"? ><linearlayout xmlns:android= "http://schemas.android.com/apk/res/    Android "Android:layout_width=" Fill_parent "android:layout_height=" wrap_content "android:gravity=" Bottom "> <relativelayout android:id= "@+id/xlistview_header_content" android:layout_width= "Fill_parent" and roid:layout_height= "60DP" > <linearlayout android:layout_width= "wrap_content" android:l            ayout_height= "Wrap_content" android:layout_centerinparent= "true" android:gravity= "center" android:orientation= "vertical" android:id= "@+id/xlistview_header_text" > <textview Android : id= "@+id/xlistview_header_hint_textview" android:layout_width= "Wrap_content" android:layout _height= "Wrap_content" android:text= "@string/xlistview_header_hint_normal"/> <linearlayou T Android:layout_widTh= "Wrap_content" android:layout_height= "wrap_content" android:layout_margintop= "3DP" > <textview android:layout_width= "Wrap_content" android:layout_height = "Wrap_content" android:text= "@string/xlistview_header_last_time" android:textsize= " 12sp "/> <textview android:id=" @+id/xlistview_header_time "Android Oid:layout_width= "Wrap_content" android:layout_height= "Wrap_content" Android:textsiz E= "12SP"/> </LinearLayout> </LinearLayout> <imageview android:id= "@             +id/xlistview_header_arrow "android:layout_width=" wrap_content "android:layout_height=" Wrap_content "            android:layout_alignleft= "@id/xlistview_header_text" android:layout_centervertical= "true" Android:layout_marginleft= " -35DP" android:src= "@drawable/xlistview_arrow"/> <progressbar android:id= "@+id/xl Istview_header_progressbar "android:layout_width=" 30DP "android:layout_height=" 30DP "Android oid:layout_alignleft= "@id/xlistview_header_text" android:layout_centervertical= "true" android:layout _marginleft= " -40DP" android:visibility= "invisible"/> </RelativeLayout></LinearLayout>

Head Java class

public class Xlistviewheader extends LinearLayout {/** * drop-down layout body */private linearlayout mcontainer;/** * drop-down arrow */private Ima  Geview marrowimageview;/** * Ring progress bar */private ProgressBar mprogressbar;/** * hint text */private TextView mhinttextview;private int mstate = state_normal;private Animation mrotateupanim;private Animation mrotatedownanim;/** * Animation time */private final in T rotate_anim_duration = 180;public final static int state_normal = 0;//normal state public final static int state_ready = 1;//Pull Ready Refresh public final static int state_refreshing = 2;//Loading public xlistviewheader (context context) {super (context); Initview ( context);} /** * @param context * @param attrs */public Xlistviewheader (context context, AttributeSet Attrs) {Super (context, attrs); I Nitview (context);} private void Initview (context context) {///initial case, set dropdown refresh view height to 0linearlayout.layoutparams LP = new Linearlayout.layoutparams (layoutparams.fill_parent, 0); Mcontainer = (linearlayout) layoutinflater.from (context). Inflate (R.layout.xlistview_header, null); AddView (Mcontainer, LP); setgravity (gravity.bottom); Marrowimageview = (ImageView) Findviewbyid (r.id.xlistview_ Header_arrow); Mhinttextview = (TextView) Findviewbyid (r.id.xlistview_header_hint_textview); Mprogressbar = ( ProgressBar) Findviewbyid (R.id.xlistview_header_progressbar);//Rotate animation Mrotateupanim = new Rotateanimation (0.0f,- 180.0f,animation.relative_to_self, 0.5f, animation.relative_to_self,0.5f);//Set animation time Mrotateupanim.setduration ( rotate_anim_duration);//The animation stops at the last frame, that is, the state after the animation is preserved mrotateupanim.setfillafter (true);//Rotate animation Mrotatedownanim = new Rotateanimation ( -180.0f, 0.0f,animation.relative_to_self, 0.5f, animation.relative_to_self,0.5f); Mrotatedownanim.setduration (rotate_anim_duration); Mrotatedownanim.setfillafter (true);} public void SetState (int.) {if (state = = mstate) return; if (state = = state_refreshing) {//Show Progress Marrowimageview.clear Animation (); marrowimageview.setvisibility (view.invisible); mprogressbar.setvisibility (view.visible);} else {//show arrows picture marrowimageview.setvisibility (View.visible); mprogressbar.setvisibility (view.invisible);} Switch (state) {case State_normal:if (mstate = = State_ready) {marrowimageview.startanimation (Mrotatedownanim);} if (mstate = = state_refreshing) {marrowimageview.clearanimation ();} Mhinttextview.settext (r.string.xlistview_header_hint_normal); Break;case state_ready:if (mState! = STATE_READY) { Marrowimageview.clearanimation (); marrowimageview.startanimation (Mrotateupanim); Mhinttextview.settext ( R.string.xlistview_header_hint_ready);} Break;case STATE_REFRESHING:mHintTextView.setText (r.string.xlistview_header_hint_loading); break;default:}mstate = State;} /** * Set the drop head effective height * @param height */public void setvisiableheight (int height) {if (height < 0) height = 0; Linearlayout.layoutparams LP = (linearlayout.layoutparams) mcontainer.getlayoutparams (); lp.height = height; MCONTAINER.SETLAYOUTPARAMS (LP);} /** * Get the drop head effective height * @return */public int getvisiableheight () {return mcontainer.getlayoutparams (). Height;}}
The above comments have been made very clear, and I will make some more explanations.

The first is the initialization function Initview () the control in the layout is obtained, the arrow rotation animation is set, the header height is set to 0

Then the SetState () function, depending on the state passed in, determines whether the control is hidden, which rotation animation is called, and so on, and the function will be called externally

There is also the setvisiableheight (int height) function, which is used to record the drop-down distance (which is actually the height of the incoming), which is very important when the distance is clearly stated and used to judge the state of the drop.

The Getvisiableheight () function has nothing to say.


OK, finish the header, let's see Xlistview.

The first is some basic properties, for everyone in the next source code, do reference, we can ignore it, but encountered the attribute of unknown meaning, look back to find out

public class Xlistview extends ListView implements Onscrolllistener {private float mlasty =-1;//Save Event y/** * for pull-down , sliding back to */private Scroller Mscroller; Used for scroll backprivate onscrolllistener mscrolllistener; User ' s scroll listener//the interface to trigger refresh and load More.private ixlistviewlistener mlistviewlistener;/* * Drop-down head */private xlistviewheader mheaderview;/** * drop-down head body, used to calculate the height of the head * when not refreshed, is hidden */private relativelayout mheaderviewcont ent;/** * drop-down arrow */private TextView mheadertimeview;/** * drop-down head height */private int mheaderviewheight;/** * can pull down refresh */private Boo Lean Menablepullrefresh = true;/** * is refreshing, false indicates that the */private Boolean mpullrefreshing = False is being refreshed; is refreashing.//--footer Viewprivate Xlistviewfooter Mfooterview;private boolean menablepullload;private boolean MP  Ullloading;private Boolean misfooterready = false;//Total list items, used to detect are at the bottom of listview.private int mtotalitemcount;//for Mscroller, scroll to header or Footer.private int mscrollback;/** * Head slide return */private final static int scrollback_header = 0;/** * Footer slide back to */private final static int scrollback_footer = 1;private final static int scroll_duration = 400; Scroll back durationprivate final static int pull_load_more_delta = 50; When pulling up >= 50px//at bottom, trigger//load more.private final static float Offset_radio = 1.8f; Support for IOS like pull//feature.

Next is the initialization

        Public Xlistview (context context, AttributeSet attrs, int defstyle) {Super (context, attrs, defstyle); initwithconte XT (context);} private void Initwithcontext (context context) {Mscroller = new Scroller (context, new Decelerateinterpolator ());// Xlistview need the scroll event, and it'll dispatch the event to//user ' s listener (as a proxy). Super.setonscrolllistene R (this);//Initialize the drop-down head Mheaderview = new Xlistviewheader (context); mheaderviewcontent = (relativelayout) Mheaderview.findviewbyid (r.id.xlistview_header_content); Mheadertimeview = (TextView) Mheaderview.findviewbyid ( R.id.xlistview_header_time); Addheaderview (Mheaderview);//Initialize Bottom Mfooterview = new Xlistviewfooter (context);// Initialize the drop head height mheaderview.getviewtreeobserver (). Addongloballayoutlistener (New Ongloballayoutlistener () {@ overridepublic void Ongloballayout () {mheaderviewheight = Mheaderviewcontent.getheight ();// Get the height of the drop Head getviewtreeobserver (). Removeglobalonlayoutlistener (this);});}
The initialization function does a very important thing, which is to get the actual height of the drop-down mheaderviewheight, which is another important indicator that we use to judge the dropdown state.

After the control is drawn, let's deal with the pull-down problem

@Overridepublic boolean ontouchevent (motionevent ev) {if (mlasty = =-1) {//Get touch y-coordinate mlasty = Ev.getrawy ();} Switch (ev.getaction ()) {Case MotionEvent.ACTION_DOWN:mLastY = Ev.getrawy (); Break;case MotionEvent.ACTION_MOVE:final float DeltaY = Ev.getrawy ()-mlasty;//pull down or pull up how much offsetmlasty = Ev.getrawy (); if (getfirstvisibleposition () = = 0&& (Mheaderview.getvisiableheight () > 0 | | deltay > 0)) {//The first item is visible and the header is partially displayed or pulled down, it is in the drop-down refresh state//The primary item is showing, and the header has a shown or pull Down.updateheaderheight (delt Ay/offset_radio); invokeonscrolling ();} else if (getlastvisibleposition () = = mtotalitemcount-1&& (mfooterview.getbottommargin () > 0 | | DeltaY < 0) {//The last item is visible and footer is either pulled up or pulled up, it indicates a pull-up refresh state/previous item, already pulled up or want to pull up.updatefooterheight (-delt Ay/offset_radio);} Break;default://action_upmlasty =-1; Resetif (getfirstvisibleposition () = = 0) {//Invoke Refreshif (menablepullrefresh&& Mheaderview.getvisiableheight () > mHeaderviewheight) {mpullrefreshing = True;mheaderview.setstate (xlistviewheader.state_refreshing); Mlistviewlistener! = null) {Mlistviewlistener.onrefresh ();}} Resetheaderheight ();} else if (getlastvisibleposition () = = mTotalItemCount-1) {//Invoke load More.if (menablepullload && Mfootervi Ew.getbottommargin () > Pull_load_more_delta &&!mpullloading) {Startloadmore ();} Resetfooterheight ();} break;} return super.ontouchevent (EV);}
As you can see from the code above

Action_down: Get the coordinates of the finger touch

Action_move: At this point, we can change the height of the header according to the moving coordinates (it may also be the pull-up, judging the comment above)

The decision is a drop-down refresh, first of all to check whether the first item of the ListView is visible (that is, the header), if visible, there are two cases, a part of the display, a drop-down operation

The Updateheaderheight () is then called to update the height of the header so that the header is displayed and the distance from the drop is recorded

/** * Update Head Height * This function is used to pull down when recording how much is pulled down * @param delta */private void Updateheaderheight (float delta) {Mheaderview.setvisiablehei ght (int) delta+ mheaderview.getvisiableheight ()); if (Menablepullrefresh &&!mpullrefreshing) {//Not in refresh State, Update arrows if (Mheaderview.getvisiableheight () > Mheaderviewheight) {mheaderview.setstate (Xlistviewheader.state_ready) ;} else {mheaderview.setstate (xlistviewheader.state_normal);}} Slide to head setselection (0); Scroll to top each time}
Updateheaderheight () has a setselection (0) to make it difficult to pull down (feeling)

Height has been updated, so the header will be larger and bigger

Finally, let's go.

ACTION_UP: The same judgment is pull or pull, so we'll just look at the pull-down section

if (menablepullrefresh&& mheaderview.getvisiableheight () > Mheaderviewheight) {
Determine if the pull-down is turned on, and the drop-down height is greater than the actual height of the drop-down mheaderviewheight, as I said before. Mheaderviewheight is an important indicator that determines whether a pull-down head is fully displayed to determine if a callback operation is required

Mlistviewlistener.onrefresh ();
Finally, Resetheaderheight () is called to reset the header correctly (note that this time the reset is not completely hidden header, but the header is in the updated state)


Let's see Resetheaderheight ()

/** * Reset Header view ' s height. * Reset Head height */private void resetheaderheight () {int height = mheaderview.getvisiableheight (); if (height = = 0)//not visible. return;//refreshing and header isn ' t shown fully. Do nothing.//is refreshing, or the head is not fully displayed, return if (mpullrefreshing && height <= mheaderviewheight) {return;} int finalheight = 0; The default final height, which means to let the head disappear//is refreshing, just scroll back to show all the header.//are refreshing, and the drop-down header fully displays if (Mpullrefreshing &&A mp Height > mheaderviewheight) {finalheight = Mheaderviewheight;} Mscrollback = scrollback_header;//from the current position, returning to the head is hidden mscroller.startscroll (0, height, 0, Finalheight-height,scroll_ DURATION);//Trigger Computescrollinvalidate ();}

So there is an important judgment that
if (mpullrefreshing && height > Mheaderviewheight)
Used to determine whether the drop-down, to load the data state, or to load the data finished, to the hidden head state

After the drop-down, height (the height of the header added during the drop-down) is greater than mheaderviewheight (the true height of the header), so we change the finalheight so that the header slides to the loaded state

While in the load state, when height equals mheaderviewheight, so finalheight=0, we call Resetheaderheight again (), you can make the header hidden

It's a bit around here, but it's key, I hope you understand carefully.

How to slide the rollback, of course, is to use scroller

From the current position, return to the head is hidden mscroller.startscroll (0, height, 0, finalheight-height,scroll_duration);
The real rollback is realized in the Computescroll.

@Overridepublic void Computescroll () {if (Mscroller.computescrolloffset ()) {if (Mscrollback = = Scrollback_header) { Mheaderview.setvisiableheight (Mscroller.getcurry ());//Change the head height, implement rollback} else {Mfooterview.setbottommargin ( Mscroller.getcurry ());} Postinvalidate (); invokeonscrolling ();} Super.computescroll ();}

Scroller through the position, change the head height, achieve rollback.

At this point, the drop-down refresh is resolved, but we do not see the second call Resetheaderheight (), so that the drop-down head hidden operations AH

Of course not, because we have to load the data, only call this function, that is, the call timing is uncertain, according to the specific needs, so the control has no way to think when to call, this call is in your hand, that is, we load the data, we need to actively call

Xlistview provides us with a public for active invocation, internal resetheaderheight () operation

/** * Stop Refresh, reset header view. * Stop Refresh, reset head */public void Stoprefresh () {if (mpullrefreshing = = true) {mpullrefreshing = False;resetheaderheight ();}}
OK, drop-down refresh believe has been said clearly, next we look at pull load more

Is there any difficulty in this? It's just a different way of pulling.

I still mention a few important points for you, the first is to ensure that footer forever in the last one of the ListView, how to guarantee it? Look underneath.

@Overridepublic void Setadapter (ListAdapter adapter) {/* * will pull up loads more footer join the bottom of the ListView * and ensure that only one */if is added (Misfooterready = = False) {Misfooterready = True;addfooterview (Mfooterview);//listview Native method}super.setadapter (adapter);}

Inside the Setadapter, call Addfooterview () to ensure the location of the footer

The Xlistview loading function is not turned on by default, we need to call the Setpullloadenable () function actively, complete the initialization operation

Note that if you do not turn on the pull-up load, hide the footer when you want to hide the split line from the ListView

/** * Set the pull-up load more features open * If you want to turn on, you must actively call this function  * @param enable */public void Setpullloadenable (Boolean enable) {Menablepulll Oad = Enable;if (!menablepullload) {//if mfooterview.hide () is not turned on;//Hide Footermfooterview.setonclicklistener (null);// Cancel the Listener//make sure "pull up" don't show a line in bottom when the ListView with one page//guarantee that the divider between the ListView item disappears (last) Setfooter Dividersenabled (false);//listview Native Method} else {mpullloading = False;mfooterview.show (); Mfooterview.setstate ( Xlistviewfooter.state_normal);//make sure "Pull up" don ' t show a line in bottom when the ListView with one page  setfooterd Ividersenabled (TRUE);//Both "pull Up" and "click" To invoke Load More.mFooterView.setOnClickListener (new Onclicklistener () {@Overridepublic void OnClick (View v) {startloadmore ();}});}}

Other parts with the header is similar, change footer Margin-bottom, can produce pull-up effect.

Xlistview parsing finished, I will be in the "maxwin-z/xlistview-android (pull up Reload) source code parsing (ii)" Paste out a few classes of specific code, very xlistview simple use

Maxwin-z/xlistview-android (Pull-up reload) source parsing (i)

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.