之所以做这件事,是因为前者已经没有在维护了,所以把现在的下拉刷新框架PullToRefresh框架换成Android-Ultra-Pull-To-Refresh框架。
一、下载和配置
在网址为:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh的地址,把这个项目下载下来,解压后找到名为“ptr_lib”的文件夹,直接丢进我们的项目中。(注:这里我为了方便自己识别,改为了AndroidPtr_Library)。
第一步:在整个工程的settings.gradle文件中添加
include:’ AndroidPtr_Library’
后,在Project Structure中找到我们的项目,点击弹框右侧添加按钮,添加一个命名为“AndroidPtr_Library”的Module Dependency。
第二步:在项目的build.gradle文件中添加依赖
compile project(':AndroidPtr_Library')
做完上面这两步后,点击“Rebuild Project”,“,然后就报错了,报错内容如下:
“Error:(4, 1) A problemoccurred evaluating project ':AndroidPtr_Library'.
> Could not getunknown property 'ANDROID_BUILD_SDK_VERSION' for project':AndroidPtr_Library' of type org.gradle.api.Project.”
所以就有了第三步:在整个工程的gradle.properties文件中添加
ANDROID_BUILD_MIN_SDK_VERSION=8
ANDROID_BUILD_TARGET_SDK_VERSION=22
ANDROID_BUILD_SDK_VERSION=22
ANDROID_BUILD_TOOLS_VERSION=22.0.1
接着运行便没有问题了,也可以看到下拉刷新的框架已经加进来了。
二、Header改造之PtrClassicDefaultHeader类
在源码中有很多叫***Header的类,也就是我们下拉刷新的头部对应的类,这里我们先来看最原始的PtrClassicDefaultHeader的类。
在这个类里面有几个主要方法:
initView() 初始化视图
buildAnimation() 初始化动画对象,这里面有两个旋转动画用于图片为一个箭头的ImageView在下拉时改变箭头方向。
重写onDetachedFromWindow() 当视图销毁时,判断下拉时更新时间的线程是否为空,如果不为空就停止该线程。
tryUpdateLastUpdateTime() 更新最后的刷新时间
getLastUpdateTime() 也就是获得最后的刷新时间
重写onUIPositionChange() 这个方法是用来判断位置变化从而在里面对ImageView进行动画处理
了解了这些方法后,再来看这个类对应的布局,也就是一个命名为“cube_ptr_classic_default_header”的布局文件,它由四个部分组成,一个根据旋转动画RotateAnimation变化方向的ImageView,两个TextView,一个用来刷新时显示刷新的状态(下拉刷新-释放刷新-加载中-更新完成),一个则是在下拉刷新时提示上次更新的时间,一个ProgressBar(刷新时转动)。
接着我们就可以对下拉刷新的头部进行改造了。这里我新建了一个类命名为“SherryPtrClassicDefaultHeader”,在这个类里面我只用到了ImageView和TextView。布局文件如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <RelativeLayout android:layout_width="fill_parent" android:layout_height="60dp"> <LinearLayout android:id="@+id/sherry_ptr_classic_header_rotate_view_header_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_marginLeft="10dp" android:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/sherry_ptr_classic_header_rotate_view_header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#666666" android:textSize="14sp" /> </LinearLayout> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toLeftOf="@+id/sherry_ptr_classic_header_rotate_view_header_text"> <ImageView android:id="@+id/sherry_ptr_classic_header_rotate_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/default_ptr_rotate"/> </FrameLayout> </RelativeLayout> </LinearLayout>
这里我的ImageView的默认图片是一个类似于圆形ProgressBar的图片,也就是上面的“default_ptr_rotate”。接着附上我的java类代码:
public class SherryPtrClassicDefaultHeader extends FrameLayout implements PtrUIHandler { private final static String KEY_SharedPreferences = "cube_ptr_classic_last_update"; private static SimpleDateFormat sDataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final int ROTATION_ANIMATION_DURATION = 1200; private int mRotateAniTime = 150; private RotateAnimation mFlipAnimation; private RotateAnimation mReverseFlipAnimation; private TextView mTitleTextView; private View mRotateView; private float mDownHeight = 0; private long mLastUpdateTime = -1; private String mLastUpdateTimeKey; public SherryPtrClassicDefaultHeader(Context context) { super(context); initViews(null); } public SherryPtrClassicDefaultHeader(Context context, AttributeSet attrs) { super(context, attrs); initViews(attrs); } public SherryPtrClassicDefaultHeader(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initViews(attrs); } protected void initViews(AttributeSet attrs) { TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.PtrClassicHeader, 0, 0); if (arr != null) { mRotateAniTime = arr.getInt(R.styleable.PtrClassicHeader_ptr_rotate_ani_time, mRotateAniTime); } buildAnimation(); View header = LayoutInflater.from(getContext()).inflate(R.layout.sherry_ptr_classic_default_header, this); mRotateView = header.findViewById(R.id.sherry_ptr_classic_header_rotate_view); mTitleTextView = (TextView) header.findViewById(R.id.sherry_ptr_classic_header_rotate_view_header_title); resetView(); } public void setRotateAniTime(int time) { if (time == mRotateAniTime || time == 0) { return; } mRotateAniTime = time; buildAnimation(); } /** * Specify the last update time by this key string * * @param key */ public void setLastUpdateTimeKey(String key) { if (TextUtils.isEmpty(key)) { return; } mLastUpdateTimeKey = key; } /** * Using an object to specify the last update time. * * @param object */ public void setLastUpdateTimeRelateObject(Object object) { setLastUpdateTimeKey(object.getClass().getName()); } private void buildAnimation() { mFlipAnimation = new RotateAnimation(0, mDownHeight * 360 / 100, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mFlipAnimation.setInterpolator(new LinearInterpolator()); mFlipAnimation.setDuration(100); mFlipAnimation.setRepeatCount(Animation.INFINITE); mFlipAnimation.setRepeatMode(Animation.RESTART); mReverseFlipAnimation = new RotateAnimation(0, 720, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mReverseFlipAnimation.setInterpolator(new LinearInterpolator()); mReverseFlipAnimation.setDuration(ROTATION_ANIMATION_DURATION); mReverseFlipAnimation.setRepeatCount(Animation.INFINITE); mReverseFlipAnimation.setRepeatMode(Animation.RESTART); } private void resetView() { hideRotateView(); } private void hideRotateView() { mRotateView.clearAnimation(); mRotateView.setVisibility(INVISIBLE); } @Override public void onUIReset(PtrFrameLayout frame) { resetView(); } @Override public void onUIRefreshPrepare(PtrFrameLayout frame) { mRotateView.setVisibility(VISIBLE); mTitleTextView.setVisibility(VISIBLE); if (frame.isPullToRefresh()) { mTitleTextView.setText(getResources().getString(R.string.ynf_ptr_pull_down_to_refresh)); } else { mTitleTextView.setText(getResources().getString(R.string.ynf_ptr_pull_down)); } } @Override public void onUIRefreshBegin(PtrFrameLayout frame) { mTitleTextView.setVisibility(VISIBLE); mTitleTextView.setText(R.string.ynf_ptr_refreshing); } @Override public void onUIRefreshComplete(PtrFrameLayout frame) { mTitleTextView.setVisibility(VISIBLE); mTitleTextView.setText(getResources().getString(R.string.ynf_ptr_refresh_complete)); // update last update time SharedPreferences sharedPreferences = getContext().getSharedPreferences(KEY_SharedPreferences, 0); if (!TextUtils.isEmpty(mLastUpdateTimeKey)) { mLastUpdateTime = new Date().getTime(); sharedPreferences.edit().putLong(mLastUpdateTimeKey, mLastUpdateTime).commit(); } } @Override public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { final int mOffsetToRefresh = frame.getOffsetToRefresh(); final int currentPos = ptrIndicator.getCurrentPosY(); final int lastPos = ptrIndicator.getLastPosY(); mDownHeight = mOffsetToRefresh; Log.d("xueli-----------", "currentPos==" + currentPos + ",lastPos==" + lastPos + ",mOffsetToRefresh==" + mOffsetToRefresh +"isUnderTouch==" + isUnderTouch); if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) { if (status == PtrFrameLayout.PTR_STATUS_LOADING) { crossRotateLineFromBottomUnderTouch(frame); if (mRotateView != null) { Log.d("xueli----2------", "mReverseFlipAnimation"); mRotateView.clearAnimation(); mRotateView.startAnimation(mReverseFlipAnimation); } } } else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) { if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) { crossRotateLineFromTopUnderTouch(frame); if (mRotateView != null) { Log.d("xueli----1------", "mFlipAnimation"); mRotateView.clearAnimation(); mRotateView.startAnimation(mFlipAnimation); } } } } private void crossRotateLineFromTopUnderTouch(PtrFrameLayout frame) { if (!frame.isPullToRefresh()) { mTitleTextView.setVisibility(VISIBLE); mTitleTextView.setText(R.string.ynf_ptr_release_to_refresh); } } private void crossRotateLineFromBottomUnderTouch(PtrFrameLayout frame) { mTitleTextView.setVisibility(VISIBLE); if (frame.isPullToRefresh()) { mTitleTextView.setText(getResources().getString(R.string.ynf_ptr_pull_down_to_refresh)); } else { mTitleTextView.setText(getResources().getString(R.string.ynf_ptr_pull_down)); } } }
改完了之后,要注意的一点就是把除了PtrClassicDefaultHeader以外,用到PtrClassicDefaultHeader的地方,把名字都改成SherryPtrClassicDefaultHeader,其实也就是PtrClassicFrameLayout类中的PtrClassicDefaultHeader。
三、下拉刷新控件的使用
首先看布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cube_ptr="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/gray_shallow" android:orientation="vertical"> <include layout="@layout/title" /> <in.srain.cube.views.ptr.PtrClassicFrameLayout android:id="@+id/ptr_my_coupon" android:layout_width="match_parent" android:layout_height="match_parent" cube_ptr:ptr_duration_to_close="200" cube_ptr:ptr_duration_to_close_header="1000" cube_ptr:ptr_keep_header_when_refresh="true" cube_ptr:ptr_pull_to_fresh="false" cube_ptr:ptr_ratio_of_header_height_to_refresh="1.2" cube_ptr:ptr_resistance="1.7"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/lv_my_coupon" android:layout_width="match_parent" android:layout_height="match_parent" android:cacheColorHint="@android:color/transparent" android:divider="@null" android:fadingEdge="none" android:fastScrollEnabled="false" android:listSelector="@null" android:overScrollMode="never" android:scrollbars="none" /> <include android:id="@+id/my_coupons_empty" layout="@layout/my_coupon_empty" android:visibility="gone" /> </RelativeLayout> </in.srain.cube.views.ptr.PtrClassicFrameLayout> </LinearLayout>
从布局文件我们可以看到,这里我们是直接使用了<in.srain.cube.views.ptr.PtrClassicFrameLayout />,然后在这个FrameLayout里面再放入我们想要下拉刷新显示的布局,要注意的是,这个里面只能有一个子View,不然会报错,所以这里可以看到的是我用的一个相对布局,那么在代码里面当页面为空时,我可以设置ListView不可见,然后显示页面为空时的布局,也就是my_coupon_empty布局文件。
附上java类里面使用的主要代码:
vPtrClsFl = (PtrClassicFrameLayout) findViewById(R.id.ptr_my_coupon); vPtrClsFl.setPtrHandler(new PtrHandler() { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); } @Override public void onRefreshBegin(PtrFrameLayout frame) { // 调接口拿数据 } });