天天看點

Android自定義控件實戰——下拉重新整理控件終結者:PullToRefreshLayout

轉載請聲明出處http://blog.csdn.net/zhongkejingwang/article/details/38340701  

       說到下拉重新整理控件,網上版本有很多,很多軟體也都有下拉重新整理功能。有一個叫xlistview的,我看别人用過,沒看過是咋實作的,看這名字估計是繼承自listview修改的,不過效果看起來挺醜的,也沒什麼擴充性,太單調了。看了qq2014的清單下拉重新整理,發現挺好看的,我喜歡,貼一下圖看一下qq的下拉重新整理效果:

Android自定義控件實戰——下拉重新整理控件終結者:PullToRefreshLayout

    不錯吧?嗯,是的。一看就知道實作方式不一樣。咱們今天就來實作一個下拉重新整理控件。由于有時候不僅僅是listview需要下拉重新整理,expandablelistview和gridview也有這個需求,由于listview,gridview都是abslistview的子類,expandablelistview是listview的子類是以也是abslistview的子類。是以我的思路是自定義一個對所有abslistview的子類通用的下拉管理布局,叫pulltorefreshlayout,如果需要gridview,隻需要在布局檔案裡将listview換成gridview就行了,expandablelistview也一樣,不需要再繼承什麼gridview啊listview啊亂七八糟的。

Android自定義控件實戰——下拉重新整理控件終結者:PullToRefreshLayout

看上圖,主要工作就是定義黑色大布局,紅色部分是不下拉的時候的可見部分,可以是任意的abslistview的子類(gridview,listview,expandablelistview等等)。其實我已經寫好了,先看一下效果圖:

正常拉法:

Android自定義控件實戰——下拉重新整理控件終結者:PullToRefreshLayout

強迫症拉法:

Android自定義控件實戰——下拉重新整理控件終結者:PullToRefreshLayout

上面是listview的,下面是gridview的

Android自定義控件實戰——下拉重新整理控件終結者:PullToRefreshLayout

再來看一下expandablelistview的下拉重新整理效果:

Android自定義控件實戰——下拉重新整理控件終結者:PullToRefreshLayout

可以看到,點選事件和長按事件都能正常觸發而不會誤觸發,在使用expandablelistview的時候需要注意禁止展開時自動滾動,否則會出現bug。後面會提供demo源碼下載下傳,可以根據自己的需求去修改。

下面講解pulltorefreshlayout的實作,在貼完整的源碼之前先了解整個類的大概思路:

[java] view

plaincopy

public class pulltorefreshlayout extends relativelayout implements ontouchlistener  

{  

    // 下拉的距離  

    public float movedeltay = 0;  

    // 是否可以下拉  

    private boolean canpull = true;  

    private void hidehead()  

    {  

        // 在這裡開始異步隐藏下拉頭,在松手的時候或這重新整理完畢的時候隐藏  

    }  

    public void refreshfinish(int refreshresult)  

        // 完成重新整理操作,顯示重新整理結果  

    private void changestate(int to)  

        // 改變目前所處的狀态,有四個狀态:下拉重新整理、釋放重新整理、正在重新整理、重新整理完成  

    /* 

     * (非 javadoc)由父控件決定是否分發事件,防止事件沖突 

     *  

     * @see android.view.viewgroup#dispatchtouchevent(android.view.motionevent) 

     */  

    @override  

    public boolean dispatchtouchevent(motionevent ev)  

        switch (ev.getactionmasked())  

        {  

        case motionevent.action_down:  

            /*手指按下的時候,無法判斷是否将要下拉,是以這時候break讓父類把down事件分發給子view 

            記錄按下的坐标*/  

            break;  

        case motionevent.action_move:  

            /*如果往上滑動且movedetay==0則說明不在下拉,break繼續将move事件分發給子view 

            如果往下拉,則計算下拉的距離movedeltay,根據movedeltay重新layout子控件。但是 

            由于down事件傳到了子view,如果不清除子view的事件,會導緻子view誤觸發長按事件和點選事件。是以在這裡清除子view的事件回調。 

            下拉超過一定的距離時,改變目前狀态*/  

        case motionevent.action_up:  

            //根據目前狀态執行重新整理操作或者hidehead  

        default:  

        }  

        // 事件分發交給父類  

        return super.dispatchtouchevent(ev);  

     * (非 javadoc)繪制陰影效果,顔色值可以修改 

     * @see android.view.viewgroup#dispatchdraw(android.graphics.canvas) 

    protected void dispatchdraw(canvas canvas)  

        //在這裡用一個漸變繪制分界線陰影  

    protected void onlayout(boolean changed, int l, int t, int r, int b)  

        //這個方法就是重新layout子view了,根據movedeltay來定位子view的位置  

    public boolean ontouch(view v, motionevent event)  

        //這個是ontouchlistener的方法,隻判斷abslistview的狀态來決定是否canpull,除此之外不做其他處理  

}  

可以看到,這裡複寫了viewgroup的dispatchtouchevent,這樣就可以掌控事件的分發,如果不了解這個方法可以看一下這篇android事件分發、view事件listener全解析。之是以要控制事件分發是因為我們不可能知道手指down在abslistview上之後将往上滑還是往下拉,是以down事件會分發給abslistview的,但是在move的時候就需要看情況了,因為我們不想在下拉的同時abslistview也在滑動,是以在下拉的時候不分發move事件,但這樣問題又來了,前面abslistview已經接收了down事件,如果這時候不分發move事件給它,它會觸發長按事件或者點選事件,是以在這裡還需要清除abslistview消息清單中的callback。

onlayout用于重新布置下拉頭和abslistview的位置的,這個不難了解。

了解了大概思路之後,看一下pulltorefreshlayout完整的源碼吧~

package com.jingchen.pulltorefresh;  

import java.lang.reflect.field;  

import java.util.timer;  

import java.util.timertask;  

import android.content.context;  

import android.graphics.canvas;  

import android.graphics.lineargradient;  

import android.graphics.paint;  

import android.graphics.paint.style;  

import android.graphics.rectf;  

import android.graphics.shader.tilemode;  

import android.os.handler;  

import android.os.message;  

import android.util.attributeset;  

import android.util.log;  

import android.view.motionevent;  

import android.view.view;  

import android.view.view.ontouchlistener;  

import android.view.viewgroup;  

import android.view.animation.animationutils;  

import android.view.animation.linearinterpolator;  

import android.view.animation.rotateanimation;  

import android.widget.abslistview;  

import android.widget.relativelayout;  

import android.widget.textview;  

/** 

 * 整個下拉重新整理就這一個布局,用來管理兩個子控件,其中一個是下拉頭,另一個是包含内容的contentview(可以是abslistview的任何子類) 

 *  

 * @author 陳靖 

 */  

    public static final string tag = "pulltorefreshlayout";  

    // 下拉重新整理  

    public static final int pull_to_refresh = 0;  

    // 釋放重新整理  

    public static final int release_to_refresh = 1;  

    // 正在重新整理  

    public static final int refreshing = 2;  

    // 重新整理完畢  

    public static final int done = 3;  

    // 目前狀态  

    private int state = pull_to_refresh;  

    // 重新整理回調接口  

    private onrefreshlistener mlistener;  

    // 重新整理成功  

    public static final int refresh_succeed = 0;  

    // 重新整理失敗  

    public static final int refresh_fail = 1;  

    // 下拉頭  

    private view headview;  

    // 内容  

    private view contentview;  

    // 按下y坐标,上一個事件點y坐标  

    private float downy, lasty;  

    // 釋放重新整理的距離  

    private float refreshdist = 200;  

    private timer timer;  

    private mytimertask mtask;  

    // 復原速度  

    public float move_speed = 8;  

    // 第一次執行布局  

    private boolean islayout = false;  

    // 在重新整理過程中滑動操作  

    private boolean istouchinrefreshing = false;  

    // 手指滑動距離與下拉頭的滑動距離比,中間會随正切函數變化  

    private float radio = 2;  

    // 下拉箭頭的轉180°動畫  

    private rotateanimation rotateanimation;  

    // 均勻旋轉動畫  

    private rotateanimation refreshinganimation;  

    // 下拉的箭頭  

    private view pullview;  

    // 正在重新整理的圖示  

    private view refreshingview;  

    // 重新整理結果圖示  

    private view stateimageview;  

    // 重新整理結果:成功或失敗  

    private textview statetextview;  

    /** 

     * 執行自動復原的handler 

    handler updatehandler = new handler()  

        @override  

        public void handlemessage(message msg)  

            // 回彈速度随下拉距離movedeltay增大而增大  

            move_speed = (float) (8 + 5 * math.tan(math.pi / 2 / getmeasuredheight() * movedeltay));  

            if (state == refreshing && movedeltay <= refreshdist && !istouchinrefreshing)  

            {  

                // 正在重新整理,且沒有往上推的話則懸停,顯示"正在重新整理..."  

                movedeltay = refreshdist;  

                mtask.cancel();  

            }  

            if (canpull)  

                movedeltay -= move_speed;  

            if (movedeltay <= 0)  

                // 已完成回彈  

                movedeltay = 0;  

                pullview.clearanimation();  

                // 隐藏下拉頭時有可能還在重新整理,隻有目前狀态不是正在重新整理時才改變狀态  

                if (state != refreshing)  

                    changestate(pull_to_refresh);  

            // 重新整理布局,會自動調用onlayout  

            requestlayout();  

    };  

    public void setonrefreshlistener(onrefreshlistener listener)  

        mlistener = listener;  

    public pulltorefreshlayout(context context)  

        super(context);  

        initview(context);  

    public pulltorefreshlayout(context context, attributeset attrs)  

        super(context, attrs);  

    public pulltorefreshlayout(context context, attributeset attrs, int defstyle)  

        super(context, attrs, defstyle);  

    private void initview(context context)  

        timer = new timer();  

        mtask = new mytimertask(updatehandler);  

        rotateanimation = (rotateanimation) animationutils.loadanimation(context, r.anim.reverse_anim);  

        refreshinganimation = (rotateanimation) animationutils.loadanimation(context, r.anim.rotating);  

        // 添加勻速轉動動畫  

        linearinterpolator lir = new linearinterpolator();  

        rotateanimation.setinterpolator(lir);  

        refreshinganimation.setinterpolator(lir);  

        if (mtask != null)  

            mtask.cancel();  

            mtask = null;  

        timer.schedule(mtask, 0, 5);  

     * 完成重新整理操作,顯示重新整理結果 

        refreshingview.clearanimation();  

        refreshingview.setvisibility(view.gone);  

        switch (refreshresult)  

        case refresh_succeed:  

            // 重新整理成功  

            stateimageview.setvisibility(view.visible);  

            statetextview.settext(r.string.refresh_succeed);  

            stateimageview.setbackgroundresource(r.drawable.refresh_succeed);  

        case refresh_fail:  

            // 重新整理失敗  

            statetextview.settext(r.string.refresh_fail);  

            stateimageview.setbackgroundresource(r.drawable.refresh_failed);  

        // 重新整理結果停留1秒  

        new handler()  

            @override  

            public void handlemessage(message msg)  

                state = pull_to_refresh;  

                hidehead();  

        }.sendemptymessagedelayed(0, 1000);  

        state = to;  

        switch (state)  

        case pull_to_refresh:  

            // 下拉重新整理  

            stateimageview.setvisibility(view.gone);  

            statetextview.settext(r.string.pull_to_refresh);  

            pullview.clearanimation();  

            pullview.setvisibility(view.visible);  

        case release_to_refresh:  

            // 釋放重新整理  

            statetextview.settext(r.string.release_to_refresh);  

            pullview.startanimation(rotateanimation);  

        case refreshing:  

            // 正在重新整理  

            refreshingview.setvisibility(view.visible);  

            pullview.setvisibility(view.invisible);  

            refreshingview.startanimation(refreshinganimation);  

            statetextview.settext(r.string.refreshing);  

            downy = ev.gety();  

            lasty = downy;  

            if (mtask != null)  

            /* 

             * 觸碰的地方位于下拉頭布局,由于我們沒有對下拉頭做事件響應,這時候它會給咱傳回一個false導緻接下來的事件不再分發進來。 

             * 是以我們不能交給父類分發,直接傳回true 

             */  

            if (ev.gety() < movedeltay)  

                return true;  

            // canpull這個值在底下ontouch中會根據listview是否滑到頂部來改變,意思是是否可下拉  

                // 對實際滑動距離做縮小,造成用力拉的感覺  

                movedeltay = movedeltay + (ev.gety() - lasty) / radio;  

                if (movedeltay < 0)  

                    movedeltay = 0;  

                if (movedeltay > getmeasuredheight())  

                    movedeltay = getmeasuredheight();  

                if (state == refreshing)  

                {  

                    // 正在重新整理的時候觸摸移動  

                    istouchinrefreshing = true;  

                }  

            lasty = ev.gety();  

            // 根據下拉距離改變比例  

            radio = (float) (2 + 2 * math.tan(math.pi / 2 / getmeasuredheight() * movedeltay));  

            if (movedeltay <= refreshdist && state == release_to_refresh)  

                // 如果下拉距離沒達到重新整理的距離且目前狀态是釋放重新整理,改變狀态為下拉重新整理  

                changestate(pull_to_refresh);  

            if (movedeltay >= refreshdist && state == pull_to_refresh)  

                changestate(release_to_refresh);  

            if (movedeltay > 8)  

                // 防止下拉過程中誤觸發長按事件和點選事件  

                clearcontentviewevents();  

            if (movedeltay > 0)  

                // 正在下拉,不讓子控件捕獲事件  

            if (movedeltay > refreshdist)  

                // 正在重新整理時往下拉釋放後下拉頭不隐藏  

                istouchinrefreshing = false;  

            if (state == release_to_refresh)  

                changestate(refreshing);  

                // 重新整理操作  

                if (mlistener != null)  

                    mlistener.onrefresh();  

            } else  

            hidehead();  

     * 通過反射修改字段去掉長按事件和點選事件 

    private void clearcontentviewevents()  

        try  

            field[] fields = abslistview.class.getdeclaredfields();  

            for (int i = 0; i < fields.length; i++)  

                if (fields[i].getname().equals("mpendingcheckforlongpress"))  

                    // mpendingcheckforlongpress是abslistview中的字段,通過反射擷取并從消息清單删除,去掉長按事件  

                    fields[i].setaccessible(true);  

                    contentview.gethandler().removecallbacks((runnable) fields[i].get(contentview));  

                } else if (fields[i].getname().equals("mtouchmode"))  

                    // touch_mode_rest = -1, 這個可以去除點選事件  

                    fields[i].set(contentview, -1);  

            // 去掉焦點  

            ((abslistview) contentview).getselector().setstate(new int[]  

            { 0 });  

        } catch (exception e)  

            log.d(tag, "error : " + e.tostring());  

        super.dispatchdraw(canvas);  

        if (movedeltay == 0)  

            return;  

        rectf rectf = new rectf(0, 0, getmeasuredwidth(), movedeltay);  

        paint paint = new paint();  

        paint.setantialias(true);  

        // 陰影的高度為26  

        lineargradient lineargradient = new lineargradient(0, movedeltay, 0, movedeltay - 26, 0x66000000, 0x00000000, tilemode.clamp);  

        paint.setshader(lineargradient);  

        paint.setstyle(style.fill);  

        // 在movedeltay處往上變淡  

        canvas.drawrect(rectf, paint);  

    private void initview()  

        pullview = headview.findviewbyid(r.id.pull_icon);  

        statetextview = (textview) headview.findviewbyid(r.id.state_tv);  

        refreshingview = headview.findviewbyid(r.id.refreshing_icon);  

        stateimageview = headview.findviewbyid(r.id.state_iv);  

        if (!islayout)  

            // 這裡是第一次進來的時候做一些初始化  

            headview = getchildat(0);  

            contentview = getchildat(1);  

            // 給abslistview設定ontouchlistener  

            contentview.setontouchlistener(this);  

            islayout = true;  

            initview();  

            refreshdist = ((viewgroup) headview).getchildat(0).getmeasuredheight();  

        if (canpull)  

            // 改變子控件的布局  

            headview.layout(0, (int) movedeltay - headview.getmeasuredheight(), headview.getmeasuredwidth(), (int) movedeltay);  

            contentview.layout(0, (int) movedeltay, contentview.getmeasuredwidth(), (int) movedeltay + contentview.getmeasuredheight());  

        }else super.onlayout(changed, l, t, r, b);  

    class mytimertask extends timertask  

        handler handler;  

        public mytimertask(handler handler)  

            this.handler = handler;  

        public void run()  

            handler.sendmessage(handler.obtainmessage());  

        // 第一個item可見且滑動到頂部  

        abslistview alv = null;  

            alv = (abslistview) v;  

            log.d(tag, e.getmessage());  

            return false;  

        if (alv.getcount() == 0)  

            // 沒有item的時候也可以下拉重新整理  

            canpull = true;  

        } else if (alv.getfirstvisibleposition() == 0 && alv.getchildat(0).gettop() >= 0)  

            // 滑到abslistview的頂部了  

        } else  

            canpull = false;  

        return false;  

代碼中的注釋已經寫的很清楚了。

既然pulltorefreshlayout已經寫好了,接下來就來使用這個layout實作下拉重新整理~

首先得寫個onrefreshlistener接口來回調重新整理操作:

public interface onrefreshlistener {  

    void onrefresh();  

就一個重新整理操作的方法,待會兒讓activity實作這個接口就可以在activity中執行重新整理操作了。

看一下mainactivity的布局:

[html] view

<com.jingchen.pulltorefresh.pulltorefreshlayout xmlns:android="http://schemas.android.com/apk/res/android"  

    android:id="@+id/refresh_view"  

    android:layout_width="match_parent"  

    android:layout_height="match_parent" >  

    <include layout="@layout/refresh_head" />  

    <!-- 支援abslistview的所有子類 -->  

    <listview  

        android:id="@+id/content_view"  

        android:layout_width="match_parent"  

        android:layout_height="match_parent"  

        android:background="@color/white"  

        android:divider="@color/gray"  

        android:dividerheight="1dp" >  

    </listview>  

</com.jingchen.pulltorefresh.pulltorefreshlayout>  

pulltorefreshlayout隻能包含兩個子控件:refresh_head和content_view。

看一下refresh_head的布局:

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

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

    android:id="@+id/head_view"  

    android:layout_height="match_parent"  

    android:background="@color/light_blue" >  

    <relativelayout  

        android:layout_height="wrap_content"  

        android:layout_alignparentbottom="true"  

        android:paddingbottom="20dp"  

        android:paddingtop="20dp" >  

        <relativelayout  

            android:layout_width="match_parent"  

            android:layout_height="wrap_content"  

            android:layout_centerinparent="true" >  

            <imageview  

                android:id="@+id/pull_icon"  

                android:layout_width="wrap_content"  

                android:layout_height="wrap_content"  

                android:layout_centervertical="true"  

                android:layout_marginleft="60dp"  

                android:background="@drawable/pull_icon_big" />  

                android:id="@+id/refreshing_icon"  

                android:background="@drawable/refreshing"  

                android:visibility="gone" />  

            <textview  

                android:id="@+id/state_tv"  

                android:layout_centerinparent="true"  

                android:text="@string/pull_to_refresh"  

                android:textcolor="@color/white"  

                android:textsize="16sp" />  

                android:id="@+id/state_iv"  

                android:layout_marginright="8dp"  

                android:layout_toleftof="@id/state_tv"  

        </relativelayout>  

    </relativelayout>  

</relativelayout>  

可以根據需要修改refresh_head的布局然後在pulltorefreshlayout中處理,但是相關view的id要和pulltorefreshlayout中用到的保持同步!

接下來是mainactivity的代碼:

import java.util.arraylist;  

import java.util.list;  

import android.app.activity;  

import android.os.bundle;  

import android.support.v4.view.pageradapter;  

import android.support.v4.view.viewpager;  

import android.support.v4.view.viewpager.onpagechangelistener;  

import android.view.layoutinflater;  

import android.view.view.onclicklistener;  

import android.widget.adapterview;  

import android.widget.adapterview.onitemclicklistener;  

import android.widget.adapterview.onitemlongclicklistener;  

import android.widget.baseexpandablelistadapter;  

import android.widget.expandablelistview;  

import android.widget.expandablelistview.onchildclicklistener;  

import android.widget.expandablelistview.ongroupclicklistener;  

import android.widget.listview;  

import android.widget.toast;  

 * 除了下拉重新整理,在contenview為listview的情況下我給listview增加了footerview,實作點選加載更多 

public class mainactivity extends activity implements onrefreshlistener, onclicklistener  

    private abslistview alv;  

    private pulltorefreshlayout refreshlayout;  

    private view loading;  

    private rotateanimation loadinganimation;  

    private textview loadtextview;  

    private myadapter adapter;  

    private boolean isloading = false;  

    protected void oncreate(bundle savedinstancestate)  

        super.oncreate(savedinstancestate);  

        setcontentview(r.layout.activity_main);  

        init();  

    private void init()  

        alv = (abslistview) findviewbyid(r.id.content_view);  

        refreshlayout = (pulltorefreshlayout) findviewbyid(r.id.refresh_view);  

        refreshlayout.setonrefreshlistener(this);  

        initlistview();  

        loadinganimation = (rotateanimation) animationutils.loadanimation(this, r.anim.rotating);  

        loadinganimation.setinterpolator(lir);  

     * listview初始化方法 

    private void initlistview()  

        list<string> items = new arraylist<string>();  

        for (int i = 0; i < 30; i++)  

            items.add("這裡是item " + i);  

        // 添加head  

        view headview = getlayoutinflater().inflate(r.layout.listview_head, null);  

        ((listview) alv).addheaderview(headview, null, false);  

        // 添加footer  

        view footerview = getlayoutinflater().inflate(r.layout.load_more, null);  

        loading = footerview.findviewbyid(r.id.loading_icon);  

        loadtextview = (textview) footerview.findviewbyid(r.id.loadmore_tv);  

        ((listview) alv).addfooterview(footerview, null, false);  

        footerview.setonclicklistener(this);  

        adapter = new myadapter(this, items);  

        alv.setadapter(adapter);  

        alv.setonitemlongclicklistener(new onitemlongclicklistener()  

            public boolean onitemlongclick(adapterview<?> parent, view view, int position, long id)  

                toast.maketext(mainactivity.this, "longclick on " + parent.getadapter().getitemid(position), toast.length_short).show();  

        });  

        alv.setonitemclicklistener(new onitemclicklistener()  

            public void onitemclick(adapterview<?> parent, view view, int position, long id)  

                toast.maketext(mainactivity.this, " click on " + parent.getadapter().getitemid(position), toast.length_short).show();  

     * gridview初始化方法 

    private void initgridview()  

     * expandablelistview初始化方法 

    private void initexpandablelistview()  

        ((expandablelistview) alv).setadapter(new expandablelistadapter(this));  

        ((expandablelistview) alv).setonchildclicklistener(new onchildclicklistener()  

            public boolean onchildclick(expandablelistview parent, view v, int groupposition, int childposition, long id)  

                toast.maketext(mainactivity.this, " click on group " + groupposition + " item " + childposition, toast.length_short).show();  

        ((expandablelistview) alv).setonitemlongclicklistener(new onitemlongclicklistener()  

        ((expandablelistview) alv).setongroupclicklistener(new ongroupclicklistener()  

            public boolean ongroupclick(expandablelistview parent, view v, int groupposition, long id)  

                if (parent.isgroupexpanded(groupposition))  

                    // 如果展開則關閉  

                    parent.collapsegroup(groupposition);  

                } else  

                    // 如果關閉則打開,注意這裡是手動打開不要預設滾動否則會有bug  

                    parent.expandgroup(groupposition);  

    public void onrefresh()  

        // 下拉重新整理操作  

                refreshlayout.refreshfinish(pulltorefreshlayout.refresh_succeed);  

        }.sendemptymessagedelayed(0, 5000);  

    public void onclick(view v)  

        switch (v.getid())  

        case r.id.loadmore_layout:  

            if (!isloading)  

                loading.setvisibility(view.visible);  

                loading.startanimation(loadinganimation);  

                loadtextview.settext(r.string.loading);  

                isloading = true;  

    class expandablelistadapter extends baseexpandablelistadapter  

        private string[] groupsstrings;// = new string[] { "這裡是group 0",  

                                        // "這裡是group 1", "這裡是group 2" };  

        private string[][] groupitems;  

        private context context;  

        public expandablelistadapter(context context)  

            this.context = context;  

            groupsstrings = new string[8];  

            for (int i = 0; i < groupsstrings.length; i++)  

                groupsstrings[i] = new string("這裡是group " + i);  

            groupitems = new string[8][8];  

            for (int i = 0; i < groupitems.length; i++)  

                for (int j = 0; j < groupitems[i].length; j++)  

                    groupitems[i][j] = new string("這裡是group " + i + "裡的item " + j);  

        public int getgroupcount()  

            return groupsstrings.length;  

        public int getchildrencount(int groupposition)  

            return groupitems[groupposition].length;  

        public object getgroup(int groupposition)  

            return groupsstrings[groupposition];  

        public object getchild(int groupposition, int childposition)  

            return groupitems[groupposition][childposition];  

        public long getgroupid(int groupposition)  

            return groupposition;  

        public long getchildid(int groupposition, int childposition)  

            return childposition;  

        public boolean hasstableids()  

            return true;  

        public view getgroupview(int groupposition, boolean isexpanded, view convertview, viewgroup parent)  

            view view = layoutinflater.from(context).inflate(r.layout.list_item_layout, null);  

            textview tv = (textview) view.findviewbyid(r.id.name_tv);  

            tv.settext(groupsstrings[groupposition]);  

            return view;  

        public view getchildview(int groupposition, int childposition, boolean islastchild, view convertview, viewgroup parent)  

            tv.settext(groupitems[groupposition][childposition]);  

        public boolean ischildselectable(int groupposition, int childposition)  

在mainactivity中判斷contentview是listview的話給listview添加了footerview實作點選加載更多的功能。這隻是一個示範pulltorefreshlayout使用的demo,可以參照一下修改。我已經在裡面寫了listview,gridview和expandablelistview的初始化方法,根據自己使用的是哪個來調用吧。那麼這是listview的下拉重新整理和加載更多。如果我要gridview也有下拉重新整理功能呢?那就把mainactivity的布局換成這樣:

    <gridview  

        android:columnwidth="90dp"  

        android:gravity="center"  

        android:horizontalspacing="10dp"  

        android:numcolumns="auto_fit"  

        android:stretchmode="columnwidth"  

        android:verticalspacing="15dp" >  

    </gridview>  

如果是expandablelistview則把布局改成這樣:

    <expandablelistview  

        android:background="@color/white" >  

    </expandablelistview>  

怎麼樣?很簡單吧?簡單易用,不用再去繼承修改了~

點選下載下傳源碼

繼續閱讀