下拉刷新框架Android-Ultra-Pull-To-Refresh的学习与

简介: 之所以做这件事,是因为前者已经没有在维护了,所以把现在的下拉刷新框架PullToRefresh框架换成Android-Ultra-Pull-To-Refresh框架。

之所以做这件事,是因为前者已经没有在维护了,所以把现在的下拉刷新框架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) {
         // 调接口拿数据
    }
});


好的,那么以上就是我学习与使用下拉刷新框架Android-Ultra-Pull-To-Refresh的一些小心得,有不对的地方欢迎大家指正,转载请附链接。





目录
相关文章
|
4月前
|
物联网 区块链 vr&ar
未来已来:探索区块链、物联网与虚拟现实技术的融合与应用安卓与iOS开发中的跨平台框架选择
【8月更文挑战第30天】在科技的巨轮下,新技术不断涌现,引领着社会进步。本文将聚焦于当前最前沿的技术——区块链、物联网和虚拟现实,探讨它们各自的发展趋势及其在未来可能的应用场景。我们将从这些技术的基本定义出发,逐步深入到它们的相互作用和集成应用,最后展望它们如何共同塑造一个全新的数字生态系统。
|
5月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台框架解析
在移动应用开发的广阔舞台上,安卓和iOS一直是两大主角。随着技术的进步,开发者们渴望能有一种方式,让他们的应用能同时在这两大平台上运行,而不必为每一个平台单独编写代码。这就是跨平台框架诞生的背景。本文将探讨几种流行的跨平台框架,包括它们的优势、局限性,以及如何根据项目需求选择合适的框架。我们将从技术的深度和广度两个维度,对这些框架进行比较分析,旨在为开发者提供一个清晰的指南,帮助他们在安卓和iOS的开发旅程中,做出明智的选择。
|
23天前
|
算法 JavaScript Android开发
|
2月前
|
Java 程序员 API
Android|集成 slf4j + logback 作为日志框架
做个简单改造,统一 Android APP 和 Java 后端项目打印日志的体验。
101 1
|
3月前
|
Java Maven 开发工具
第一个安卓项目 | 中国象棋demo学习
本文是作者关于其第一个安卓项目——中国象棋demo的学习记录,展示了demo的运行结果、爬坑记录以及参考资料,包括解决Android Studio和maven相关问题的方法。
第一个安卓项目 | 中国象棋demo学习
|
2月前
|
Web App开发 编解码 视频直播
视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术
本文详细介绍了Android端直播技术的全貌,涵盖了从实时音视频采集、编码、传输到解码与播放的各个环节。文章还探讨了直播中音视频同步、编解码器选择、传输协议以及直播延迟优化等关键问题。希望本文能为你提供有关Andriod端直播技术的深入理解和实践指导。
50 0
|
3月前
|
前端开发 Java 数据库
💡Android开发者必看!掌握这5大框架,轻松打造爆款应用不是梦!🏆
在Android开发领域,框架犹如指路明灯,助力开发者加速应用开发并提升品质。本文将介绍五大必备框架:Retrofit简化网络请求,Room优化数据库访问,MVVM架构提高代码可维护性,Dagger 2管理依赖注入,Jetpack Compose革新UI开发。掌握这些框架,助你在竞争激烈的市场中脱颖而出,打造爆款应用。
380 3
|
3月前
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
85 8
|
3月前
|
Android开发
Android学习 —— 测试init.rc中的条件触发的处理顺序
Android学习 —— 测试init.rc中的条件触发的处理顺序
|
4月前
|
设计模式 Java Android开发
探索安卓应用开发:从新手到专家的旅程探索iOS开发中的SwiftUI框架
【8月更文挑战第29天】本文旨在通过一个易于理解的旅程比喻,带领读者深入探讨安卓应用开发的各个方面。我们将从基础概念入手,逐步过渡到高级技术,最后讨论如何维护和推广你的应用。无论你是编程新手还是有经验的开发者,这篇文章都将为你提供有价值的见解和实用的代码示例。让我们一起开始这段激动人心的旅程吧!