Android easy to implement the imitation QQ space drop-down refresh

Source: Internet
Author: User
Tags control label gety

(This article explains the process of implementing the dynamic effect of the dropdown refresh in Android, with the source code at the end of the text.) )

After reading this article, you can learn:
1. The implementation of the pull-down refresh principle

2. Customize the Android control to override its listview

3.ScrollListener scrolling Monitoring

Use of 4.Adapter adapters


Talk not much, first look at:




Next, we achieve the above results step-by-step.


First, the ListViewItem of illustrations

Take a look at this step:



First, we're going to implement a ListView with a pull-down refresh effect. So we chose ourselves to rewrite the native control ListView. Just write a class to inherit it, and do not add any concrete implementations first.

Refreshlistview.java:

public class Refreshlistview extends ListView {public Refreshlistview (context context) {super (context);//TODO Auto-generated Constructor Stub}public Refreshlistview (context context, AttributeSet Attrs) {Super (context, attrs);// TODO Auto-generated Constructor Stub}public Refreshlistview (context context, AttributeSet attrs, int defstyle) {super ( Context, attrs, defstyle);//TODO auto-generated constructor stub}}

To achieve an illustrated listviewitem, the next thing is to define its own layout of the item, this can be unlimited play, I just take the picture and text to do a simple implementation:

Listview_item.xml:

<?xml version= "1.0" encoding= "Utf-8"? ><linearlayout xmlns:android= "http://schemas.android.com/apk/res/ Android "    android:layout_width=" match_parent "    android:layout_height=" match_parent "    android:gravity= "Center_vertical"    android:orientation= "Horizontal" >    <imageview        android:id= "@+id/image        " Android:layout_width= "Wrap_content"        android:layout_height= "wrap_content"        android:layout_marginleft= " 10DP "/>    <textview        android:id=" @+id/text "        android:layout_width=" Wrap_content "        android: layout_height= "Wrap_content"        android:layout_marginleft= "10DP"        android:textsize= "25SP"/></ Linearlayout>

In this layout, I just put an image and a text. You can define yourself more complex.

It is then necessary to note that since we have defined the ListView ourselves, the layout of our main interface has also been modified in response:

Activity_main.xml:

<relativelayout xmlns:android= "http://schemas.android.com/apk/res/android"    xmlns:tools= "http// Schemas.android.com/tools "    android:layout_width=" match_parent "    android:layout_height=" Match_parent "    tools:context= ". Mainactivity ">    <com.example.mylistviewrefresh.refreshlistview        android:id=" @+id/listview "        Android:layout_width= "Fill_parent"        android:layout_height= "wrap_content"        android:cachecolorhint= "# 00000000 "        android:dividerheight=" 5dip "/></relativelayout>
We can see that we have modified its control label to the full path of our own defined class.

Finally, the protagonist Mainactivity.java, some of the code inside I gave the comments in detail. The use of the adapter is to be noted here.

public class Mainactivity extends Activity {private Refreshlistview listview;private simpleadapter simple_adapter; Private list<map<string, object>> List, @Overrideprotected void OnCreate (Bundle savedinstancestate) { Super.oncreate (savedinstancestate); Setcontentview (r.layout.activity_main); ListView = (Refreshlistview)   Findviewbyid (R.id.listview); Inidata (); To initialize the data, we add 20 to it item//set Simpleadapter listener/** * Simpleadapter five parameters meaning: * First: Context context * Second: data for display, List of map * The third: The layout of item, which is our custom file * Fourth: Close to the second parameter, closely related to the fifth, is the key value in the map * Fifth: We see an array of ID (int type), where does the object in this array come from? We define it ourselves in the layout file, and the forgotten reader can go back and look at it. * These parameters may not know what to do, but I think it's a good idea to unite together. */simple_adapter = new Simpleadapter (Mainactivity.this, List,r.layout.listview_item, new string[] {"Image", "Text"}, New int[] {r.id.image, r.id.text});//Set Adapter Listview.setadapter (Simple_adapter);} Initialize the Simpleadapter DataSet private list<map<string, Object>> Inidata () {List = new arraylist<map<string, Object>> (); for (int i = 0; i < 20; i++) {map<string, object> Map = new hashmap<string, object> ();//Explain the data here, key corresponds to the third parameter of Simpleadapter, must contain them. The values correspond to the fifth parameter, namely the picture and text Map.put ("text", I), Map.put ("image", r.drawable.ic_launcher); List.add (map);} return list;} @Overridepublic boolean Oncreateoptionsmenu (Menu menu) {//Inflate the menu; This adds items to the action bar if it is PR Esent.getmenuinflater (). Inflate (R.menu.main, menu); return true;}}

In this step, you should be able to achieve the picture + text of the ListView, drink tea, we continue to watch.


Second, add the hidden header

Here, let's talk about the implementation of the pull-down refresh:

First of all, the drop-down refreshes that we normally use are the ones that show some previously hidden controls, such as the drop-down arrows, progress bar, and so on, after the drop-down screen.

So can we just set them to be invisible? Obviously, it's not possible. Because these spaces are displayed or not, there is a gradual process, not just a brush.

So we should do this:

Add a hidden layout and put it on top of the screen. The control that displays the response according to the drop-down range.

This step, we want to achieve is to join the hidden layout, specifically how to adjust the status of the drop-down in real-time adjustment of the status of the header, we elaborate below.


We customize a new layout for the header that needs to be hidden, header.xml:

<?xml version= "1.0" encoding= "Utf-8"? ><linearlayout xmlns:android= "http://schemas.android.com/apk/res/ Android "Android:layout_width=" Match_parent "android:layout_height=" wrap_content "android:orientation=" vertical " > <relativelayout android:layout_width= "match_parent" android:layout_height= "Wrap_content" a Ndroid:paddingbottom= "10dip" android:paddingtop= "10dip" > <linearlayout android:id= "@+id/la Yout "android:layout_width=" wrap_content "android:layout_height=" Wrap_content "Android:la Yout_centerinparent= "true" android:gravity= "center" android:orientation= "vertical" > & Lt TextView android:id= "@+id/tip" android:layout_width= "Wrap_content" Android:la yout_height= "Wrap_content" android:text= "drop-down can be refreshed!           "/> <textview android:id=" @+id/lastupdate_time "     Android:layout_width= "Wrap_content" android:layout_height= "wrap_content"/> </linearlayou t> <imageview android:id= "@+id/arrow" android:layout_width= "Wrap_content" an droid:layout_height= "Wrap_content" android:layout_toleftof= "@id/layout" android:layout_marginright= " 20dip "android:src=" @drawable/pull_to_refresh_arrow "/> <progressbar android:id=" @+id/p            Rogress "style="? Android:attr/progressbarstylesmall "Android:layout_width=" Wrap_content " android:layout_height= "Wrap_content" android:layout_toleftof= "@id/layout" android:layout_marginright = "20dip" android:visibility= "Gone"/> </RelativeLayout></LinearLayout>


This layout contains the prompt "dropdown can be refreshed", the latest update time, the drop-down arrow of the picture (has been pre-placed in the Drawable folder, the reader can find a picture on their own to put in, named PULL_TO_REFRESH_ARROW.PHG), An update is displayed only for ProgressBar (now hidden).

To add this layout to our list of definitions, we need to overwrite the previously customized Refreshlistview control:

public class Refreshlistview extends ListView {View header;//top layout file; int headerheight;//height of top layout file; public Refreshlistview (Context context) {super (context);//TODO auto-generated Constructor Stubinitview (context);} Public Refreshlistview (context context, AttributeSet Attrs) {Super (context, attrs);//TODO Auto-generated constructor Stubinitview (context);} Public Refreshlistview (context context, AttributeSet attrs, int defstyle) {Super (context, attrs, defstyle);//TODO Auto-g Enerated constructor Stubinitview (context);} /** * Initialize interface, add top layout file to ListView */private void Initview (context context) {Layoutinflater inflater = Layoutinflater.from (co ntext); header = Inflater.inflate (R.layout.header, null); Measureview (header); headerheight = Header.getmeasuredheight ();   LOG.I ("tag", "headerheight =" + headerheight);//toppadding (-headerheight); This line has been commented out by me, if you remove the comment, you can display the This.addheaderview (header);} /** * Notifies the parent layout, occupies a wide, high, */private void Measureview (view view) {Viewgroup.layoutparams p = view.getlayoutparams ();if (p = = null) {p = new Viewgroup.layoutparams (VIEWGROUP.LAYOUTPARAMS.MATCH_PARENT,VIEWGROUP.LAYOUTPARAMS.WRAP_ CONTENT);} int width = viewgroup.getchildmeasurespec (0, 0, p.width), int height;int tempheight = p.height;if (Tempheight > 0) {Heig HT = Measurespec.makemeasurespec (tempheight,measurespec.exactly);} else {height = measurespec.makemeasurespec (0, measurespec.unspecified);} View.measure (width, height);} /** * Set header layout top margin; */private void toppadding (int toppadding) {header.setpadding (Header.getpaddingleft (), toppadding, Header.getpaddingright (), Header.getpaddingbottom ()); Header.invalidate ();}}

The main implementation is to add a new header and hide it. Now that I have commented on the line of code that hides the header, let's look at the effect now:


If you remove the comment, the header is hidden.


Third, join the scroll monitor real-time adjust the header display status and refresh after adding data

Ideas:

Add screen touch monitoring and screen scrolling monitoring.

Touch monitoring is especially important:

Recording the Y-value of the touch coordinates while touching, and then listening for the current Y value during the move, and judging the current moving distance based on the interpolation of the two, compared with some critical values . starty

After comparison, the current status is drawn : Prompt drop-down status, prompt release status, refresh status. Refreshes the display of the header layout based on the current state.

The purpose of the scrolling listener is to determine whether the current is at the top of the list (by judging whether the position of the first item currently visible is 0), and then to determine the scrolling state of the screen.


In addition, an interface is defined in the custom ListView class that implements this interface in mainactivity to refresh the data. We used a handler delay of two seconds in the refresh to see the effect of the refresh clearly.

Modified Mainactivity and ListView:

ListView: (Many comments inside, you should understand it well)

public class Refreshlistview extends ListView implements Onscrolllistener {View header;//top layout file; int headerheight;//top layout The height of the file, int firstvisibleitem;//The position of the currently first visible item, int scrollstate;//The current scrolling state of the ListView, the Boolean isremark;//tag, Currently at the top of the ListView, the Y value when int starty;//is pressed; int state;//the current state; final int NONE = 0;//normal state; final int pull = 1;//hint drop-down state; final I NT Release = 2;//prompt release status; final int refreshing = 3;//refresh State; Irefreshlistener irefreshlistener;//Flush Data Interface Public REFRESHLISTVI EW (Context context) {super (context);//TODO auto-generated Constructor Stubinitview (context);} Public Refreshlistview (context context, AttributeSet Attrs) {Super (context, attrs);//TODO Auto-generated constructor Stubinitview (context);} Public Refreshlistview (context context, AttributeSet attrs, int defstyle) {Super (context, attrs, defstyle);//TODO Auto-g Enerated constructor Stubinitview (context);} /** * Initialize interface, add top layout file to ListView * * @param context */private void Initview (context context) {Layoutinflater Inflater = Lay OutinflateR.from (context); header = Inflater.inflate (R.layout.header, null); Measureview (header); headerheight = Header.getmeasuredheight (); LOG.I ("tag", "headerheight =" + headerheight); toppadding (-headerheight); This.addheaderview (header); This.setonscrolllistener (this);} /** * Notify parent layout, Occupy wide, high; * * @param view */private void Measureview (view view) {Viewgroup.layoutparams p = View.getlayoutparam s (); if (p = = null) {p = new Viewgroup.layoutparams (VIEWGROUP.LAYOUTPARAMS.MATCH_PARENT,VIEWGROUP.LAYOUTPARAMS.WRAP_ CONTENT);} int width = viewgroup.getchildmeasurespec (0, 0, p.width), int height;int tempheight = p.height;if (Tempheight > 0) {Heig HT = Measurespec.makemeasurespec (tempheight,measurespec.exactly);} else {height = measurespec.makemeasurespec (0, measurespec.unspecified);} View.measure (width, height);} /** * Set header layout top margin; * * @param toppadding */private void toppadding (int toppadding) {header.setpadding (Header.getpaddi Ngleft (), Toppadding,header.getpaddingright (), Header.getpaddingbottom ()); header.iNvalidate ();} @Overridepublic void Onscroll (abslistview view, int firstvisibleitem,int visibleitemcount, int totalitemcount) {//TODO A Uto-generated method stubthis.firstvisibleitem = Firstvisibleitem;} @Overridepublic void onscrollstatechanged (abslistview view, int scrollstate) {//TODO auto-generated method Stubthis.scrollstate = scrollstate;} /** * Monitoring of screen touch, * First to determine whether the current is at the top. If it is at the top, record the Y value that you started to slide * and then during the sliding process (the action_move is heard), constantly determine whether the current sliding range reaches the level that should be refreshed. * (judging by the relationship between the value of the current Y-starty and the height of our control) * and then, when the supervisor hears the finger release, according to the current state (we calculated in OnMove ()), do the corresponding operation. */@Overridepublic Boolean ontouchevent (motionevent ev) {//TODO auto-generated method Stubswitch (Ev.getaction ()) {case M OtionEvent.ACTION_DOWN:if (Firstvisibleitem = = 0) {Isremark = True;starty = (int) ev.gety ();} Break;case MotionEvent.ACTION_MOVE:onMove (EV), break;case MotionEvent.ACTION_UP:if (state = = release) {//That prompts to release the refresh status, Once released, enter into the refresh, and the data is ready to load! state = refreshing;//Load up-to-date data; Refreshviewbystate (); Irefreshlistener.onrefresh ();} else if (state = = pull) {//hintThe state of the drop-down state, if it is dropped, everything is restored, nothing has been done. = None;isremark = False;refreshviewbystate ();} break;} return super.ontouchevent (EV);} /** * To determine the action of the move: * If it is not the top, do not need to do any action * Otherwise get the current Y value, compared with the starting Y value. * Determine the height of the drop, with some of the thresholds we define (in fact, this threshold you can define yourself) * * @param ev */private void OnMove (motionevent ev) {if (!isremark) {return;} int tempy = (int) ev.gety (); int space = Tempy-starty;int toppadding = Space-headerheight;switch (state) {case None:if (Space > 0)  {state = pull; Being pulled down refreshviewbystate ();} Break;case pull:toppadding (toppadding);//If it is greater than a certain height and the scroll state is scrolling, it is time to release the state if it can be refreshed if (Space > Headerheight + 30& & scrollstate = = scroll_state_touch_scroll) {state = Release;refreshviewbystate ();}  Break;case release:toppadding (toppadding);//When prompted to release the refresh, if you drag up, distance is less than a certain height, the hint drop can refresh if (Space < Headerheight + 30) { State = Pull;refreshviewbystate ();} Break;}} /** * Depending on the current state, change the interface display; */private void Refreshviewbystate () {//If you want to improve performance, these should be written in OnCreate, but. There are too many parameters, for everyone to read the code more comfortable, it is written here. TextView tip = (TextView) Header.findviewbyid (r.iD.TIP); ImageView arrow = (ImageView) Header.findviewbyid (R.id.arrow); ProgressBar progress = (ProgressBar) Header.findviewbyid (r.id.progress); Rotateanimation anim = new Rotateanimation (0, 180,rotateanimation.relative_to_self, 0.5f,rotateanimation.relative_to _self, 0.5f); anim.setduration (+); Anim.setfillafter (true); Rotateanimation anim1 = new Rotateanimation (0,rotateanimation.relative_to_self, 0.5f,rotateanimation.relative_ To_self, 0.5f); anim1.setduration (+); Anim1.setfillafter (true); switch (state) {case NONE:// The normal state does not show arrow.clearanimation (); toppadding (-headerheight); Break;case Pull://drop-down status display arrows, hide progress bars, and the following States are similar. To modify according to the actual situation. Arrow.setvisibility (view.visible);p rogress.setvisibility (View.gone); Tip.settext ("drop-down can be refreshed! "); Arrow.clearanimation (); arrow.setanimation (ANIM1); Break;case RELEASE:arrow.setVisibility (view.visible); Progress.setvisibility (View.gone); Tip.settext ("Release can be refreshed! "); Arrow.clearanimation (); arrow.setanimation (anim); break;case refreshing:toppadding (); Arrow.setvisibility (VIew. GONE);p rogress.setvisibility (view.visible); Tip.settext ("refreshing ..."); Arrow.clearanimation (); break;}} /** * After getting the data */public void Refreshcomplete () {state = None;isremark = False;refreshviewbystate (); TextView lastupdatetime = (TextView) Header.findviewbyid (r.id.lastupdate_time); SimpleDateFormat format = new SimpleDateFormat ("yyyy mm month DD Day HH:MM:SS");D ate date = new Date (System.currenttimemillis ()) ; String time = Format.format (date); Lastupdatetime.settext (time);} public void Setinterface (Irefreshlistener irefreshlistener) {this.irefreshlistener = Irefreshlistener;} /** * Refresh Data interface * @author Administrator */public interface irefreshlistener{public void Onrefresh ();}}

Mainactivity: (Added an interface callback, which is the method of calling main's add data in the ListView)

public class Mainactivity extends Activity implements Irefreshlistener {private Refreshlistview listview;private Simplea Dapter simple_adapter;private list<map<string, object>> List; @Overrideprotected void OnCreate (Bundle Savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview (r.layout.activity_main); ListView = (   Refreshlistview) Findviewbyid (R.id.listview); Inidata (); To initialize the data, we add 20 to it item//set Simpleadapter listener/** * Simpleadapter five parameters meaning: * First: Context context * Second: data for display, List of map * The third: The layout of item, which is our custom file * Fourth: Close to the second parameter, closely related to the fifth, is the key value in the map * Fifth: We see an array of ID (int type), where does the object in this array come from? We define it ourselves in the layout file, and the forgotten reader can go back and look at it. * These parameters may not know what to do, but I think it's a good idea to unite together. */simple_adapter = new Simpleadapter (Mainactivity.this, List,r.layout.listview_item, new string[] {"Image", "Text"}, New int[] {r.id.image, r.id.text});//Set Adapter Listview.setadapter (Simple_adapter);//set interface to update data Listview.setinterface (t His);} Initializes the Simpleadapter dataset private list<map<string, Object>> inidata ({list = new arraylist<map<string, object>> (); for (int i = 0; i <; i++) {map<string, object> Map = new hashmap<string, object> ();//Explain the data here, the key corresponds to the third parameter of the Simpleadapter, must contain them. The values correspond to the fifth parameter, namely the picture and text Map.put ("text", I), Map.put ("image", r.drawable.ic_launcher); List.add (map);} return list;} @Overridepublic boolean Oncreateoptionsmenu (Menu menu) {//Inflate the menu; This adds items to the action bar if it is PR Esent.getmenuinflater (). Inflate (R.menu.main, menu); return true;} /** * Interface callback, this method can be called in Refreshlistview for data addition. */@Overridepublic void Onrefresh () {//TODO auto-generated method stub handler handler = new handler (); Handler.postdelayed (new Runnable () { @Overridepublic void Run () {//TODO auto-generated method stubmap<string, object> map = new hashmap<string, objec T> (); Map.put ("Text", "scroll Add"), Map.put ("image", r.drawable.ic_launcher); List.add (0, map); Listview.setadapter ( Simple_adapter); simple_adapter.notifydatasetchanged (); Listview.refreshcomplete ();}}, 2000);}}

When we get here, we realize the beginning of the article.


========================================

Write in the back:

The source code has been uploaded to my github, or to the Web drive for download.

Any questions, welcome message exchange!


Android easy to implement the imitation QQ space drop-down refresh

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.