Learning and Use of Dropdown Refresh Framework Android-Ultra-Pull-To-Refresh

Keywords: Android Gradle Java github

The reason for doing this is that my mentor's pre-departure 2016 plan includes the following: replace the current drop-down refresh framework PullToRefresh framework with Android-Ultra-Pull-To-Refresh framework, because the former is no longer maintained. I may be leaving now, but last year I was so busy that I didn't change the framework. I regretted that, so I did some research in the two days after I finished my work.

I. Download and Configuration

The address is: https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh Address, download the project, uncompress and find a folder named "ptr_lib" and drop it directly into our project. (Note: Here I changed to Android Ptr_Library for my own convenience.

 

Step 1: Add in the settings.gradle file for the entire project

include:' AndroidPtr_Library'

Later, we found our project in the Project Structure, clicked the Add button on the right side of the bullet box, and added a Module Dependency named "Android Ptr_Library".

 

Step 2: Adding dependencies to the project's build.gradle file

compile project(':AndroidPtr_Library')


After completing the above two steps, click on "Rebuild Project" and then report the error as follows:

"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."


So there's a third step: add it to the gradle.properties file for the entire project

ANDROID_BUILD_MIN_SDK_VERSION=8

ANDROID_BUILD_TARGET_SDK_VERSION=22

ANDROID_BUILD_SDK_VERSION=22

ANDROID_BUILD_TOOLS_VERSION=22.0.1


Then there's no problem running, and you can see that the drop-down refresh framework has been added.


2. PtrClassicDefaultHeader Class for Header Reform

There are many classes in the source code called *** Header, which is the corresponding class of our drop-down refresh header. Let's first look at the original class of PtrClassicDefaultHeader.

There are several main methods in this class:

initView() Initializes the view

BuilAnimation () initializes the animation object, where two rotating animations are used to change the direction of the arrow when the image is pulled down as an arrow.

Rewrite onDetachedFromWindow() When the view is destroyed, determine whether the thread that updates the time when it drops down is empty, and stop the thread if it is not empty.

tryUpdateLastUpdateTime() Updates the last refresh time

getLastUpdateTime() is the last refresh time

Override onUIPositionChange() This method is used to determine the location change and animate the ImageView in it.

After understanding these methods, look at the corresponding layout of this class, namely a layout file named "cube_ptr_classic_default_header", which consists of four parts: an ImageView based on the direction of rotation animation Rotate Animation change, two TextView s, one to display the refresh status when refreshing (pull-down refresh-load refresh-update complete) One is when the last update was prompted when the refresh was pulled down, and the other is ProgressBar.

Then we can transform the pull-down refresh head. Here I created a new class named "SherryPtrClassicDefaultHeader", in which I only used ImageView and TextView. The layout file is as follows:

<?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>

Here my default image of ImageView is a circular ProgressBar-like image, which is the "default_ptr_rotate" above. Then I attach my java class code:

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));
        }
    }
}

After that, one thing to note is to change the name of the PtrClassic DefaultHeader to SherryPtrClassic DefaultHeader, which is actually the PtrClassic DefaultHeader in the PtrClassic FrameLayout class, in addition to the PtrClassic DefaultHeader.

3. Use of drop-down refresh control

First, look at the layout file:

<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>

From the layout file, we can see that we directly use <in.srain.cube.views.ptr.Ptr Classic FrameLayout/> and then put the layout we want to drop-down refresh display in this FrameLayout. It should be noted that there can only be a sub-View in this FrameLayout, otherwise it will report errors, so what we can see here is a relative layout I use, so in the code. Inside, when the page is empty, I can set the ListView invisible, and then display the layout of the page when it is empty, which is my_coupon_empty layout file.

Attach the main code used in the java class:

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) {
         // Modify interface to get data
    }
});


Okay, so that's some of my precautions in learning and using the drop-down refresh framework Android-Ultra-Pull-To-Refresh. You are welcome to point out the mistakes. Please attach a link to it.




Posted by bradsteele on Wed, 27 Mar 2019 17:06:29 -0700