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中根據滾動狀態來判斷刷新狀態,當滾動的距離大於設定的距離是變爲釋放刷新狀態,小於設定距離時變爲下拉狀態