1. 程式人生 > >一個簡潔而不簡單的安卓上下拉重新整理框架

一個簡潔而不簡單的安卓上下拉重新整理框架

首先先看一下效果圖吧==

這裡寫圖片描述這裡寫圖片描述

重新整理動畫都很熟悉吧…so 本專案並不是重新整理控制元件,而是一個框架,定義了一個標準,可以整合實現自己想要的效果!
原始碼地址:https://github.com/tohodog/QSRefreshLayout
框架內建4個重新整理view
CircleImageView 小圓球
BarRefreshView 變色的細條
IOSRefreshView ios上的一款重新整理view
XMLRefreshView 就是那款經典的上下拉重新整理,不過很簡陋,有需要的同學可以修改
其他的餓了麼京東等均在demo裡

一、簡介

  • 重新整理view模組化,可自由更換擴充套件,head foot可通用
  • 輕鬆實現各種重新整理效果,不用自己處理觸控事件
  • 支援任意可滑動的控制元件
  • 更多效果更新中…

二、框架使用

1. XML

可以在xml設定head foot控制元件

    <org.song.refreshlayout.QSRefreshLayout
        android:id="@+id/qs"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--head-->
        <org.song.refreshlayout.refreshview.CircleImageView
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" android:background
="#ffffff" />
<!--foot--> <org.song.refreshlayout.refreshview.BarRefreshView android:layout_width="match_parent" android:layout_height="2dp" /> </org.song.refreshlayout.QSRefreshLayout>

2.JAVA

一些基本的控制

QSRefreshLayout qsRefreshLayout = (QSRefreshLayout) findViewById(R.id.qs);

//qsRefreshLayout.setHeadRefreshView(new CircleImageView(this));

//qsRefreshLayout.setFootRefreshView(new BarRefreshView(this));
//自動進入頭部重新整理
qsRefreshLayout.enterHeadRefreshing(true);
//監聽
qsRefreshLayout.setRefreshListener(new QSRefreshLayout.RefreshListener() {
            @Override
            public void changeStatus(boolean isHead, int status) {
                if (status == QSRefreshLayout.STATUS_REFRESHING) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            qsRefreshLayout.refreshComplete();
                        }
                    }, 3000);
                }
            }
});

//refreshview setting
CircleImageView circleImageView= (CicleImageView) qsRefreshLayout.getHeadRefreshView();
circleImageView.setColorScheme(R.color.xxx,...);

三、如何DIY一個自己的重新整理view

需要view實現IRefreshView介面,定義如下

    View getView();

    void updateStatus(int status);//更新重新整理狀態

    void updateProgress(float progress);//重新整理進度 0 ~ 1(觸發重新整理)~ 更大

    boolean isBringToFront();//是否view放在頂層

    float dragRate();//滑動速度控制

    int triggerDistance();//觸發重新整理的距離 [實際觸控距離*dragRate()>triggerDistance() 觸發重新整理

    int maxDistance();//最大滑動距離 <=0不限制

    int getThisViewOffset(int offset);//根據觸控位移 確定該view的位移 大於0=headview 小於0=footview

    int getTargetOffset(int offset);//根據觸控位移 確定滾動view的位移 大=headview 小於0=footview

    int completeAnimaDuration();//完成重新整理後到消失的動畫時間, <=0使用預設時間

    void isHeadView(boolean isHead);//是否頂部重新整理view

一個詳細的demo實現


public class DemoRefreshView extends FrameLayout implements IRefreshView {


    private int h;

    public DemoRefreshView(@NonNull Context context) {
        this(context, null);
    }

    public DemoRefreshView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        float density = context.getResources().getDisplayMetrics().density;
        h = (int) (density * 50);

        //todo 如果在xml配置view 則此引數無效 會被xml的引數覆蓋
        //設定這個空間的大小 目前不支援margin gravity等引數,預設居中
        setLayoutParams(new ViewGroup.LayoutParams(h, h));
        setBackgroundColor(Color.BLUE);

    }

    private void init() {

    }

    public void setH(int h) {
        this.h = h;
        requestLayout();
    }

    @Override//確定view大小,你可以在這裡強制配置自己的view大小
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //如我的view需要長寬都鎖死,不被setLayoutParams所修改
        //則可以這樣,然後再提供setH(int h)給外部
        setMeasuredDimension(h, h);
    }

    //返回當前view的例項即可
    @Override
    public View getView() {
        return this;
    }

    /**
     * 重新整理的狀態回撥
     * 重新整理控制元件共有5種狀態
     * STATUS_NORMAL = 0;//普通狀態
     * STATUS_DRAGGING = 1;//手指拖曳時(未到觸發距離)
     * STATUS_DRAGGING_REACH = 2;//手指拖曳(可以觸發重新整理的距離)
     * STATUS_REFRESHING = 3;//重新整理中
     * STATUS_REFRESHED = 4;//重新整理完成後到view隱藏的這一段狀態
     * 開發者可以根據狀態來設定view
     */
    private int status;

    @Override
    public void updateStatus(int status) {
        this.status = status;
        switch (status) {
            //就只是變下顏色
            case QSRefreshLayout.STATUS_DRAGGING_REACH:
                setBackgroundColor(Color.RED);
                break;
            case QSRefreshLayout.STATUS_REFRESHING:
                setBackgroundColor(Color.GREEN);
                break;
            case QSRefreshLayout.STATUS_DRAGGING:
            case QSRefreshLayout.STATUS_REFRESHED:
            case QSRefreshLayout.STATUS_NORMAL:
                setBackgroundColor(Color.BLUE);
                break;
        }
    }

    /**
     * 重新整理的進度
     * 重新整理進度 0 ~ 1(觸發重新整理)~ 更大
     * 一些特效動畫就可以根據這個值來更新狀態
     */
    @Override
    public void updateProgress(float progress) {
        //不是拖曳狀態忽略
        if (status != QSRefreshLayout.STATUS_DRAGGING && status != QSRefreshLayout.STATUS_DRAGGING_REACH)
            return;
        if (progress > 1)
            progress = 1;
        //這裡就實現一個顏色漸變吧

        int startColor = Color.BLUE;
        int a1 = (startColor >> 24) & 0x000000FF;
        int r1 = (startColor >> 16) & 0x000000FF;
        int g1 = (startColor >> 8) & 0x000000FF;
        int b1 = startColor & 0x000000FF;

        int endColor = Color.RED;
        int a2 = (endColor >> 24) & 0x000000FF;
        int r2 = (endColor >> 16) & 0x000000FF;
        int g2 = (endColor >> 8) & 0x000000FF;
        int b2 = endColor & 0x000000FF;

        int r = (int) (r1 + (r2 - r1) * progress);
        int g = (int) (g1 + (g2 - g1) * progress);
        int b = (int) (b1 + (b2 - b1) * progress);
        int a = (int) (a1 + (a2 - a1) * progress);

        int newColor = Color.argb(a, r, g, b);
        setBackgroundColor(newColor);

    }

    /**
     * 是否view在頂層
     * 比如餓了麼重新整理就需要在底層 返回false
     * 谷歌的小圓球重新整理就需要在頂層 返回true
     */
    @Override
    public boolean isBringToFront() {
        return false;//顯示在目標的下方
    }

    /**
     * 控制拖曳的速率
     * 比如返回.5f,手指拖動100畫素,本框架會認為是50畫素
     */
    @Override
    public float dragRate() {
        return .5f;
    }

    /**
     * 觸發重新整理的距離
     */
    @Override
    public int triggerDistance() {
        return getMeasuredHeight();//觸發距離為本view的高度
    }

    /**
     * 最大拖動的距離
     * <=0不限制
     */
    @Override
    public int maxDistance() {
        return 0;//不限制
    }

    /**
     * 根據觸控位移 確定該view的位移
     *
     * @param offset 當前的拖動距離(實際觸控距離*dragRate()), headview大於0, footview小於0
     * @return 返回這個view的移動值 這個值將會確定view的位置
     */
    @Override
    public int getThisViewOffset(int offset) {
        //這裡我們實現一個視差移動
        offset = Math.abs(offset);
        int t = triggerDistance();
        int i;
        if (offset > t)
            i = offset;
        else
            i = t / 2 + offset / 2;
        return isHead ? i : -i;//頂部和底部view 移動值是相反的
    }

    /**
     * 根據觸控位移 確定目標view的位移
     *
     * @param offset 當前的拖動距離(實際觸控距離*dragRate()), headview大於0, footview小於0
     * @return 返回目標view(就是listview, 等)的移動值 這個值將會確定view的位置
     * 這個返回值一般要麼是
     * 0不會動
     * offset 原值返回
     */
    @Override
    public int getTargetOffset(int offset) {
        return offset;//返回原值,會跟隨手指而移動
    }

    /**
     * 完成重新整理後到消失的動畫時間, <=0使用預設時間300ms
     * 如果有一些動畫重新整理完成後執行時間較長或需要調慢 可以這裡返回時間ms
     * (BarRefreshView使用了)
     */
    @Override
    public int completeAnimaDuration() {
        return 0;
    }

    private boolean isHead;

    //標記這個view是拿去做head還是foot,用變數記錄下來,也可以在這裡實現一些初始化
    @Override
    public void isHeadView(boolean isHead) {
        this.isHead = isHead;
    }
}

執行效果如下,recycle設定半透明瞭,方便看效果
這裡寫圖片描述