您的位置:首页 > 其它

欢迎使用CSDN-markdown编辑器

2016-08-25 09:40 423 查看
分享一款通用的下拉刷新控件,PullLinearLayout

现在网上关于下拉刷新的控件有很多,大家度娘一下哗啦啦出来一大堆,或者 Git上搜一大堆,有谷歌官方的还有好多大牛写的,PullLinearLayout是公司用的控件,说它好用是因为简单,但功能完整,下面是代码时刻:

public class PullLinearLayout extends LinearLayout {
//  private static final String TAG = "PullLinearLayout";
//刷新状态
private static final int PULL_TO_REFRESH = 2;
private static final int RELEASE_TO_REFRESH = 3;
private static final int REFRESHING = 4;
//滑动状态
private static final int PULL_UP_STATE = 0;
private static final int PULL_DOWN_STATE = 1;
private int mLastMotionY;//Y值
//  private boolean mLock;
private View mHeaderView;
private View mFooterView;
private AdapterView<?> mAdapterView;//list or grid
private ScrollView mScrollView;
private int mHeaderViewHeight;//头部高度
private int mFooterViewHeight;//底部高度
private ImageView mHeaderImageView;
private ImageView mFooterImageView;
private TextView mHeaderTextView;
private TextView mFooterTextView;
private TextView mHeaderUpdateTextView;//刷新时间
// private TextView mFooterUpdateTextView;//加载时间
private FrameLayout mHeaderProgressBar;
private FrameLayout mFooterProgressBar;
private LayoutInflater mInflater;
private int mHeaderState;
private int mFooterState;
private int mPullState;//PULL_UP_STATE或PULL_DOWN_STATE
private RotateAnimation mFlipAnimation;//变为向下的箭头,改变箭头方向
private RotateAnimation mReverseFlipAnimation;//变为逆向的箭头,旋转
private OnRefreshListener mListener;
private boolean loadMoreEnabled=true;
private boolean refreashEnabled=true;
private float gTouchStartX, gTouchStartY;
//  private String mLastUpdateTime;

public PullLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
if (isInEditMode()) {return;}
init();
}

public PullLinearLayout(Context context) {
super(context);
if (isInEditMode()) {return;}
init();
}

/**
* 初始化
* @param context
*/
private void init() {
//需要设置成vertical
setOrientation(LinearLayout.VERTICAL);
mFlipAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mFlipAnimation.setInterpolator(new LinearInterpolator());
mFlipAnimation.setDuration(250);
mFlipAnimation.setFillAfter(true);
mReverseFlipAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
mReverseFlipAnimation.setDuration(250);
mReverseFlipAnimation.setFillAfter(true);

mInflater = LayoutInflater.from(getContext());
// header view 在此添加,保证是第一个添加到linearlayout的最上端
addHeaderView();
}

private void addHeaderView() {
// 头部View
mHeaderView = mInflater.inflate(R.layout.zlistview_header, this, false);

mHeaderImageView = (ImageView) mHeaderView
.findViewById(R.id.zlistview_header_arrow);
mHeaderTextView = (TextView) mHeaderView
.findViewById(R.id.zlistview_header_hint_textview);
mHeaderUpdateTextView = (TextView) mHeaderView
.findViewById(R.id.zlistview_header_time);
mHeaderProgressBar = (FrameLayout) mHeaderView
.findViewById(R.id.fl_progress);
measureView(mHeaderView);
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
mHeaderViewHeight);
// 设置topMargin的值为负的header View高度,即将其隐藏在最上方
params.topMargin = -(mHeaderViewHeight);
// mHeaderView.setLayoutParams(params1);
addView(mHeaderView, params);

}

private void addFooterView() {
//底部View
mFooterView = mInflater.inflate(R.layout.zlistview_footer, this, false);

4000
mFooterImageView = (ImageView) mFooterView.findViewById(R.id.pull_to_load_image);
mFooterTextView = (TextView) mFooterView
.findViewById(R.id.zlistview_footer_hint_textview);
mFooterProgressBar = (FrameLayout) mFooterView
.findViewById(R.id.fl_progress);
measureView(mFooterView);
mFooterViewHeight = mFooterView.getMeasuredHeight();
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
mFooterViewHeight);
// int top = getHeight();
// params.topMargin
// =getHeight();//在这里getHeight()==0,但在onInterceptTouchEvent()方法里getHeight()已经有值了,不再是0;
// getHeight()什么时候会赋值,稍候再研究一下
// 由于是线性布局可以直接添加,只要AdapterView的高度是MATCH_PARENT,那么footer view就会被添加到最后,并隐藏
addView(mFooterView, params);
}

public void hidFooterView(){
mFooterView.setVisibility(View.GONE);
}
public void showFooterView(){
mFooterView.setVisibility(View.VISIBLE);
}

@Override
protected void onFinishInflate() {
super.onFinishInflate();
// footer view 在此添加保证添加到linearlayout中的最后
addFooterView();
initContentAdapterView();
}

/**
* 初始化AdapterView,ListView,GridView;初始化ScrollView
*
*/
private void initContentAdapterView() {
int count = getChildCount();
if (count < 3) {
throw new IllegalArgumentException(
"这个View必须包含 3 子 views,并且AdapterView 或 ScrollView 必须在第二个位置!");
}
View view = null;
for (int i = 0; i < count - 1; ++i) {
view = getChildAt(i);
if (view instanceof AdapterView<?>) {
mAdapterView = (AdapterView<?>) view;
}
if (view instanceof ScrollView) {
mScrollView = (ScrollView) view;
}
}
if (mAdapterView == null && mScrollView == null) {
throw new IllegalArgumentException(
"这个View必须包含 AdapterView 或 ScrollView!");
}
}

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

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int y = (int) e.getRawY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
// 首先拦截down事件,记录y坐标
gTouchStartX = e.getX();
gTouchStartY = e.getY();
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
// deltaY > 0 是向下运动,< 0是向上运动
int deltaY = y - mLastMotionY;
final float touchDistancesX = Math.abs(e.getX() - gTouchStartX);
final float touchDistancesY = Math.abs(e.getY() - gTouchStartY);
if (touchDistancesY * 2 >= touchDistancesX) {
if (isRefreshViewScroll(deltaY)) {
return true;
}
return false;
} else {
return false;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return false;
}

/*
* 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return false)则
* 由PullLinearLayout 的子View来处理;否则由下面的方法来处理(即由PullLinearLayout自己来处理)
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
//      if (mLock) {
//          return true;
//      }
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// onInterceptTouchEvent已经记录
// mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
int deltaY = y - mLastMotionY;
if (mPullState == PULL_DOWN_STATE) {//执行下拉
if(refreashEnabled){
headerPrepareToRefresh(deltaY);
}
// setHeaderPadding(-mHeaderViewHeight);
} else if (mPullState == PULL_UP_STATE) {//执行上拉
if(loadMoreEnabled){
footerPrepareToRefresh(deltaY);
}
}
mLastMotionY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
int topMargin = getHeaderTopMargin();
if (mPullState == PULL_DOWN_STATE) {
if (topMargin >= 0) {
headerRefreshing();//开始刷新
} else {
setHeaderTopMargin(-mHeaderViewHeight);// 还没有执行刷新,重新隐藏
}
} else if (mPullState == PULL_UP_STATE) {
if (Math.abs(topMargin) >= mHeaderViewHeight
+ mFooterViewHeight) {
footerRefreshing();// 开始执行footer 刷新
} else {
setHeaderTopMargin(-mHeaderViewHeight);// 还没有执行刷新,重新隐藏
}
}
break;
}
return super.onTouchEvent(event);
}

/**
* 是否应该到了父View,即PullLinearLayout滑动
*
* @param deltaY
*            , deltaY > 0 是向下运动,< 0是向上运动
* @return
*/
private boolean isRefreshViewScroll(int deltaY) {
if (mHeaderState == REFRESHING || mFooterState == REFRESHING) {
return false;
}
//对于ListView和GridView
if (mAdapterView != null) {
// 子view(ListView or GridView)滑动到最顶端
if (deltaY > 0) {
View child = mAdapterView.getChildAt(0);
if (child == null) {
// 如果mAdapterView中没有数据,不拦截
return false;
}
if (mAdapterView.getFirstVisiblePosition() == 0
&& child.getTop() == 0) {
mPullState = PULL_DOWN_STATE;
return true;
}
int top = child.getTop();
int padding = mAdapterView.getPaddingTop();
if (mAdapterView.getFirstVisiblePosition() == 0
&& Math.abs(top - padding) <= 8) {//这里之前用3可以判断,但现在不行,还没找到原因
mPullState = PULL_DOWN_STATE;
return true;
}

} else if (deltaY < 0) {
View lastChild = mAdapterView.getChildAt(mAdapterView
.getChildCount() - 1);
if (lastChild == null) {
// 如果mAdapterView中没有数据,不拦截
return false;
}
// 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view,
// 等于父View的高度说明mAdapterView已经滑动到最后
if (lastChild.getBottom() <= getHeight() && mAdapterView.getLastVisiblePosition() == mAdapterView.getCount() - 1) {
mPullState = PULL_UP_STATE;
return true;
}
}
}
// 对于ScrollView
if (mScrollView != null) {
// 子scroll view滑动到最顶端
View child = mScrollView.getChildAt(0);
if (deltaY > 0 && mScrollView.getScrollY() == 0) {
mPullState = PULL_DOWN_STATE;
return true;
} else if (deltaY < 0 && child.getMeasuredHeight() <= getHeight() + mScrollView.getScrollY()) {
mPullState = PULL_UP_STATE;
return true;
}
}
return false;
}

/**
* header 准备刷新,手指移动过程,还没有释放
*
* @param deltaY
*            ,手指滑动的距离
*/
private void headerPrepareToRefresh(int deltaY) {
int newTopMargin = changingHeaderViewTopMargin(deltaY);
// 当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态
if (newTopMargin >= 0 && mHeaderState != RELEASE_TO_REFRESH) {
mHeaderTextView.setText(R.string.zlistview_header_hint_ready);
mHeaderUpdateTextView.setVisibility(View.VISIBLE);
mHeaderImageView.clearAnimation();
mHeaderImageView.startAnimation(mFlipAnimation);
mHeaderState = RELEASE_TO_REFRESH;
} else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight) {// 拖动时没有释放
mHeaderImageView.clearAnimation();
mHeaderImageView.startAnimation(mFlipAnimation);
// mHeaderImageView.
mHeaderTextView.setText(R.string.pull_to_refresh);
mHeaderState = PULL_TO_REFRESH;
}
}

/**
* footer 准备刷新,手指移动过程,还没有释放 移动footer view高度同样和移动header view
* 高度是一样,都是通过修改header view的topmargin的值来达到
*
* @param deltaY
*            ,手指滑动的距离
*/
private void footerPrepareToRefresh(int deltaY) {
int newTopMargin = changingHeaderViewTopMargin(deltaY);
// 如果header view topMargin 的绝对值大于或等于header + footer 的高度
// 说明footer view 完全显示出来了,修改footer view 的提示状态
if (Math.abs(newTopMargin) >= (mHeaderViewHeight + mFooterViewHeight) && mFooterState != RELEASE_TO_REFRESH) {
mFooterTextView.setText(R.string.zlistview_footer_hint_ready);
mFooterImageView.clearAnimation();
mFooterImageView.startAnimation(mFlipAnimation);
mFooterState = RELEASE_TO_REFRESH;
} else if (Math.abs(newTopMargin) < (mHeaderViewHeight + mFooterViewHeight)) {
mFooterImageView.clearAnimation();
mFooterImageView.startAnimation(mFlipAnimation);
mFooterTextView.setText(R.string.zlistview_footer_hint_normal);
mFooterState = PULL_TO_REFRESH;
}
}

/**
* 修改Header view top margin的值
*
* @param deltaY
*/
private int changingHeaderViewTopMargin(int deltaY) {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
float newTopMargin = params.topMargin + deltaY * 0.3f;
//这里对上拉做一下限制,因为当前上拉后然后不释放手指直接下拉,会把下拉刷新给触发了,感谢网友yufengzungzhe的指出
//表示如果是在上拉后一段距离,然后直接下拉
if(deltaY>0&&mPullState == PULL_UP_STATE&&Math.abs(params.topMargin) <= mHeaderViewHeight){
return params.topMargin;
}
//同样地,对下拉做一下限制,避免出现跟上拉操作时一样的bug
if(deltaY<0&&mPullState == PULL_DOWN_STATE&&Math.abs(params.topMargin)>=mHeaderViewHeight){
return params.topMargin;
}
params.topMargin = (int) newTopMargin;
mHeaderView.setLayoutParams(params);
invalidate();
return params.topMargin;
}

//头部刷新
private void headerRefreshing() {
mHeaderState = REFRESHING;
setHeaderTopMargin(0);
mHeaderImageView.setVisibility(View.GONE);
mHeaderImageView.clearAnimation();
mHeaderImageView.setImageDrawable(null);
mHeaderProgressBar.setVisibility(View.VISIBLE);
mHeaderTextView.setText(R.string.zlistview_header_hint_loading);
if (mListener != null) {
//          mOnHeaderRefreshListener.onHeaderRefresh(this);
mListener.onRefresh();
}
}

//底部更新
private void footerRefreshing() {
mFooterState = REFRESHING;
int top = mHeaderViewHeight + mFooterViewHeight;
setHeaderTopMargin(-top);
mFooterImageView.setVisibility(View.GONE);
mFooterImageView.clearAnimation();
mFooterImageView.setImageDrawable(null);
mFooterProgressBar.setVisibility(View.VISIBLE);
mFooterTextView
.setText(R.string.zlistview_header_hint_loading);
if (mListener != null) {
//mOnFooterRefreshListener.onFooterRefresh(this);
mListener.onLoadMore();
}
}

/**
* 设置header view 的topMargin的值
*
* @param topMargin
*            ,为0时,说明header view 刚好完全显示出来; 为-mHeaderViewHeight时,说明完全隐藏了
*/
private void setHeaderTopMargin(int topMargin) {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
params.topMargin = topMargin;
mHeaderView.setLayoutParams(params);
invalidate();
}

//获取当前header view 的topMargin
public void onHeaderRefreshComplete() {
setHeaderTopMargin(-mHeaderViewHeight);
mHeaderImageView.setVisibility(View.VISIBLE);
mHeaderImageView.setImageResource(R.drawable.zlistview_arrow);
mHeaderTextView.setText(R.string.pull_to_refresh);
mHeaderProgressBar.setVisibility(View.GONE);
// mHeaderUpdateTextView.setText("");
mHeaderState = PULL_TO_REFRESH;
}

//刷新后重设list到初始状态
public void onHeaderRefreshComplete(CharSequence lastUpdated) {
setLastUpdated(lastUpdated);
onHeaderRefreshComplete();
}

//footer view 完成更新后恢复初始状态
public void onFooterRefreshComplete() {
setHeaderTopMargin(-mHeaderViewHeight);
mFooterImageView.setVisibility(View.VISIBLE);
mFooterImageView.setImageResource(R.drawable.zlistview_arrow_up);
mFooterTextView.setText(R.string.zlistview_footer_hint_normal);
mFooterProgressBar.setVisibility(View.INVISIBLE);
// mHeaderUpdateTextView.setText("");
mFooterState = PULL_TO_REFRESH;
}

//更新后设置文字提示
public void setLastUpdated(CharSequence lastUpdated) {
if (lastUpdated != null) {
mHeaderUpdateTextView.setVisibility(View.VISIBLE);
mHeaderUpdateTextView.setText(lastUpdated);
} else {
mHeaderUpdateTextView.setVisibility(View.GONE);
}
}

//获取当前header view 的topMargin
private int getHeaderTopMargin() {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
return params.topMargin;
}

//  private void lock() {
//      mLock = true;
//  }

//  private void unlock() {
//      mLock = false;
//  }

//是否可以上拉加载
public void setLoadMore(boolean bool){
loadMoreEnabled=bool;
}

//是否可以下拉刷新
public void setRefresh(boolean bool){
refreashEnabled=bool;
}

public void setOnRefreshListener(OnRefreshListener listener) {
mListener = listener;
setLastUpdated(StringUtil.GetCurrentFormatTime());
}

public interface OnRefreshListener {
void onRefresh();//刷新操作
void onLoadMore();//加载操作
}
}


大家可以看到,它是继承于LinearLayout,这里有两个布局文件,分别是头部和底部显示的ui。

footer:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >

<RelativeLayout
android:id="@+id/zlistview_footer_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dp" >

<FrameLayout
android:id="@+id/fl_progress"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_marginRight="20dp"
android:layout_toLeftOf="@+id/zlistview_footer_hint_textview"
android:visibility="invisible" >

<ProgressBar
android:id="@+id/zlistview_footer_progressbar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center" />

<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:padding="7dp"
android:src="@drawable/none" />
</FrameLayout>

<ImageView
android:id="@+id/zlistview_footer_nomore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_toLeftOf="@+id/zlistview_footer_hint_textview"
android:paddingRight="10dp"
android:src="@drawable/nomore"
android:visibility="invisible" />

<ImageView
android:id="@+id/pull_to_load_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="30dp"
android:layout_marginRight="20dp"
android:layout_toLeftOf="@+id/zlistview_footer_hint_textview"
android:gravity="center"
android:src="@drawable/zlistview_arrow_up"
android:visibility="invisible" />

<TextView
android:id="@+id/zlistview_footer_hint_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/zlistview_footer_hint_normal"
android:textColor="@color/black_666"
android:textSize="@dimen/font_size_14dp" />
</RelativeLayout>

</LinearLayout>


header:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="bottom" >

<RelativeLayout
android:id="@+id/zlistview_header_content"
android:layout_width="fill_parent"
android:layout_height="60dp" >

<LinearLayout
android:id="@+id/zlistview_header_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:orientation="vertical" >

<TextView
android:id="@+id/zlistview_header_hint_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pull_to_refresh"
android:textColor="@color/black_666"
android:textSize="@dimen/font_size_14dp" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:textColor="@color/black_666"
android:textSize="@dimen/font_size_12dp" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/last_update_time"
android:textColor="@color/black_666"
android:textSize="@dimen/font_size_12dp" />

<TextView
android:id="@+id/zlistview_header_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black_666"
android:textSize="@dimen/font_size_12dp" />
</LinearLayout>
</LinearLayout>

<ImageView
android:id="@+id/zlistview_header_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/zlistview_header_text"
android:layout_centerVertical="true"
android:layout_marginLeft="-35dp"
android:src="@drawable/zlistview_arrow" />

<FrameLayout
android:id="@+id/fl_progress"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignLeft="@id/zlistview_header_text"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_marginLeft="-40dp"
android:visibility="invisible" >

<ProgressBar
android:id="@+id/zlistview_header_progressbar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center" />

<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:padding="7dp"
android:src="@drawable/none" />
</FrameLayout>
</RelativeLayout>

</LinearLayout>


至于String.xml就不贴出了,这个大家自己可以设置。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: