前些天搞了个系统的下拉刷新跟上拉加载,由于效果一般所以才会有了今天这篇博文
对于大多数的码农来说,能弄出些自己感兴趣的好东西还是比较开心的。--
package com.example.administrator.xlistview;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import com.example.administrator.stock_sell_storage_app.R;
/**
* Created by Administrator on 2016-04-26.
*/
public class PullToRefreshView extends LinearLayout {
private static final String TAG = "PullToRefreshView";
// refresh states
private static final int PULL_TO_REFRESH = 2;
private static final int RELEASE_TO_REFRESH = 3;
private static final int REFRESHING = 4;
// pull state
private static final int PULL_UP_STATE = 0;
private static final int PULL_DOWN_STATE = 1;
/**
* last y
*/
private int mLastMotionY;
/**
* lock
*/
private boolean mLock;
/**
* header view
*/
private View mHeaderView;
/**
* footer view
*/
private View mFooterView;
/**
* list or grid
*/
private AdapterView> mAdapterView;
/**
* scrollview
*/
private ScrollView mScrollView;
/**
* header view height
*/
private int mHeaderViewHeight;
/**
* footer view height
*/
private int mFooterViewHeight;
/**
* header view image
*/
private ImageView mHeaderImageView;
/**
* footer view image
*/
private ImageView mFooterImageView;
/**
* header tip text
*/
private TextView mHeaderTextView;
/**
* footer tip text
*/
private TextView mFooterTextView;
/**
* header refresh time
*/
private TextView mHeaderUpdateTextView;
/**
* layout inflater
*/
private LayoutInflater mInflater;
/**
* header view current state
*/
private int mHeaderState;
/**
* footer view current state
*/
private int mFooterState;
/**
* pull state,pull up or pull down;PULL_UP_STATE or PULL_DOWN_STATE
*/
private int mPullState;
/**
* footer refresh listener
*/
private OnFooterRefreshListener mOnFooterRefreshListener;
/**
* footer refresh listener
*/
private OnHeaderRefreshListener mOnHeaderRefreshListener;
/**
* last update time
*/
// private String mLastUpdateTime;
private AnimationDrawable mAnimation;
public PullToRefreshView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PullToRefreshView(Context context) {
super(context);
init();
}
private void init() {
mInflater = LayoutInflater.from(getContext());
// header view 在此添加,保证是第一个添加到linearlayout的最上端
addHeaderView();
}
private void addHeaderView() {
// header view
mHeaderView = mInflater.inflate(R.layout.refresh_header, this, false);
mHeaderImageView = (ImageView) mHeaderView
.findViewById(R.id.pull_to_refresh_image);
mHeaderTextView = (TextView) mHeaderView
.findViewById(R.id.pull_to_refresh_text);
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() {
// footer view
mFooterView = mInflater.inflate(R.layout.refresh_footer, this, false);
mFooterImageView = (ImageView) mFooterView
.findViewById(R.id.pull_to_load_image);
mFooterTextView = (TextView) mFooterView
.findViewById(R.id.pull_to_load_text);
measureView(mFooterView);
mFooterViewHeight = mFooterView.getMeasuredHeight();
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
mFooterViewHeight);
addView(mFooterView, params);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// footer view 在此添加保证添加到linearlayout中的最后
addFooterView();
initContentAdapterView();
}
/**
* init AdapterView like ListView,GridView and so on;or init ScrollView
*
* @description hylin 2012-7-30下午8:48:12
*/
private void initContentAdapterView() {
int count = getChildCount();
if (count < 3) {
throw new IllegalArgumentException(
"this layout must contain 3 child views,and AdapterView or ScrollView must in the second position!");
}
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) {
// finish later
mScrollView = (ScrollView) view;
}
}
if (mAdapterView == null && mScrollView == null) {
throw new IllegalArgumentException(
"must contain a AdapterView or ScrollView in this layout!");
}
}
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(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坐标
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
// deltaY > 0 是向下运动,< 0是向上运动
int deltaY = y - mLastMotionY;
if (isRefreshViewScroll(deltaY)) {
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return false;
}
/*
* 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return
* false)则由PullToRefreshView 的子View来处理;否则由下面的方法来处理(即由PullToRefreshView自己来处理)
*/
@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) {
// PullToRefreshView执行下拉
Log.i(TAG, " pull down!parent view move!");
headerPrepareToRefresh(deltaY);
// setHeaderPadding(-mHeaderViewHeight);
} else if (mPullState == PULL_UP_STATE) {
// PullToRefreshView执行上拉
Log.i(TAG, "pull up!parent view move!");
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) {
// 开始执行footer 刷新
footerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
}
break;
}
return super.onTouchEvent(event);
}
/**
* 是否应该到了父View,即PullToRefreshView滑动
*
* @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.pull_to_refresh_release_label);
mHeaderState = RELEASE_TO_REFRESH;
} else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight) {// 拖动时没有释放
mHeaderTextView.setText(R.string.pull_to_refresh_pull_label);
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.pull_to_refresh_footer_release_label);
mFooterState = RELEASE_TO_REFRESH;
} else if (Math.abs(newTopMargin) < (mHeaderViewHeight + mFooterViewHeight)) {
mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label);
mFooterState = PULL_TO_REFRESH;
}
}
/**
* 修改Header view top margin的值
*
* @description
* @param deltaY
* @return hylin 2012-7-31下午1:14:31
*/
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;
}
/**
* header refreshing
*
* @description hylin 2012-7-31上午9:10:12
*/
private void headerRefreshing() {
mHeaderState = REFRESHING;
setHeaderTopMargin(0);
mHeaderTextView.setText(R.string.pull_to_refresh_refreshing_label);
if (mOnHeaderRefreshListener != null) {
mOnHeaderRefreshListener.onHeaderRefresh(this);
}
headerData();
}
/**
* 头部帧动画
*/
private void headerData() {
// 通过ImageView对象拿到背景显示的AnimationDrawable
mAnimation = (AnimationDrawable) mHeaderImageView.getBackground();
// 为了防止在onCreate方法中只显示第一帧的解决方案之一
mHeaderImageView.post(new Runnable() {
@Override
public void run() {
mAnimation.start();
}
});
}
/**
* 底部帧动画
*/
private void footerData() {
// 通过ImageView对象拿到背景显示的AnimationDrawable
mAnimation = (AnimationDrawable) mFooterImageView.getBackground();
// 为了防止在onCreate方法中只显示第一帧的解决方案之一
mFooterImageView.post(new Runnable() {
@Override
public void run() {
mAnimation.start();
}
});
}
/**
* footer refreshing
*
* @description hylin 2012-7-31上午9:09:59
*/
private void footerRefreshing() {
mFooterState = REFRESHING;
int top = mHeaderViewHeight + mFooterViewHeight;
setHeaderTopMargin(-top);
mFooterTextView
.setText(R.string.pull_to_refresh_footer_refreshing_label);
if (mOnFooterRefreshListener != null) {
mOnFooterRefreshListener.onFooterRefresh(this);
}
footerData();
}
/**
* 设置header view 的topMargin的值
*
* @description
* @param topMargin
* ,为0时,说明header view 刚好完全显示出来; 为-mHeaderViewHeight时,说明完全隐藏了
* hylin 2012-7-31上午11:24:06
*/
private void setHeaderTopMargin(int topMargin) {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
params.topMargin = topMargin;
mHeaderView.setLayoutParams(params);
invalidate();
}
/**
* header view 完成更新后恢复初始状态
*
* @description hylin 2012-7-31上午11:54:23
*/
public void onHeaderRefreshComplete() {
mAnimation.stop();
setHeaderTopMargin(-mHeaderViewHeight);
mHeaderImageView.setVisibility(View.VISIBLE);
mHeaderTextView.setText(R.string.pull_to_refresh_pull_label);
mHeaderState = PULL_TO_REFRESH;
}
/**
* footer view 完成更新后恢复初始状态
*/
public void onFooterRefreshComplete() {
mAnimation.stop();
setHeaderTopMargin(-mHeaderViewHeight);
mFooterImageView.setVisibility(View.VISIBLE);
mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label);
mFooterState = PULL_TO_REFRESH;
}
/**
* 获取当前header view 的topMargin
*
* @description
* @return hylin 2012-7-31上午11:22:50
*/
private int getHeaderTopMargin() {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
return params.topMargin;
}
/**
* set headerRefreshListener
*
* @description
* @param headerRefreshListener
* hylin 2012-7-31上午11:43:58
*/
public void setOnHeaderRefreshListener(
OnHeaderRefreshListener headerRefreshListener) {
mOnHeaderRefreshListener = headerRefreshListener;
}
public void setOnFooterRefreshListener(
OnFooterRefreshListener footerRefreshListener) {
mOnFooterRefreshListener = footerRefreshListener;
}
/**
* Interface definition for a callback to be invoked when list/grid footer
* view should be refreshed.
*/
public interface OnFooterRefreshListener {
public void onFooterRefresh(PullToRefreshView view);
}
/**
* Interface definition for a callback to be invoked when list/grid header
* view should be refreshed.
*/
public interface OnHeaderRefreshListener {
public void onHeaderRefresh(PullToRefreshView view);
}
}
上边就是一个自定义布局,接下来我把xml文件也贴在下边
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.example.administrator.xlistview.PullToRefreshView
android:id="@+id/main_pull_refresh_view_two"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/listview_test"
>
ListView>
com.example.administrator.xlistview.PullToRefreshView>
LinearLayout>
现在就是自定义一个帧动画
<animation-list
android:oneshot="false"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<item android:drawable="@mipmap/progress_loading_image_01" android:duration="150"/>
<item android:drawable="@mipmap/progress_loading_image_02" android:duration="150"/>
animation-list>
定义好以后,在你的头部刷新跟底部刷新的xml文件中调用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pull_to_refresh_header"
android:background="@android:color/white"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center|right">
<ImageView
android:id="@+id/pull_to_refresh_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:background="@drawable/frame"
android:visibility="visible"
android:layout_centerHorizontal="true" />
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center|left"
android:orientation="vertical">
<TextView
android:id="@+id/pull_to_refresh_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:gravity="center"
android:text="@string/pull_to_refresh_pull_label"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
LinearLayout>
LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pull_to_refresh_header"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:gravity="center_horizontal"
android:orientation="horizontal"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center|right">
<ImageView
android:id="@+id/pull_to_load_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:background="@drawable/frame"
android:visibility="visible"
android:layout_marginLeft="25dp"
android:layout_centerHorizontal="true"
/>
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center|left"
android:orientation="vertical">
<TextView
android:id="@+id/pull_to_load_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:gravity="center"
android:text="@string/pull_to_refresh_footer_pull_label"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
LinearLayout>
LinearLayout>
到这为止一个自定义的刷新动画就完成了,接下来就直接在你需要执行刷新的界面调用方法。代码如下:
package com.example.administrator.stock_sell_storage_app;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import com.example.administrator.dapter.DetailAdapter;
import com.example.administrator.xlistview.PullToRefreshView;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.view.annotation.ViewInject;
public class DetailFragment extends Fragment implements PullToRefreshView.OnHeaderRefreshListener, PullToRefreshView.OnFooterRefreshListener {
private View view;
@ViewInject(R.id.listview_test)ListView listview_test;
@ViewInject(R.id.main_pull_refresh_view_two)PullToRefreshView main_pull_refresh_view_two;
DetailAdapter detailAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.detai_fragment, container, false);
ViewUtils.inject(this,view); //注入view和事件
Instantiation();
return view;
}
public void Instantiation(){
main_pull_refresh_view_two.setOnHeaderRefreshListener(this);
main_pull_refresh_view_two.setOnFooterRefreshListener(this);
detailAdapter = new DetailAdapter(getActivity());
listview_test.setAdapter(detailAdapter);
}
//上拉加载
@Override
public void onFooterRefresh(PullToRefreshView view) {
main_pull_refresh_view_two.postDelayed(new Runnable() {
//线程
@Override
public void run() {
main_pull_refresh_view_two.onFooterRefreshComplete();
}
},2000);
}
//下拉刷新
@Override
public void onHeaderRefresh(PullToRefreshView view) {
main_pull_refresh_view_two.postDelayed(new Runnable() {
//线程
@Override
public void run() {
main_pull_refresh_view_two.onHeaderRefreshComplete();
}
},2000);
}
}
给大家看看我这边运行之后的效果图: