天天看點

android上拉下拉加載更多資料

最近項目中用到了listview的下拉重新整理的功能,總結了一下前輩們的代碼,單獨抽取出來寫了一個demo作為示例。

效果圖

下拉重新整理:

android上拉下拉加載更多資料

加載更多:

android上拉下拉加載更多資料

customlistview.java

package com.example.uitest.view;  

import java.util.date;  

import com.example.uitest.r;  

import android.content.context;  

import android.util.attributeset;  

import android.util.log;  

import android.view.layoutinflater;  

import android.view.motionevent;  

import android.view.view;  

import android.view.viewgroup;  

import android.view.animation.linearinterpolator;  

import android.view.animation.rotateanimation;  

import android.widget.abslistview;  

import android.widget.abslistview.onscrolllistener;  

import android.widget.baseadapter;  

import android.widget.imageview;  

import android.widget.linearlayout;  

import android.widget.listview;  

import android.widget.progressbar;  

import android.widget.textview;  

/** 

 * listview下拉重新整理 

 * 

 */  

public class customlistview extends listview implements onscrolllistener {  

    private final static int release_to_refresh = 0;  

    private final static int pull_to_refresh = 1;  

    private final static int refreshing = 2;  

    private final static int done = 3;  

    private final static int loading = 4;  

    // 實際的padding的距離與界面上偏移距離的比例  

    private final static int ratio = 3;  

    private layoutinflater inflater;  

    private linearlayout headview;  

    private textview tipstextview;  

    private textview lastupdatedtextview;  

    private imageview arrowimageview;  

    private progressbar progressbar;  

    private rotateanimation animation;  

    private rotateanimation reverseanimation;  

    // 用于保證starty的值在一個完整的touch事件中隻被記錄一次  

    private boolean isrecored;  

    private int headcontentwidth;  

    private int headcontentheight;  

    private int starty;  

    private int firstitemindex;  

    private int state;  

    private boolean isback;  

    private onrefreshlistener refreshlistener;  

    private onloadlistener loadlistener;  

    private boolean isrefreshable;  

    private progressbar moreprogressbar;  

    private textview loadmoreview;  

    private view moreview;  

    public customlistview(context context) {  

        super(context);  

        init(context);  

    }  

    public customlistview(context context, attributeset attrs) {  

        super(context, attrs);  

    private void init(context context) {  

        setcachecolorhint(context.getresources().getcolor(r.color.transparent));  

        inflater = layoutinflater.from(context);  

        headview = (linearlayout) inflater.inflate(r.layout.head, null);  

        arrowimageview = (imageview) headview.findviewbyid(r.id.head_arrowimageview);  

        arrowimageview.setminimumwidth(70);  

        arrowimageview.setminimumheight(50);  

        progressbar = (progressbar) headview.findviewbyid(r.id.head_progressbar);  

        tipstextview = (textview) headview.findviewbyid(r.id.head_tipstextview);  

        lastupdatedtextview = (textview) headview.findviewbyid(r.id.head_lastupdatedtextview);  

        measureview(headview);  

        headcontentheight = headview.getmeasuredheight();  

        headcontentwidth = headview.getmeasuredwidth();  

        headview.setpadding(0, -1 * headcontentheight, 0, 0);  

        headview.invalidate();  

        log.v("size", "width:" + headcontentwidth + " height:" + headcontentheight);  

        addheaderview(headview, null, false);  

        setonscrolllistener(this);  

        animation = new rotateanimation(0, -180, rotateanimation.relative_to_self, 0.5f, rotateanimation.relative_to_self, 0.5f);  

        animation.setinterpolator(new linearinterpolator());  

        animation.setduration(250);  

        animation.setfillafter(true);  

        reverseanimation = new rotateanimation(-180, 0, rotateanimation.relative_to_self, 0.5f, rotateanimation.relative_to_self, 0.5f);  

        reverseanimation.setinterpolator(new linearinterpolator());  

        reverseanimation.setduration(200);  

        reverseanimation.setfillafter(true);  

        state = done;  

        isrefreshable = false;  

        moreview = layoutinflater.from(context).inflate(r.layout.listfooter_more, null);  

        moreview.setvisibility(view.visible);  

        moreprogressbar = (progressbar) moreview.findviewbyid(r.id.pull_to_refresh_progress);  

        loadmoreview = (textview) moreview.findviewbyid(r.id.load_more);  

        moreview.setonclicklistener(new view.onclicklistener() {  

            @override  

            public void onclick(view v) {  

                onload();  

            }  

        });  

        addfooterview(moreview);  

    public void onscroll(abslistview arg0, int firstvisiableitem, int arg2, int arg3) {  

        firstitemindex = firstvisiableitem;  

    public void onscrollstatechanged(abslistview arg0, int arg1) {  

    public boolean ontouchevent(motionevent event) {  

        if (isrefreshable) {  

            switch (event.getaction()) {  

            case motionevent.action_down:  

                if (firstitemindex == 0 && !isrecored) {  

                    isrecored = true;  

                    starty = (int) event.gety();  

                }  

                break;  

            case motionevent.action_up:  

                if (state != refreshing && state != loading) {  

                    if (state == done) {  

                    }  

                    if (state == pull_to_refresh) {  

                        state = done;  

                        changeheaderviewbystate();  

                    if (state == release_to_refresh) {  

                        state = refreshing;  

                        onrefresh();  

                isrecored = false;  

                isback = false;  

            case motionevent.action_move:  

                int tempy = (int) event.gety();  

                if (!isrecored && firstitemindex == 0) {  

                    starty = tempy;  

                if (state != refreshing && isrecored && state != loading) {  

                    // 保證在設定padding的過程中,目前的位置一直是在head,否則如果當清單超出螢幕的話,當在上推的時候,清單會同時進行滾動  

                    // 可以松手去重新整理了  

                        setselection(0);  

                        // 往上推了,推到了螢幕足夠掩蓋head的程度,但是還沒有推到全部掩蓋的地步  

                        if (((tempy - starty) / ratio < headcontentheight) && (tempy - starty) > 0) {  

                            state = pull_to_refresh;  

                            changeheaderviewbystate();  

                        }  

                        // 一下子推到頂了  

                        else if (tempy - starty <= 0) {  

                            state = done;  

                        // 往下拉了,或者還沒有上推到螢幕頂部掩蓋head的地步  

                    // 還沒有到達顯示松開重新整理的時候,done或者是pull_to_refresh狀态  

                        // 下拉到可以進入release_to_refresh的狀态  

                        if ((tempy - starty) / ratio >= headcontentheight) {  

                            state = release_to_refresh;  

                            isback = true;  

                        if (tempy - starty > 0) {  

                        headview.setpadding(0, -1 * headcontentheight + (tempy - starty) / ratio, 0, 0);  

                        headview.setpadding(0, (tempy - starty) / ratio - headcontentheight, 0, 0);  

        }  

        return super.ontouchevent(event);  

    // 當狀态改變時候,調用該方法,以更新界面  

    private void changeheaderviewbystate() {  

        switch (state) {  

        case release_to_refresh:  

            arrowimageview.setvisibility(view.visible);  

            progressbar.setvisibility(view.gone);  

            tipstextview.setvisibility(view.visible);  

            lastupdatedtextview.setvisibility(view.visible);  

            arrowimageview.clearanimation();  

            arrowimageview.startanimation(animation);  

            tipstextview.settext("松開重新整理");  

            break;  

        case pull_to_refresh:  

            // 是由release_to_refresh狀态轉變來的  

            if (isback) {  

                arrowimageview.clearanimation();  

                arrowimageview.startanimation(reverseanimation);  

                tipstextview.settext("下拉重新整理");  

            } else {  

        case refreshing:  

            headview.setpadding(0, 0, 0, 0);  

            progressbar.setvisibility(view.visible);  

            arrowimageview.setvisibility(view.gone);  

            tipstextview.settext("正在重新整理...");  

        case done:  

            headview.setpadding(0, -1 * headcontentheight, 0, 0);  

            arrowimageview.setimageresource(r.drawable.arrow);  

            tipstextview.settext("下拉重新整理");  

    public void setonrefreshlistener(onrefreshlistener refreshlistener) {  

        this.refreshlistener = refreshlistener;  

        isrefreshable = true;  

    public void setonloadlistener(onloadlistener loadlistener) {  

        this.loadlistener = loadlistener;  

    public interface onrefreshlistener {  

        public void onrefresh();  

    public interface onloadlistener {  

        public void onload();  

    @suppresswarnings("deprecation")  

    public void onrefreshcomplete() {  

        lastupdatedtextview.settext("最近更新:" + new date().tolocalestring());  

        changeheaderviewbystate();  

    private void onload() {  

        if (loadlistener != null) {  

            moreprogressbar.setvisibility(view.visible);  

            loadmoreview.settext(getcontext().getstring(r.string.load_more));  

            loadlistener.onload();  

    public void onloadcomplete() {  

//      moreview.setvisibility(view.gone);  

        moreprogressbar.setvisibility(view.gone);  

        loadmoreview.settext(getcontext().getstring(r.string.more_data));  

    private void onrefresh() {  

        if (refreshlistener != null) {  

            refreshlistener.onrefresh();  

    private void measureview(view child) {  

        viewgroup.layoutparams p = child.getlayoutparams();  

        if (p == null) {  

            p = new viewgroup.layoutparams(viewgroup.layoutparams.match_parent, viewgroup.layoutparams.wrap_content);  

        int childwidthspec = viewgroup.getchildmeasurespec(0, 0 + 0, p.width);  

        int lpheight = p.height;  

        int childheightspec;  

        if (lpheight > 0) {  

            childheightspec = measurespec.makemeasurespec(lpheight, measurespec.exactly);  

        } else {  

            childheightspec = measurespec.makemeasurespec(0, measurespec.unspecified);  

        child.measure(childwidthspec, childheightspec);  

    public void setadapter(baseadapter adapter) {  

        super.setadapter(adapter);  

}  

在 customlistview 中有2個回調接口,onrefreshlistener 和 onloadlistener ,分别對應 下拉和點選加載更多 時候的回調函數。在下拉重新整理完成之後要調用 mlistview.onrefreshcomplete(); 來隐藏掉 頭部,調用 mlistview.onloadcomplete(); 隐藏掉 底部的加載view。

header.xml

<?xml version="1.0" encoding="utf-8"?>  

<!-- listview的頭部 -->  

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"  

    android:layout_width="fill_parent"  

    android:layout_height="wrap_content" >  

    <!-- 内容 -->  

    <relativelayout  

        android:id="@+id/head_contentlayout"  

        android:layout_width="fill_parent"  

        android:layout_height="wrap_content"  

        android:paddingleft="30dp" >  

        <!-- 箭頭圖像、進度條 -->  

        <framelayout  

            android:layout_width="wrap_content"  

            android:layout_height="wrap_content"  

            android:layout_alignparentleft="true"  

            android:layout_centervertical="true" >  

            <!-- 箭頭 -->  

            <imageview  

                android:id="@+id/head_arrowimageview"  

                android:layout_width="wrap_content"  

                android:layout_height="wrap_content"  

                android:layout_gravity="center"  

                android:contentdescription="@string/app_name"  

                android:src="@drawable/arrow" />  

            <!-- 進度條 -->  

            <progressbar  

                android:id="@+id/head_progressbar"  

                style="?android:attr/progressbarstylesmall"  

                android:indeterminatedrawable="@drawable/progressbar_bg"  

                android:visibility="gone" />  

        </framelayout>  

        <!-- 提示、最近更新 -->  

        <linearlayout  

            android:layout_centerhorizontal="true"  

            android:gravity="center_horizontal"  

            android:orientation="vertical" >  

            <!-- 提示 -->  

            <textview  

                android:id="@+id/head_tipstextview"  

                android:text="@string/pull_to_refresh_pull_label"  

                android:textcolor="@color/pull_refresh_textview"  

                android:textsize="20sp" />  

            <!-- 最近更新 -->  

                android:id="@+id/head_lastupdatedtextview"  

                android:text="@string/pull_to_refresh_refresh_lasttime"  

                android:textcolor="@color/gold"  

                android:textsize="10sp" />  

        </linearlayout>  

    </relativelayout>  

</linearlayout>  

listfooter_more.xml

    android:layout_height="fill_parent"  

    android:gravity="center_horizontal"  

    android:orientation="horizontal"  

    android:padding="15dp"  

     >  

    <progressbar  

        android:id="@+id/pull_to_refresh_progress"  

        style="@android:style/widget.progressbar.small.inverse"  

        android:layout_width="wrap_content"  

        android:gravity="center"  

        android:indeterminate="true"  

        android:visibility="gone" >  

    </progressbar>  

    <textview  

        android:id="@+id/load_more"  

        android:layout_marginleft="10.0dp"  

        android:text="@string/more_data"  

        android:textcolor="@color/black" >  

    </textview>  

mainactivity.java

package com.example.uitest;  

import java.util.arraylist;  

import java.util.list;  

import android.app.activity;  

import android.graphics.bitmapfactory;  

import android.os.bundle;  

import android.os.handler;  

import android.view.menu;  

import android.widget.adapterview;  

import android.widget.adapterview.onitemclicklistener;  

import com.example.uitest.model.appinfo;  

import com.example.uitest.view.customlistview;  

import com.example.uitest.view.customlistview.onloadlistener;  

import com.example.uitest.view.customlistview.onrefreshlistener;  

public class mainactivity extends activity {  

    private static final string tag = mainactivity.class.getsimplename();  

    private static final int load_data_finish = 10;  

    private static final int refresh_data_finish = 11;  

    private list<appinfo> mlist = new arraylist<appinfo>();  

    private customlistadapter madapter;  

    private customlistview mlistview;  

    private int count = 10;  

    private handler handler = new handler(){  

        public void handlemessage(android.os.message msg) {  

            switch (msg.what) {  

            case refresh_data_finish:  

                if(madapter!=null){  

                    madapter.notifydatasetchanged();  

                mlistview.onrefreshcomplete();  //下拉重新整理完成  

            case load_data_finish:  

                mlistview.onloadcomplete(); //加載更多完成  

            default:  

        };  

    };  

    @override  

    protected void oncreate(bundle savedinstancestate) {  

        super.oncreate(savedinstancestate);  

        setcontentview(r.layout.activity_main);  

        buildappdata();  

        madapter = new customlistadapter(this);  

        mlistview = (customlistview) findviewbyid(r.id.mlistview);  

        mlistview.setadapter(madapter);  

        mlistview.setonrefreshlistener(new onrefreshlistener() {  

            public void onrefresh() {  

                //todo 下拉重新整理  

                log.e(tag, "onrefresh");  

                loaddata(0);  

        mlistview.setonloadlistener(new onloadlistener() {  

            public void onload() {  

                //todo 加載更多  

                log.e(tag, "onload");  

                loaddata(1);  

        mlistview.setonitemclicklistener(new onitemclicklistener() {  

            public void onitemclick(adapterview<?> parent, view view,  

                    int position, long id) {  

                log.e(tag, "click position:" + position);  

    public void loaddata(final int type){  

        new thread(){  

            public void run() {  

                for(int i=count;i<count+10;i++){  

                    appinfo ai = new appinfo();  

                    ai.setappicon(bitmapfactory.decoderesource(getresources(),  

                            r.drawable.ic_launcher));  

                    ai.setappname("應用demo_" + i);  

                    ai.setappver("版本: " + (i % 10 + 1) + "." + (i % 8 + 2) + "."  

                            + (i % 6 + 3));  

                    ai.setappsize("大小: " + i * 10 + "mb");  

                    mlist.add(ai);  

                count += 10;  

                try {  

                    thread.sleep(300);  

                } catch (interruptedexception e) {  

                    e.printstacktrace();  

                if(type==0){    //下拉重新整理  

//                  collections.reverse(mlist); //逆序  

                    handler.sendemptymessage(refresh_data_finish);  

                }else if(type==1){  

                    handler.sendemptymessage(load_data_finish);  

        }.start();  

    /** 

     * 初始化應用資料 

     */  

    private void buildappdata() {  

        for (int i = 0; i < 10; i++) {  

            appinfo ai = new appinfo();  

            ai.setappicon(bitmapfactory.decoderesource(getresources(),  

                    r.drawable.ic_launcher));  

            ai.setappname("應用demo_" + i);  

            ai.setappver("版本: " + (i % 10 + 1) + "." + (i % 8 + 2) + "."  

                    + (i % 6 + 3));  

            ai.setappsize("大小: " + i * 10 + "mb");  

            mlist.add(ai);  

    public boolean oncreateoptionsmenu(menu menu) {  

        // inflate the menu; this adds items to the action bar if it is present.  

        getmenuinflater().inflate(r.menu.main, menu);  

        return true;  

    public class customlistadapter extends baseadapter {  

        private layoutinflater minflater;  

        public customlistadapter(context context) {  

            minflater = layoutinflater.from(context);  

        @override  

        public int getcount() {  

            return mlist.size();  

        public object getitem(int arg0) {  

            return mlist.get(arg0);  

        public long getitemid(int position) {  

            return position;  

        public view getview(int position, view convertview, viewgroup parent) {  

            if (getcount() == 0) {  

                return null;  

            viewholder holder = null;  

            if (convertview == null) {  

                convertview = minflater.inflate(r.layout.list_item, null);  

                holder = new viewholder();  

                holder.ivimage = (imageview) convertview  

                        .findviewbyid(r.id.ivicon);  

                holder.tvname = (textview) convertview  

                        .findviewbyid(r.id.tvname);  

                holder.tvver = (textview) convertview.findviewbyid(r.id.tvver);  

                holder.tvsize = (textview) convertview  

                        .findviewbyid(r.id.tvsize);  

                convertview.settag(holder);  

                holder = (viewholder) convertview.gettag();  

            appinfo ai = mlist.get(position);  

            holder.ivimage.setimagebitmap(ai.getappicon());  

            holder.tvname.settext(ai.getappname());  

            holder.tvver.settext(ai.getappver());  

            holder.tvsize.settext(ai.getappsize());  

            return convertview;  

    public static class viewholder {  

        private imageview ivimage;  

        private textview tvname;  

        private textview tvver;  

        private textview tvsize;  

list_item.xml

<relativelayout   

    xmlns:android="http://schemas.android.com/apk/res/android"  

    android:layout_width="match_parent"  

    <imageview  

        android:id="@+id/ivicon"  

        android:contentdescription="@string/image_desc"  

        android:src="@drawable/ic_launcher" />  

    <linearlayout  

        android:id="@+id/appinfo"  

        android:layout_width="match_parent"  

        android:layout_marginleft="5dip"  

        android:layout_torightof="@id/ivicon"  

        android:orientation="vertical" >  

        <textview  

            android:id="@+id/tvname"  

            android:text="@string/name"  

            android:textcolor="#000000"  

            android:textsize="16sp" />  

            android:id="@+id/tvver"  

            android:text="@string/ver"  

            android:textcolor="#666666"  

            android:textsize="13sp" />  

            android:id="@+id/tvsize"  

            android:text="@string/size"  

    </linearlayout>  

    <button  

        android:id="@+id/btnclick"  

        android:layout_width="80dip"  

        android:layout_alignparentright="true"  

        android:layout_centervertical="true"  

        android:focusable="false"  

        android:text="@string/mgr"  

        android:textcolor="#000000"  

        android:textsize="16sp" />  

</relativelayout>