ListView下拉刷新

ListView下拉刷新是在開發過程中經常用到的一個功能,整理如下,如果初學者有需要可以作爲參考。
下拉刷新頭部佈局如下:
   <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/drop_drown_list_header_padding_bottom"
android:paddingTop="@dimen/drop_drown_list_header_padding_top">

<!--進度條  -->
<ProgressBar 
    android:id="@+id/drop_drown_list_header_progress_bar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    style="@style/drop_drown_list_header_progress_bar_style"
    android:indeterminate="true"
    android:layout_toLeftOf="@+id/drop_drown_list_header_default_text_layout"
    android:visibility="gone"/>

<!--翻轉圖片  -->
<ImageView
    android:id="@+id/drop_drown_list_header_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="@string/image_content"
    android:src="@drawable/drop_down_list_arrow"
    android:layout_toLeftOf="@+id/drop_drown_list_header_default_text_layout"
    android:visibility="gone"/>

<!--文字提示  -->
<RelativeLayout 
    android:id="@+id/drop_drown_list_header_default_text_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_marginLeft="10dp"
    android:gravity="center">

    <TextView 
        android:id="@+id/drop_drown_list_header_default_text"
        style="@style/drop_drown_list_header_font_style"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:gravity="center"/>


    <TextView 
        android:id="@+id/drop_down_list_header_second_text"
        style="@style/drop_drown_list_header_font_style"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/drop_drown_list_header_default_text"
        android:gravity="center"
        android:visibility="gone"/>

</RelativeLayout>

在佈局中添加了一個ImageView用於顯示下拉的箭頭,一個ProgressBar正在加載時候的等待狀態,兩個TextView用於狀態提示和顯示時間
接下來是在listView中添加header佈局,並在onTouchEvent和滾動事件中改變header中個組件的狀態和樣式:
public class DropDrownListView extends ListView implements OnScrollListener {

private boolean isDropDrownStyle = true;

private boolean hasReachedTop = false;
//頭部不同狀態的提示文字
private String headerDefaultText;
private String headerPullText;
private String headerReleaseText;
private String headerLoadingText;

private Context context;
private int currentScrollState;//listView的滾動狀態
private int currentHeaderStatus;//頭部佈局的狀態有四種

private RelativeLayout headerLayout;
private ImageView headerImage;
private ProgressBar headerProgressBar;
private TextView headerText;
private TextView headerSecondText;
//動畫
private RotateAnimation flipAnimation;
private RotateAnimation reverseFlipAnimation;

private OnScrollListener onScrollListener;

private int headerReleaseMinDistance;

private int headerOriginalHeight;
private int headerOriginalTopPadding;
private float actionDownPointY;
private float headerPaaddingTopRate = 1.5f;
//頭部佈局的四種狀態
public static final int HEADER_STATUS_CLICK_TO_LOAD = 1;
public static final int HEADER_STATUS_DROP_DROWN_TO_LAOD = 2;
public static final int HEADER_STATUS_RELEASE_TO_LOAD = 3;
public static final int HEADER_STATUS_LOADING = 4;

public DropDrownListView(Context context) {
    super(context);
    init(context);
}

public DropDrownListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    getAttrs(context, attrs);
    init(context);
}

public DropDrownListView(Context context, AttributeSet attrs,
        int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    getAttrs(context, attrs);
    init(context);
}

/**
 * 初始化佈局
 * 
 * @param context
 */
private void init(Context context) {
    this.context = context;
    initDropDownStyle();
    super.setOnScrollListener(this);
}

private void initDropDownStyle() {
    if (headerLayout != null) {
        if (isDropDrownStyle) {
            addHeaderView(headerLayout);
        } else {
            removeHeaderView(headerLayout);
        }
        return;
    }
    if (!isDropDrownStyle) {
        return;
    }

    headerReleaseMinDistance = getResources().getDimensionPixelSize(
            R.dimen.drop_drown_list_header_release_min_distance);
    flipAnimation = new RotateAnimation(0, 180,
            RotateAnimation.RELATIVE_TO_SELF, 0.5f,
            RotateAnimation.RELATIVE_TO_SELF, 0.5f);
    flipAnimation.setInterpolator(new LinearInterpolator());
    flipAnimation.setDuration(250);
    flipAnimation.setFillAfter(true);

    reverseFlipAnimation = new RotateAnimation(-180, 0,
            RotateAnimation.RELATIVE_TO_SELF, 0.5f,
            RotateAnimation.RELATIVE_TO_SELF, 0.5f);
    reverseFlipAnimation.setInterpolator(new LinearInterpolator());
    reverseFlipAnimation.setDuration(250);
    reverseFlipAnimation.setFillAfter(true);

    headerDefaultText = context
            .getString(R.string.drop_drown_list_header_default_text);
    headerPullText = context
            .getString(R.string.drop_drown_list_header_pull_text);
    headerReleaseText = context
            .getString(R.string.drop_drown_list_header_release_text);
    headerLoadingText = context
            .getString(R.string.drop_drown_list_header_loading_text);

    LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    headerLayout = (RelativeLayout) inflater.inflate(
            R.layout.drop_drown_list_header, null);
    headerText = (TextView) headerLayout
            .findViewById(R.id.drop_drown_list_header_default_text);
    headerImage = (ImageView) headerLayout
            .findViewById(R.id.drop_drown_list_header_image);
    headerProgressBar = (ProgressBar) headerLayout
            .findViewById(R.id.drop_drown_list_header_progress_bar);
    headerSecondText = (TextView) headerLayout
            .findViewById(R.id.drop_down_list_header_second_text);
    headerLayout.setClickable(true);
    headerLayout.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            onDropDown();
        }
    });
    headerText.setText(headerDefaultText);
    addHeaderView(headerLayout);

    measureHeaderLayout(headerLayout);
    headerOriginalHeight = headerLayout.getMeasuredHeight();
    headerOriginalTopPadding = headerLayout.getPaddingTop();
    currentHeaderStatus = HEADER_STATUS_CLICK_TO_LOAD;

}

@Override
public boolean onTouchEvent(MotionEvent ev) {

    hasReachedTop = false;
    switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
        actionDownPointY = ev.getY();
        break;

    case MotionEvent.ACTION_MOVE:
        adjustHeaderPadding(ev);
        break;

    case MotionEvent.ACTION_UP:
        if (!isVerticalFadingEdgeEnabled()) {
            setVerticalScrollBarEnabled(false);
        }

        if (getFirstVisiblePosition() == 0
                && currentHeaderStatus != HEADER_STATUS_LOADING) {
            switch (currentHeaderStatus) {
            case HEADER_STATUS_RELEASE_TO_LOAD:
                onDropDown();
                break;

            case HEADER_STATUS_DROP_DROWN_TO_LAOD:
                setHeaderStatusClickToLoad();
                setSecondPositionVisible();
                break;
            }
        }

        break;
    }

    return super.onTouchEvent(ev);
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
    currentScrollState = scrollState;
    if (currentScrollState == SCROLL_STATE_IDLE) {
        hasReachedTop = false;
    }
    if (onScrollListener != null) {
        onScrollListener.onScrollStateChanged(view, scrollState);
    }

}

@Override
public void onScroll(AbsListView view, int firstVisibleItem,
        int visibleItemCount, int totalItemCount) {
    if (currentScrollState == SCROLL_STATE_TOUCH_SCROLL
            && currentHeaderStatus != HEADER_STATUS_LOADING) {
        if (firstVisibleItem == 0) {
            headerImage.setVisibility(View.VISIBLE);
            int pointButtom = headerOriginalHeight
                    + headerReleaseMinDistance;
            if (headerLayout.getBottom() >= pointButtom) {
                setHeaderStatusReleaseToLoad();
            } else if (headerLayout.getBottom() < pointButtom) {
                setHeaderStatusDropDownToLoad();
            }
        } else {
            setHeaderStatusClickToLoad();
        }

    } else if (currentHeaderStatus == SCROLL_STATE_FLING
            && firstVisibleItem == 0
            && currentHeaderStatus != HEADER_STATUS_LOADING) {
        setSecondPositionVisible();
        hasReachedTop = true;
    } else if (currentScrollState == SCROLL_STATE_FLING && hasReachedTop) {
        setSecondPositionVisible();
    }
    if (onScrollListener != null) {
        onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,
                totalItemCount);
    }
}

public void setSecondPositionVisible() {
    if (getAdapter() != null && getAdapter().getCount() > 0
            && getFirstVisiblePosition() == 0) {
        setSelection(1);
    }
}

private void adjustHeaderPadding(MotionEvent ev) {
    int pointCount = ev.getHistorySize();
    if (isVerticalFadingEdgeEnabled()) {
        setVerticalScrollBarEnabled(false);
    }
    for (int i = 0; i < pointCount; i++) {
        if (currentHeaderStatus == HEADER_STATUS_DROP_DROWN_TO_LAOD
                || currentHeaderStatus == HEADER_STATUS_RELEASE_TO_LOAD) {
            headerLayout
                    .setPadding(
                            headerLayout.getPaddingLeft(),
                            (int) (((ev.getHistoricalY(i) - actionDownPointY) - headerOriginalHeight) / headerPaaddingTopRate),
                            headerLayout.getPaddingRight(),
                            headerLayout.getPaddingBottom());
        }
    }

}

private void onDropDown() {
    if (currentHeaderStatus != HEADER_STATUS_LOADING
            && onDropDownListener != null) {
        onDropDownBegin();
        onDropDownListener.onDropDown();
    }
}

private void onDropDownBegin() {
    setHeaderStatusLoading();
}

/**
 * headerLayout的初始狀態
 */
private void setHeaderStatusClickToLoad() {
    if (currentHeaderStatus != HEADER_STATUS_CLICK_TO_LOAD) {
        resetHeaderPadding();
        headerImage.clearAnimation();
        headerImage.setVisibility(View.GONE);
        headerProgressBar.setVisibility(View.GONE);
        headerText.setText(headerDefaultText);
        currentHeaderStatus = HEADER_STATUS_CLICK_TO_LOAD;

    }
}

/**
 * headerLayout的下拉狀態
 */
private void setHeaderStatusDropDownToLoad() {
    if (currentHeaderStatus != HEADER_STATUS_DROP_DROWN_TO_LAOD) {
        headerImage.setVisibility(View.VISIBLE);
        if (currentHeaderStatus != HEADER_STATUS_CLICK_TO_LOAD) {
            headerImage.clearAnimation();
            headerImage.startAnimation(reverseFlipAnimation);
        }
        headerProgressBar.setVisibility(View.GONE);
        headerText.setText(headerPullText);
        if (isVerticalFadingEdgeEnabled()) {
            setVerticalScrollBarEnabled(false);
        }

        currentHeaderStatus = HEADER_STATUS_DROP_DROWN_TO_LAOD;
    }
}

private void setHeaderStatusReleaseToLoad() {
    if (currentHeaderStatus != HEADER_STATUS_RELEASE_TO_LOAD) {
        headerImage.setVisibility(View.GONE);
        headerImage.clearAnimation();
        headerImage.startAnimation(flipAnimation);
        headerProgressBar.setVisibility(View.GONE);
        headerText.setText(headerReleaseText);
        currentHeaderStatus = HEADER_STATUS_RELEASE_TO_LOAD;
    }
}

private void setHeaderStatusLoading() {
    if (currentHeaderStatus != HEADER_STATUS_LOADING) {
        resetHeaderPadding();
        headerImage.setVisibility(View.GONE);
        headerImage.clearAnimation();
        headerProgressBar.setVisibility(View.VISIBLE);
        headerText.setText(headerLoadingText);
        currentScrollState = HEADER_STATUS_LOADING;
        setSelection(0);
    }
}

private void resetHeaderPadding() {
    headerLayout.setPadding(headerLayout.getPaddingLeft(),
            headerOriginalTopPadding, headerLayout.getPaddingRight(),
            headerLayout.getPaddingBottom());
}

private void measureHeaderLayout(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, p.width);
    int lpHeight = p.height;
    int childHeightSpace;
    if (lpHeight > 0) {
        childHeightSpace = MeasureSpec.makeMeasureSpec(lpHeight,
                MeasureSpec.EXACTLY);
    } else {
        childHeightSpace = MeasureSpec.makeMeasureSpec(0,
                MeasureSpec.UNSPECIFIED);
    }
    child.measure(childWidthSpec, childHeightSpace);
}

private OnDropDownListener onDropDownListener;

public void OnDropDownComplete(CharSequence secondText) {
    setHeaderSecondText(secondText);
    onDropDownComplete();
}

public void setHeaderSecondText(CharSequence secondText) {
    if (secondText == null) {
        headerSecondText.setVisibility(View.GONE);
    } else {
        headerSecondText.setVisibility(View.VISIBLE);
        headerSecondText.setText(secondText);
    }
}

public void onDropDownComplete() {
    setHeaderStatusClickToLoad();
    if (headerLayout.getBottom() > 0) {
        invalidateViews();
        setSecondPositionVisible();
    }
}

public void setOnDropDownListener(OnDropDownListener listener) {
    onDropDownListener = listener;
}

public interface OnDropDownListener {
    public void onDropDown();
}

private void getAttrs(Context context, AttributeSet attrs) {
    TypedArray ta = context.obtainStyledAttributes(attrs,
            R.styleable.drop_down_list_attr);
    isDropDrownStyle = ta.getBoolean(
            R.styleable.drop_down_list_attr_isDropDownStyle, false);
    ta.recycle();
}

/**
 * 使listView的第二個item可見
 */
@Override
public void setAdapter(ListAdapter adapter) {
    // TODO Auto-generated method stub
    super.setAdapter(adapter);
    setSecondPositionVisible();
}

}
下拉刷新中有四種狀態分別爲初始狀態, 下拉狀態, 釋放刷新狀態,刷新狀態。在onTouchEvent()中通過getAction()方法獲取到當前的按下的狀態 在ACTION_DOWN狀態獲取點擊時Y軸的座標,在ACTION_MOVE 調節header佈局的在Y軸的padding,在ACTION_UP狀態下 若在釋放刷新狀態, 就會加載刷新,在下拉狀態則返回初始狀態。
在onScroll中根據滾動狀態來判斷刷新狀態,當滾動的距離大於設定的距離是變爲釋放刷新狀態,小於設定距離時變爲下拉狀態

源碼如下http://download.csdn.net/detail/coder0408/8858767

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章