资源链接:PullRefreshLayout-互联网文档类资源-CSDN下载
PullRefreshLayout 的刷新、加载效果不仅可以在Activity中使用,也可以在Dialog或PopupWindow中使用
自定义View实现下拉刷新、上拉加载
package com.example.view.refresh;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
public class PullRefreshLayout extends ViewGroup {
private View mHeader;
private View mFooter;
private TextView mHeaderText;
private ImageView mHeaderArrow;
private ProgressBar mHeaderProgressBar;
private TextView mFooterText;
private ProgressBar mFooterProgressBar;
private ImageView progress_dialog_img_Footer;
private Context context;
private int mLayoutContentHeight;
private int mEffectiveHeaderHeight;
private int mEffictiveFooterHeight;
private int mlastMoveY;
private float mlastRotation;
private int mLastYIntercept;
private int lastChildIndex;
ObjectAnimator mAnimator;
private Status mStatus = Status.NORMAL;
private void updateStatus(Status status) {
mStatus = status;
}
private enum Status {
NORMAL, TRY_REFRESH, REFRESHING, TRY_LOADMORE, LOADING;
}
public interface OnRefreshListener {
void refreshFinished();
void loadMoreFinished();
}
private OnRefreshListener mRefreshListener;
public void setRefreshListener(OnRefreshListener mRefreshListener) {
this.mRefreshListener = mRefreshListener;
}
public PullRefreshLayout(Context context) {
super(context);
this.context = context;
}
public PullRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public PullRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
}
// 当view的所有child从xml中被初始化后调用
@Override
protected void onFinishInflate() {
super.onFinishInflate();
lastChildIndex = getChildCount() - 1;
addHeader();
addFooter();
}
private void addHeader() {
mHeader = LayoutInflater.from(getContext()).inflate(R.layout.pull_header, null, false);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
addView(mHeader, params);
mHeaderText = (TextView) findViewById(R.id.header_text);
mHeaderProgressBar = (ProgressBar) findViewById(R.id.header_progressbar);
mHeaderArrow = (ImageView) findViewById(R.id.header_arrow);
}
private void addFooter() {
mFooter = LayoutInflater.from(getContext()).inflate(R.layout.pull_footer, null, false);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
addView(mFooter, params);
mFooterText = (TextView) findViewById(R.id.footer_text);
mFooterProgressBar = (ProgressBar) findViewById(R.id.footer_progressbar);
progress_dialog_img_Footer=(ImageView) mFooter.findViewById(R.id.progress_dialog_img_Footer);
Animation anim= AnimationUtils.loadAnimation(context, R.anim.loading_dialog_progressbar);
progress_dialog_img_Footer.setAnimation(anim);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mLayoutContentHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child == mHeader) {
child.layout(0, 0 - child.getMeasuredHeight(), child.getMeasuredWidth(), 0);
mEffectiveHeaderHeight = child.getHeight();
} else if (child == mFooter) {
child.layout(0, mLayoutContentHeight, child.getMeasuredWidth(), mLayoutContentHeight + child.getMeasuredHeight());
mEffictiveFooterHeight = child.getHeight();
} else {
child.layout(0, mLayoutContentHeight, child.getMeasuredWidth(), mLayoutContentHeight + child.getMeasuredHeight());
if (i < getChildCount()) {
if (child instanceof ScrollView) {
mLayoutContentHeight += getMeasuredHeight();
continue;
}
mLayoutContentHeight += child.getMeasuredHeight();
}
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
// 正在刷新或加载更多,避免重复
if (mStatus == Status.REFRESHING || mStatus == Status.LOADING) {
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mlastMoveY = y;
break;
case MotionEvent.ACTION_MOVE:
int dy = mlastMoveY - y;
//System.out.println("dy:::::::"+dy);
// 一直在下拉
if (getScrollY() <= 0 && dy <= 0) {
if (mStatus == Status.TRY_LOADMORE) {
scrollBy(0, dy / 30);
} else {
scrollBy(0, dy / 3);
}
}
// 一直在上拉
else if (getScrollY() >= 0 && dy >= 0) {
if (mStatus == Status.TRY_REFRESH) {
scrollBy(0, dy / 30);
} else {
scrollBy(0, dy / 3);
}
} else {
scrollBy(0, dy / 3);
}
beforeRefreshing(dy);
beforeLoadMore();
break;
case MotionEvent.ACTION_UP:
// 下拉刷新,并且到达有效长度
if (getScrollY() <= -mEffectiveHeaderHeight) {
releaseWithStatusRefresh();
if (mRefreshListener != null) {
mRefreshListener.refreshFinished();
}
}
// 上拉加载更多,达到有效长度
else if (getScrollY() >= mEffictiveFooterHeight) {
releaseWithStatusLoadMore();
if (mRefreshListener != null) {
mRefreshListener.loadMoreFinished();
}
} else {
releaseWithStatusTryRefresh();
releaseWithStatusTryLoadMore();
}
break;
}
mlastMoveY = y;
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercept = false;
int y = (int) event.getY();
if (mStatus == Status.REFRESHING || mStatus == Status.LOADING) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
// 拦截时需要记录点击位置,不然下一次滑动会出错
mlastMoveY = y;
intercept = false;
break;
}
case MotionEvent.ACTION_MOVE: {
if (y > mLastYIntercept) {
View child = getChildAt(0);
intercept = getRefreshIntercept(child);
if (intercept) {
updateStatus(mStatus.TRY_REFRESH);
}
} else if (y < mLastYIntercept) {
View child = getChildAt(lastChildIndex);
intercept = getLoadMoreIntercept(child);
if (intercept) {
updateStatus(mStatus.TRY_LOADMORE);
}
} else {
intercept = false;
}
break;
}
case MotionEvent.ACTION_UP: {
intercept = false;
break;
}
}
mLastYIntercept = y;
return intercept;
}
/*汇总判断 刷新和加载是否拦截*/
private boolean getRefreshIntercept(View child) {
boolean intercept = false;
if (child instanceof AdapterView) {
intercept = adapterViewRefreshIntercept(child);
} else if (child instanceof ScrollView) {
intercept = scrollViewRefreshIntercept(child);
} else if (child instanceof RecyclerView) {
intercept = recyclerViewRefreshIntercept(child);
}
return intercept;
}
private boolean getLoadMoreIntercept(View child) {
boolean intercept = false;
if (child instanceof AdapterView) {
intercept = adapterViewLoadMoreIntercept(child);
} else if (child instanceof ScrollView) {
intercept = scrollViewLoadMoreIntercept(child);
} else if (child instanceof RecyclerView) {
intercept = recyclerViewLoadMoreIntercept(child);
}
return intercept;
}
/*汇总判断 刷新和加载是否拦截*/
/*具体判断各种View是否应该拦截*/
// 判断AdapterView下拉刷新是否拦截
private boolean adapterViewRefreshIntercept(View child) {
boolean intercept = true;
AdapterView adapterChild = (AdapterView) child;
if (adapterChild.getFirstVisiblePosition() != 0
|| (adapterChild.getChildAt(0) !=null && adapterChild.getChildAt(0).getTop() != 0)) {
intercept = false;
}
if (adapterChild.getChildAt(0) ==null){
//intercept = false;
}
return intercept;
}
// 判断AdapterView加载更多是否拦截
private boolean adapterViewLoadMoreIntercept(View child) {
boolean intercept = false;
AdapterView adapterChild = (AdapterView) child;
if (adapterChild.getLastVisiblePosition() == adapterChild.getCount() - 1 &&
adapterChild.getChildAt(adapterChild.getChildCount() - 1) != null &&
(adapterChild.getChildAt(adapterChild.getChildCount() - 1).getBottom() <= getMeasuredHeight())) {
intercept = true;
}
return intercept;
}
//使用上边的方法有几率出现bug,可替换为以下方法
private boolean adapterViewLoadMoreIntercept2(View child) {
boolean intercept = false;
AdapterView adapterChild = (AdapterView) child;
View view = adapterChild.getChildAt(adapterChild.getChildCount() - 1);
if (adapterChild.getLastVisiblePosition() == adapterChild.getCount() - 1
&&view != null
&&(view.getBottom() == getMeasuredHeight())) {//这个地方用==
intercept = true;
}
return intercept;
}
// 判断ScrollView刷新是否拦截
private boolean scrollViewRefreshIntercept(View child) {
boolean intercept = false;
if (child.getScrollY() <= 0) {
intercept = true;
}
return intercept;
}
// 判断ScrollView加载更多是否拦截
private boolean scrollViewLoadMoreIntercept(View child) {
boolean intercept = false;
ScrollView scrollView = (ScrollView) child;
View scrollChild = scrollView.getChildAt(0);
if (scrollView.getScrollY() >= (scrollChild.getHeight() - scrollView.getHeight())) {
intercept = true;
}
return intercept;
}
// 判断RecyclerView刷新是否拦截
private boolean recyclerViewRefreshIntercept(View child) {
boolean intercept = false;
RecyclerView recyclerView = (RecyclerView) child;
if (recyclerView.computeVerticalScrollOffset() <= 0) {
intercept = true;
}
return intercept;
}
// 判断RecyclerView加载更多是否拦截
private boolean recyclerViewLoadMoreIntercept(View child) {
boolean intercept = false;
RecyclerView recyclerView = (RecyclerView) child;
if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
>= recyclerView.computeVerticalScrollRange()) {
intercept = true;
}
return intercept;
}
/*具体判断各种View是否应该拦截*/
/*修改header和footer的状态*/
public void beforeRefreshing(float dy) {
//计算旋转角度
int scrollY = Math.abs(getScrollY());
//System.out.println("scrollY:::::::"+scrollY);
if (scrollY>=(mHeaderArrow.getHeight()+10)){
scrollY = scrollY > mEffectiveHeaderHeight ? mEffectiveHeaderHeight : scrollY;
float angle = (float) (scrollY * 1.0 / mEffectiveHeaderHeight * 180);
//mHeaderArrow.setRotation(-angle);
mHeaderArrow.setRotation(-180);
if (getScrollY() <= -mEffectiveHeaderHeight) {
mHeaderText.setText(R.string.pull_to_refresh_now_refresh);
} else {
mHeaderText.setText(R.string.pull_to_refresh_down);
mHeaderArrow.setRotation(0);
}}
}
public void beforeLoadMore() {
if (getScrollY() >= mEffectiveHeaderHeight) {
mFooterText.setText(R.string.pull_to_refresh_now_loading_more);
} else {
mFooterText.setText(R.string.pull_to_refresh_up);
}
}
public void refreshFinished() {
scrollTo(0, 0);
mHeaderText.setText(R.string.pull_to_refresh_down);
mHeaderProgressBar.setVisibility(GONE);
mHeaderArrow.setVisibility(VISIBLE);
updateStatus(Status.NORMAL);
}
public void loadMoreFinished() {
mFooterText.setText(R.string.pull_to_refresh_up);
mFooterProgressBar.setVisibility(GONE);
progress_dialog_img_Footer.setVisibility(GONE);
scrollTo(0, 0);
updateStatus(Status.NORMAL);
}
private void releaseWithStatusTryRefresh() {
scrollBy(0, -getScrollY());
mHeaderText.setText(R.string.pull_to_refresh_down);
updateStatus(Status.NORMAL);
}
private void releaseWithStatusTryLoadMore() {
scrollBy(0, -getScrollY());
mFooterText.setText(R.string.pull_to_refresh_now_loading_more);
updateStatus(Status.NORMAL);
}
private void releaseWithStatusRefresh() {
scrollTo(0, -mEffectiveHeaderHeight);
mHeaderProgressBar.setVisibility(VISIBLE);
mHeaderArrow.setVisibility(GONE);
mHeaderText.setText(R.string.pull_to_refresh_now);
updateStatus(Status.REFRESHING);
}
private void releaseWithStatusLoadMore() {
scrollTo(0, mEffictiveFooterHeight);
mFooterText.setText(R.string.pull_to_refresh_loading);
mFooterProgressBar.setVisibility(VISIBLE);
// progress_dialog_img_Footer.setVisibility(VISIBLE);
updateStatus(Status.LOADING);
}
/*修改header和footer的状态*/
}
布局中使用
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="15dp"
android:orientation="vertical">
<com.example.view.refresh.PullRefreshLayout
android:id="@+id/prl_pullrefresh_layout"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lvfsv_main_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#E4DDE1"
android:dividerHeight="5dp"/>
<!--
android:padding="15dp"
切记::
ListV中不要使用padding,不然会导致PullRefreshLayout无法使用
如果想设置padding,可以在ListView的item布局中添加
-->
</com.example.view.refresh.PullRefreshLayout>
<!-- 父级控件用RelativeLayout,切记不要用LinearLayout;否则数据少的情况下底部加载更多会显示 -->
</RelativeLayout>
说明:ListV中不要使用padding,不然会导致PullRefreshLayout无法使用;如果想设置padding,可以在ListView的item布局中添加