该控件的修改时根据PullToRefreshList的机制修改

下面是对ExpandableListView的扩展

package com.up91.gwy.view.componet;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import com.up91.gwy.R;

import android.content.Context;

import android.util.AttributeSet;

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.ExpandableListView;

import android.widget.AbsListView.OnScrollListener;

import android.widget.ImageView;

import android.widget.ListAdapter;

import android.widget.ProgressBar;

import android.widget.RelativeLayout;

import android.widget.TextView;

public class PullToRefreshExpandableListView

        extends ExpandableListView

        implements

            OnScrollListener {

    private static final String TAG = "PullToRefreshExpandableListView";

    private static final int TAP_TO_REFRESH = ;

    private static final int PULL_TO_REFRESH = ;

    private static final int RELEASE_TO_REFRESH = ;

    private static final int REFRESHING = ;

    private OnRefreshListener mOnRefreshListener;

    private OnScrollListener mOnScrollListener;

    private LayoutInflater  mInflater;

    private RelativeLayout  mRefreshView;

    private TextView mRefreshViewText;

    private ImageView mRefreshViewImage;

    private ProgressBar mRefreshViewProgress;

    private TextView   mRefreshViewLastUpdated;

    private int mCurrentScrollState;

    private int mRefreshState;

    private RotateAnimation mFlipAnimation;

    private RotateAnimation mReverseFlipAnimation;

    private int mRefreshViewHeight;

    private int mRefreshOriginalTopPadding;

    private int mLastMotionY;

    private boolean isInInitState;

    public PullToRefreshExpandableListView(Context context) {

        super(context);

        init(context);

    }

    public PullToRefreshExpandableListView(Context context, AttributeSet attrs) {

        super(context,attrs);

        init(context);

    }

    public PullToRefreshExpandableListView(Context context,AttributeSet attrs, int defStyle) {

        super(context,attrs,defStyle);

        init(context);

    }

    private void init(Context context){

        mFlipAnimation = new RotateAnimation(,-,

                RotateAnimation.RELATIVE_TO_SELF,0.5f,

                RotateAnimation.RELATIVE_TO_SELF,0.5f);

        mFlipAnimation.setInterpolator(new LinearInterpolator());

        mFlipAnimation.setDuration();

        mFlipAnimation.setFillAfter(true);

        mReverseFlipAnimation = new RotateAnimation(-,,

                RotateAnimation.RELATIVE_TO_SELF,0.5f,

                RotateAnimation.RELATIVE_TO_SELF,0.5f);

        mReverseFlipAnimation.setInterpolator(new LinearInterpolator());

        mReverseFlipAnimation.setDuration();

        mReverseFlipAnimation.setFillAfter(true);

        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        mRefreshView = (RelativeLayout) mInflater.inflate(R.layout.pull_to_refresh_header, this,false);

        mRefreshViewText = (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_text);

        mRefreshViewImage = (ImageView) mRefreshView.findViewById(R.id.pull_to_refresh_image);

        mRefreshViewProgress = (ProgressBar) mRefreshView.findViewById(R.id.pull_to_refresh_progress);

        mRefreshViewLastUpdated = (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_updated_at);

        mRefreshViewImage.setMinimumHeight();

        mRefreshView.setOnClickListener(new OnClickRefreshListener());

        mRefreshOriginalTopPadding = mRefreshView.getPaddingTop();

        mRefreshState = TAP_TO_REFRESH;

        addHeaderView(mRefreshView);

        super.setOnScrollListener(this);

        measureView(mRefreshView);

        mRefreshViewHeight = mRefreshView.getMeasuredHeight();

    }

    private void measureView(View child){

        ViewGroup.LayoutParams p = child.getLayoutParams();

        if(p == null){

            p = new ViewGroup.LayoutParams(

                    ViewGroup.LayoutParams.FILL_PARENT,

                    ViewGroup.LayoutParams.WRAP_CONTENT

                );

        }

        int childWidthSpec = ViewGroup.getChildMeasureSpec(, +, p.width);

        int lpHeight = p.height;

        int childHeightSpec;

        if(lpHeight > ){

            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);

        }else{

            childHeightSpec = MeasureSpec.makeMeasureSpec(, MeasureSpec.UNSPECIFIED);

        }

        child.measure(childWidthSpec,childHeightSpec);

    }

    private class OnClickRefreshListener implements OnClickListener{

        @Override

        public void onClick(View v) {

            if(mRefreshState != REFRESHING){

                prepareForRefresh();

                onRefresh();

            }

        }

    }

    public interface OnRefreshListener{

        public void onRefresh();

    }

    @Override

    protected void onAttachedToWindow() {

        super.onAttachedToWindow();

        setSelection();

    }

    @Override

    public void setAdapter(ListAdapter adapter) {

        super.setAdapter(adapter);

        setSelection();

    }

    @Override

    public void setOnScrollListener(OnScrollListener l) {

        mOnScrollListener = l;

    }

    public void setOnRefreshListener(OnRefreshListener onRefreshListener){

        mOnRefreshListener = onRefreshListener;

    }

    public void setLastUpdated(CharSequence lastUpdated){

        if(lastUpdated != null){

            mRefreshViewLastUpdated.setVisibility(View.VISIBLE);

            mRefreshViewLastUpdated.setText(lastUpdated);

        }else{

            mRefreshViewLastUpdated.setVisibility(View.GONE);

        }

    }

    public boolean onTouchEvent(MotionEvent event) {

        final int y = (int) event.getY();

        switch (event.getAction()) {

            case MotionEvent.ACTION_UP:

                if (!isVerticalScrollBarEnabled()) {

                    setVerticalScrollBarEnabled(true);

                }

                if (getFirstVisiblePosition() ==  && mRefreshState != REFRESHING) {

                    if ((mRefreshView.getBottom() > mRefreshViewHeight

                            || mRefreshView.getTop() >= )

                            && mRefreshState == RELEASE_TO_REFRESH) {

                        // Initiate the refresh

                        mRefreshState = REFRESHING;

                        prepareForRefresh();

                        onRefresh();

                    } else if (mRefreshView.getBottom() < mRefreshViewHeight

                            || mRefreshView.getTop() < ) {

                        // Abort refresh and scroll down below the refresh view

                        resetHeader();

                        setSelection();

                    }

                }

                break;

            case MotionEvent.ACTION_DOWN:

                mLastMotionY = y;

                break;

            case MotionEvent.ACTION_MOVE:

                applyHeaderPadding(event);

                break;

        }

        return super.onTouchEvent(event);

    }

    private void applyHeaderPadding(MotionEvent ev) {

        final int historySize = ev.getHistorySize();

        // Workaround for getPointerCount() which is unavailable in 1.5

        // (it's always 1 in 1.5)

        int pointerCount = ;

        try {

            Method method = MotionEvent.class.getMethod("getPointerCount");

            pointerCount = (Integer)method.invoke(ev);

        } catch (NoSuchMethodException e) {

            pointerCount = ;

        } catch (IllegalArgumentException e) {

            throw e;

        } catch (IllegalAccessException e) {

            System.err.println("unexpected " + e);

        } catch (InvocationTargetException e) {

            System.err.println("unexpected " + e);

        }

        for (int h = ; h < historySize; h++) {

            for (int p = ; p < pointerCount; p++) {

                if (mRefreshState == RELEASE_TO_REFRESH) {

                    if (isVerticalFadingEdgeEnabled()) {

                        setVerticalScrollBarEnabled(false);

                    }

                    int historicalY = ;

                    try {

                        // For Android > 2.0

                        Method method = MotionEvent.class.getMethod(

                                "getHistoricalY", Integer.TYPE, Integer.TYPE);

                        historicalY = ((Float) method.invoke(ev, p, h)).intValue();

                    } catch (NoSuchMethodException e) {

                        // For Android < 2.0

                        historicalY = (int) (ev.getHistoricalY(h));

                    } catch (IllegalArgumentException e) {

                        throw e;

                    } catch (IllegalAccessException e) {

                        System.err.println("unexpected " + e);

                    } catch (InvocationTargetException e) {

                        System.err.println("unexpected " + e);

                    }

                    // Calculate the padding to apply, we divide by 1.7 to

                    // simulate a more resistant effect during pull.

                    int topPadding = (int) (((historicalY - mLastMotionY)

                            - mRefreshViewHeight) / 1.7);

                    mRefreshView.setPadding(

                            mRefreshView.getPaddingLeft(),

                            topPadding,

                            mRefreshView.getPaddingRight(),

                            mRefreshView.getPaddingBottom());

                }

            }

        }

    }

    private void resetHeaderPadding() {

        mRefreshView.setPadding(

                mRefreshView.getPaddingLeft(),

                mRefreshOriginalTopPadding,

                mRefreshView.getPaddingRight(),

                mRefreshView.getPaddingBottom());

    }

    private void resetHeader() {

        if (mRefreshState != TAP_TO_REFRESH) {

            mRefreshState = TAP_TO_REFRESH;

            resetHeaderPadding();

            // Set refresh view text to the pull label

            mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);

            // Replace refresh drawable with arrow drawable

            mRefreshViewImage.setImageResource(R.drawable.ic_pulltorefresh_arrow);

            // Clear the full rotation animation

            mRefreshViewImage.clearAnimation();

            // Hide progress bar and arrow.

            mRefreshViewImage.setVisibility(View.GONE);

            mRefreshViewProgress.setVisibility(View.GONE);

        }

    }

    public void onScroll(AbsListView view, int firstVisibleItem,

            int visibleItemCount, int totalItemCount) {

        // When the refresh view is completely visible, change the text to say

        // "Release to refresh..." and flip the arrow drawable.

        if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL

                && mRefreshState != REFRESHING) {

            if (firstVisibleItem == ) {

                mRefreshViewImage.setVisibility(View.VISIBLE);

                if((mRefreshView.getBottom() > mRefreshViewHeight +

                        || mRefreshView.getTop() >= )

                        && mRefreshState == PULL_TO_REFRESH){

                    mRefreshViewText.setText(R.string.pull_to_refresh_release_label);

                    mRefreshViewImage.clearAnimation();

                    mRefreshViewImage.startAnimation(mFlipAnimation);

                    mRefreshState = RELEASE_TO_REFRESH;

                }else if(mRefreshState == RELEASE_TO_REFRESH

                        &&mRefreshView.getBottom() < mRefreshViewHeight + ){

                    mRefreshViewImage.setVisibility(View.GONE);

                    resetHeader();

                }else if(mRefreshView.getBottom() == mRefreshViewHeight

                        && mRefreshState == TAP_TO_REFRESH){

                    //不作为

                    mRefreshViewImage.setVisibility(View.GONE);

                    resetHeader();

                } else if(mRefreshView.getBottom() < mRefreshViewHeight +

                        && mRefreshState == TAP_TO_REFRESH){

                    mRefreshViewText.setText(R.string.pull_to_refresh_pull_label);

                    mRefreshState = PULL_TO_REFRESH;

                    mRefreshViewImage.clearAnimation();

                    mRefreshViewImage.startAnimation(mReverseFlipAnimation);

                }

            } else {

                mRefreshViewImage.setVisibility(View.GONE);

                resetHeader();

            }

        } else if (mCurrentScrollState == SCROLL_STATE_FLING

                && firstVisibleItem ==

                && mRefreshState != REFRESHING) {

            setSelection();

        }

        if (mOnScrollListener != null) {

            mOnScrollListener.onScroll(view, firstVisibleItem,

                    visibleItemCount, totalItemCount);

        }

    }

    public void onScrollStateChanged(AbsListView view, int scrollState) {

        mCurrentScrollState = scrollState;

        if (mOnScrollListener != null) {

            mOnScrollListener.onScrollStateChanged(view, scrollState);

        }

    }

    public void prepareForRefresh() {

        resetHeaderPadding();

        mRefreshViewImage.setVisibility(View.GONE);

        // We need this hack, otherwise it will keep the previous drawable.

        mRefreshViewImage.setImageDrawable(null);

        mRefreshViewProgress.setVisibility(View.VISIBLE);

        // Set refresh view text to the refreshing label

        mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label);

        mRefreshState = REFRESHING;

    }

    public void onRefresh() {

        if (mOnRefreshListener != null) {

            mOnRefreshListener.onRefresh();

            resetHeader();

        }

    }

    public void onRefreshComplete(CharSequence lastUpdated) {

        setLastUpdated(lastUpdated);

        onRefreshComplete();

    }

    public void onRefreshComplete() {

        resetHeader();

        // If refresh view is visible when loading completes, scroll down to

        // the next item.

        if (mRefreshView.getBottom() > ) {

            invalidateViews();

            setSelection();

        }

    }

} 

使用时:

expLV.setOnRefreshListener(new OnRefreshListener(){

            @Override

            public void onRefresh() {

                //远程取数据机制

        });   
04-27 07:09