com.jackie.refresh.RefreshLayoutBase.java Source code

Java tutorial

Introduction

Here is the source code for com.jackie.refresh.RefreshLayoutBase.java

Source

/*
 *    Copyright 2016 The Open Source Project of Jackie Zhu
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *
 *             $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
 *             $                                                   $
 *             $                       _oo0oo_                     $
 *             $                      o8888888o                    $
 *             $                      88" . "88                    $
 *             $                      (| -_- |)                    $
 *             $                      0\  =  /0                    $
 *             $                    ___/`---'\___                  $
 *             $                  .' \\|     |$ '.                 $
 *             $                 / \\|||  :  |||$ \                $
 *             $                / _||||| -:- |||||- \              $
 *             $               |   | \\\  -  $/ |   |              $
 *             $               | \_|  ''\---/''  |_/ |             $
 *             $               \  .-\__  '-'  ___/-. /             $
 *             $             ___'. .'  /--.--\  `. .'___           $
 *             $          ."" '<  `.___\_<|>_/___.' >' "".         $
 *             $         | | :  `- \`.;`\ _ /`;.`/ - ` : | |       $
 *             $         \  \ `_.   \_ __\ /__ _/   .-` /  /       $
 *             $     =====`-.____`.___ \_____/___.-`___.-'=====    $
 *             $                       `=---='                     $
 *             $     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   $
 *             $                                                   $
 *             $          Buddha bless         Never BUG           $
 *             $                                                   $
 *             $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
 */

package com.jackie.refresh;

import android.content.Context;
import android.graphics.Color;
import android.support.v4.view.MotionEventCompat;
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.view.animation.RotateAnimation;
import android.widget.*;
import com.jackie.refresh.listener.OnLoadListener;
import com.jackie.refresh.listener.OnRefreshListener;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by on 16/5/11.
 *
 * @param <T>
 * @author Jackie Zhu
 * @version 1.0
 */
public abstract class RefreshLayoutBase<T extends View> extends ViewGroup implements AbsListView.OnScrollListener {
    private static final String TAG = "RefreshLayoutBase";

    /**
     * ,?,,ListView, GridView
     */
    protected T mContentView;

    /**
     * 
     */
    protected Scroller mScroller;
    /**
     * Header View
     */
    protected View mHeaderView;

    /**
     * ?
     */
    protected int mScreenHeight;
    /**
     * Header View
     */
    protected ImageView mArrowImg;
    /**
     * Header View ?
     */
    protected ProgressBar mRefreshProgress;
    /**
     * Header View
     */
    protected TextView mTipsTxt;
    /**
     * Header View
     */
    protected TextView mTimeTxt;
    /**
     * Header View
     */
    protected int mHeaderHeight;
    /**
     * 
     */
    protected View mFooterView;

    /**
     * ??,Header View?
     */
    protected int mInitScrollY;

    /**
     * ?Y??
     */
    protected int mLastY;
    /**
     * Y?????
     */
    protected int mYOffset;

    /**
     * ??
     */
    private boolean isArrowUp;
    /**
     * ?
     */
    public static final int STATUS_IDLE = 0;
    /**
     * ??
     */
    public static final int STATUS_PULL_TO_REFRESH = 1;
    /**
     * ?
     */
    public static final int STATUS_RELEASE_TO_REFRESH = 2;
    /**
     * 
     */
    public static final int STATUS_REFRESHING = 3;
    /**
     * 
     */
    public static final int STATUS_LOADING = 4;
    /**
     * ??
     */
    protected int mCurrentStatus = STATUS_IDLE;

    /**
     * ?
     */
    protected OnRefreshListener mOnRefreshListener;
    /**
     * ?
     */
    protected OnLoadListener mOnLoadListener;

    public RefreshLayoutBase(Context context) {
        this(context, null);
    }

    /**
     * @param context
     * @param attrs
     */
    public RefreshLayoutBase(Context context, AttributeSet attrs) {
        super(context, attrs);
        // ?
        mScroller = new Scroller(context);
        // ??
        mScreenHeight = context.getResources().getDisplayMetrics().heightPixels;
        // header?1/4
        mHeaderHeight = mScreenHeight / 4;

        // ?
        initLayout(context);
    }

    /**
     * ?
     *
     * @param context
     */
    private void initLayout(Context context) {
        // header view
        setupHeaderView(context);
        // 
        setupContentView(context);
        // ?
        setDefaultContentLayoutParam();
        addView(mContentView);
        // Footer View
        setupFooterView(context);
    }

    /**
     * ? Header View
     *
     * @param context
     */
    private void setupHeaderView(Context context) {
        Log.d(TAG, "setupHeaderView: ");
        mHeaderView = LayoutInflater.from(context).inflate(R.layout.header_refresh, this, false);
        mHeaderView.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, mHeaderHeight));
        mHeaderView.setBackgroundColor(Color.RED);
        // Header View?1/4,,?100px, paddingTop,
        // 
        mHeaderView.setPadding(0, mHeaderHeight - 100, 0, 0);
        addView(mHeaderView);

        // ?Header View?
        mArrowImg = (ImageView) mHeaderView.findViewById(R.id.img_arrow_image);
        mRefreshProgress = (ProgressBar) mHeaderView.findViewById(R.id.progress_refresh);
        mTipsTxt = (TextView) mHeaderView.findViewById(R.id.txt_pull_to_refresh);
        mTimeTxt = (TextView) mHeaderView.findViewById(R.id.txt_update_at);
    }

    /**
     * ?,??
     *
     * @param context
     */
    protected abstract void setupContentView(Context context);

    /**
     * ?
     */
    protected void setDefaultContentLayoutParam() {
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT);
        mContentView.setLayoutParams(params);
    }

    /**
     * ?
     *
     * @param context
     */
    private void setupFooterView(Context context) {
        mFooterView = LayoutInflater.from(context).inflate(R.layout.footer_refresh, this, false);
        addView(mFooterView);
    }

    public T getContentView() {
        return mContentView;
    }

    public View getHeaderView() {
        return mHeaderView;
    }

    public View getFooterView() {
        return mFooterView;
    }

    public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
        this.mOnRefreshListener = onRefreshListener;
    }

    public void setOnLoadListener(OnLoadListener onLoadListener) {
        this.mOnLoadListener = onLoadListener;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // MeasureSpec
        int width = MeasureSpec.getSize(widthMeasureSpec);
        // ?
        int finalHeight = 0;

        for (int i = 0; i < getChildCount(); i++) {
            View childView = getChildAt(i);
            // ??
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
            // ???,
            finalHeight += childView.getMeasuredHeight();
        }

        // 
        setMeasuredDimension(width, finalHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.layout(left, top, child.getMeasuredWidth(), child.getMeasuredHeight() + top);
            top += child.getMeasuredHeight();
        }

        // ?y?
        mInitScrollY = mHeaderView.getMeasuredHeight() + getPaddingTop();
        // Header View?,???header View
        scrollTo(0, mInitScrollY);
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // ?,,?,?
        if (mOnLoadListener != null && isBottom() && mScroller.getCurrY() <= mInitScrollY && mYOffset < 0
                && mCurrentStatus == STATUS_IDLE) {
            // Footer View
            showFooterView();
            doLoadMore();
            mYOffset = 0;
        }
    }

    @Override
    public void computeScroll() {
        // ??,computeScrollOffset()false?
        if (mScroller.computeScrollOffset()) {
            // View?(x, y)??
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            // 
            postInvalidate();
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "onInterceptTouchEvent() called with: " + "ev = [" + ev.toString() + "]");
        // ?
        final int action = MotionEventCompat.getActionMasked(ev);
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            return false;
        }
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            mLastY = (int) ev.getRawY();
            break;

        case MotionEvent.ACTION_MOVE:
            mYOffset = (int) ev.getRawX() - mLastY;
            // ,,,onTouchEvent()??
            if (isTop() && mYOffset > 0) {
                return true;
            }
            break;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "onTouchEvent() called with: " + "event = [" + event.toString() + "]");
        switch (event.getAction()) {
        case MotionEvent.ACTION_MOVE: // 
            // ??y??
            int currentY = (int) event.getRawY();
            // ???
            mYOffset = currentY - mLastY;
            if (mCurrentStatus != STATUS_LOADING) {
                // Y?
                changeScrollY(mYOffset);
            }

            // Header View
            rotateHeaderArrow();
            // Header View?
            changeTips();
            // mLastYY??
            mLastY = currentY;
            break;

        case MotionEvent.ACTION_UP:
            if (isTop()) {
                // ?
                doRefresh();
            }

            break;
        default:
            break;
        }
        //        return super.onTouchEvent(event);
        return true;
    }

    /**
     * y,Header View
     *
     * @param distance ???(???)
     */
    private void changeScrollY(int distance) {
        int currentY = getScrollY();
        if (distance > 0 && currentY - distance > getPaddingTop()) {
            scrollBy(0, -distance);
        } else if (distance < 0 && currentY - distance <= mInitScrollY) {
            // 
            scrollBy(0, -distance);
        }

        currentY = getScrollY();
        int slop = mInitScrollY / 2;
        if (currentY > 0 && currentY < slop) {
            mCurrentStatus = STATUS_RELEASE_TO_REFRESH;
        } else if (currentY > slop && currentY < mInitScrollY) {
            mCurrentStatus = STATUS_PULL_TO_REFRESH;
        } else {
            mCurrentStatus = STATUS_IDLE;
        }
    }

    private void rotateHeaderArrow() {
        Log.d(TAG, "rotateHeaderArrow() called with: mCurrentStatus = " + mCurrentStatus);
        if (mCurrentStatus == STATUS_REFRESHING) {
            return;
        } else if (mCurrentStatus == STATUS_PULL_TO_REFRESH && !isArrowUp) {
            return;
        } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH && isArrowUp) {
            return;
        }

        mRefreshProgress.setVisibility(View.GONE);
        mArrowImg.setVisibility(View.VISIBLE);

        float pivotX = mArrowImg.getWidth() / 2f;
        float pivotY = mArrowImg.getHeight() / 2f;
        float fromDegrees = 0f;
        float toDegrees = 0f;
        if (mCurrentStatus == STATUS_PULL_TO_REFRESH) {
            fromDegrees = 180f;
            toDegrees = 360f;
        } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
            fromDegrees = 0f;
            toDegrees = 180f;
        }
        RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
        animation.setDuration(100);
        animation.setFillAfter(true);
        mArrowImg.startAnimation(animation);

        if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
            isArrowUp = true;
        } else {
            isArrowUp = false;
        }
    }

    private void changeTips() {
        if (mCurrentStatus == STATUS_PULL_TO_REFRESH) {
            mTipsTxt.setText(R.string.txt_pull_to_refresh);
        } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
            mTipsTxt.setText(R.string.up_to_refresh);
        }
    }

    /**
     * ?
     */
    private void doRefresh() {
        changeHeaderViewStatus();
        // ?
        if (mCurrentStatus == STATUS_REFRESHING && mOnRefreshListener != null) {
            // 
            mOnRefreshListener.onRefresh();
        }
    }

    /**
     * ,????,?Header View?(1/2), 
     * ?,?????
     */
    private void changeHeaderViewStatus() {
        Log.d(TAG, "changeHeaderViewStatus: ");
        int curScrollY = getScrollY();
        // 1/2,?
        if (curScrollY < mInitScrollY / 2) {
            mRefreshProgress.setVisibility(VISIBLE);
            mArrowImg.setVisibility(View.GONE);
            // Header View?
            mScroller.startScroll(getScrollX(), curScrollY, 0, mHeaderView.getPaddingTop() - curScrollY);
            mCurrentStatus = STATUS_REFRESHING;
            mTipsTxt.setText(R.string.txt_tip_refreshing);
        } else {
            mScroller.startScroll(getScrollX(), curScrollY, 0, mInitScrollY - curScrollY);
            mCurrentStatus = STATUS_IDLE;
        }

        invalidate();
    }

    /**
     * ?,???
     */
    public void refreshComplete() {
        mCurrentStatus = STATUS_IDLE;

        // ??Header View
        mScroller.startScroll(getScrollX(), getScrollY(), 0, mInitScrollY - getScrollY());
        invalidate();

        updateHeaderTimeStamp();

        // 200??arrowprogressBar,??
        this.postDelayed(new Runnable() {
            @Override
            public void run() {
                mRefreshProgress.setVisibility(View.GONE);
                mArrowImg.setVisibility(View.VISIBLE);
            }
        }, 100);
    }

    /**
     * Header View
     */
    private void updateHeaderTimeStamp() {
        // 
        mTimeTxt.setText(R.string.txt_last_update_time);
        SimpleDateFormat sdf = (SimpleDateFormat) SimpleDateFormat.getInstance();
        sdf.applyPattern("yyyy-MM-dd HH:mm:ss");
        mTimeTxt.append(sdf.format(new Date()));
    }

    public void loadCompute() {
        // ??Footer View
        startScroll(mInitScrollY - getScrollY());
        mCurrentStatus = STATUS_IDLE;
    }

    /**
     * Footer View
     */
    private void showFooterView() {
        startScroll(mFooterView.getMeasuredHeight());
        mCurrentStatus = STATUS_LOADING;
    }

    /**
     * ?
     */
    private void doLoadMore() {
        if (mOnLoadListener != null) {
            mOnLoadListener.onLoadMore();
        }
    }

    private void startScroll(int offSetY) {
        mScroller.startScroll(getScrollX(), getScrollY(), 0, offSetY);
        invalidate();
    }

    /**
     * ?,??,{@code true}
     *
     * @return <b>
     * true:    <br/>
     * false:   
     * </b>
     */
    protected abstract boolean isTop();

    /**
     * ?,??,{@code true}
     *
     * @return <b>
     * true:    <br/>
     * false:   
     * </b>
     */
    protected abstract boolean isBottom();
}