1. 程式人生 > >仿【咪咕動漫】列表下拉重新整理上拉載入

仿【咪咕動漫】列表下拉重新整理上拉載入

一、概述

本篇續 廈門之旅 的第二篇。這期間找工作真的心態幾多變化,剛開始興致高昂,信心滿滿,面試了幾家不錯的公司,結果都是因為工資問題不了了之。後面的連面試機會都沒有了,每天在狹小的租房裡面吃了睡,睡了玩,陌生的環境消磨這我的意志。我很討厭消沉的自我,這邊招 Android 開發並沒有我以為的那麼多,實在是太少了,想找到滿意的工作更是難上加難。引用公眾號【AndroidDeveloper】的一句話

願意積極爭取,肯努力上進的年輕人,運氣不會太差

在我快要放棄在這邊找工作的時候,收到了一家公司的面試通知,並順利的拿到了 offer,他們公司旗下有一款 咪咕動漫 的產品。本篇我以自己的方式實現了 App 中列表的下拉重新整理以及上拉載入。

二、效果展示

效果圖一欄:

refresh

三、具體實現

1、圖片資源

下載 咪咕動漫App ,修改成 .zip 格式並解壓。獲取到圖片資源如下:

pullrefresh

pull_down (下拉)

pullrelease

pull_end (釋放重新整理)

refreshing1

refreshing_01 (正在重新整理圖片_1)

refreshing2

refreshing_02 (正在重新整理圖片_2)

refreshing3

refreshing_03 (正在重新整理圖片_3)

正在重新整理的幀動畫:

<?xml version="1.0" encoding="utf-8"?>
<animation-list
    xmlns:android
="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:duration="100" android:drawable="@drawable/refreshing_01" /> <item android:duration="100" android:drawable="@drawable/refreshing_02" /> <item android:duration="100" android:drawable="@drawable/refreshing_03"
/>
</animation-list>

屬性 android:oneshot=”false” 表示動畫迴圈播放。 如果為 true,表示動畫只播放一次停止在最後一幀上。

2、下拉重新整理

原理淺析

下拉重新整理作為一個單獨控制元件新增到列表頂部,並且初始狀態的高度為 0 ,隨著手指觸控的偏移量高度而發生改變,並且在不同的狀態之間來回切換。控制元件的四種狀態:

  • STATE_NORMAL 下拉狀態 (高度小於重新整理的臨界高度) 預設 40dp

  • STATE_RELEASE_TO_REFRESH 釋放重新整理狀態 (高度大於重新整理的臨界高度)

  • STATE_REFRESHING 重新整理狀態 (高度大於重新整理的臨界高度,手指釋放後的狀態)

  • STATE_DONE 重新整理完成狀態

處了四種狀態,還需要實現三個方法:

  • void onMove(float delta); 移動,引數 delta 兩點之間的偏移量

  • boolean releaseAction(); 釋放是否滿足重新整理狀態

  • void refreshComplete(); 重新整理完成

重新整理控制元件(MiGuRefreshHeader)

MiGuRefreshHeader 控制元件繼承 LinearLayout ,MiGuRefreshHeader 的構造方法:

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

    public MiGuRefreshHeader(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MiGuRefreshHeader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

初始化 View,比較簡單我這裡就不在細講,文章最後會附上原始碼:

    private void initView() {

        // 初始情況,設定下拉重新整理view高度為0
        mContainer = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.refresh_header, null);
        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        lp.setMargins(0, 0, 0, 0);
        this.setLayoutParams(lp);
        this.setPadding(0, 0, 0, 0);

        addView(mContainer, new LayoutParams(LayoutParams.MATCH_PARENT, 0));
        setGravity(Gravity.BOTTOM);

        //圖片控制元件
        mMiGuImageView = (ImageView) findViewById(R.id.iv_refresh);
        //文字控制元件
        mStatusTextView = (TextView) findViewById(R.id.tv_status);

        //獲取控制元件的預設高度方法一
        measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        mMeasureHeight = getMeasuredHeight();//獲取控制元件高度 預設 40dp 由於測試機密度為 3 所以畫素為 120px
    }

獲取控制元件的預設高度方式二:

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mMeasureHeight = h;
    }

不同狀態下文字控制元件,圖片控制元件的顯示:

    public void setState(int state) {
        if (state == mState) return;

        if (state == STATE_NORMAL) {
            //下拉
            mMiGuImageView.setImageResource(R.drawable.pull_down);
            mStatusTextView.setText(R.string.pull_refresh);
        } else if (state == STATE_RELEASE_TO_REFRESH) {
            //釋放
            mMiGuImageView.setImageResource(R.drawable.pull_end);
            mStatusTextView.setText(R.string.release_refresh);
        } else if (state == STATE_REFRESHING) {
            //重新整理
            mStatusTextView.setText(R.string.refreshing);
            mMiGuImageView.setImageResource(R.drawable.refreshing);
            mMiGuDrawable = (AnimationDrawable) mMiGuImageView.getDrawable();
            //播放動畫
            mMiGuDrawable.start();

            smoothScrollTo(mMeasureHeight);
        } else if (state == STATE_DONE) {
            //完成
            if (mMiGuDrawable != null)
                //停止動畫
                mMiGuDrawable.stop();
        }

        mState = state;
    }

如果你需要替換文字或圖片,請修改這裡。

以下是3個方法的實現,onMove(float delta) 方法:

        if (getVisibleHeight() > 0 || delta > 0) {
            //控制元件滑動的距離
            setVisibleHeight((int) delta + getVisibleHeight());
            //處於釋放重新整理狀態
            if (mState <= STATE_RELEASE_TO_REFRESH) {
                //判定距離是否大於重新整理的臨界值
                if (getVisibleHeight() < mMeasureHeight) {
                    setState(STATE_NORMAL);
                } else {
                    setState(STATE_RELEASE_TO_REFRESH);
                }
            }
        }

releaseAction() 方法:

    @Override
    public boolean releaseAction() {

        boolean isOnRefresh = false;

        int height = getVisibleHeight();

        if (height == 0) {
            isOnRefresh = false;
        }

        if (getVisibleHeight() > mMeasureHeight && mState < STATE_REFRESHING) {
            //重新整理狀態
            setState(STATE_REFRESHING);
            isOnRefresh = true;
        }

        if (mState == STATE_REFRESHING && height <= mMeasureHeight) {
            //處於重新整理狀態,手指還在向上滑動
        }

        if (mState != STATE_REFRESHING) {
            smoothScrollTo(0);
        }

        if (mState == STATE_REFRESHING) {
            int destHeight = mMeasureHeight;
            smoothScrollTo(destHeight);
        }

        return isOnRefresh;
    }

重新整理完成 refreshComplete() 方法:

    @Override
    public void refreshComplete() {
        setState(STATE_DONE);
        reset();
    }

理解了重新整理控制元件的四種狀態,再來分析程式碼就比較容易了。接著我們處理 RecyclerView 的 onTouchEvent 方法獲取 Y 軸的偏移量作為引數傳入 onMove(float delta) 方法中。

MiGuRecyclerView

MiGuRecyclerView 繼承 RecyclerView 控制元件。主要分析 onTouchEvent 方法,如果你對其他地方還有疑問,請留言。

注意:本文使用的 RecyclerView 基於RecyclerView 之通用適配,重寫 setAdapter 方法新增頭部重新整理控制元件:

 mRefreshAdapter.addHeaderView(mMiGuRefreshHeader, 0);

onTouchEvent 方法程式碼如下:

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mLastY == -1) {
            mLastY = ev.getRawY();
        }

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastY = ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mRefreshAdapter != null) {
                    final float deltaY = ev.getRawY() - mLastY;
                    mLastY = ev.getRawY();
                    if (isScrollTop && isOnTop() && pullRefreshEnabled && appbarState == AppBarStateChangeListener.State.EXPANDED) {
                        mMiGuRefreshHeader.onMove(deltaY / DRAG_RATE);
                        if (mMiGuRefreshHeader.getVisibleHeight() > 0 && mMiGuRefreshHeader.getState() < MiGuRefreshHeader.STATE_REFRESHING) {
                            return false;
                        }
                    }
                }
                break;
            default:
                if (mRefreshAdapter != null) {
                    mLastY = -1; // reset
                    if (isScrollTop && isOnTop() && pullRefreshEnabled && appbarState == AppBarStateChangeListener.State.EXPANDED) {
                        if (mMiGuRefreshHeader.releaseAction()) {
                            if (mRefreshListener != null) {
                                mRefreshListener.onRefresh();
                            }
                        }
                    }
                }
                break;
        }

        return super.onTouchEvent(ev);
    }

ACTION_DOWN 手勢獲取相對於螢幕觸控點 Y 座標

    final float deltaY = ev.getRawY() - mLastY;
    mLastY = ev.getRawY();

獲取手勢滑動兩點的偏移量,isScrollTop 判定當前 RecyclerView 是否滑動到頂部。我採取了重寫 onScrolled 方法通過:

findFirstCompletelyVisibleItemPosition();

來判定 isScrollTop 值,如果您有好的方案可以給我留言,萬分感謝。

3、上拉載入

【咪咕動漫】採取的是播放幀動畫,比較簡單。實現方案你可以參考原始碼。

技術交流群歡迎你的加入

qq

原始碼傳送門

參考文獻

相關推薦

仿列表重新整理載入

一、概述 本篇續 廈門之旅 的第二篇。這期間找工作真的心態幾多變化,剛開始興致高昂,信心滿滿,面試了幾家不錯的公司,結果都是因為工資問題不了了之。後面的連面試機會都沒有了,每天在狹小的租房裡面吃了睡,睡了玩,陌生的環境消磨這我的意志。我很討厭消沉的自我,這邊招

Android 列表重新整理載入更多分頁功能

手機app 列表頁很常用,當資料特別多的時候,為了更好地使用者體驗,需要進行分頁處理。那麼分頁功能怎麼做呢? 看如下核心程式碼 if (mPage == 1 && mList != null) { mList.clear();

仿知乎重新整理自動載入

效果圖如下 activity_main.xml [html] view plain copy print?<?xmlversion=“1.0”encoding=“utf-8”?><RelativeLayoutxmlns:androi

Android自定義控制元件實戰——實現仿IOS重新整理載入 PullToRefreshLayout

         下拉重新整理控制元件,網上有很多版本,有自定義Layout佈局的,也有封裝控制元件的,各種實現方式的都有。但是很少有人告訴你具體如何實現的,今天我們就來一步步實現自己封裝的 PullToRefreshLayout 完美的解決下拉重新整理,上拉載入問題。  

TP5專案統一規範列表帶分頁查詢

author:咔咔 wechat:fangkangfk   整體思路步驟: 1.前端拼接js資料 2.使用layui分頁 3.請求後臺控制器 4.控制器將前端請求的頁面,跟每頁顯示的資料傳給server層 5.server層需要根據這倆個引數在模型裡邊帶

廖雪峰Python習題集列表生成式

如果list中既包含字串,又包含整數,由於非字串型別沒有lower()方法,所以列表生成式會報錯: >>> [s.lower() for s in L] Traceback (mos

從零學習openCVIOS7的openCV開發起步(Xcode5.1.1&amp;openCV2.49)

rgb load fcm 12px 轉換 sim 圖像 round ios 前言: 開發IOS7已經有一月的時間了。近期在準備推研的事,有點想往CV方向發展。於是開始自學openCV。 關註CSDN已經非常久了。也從非常多博主那學到了非常多知識,於是我也從這周開

轉錄,收集Windows批處理文件(BAT)的參數之編輯符

border cell 子字符串 文件名 soft 處理 獲取 第一個 div Windows下批處理文件(BAT)的參數之編輯符 可以在批處理文件內的任意地方使用批處理參數。 批處理參數擴展變量(%0 到 %9)。當在批處理文件中使用批處理參數時,%0 將由批處理文件名

算法題7尋找一個排列

baidu idt http .com break solution 博客 ext algorithm 來自:LeetCode 37 實現獲取下一個排列的函數,算法需要將給定數字序列重新排列成字典序中下一個更大的排列。 如果不存在下一個更大的排列,則將數字重新排列成最小的排

Django2+ 學習筆記 02win10Django及新專案部署

1. 安裝Python的Django,貌似是Django命令環境,並不是是專案。(使用--user是因為python安裝到了win的個人使用者下) pip install --user Django==2.1.3 如果本教程時間久遠,你可以用命令pip install --user Dj

ideaIU-2018.3.1Windows7 安裝及破解

一、準備 ideaIU-2018.3.1.exe 破解檔案: JetbrainsCrack-3.4-release-enc.jar 二、安裝 1、執行 2、下一步 選擇安裝目錄 3、快捷方式及支援的聯想提示語言、下一步 4、

淺談社工U盤遊戲(

凡人是沒有資格討論社會工程學的,2333333333,接下來的淺談也不過是為了研究下setoolkit平臺的使用罷了,這個社工利用平臺由parrot預裝,其中大量模組呼叫msf平臺,什麼?又是msf??? 今天我們的標題是初識U盤遊戲,因為筆者選購的裝備還沒到貨,但先來一起探索一下U盤遊戲在社會工程學中是如何

NOIP2001提高組T3統計單詞個數-字串的動態規劃

(本人本題完成於2016-7-19) 題目大意:給定一個字串(長度為20*p,不超過200)和一個包含一些單詞(個數為n,1≤n≤6)的詞典,問如何將該字串分成K(不超過40)份,使得每份中包含的單詞

java虛擬機器系列java中類與物件的載入順序

首先了解一下Java虛擬機器初始化的原理。JVM通過加裝、連線和初始化一個Java型別,使該型別可以被正在執行的Java程式所使用。型別的生命週期如下圖所示: 裝載和連線必須在初始化之前就要完成。 類初始化階段,主要是為類變數賦予正確的初始值。這裡的“正確”初始值指的是程

Centos7從零開始Centos 硬碟分割槽的最佳方案

在對硬碟進行分割槽前,應該先弄清楚計算機擔負的工作及硬碟的容量有多大,還要考慮到以下幾個問題:   第一點也是最重要的一點,要知道當前安裝LILO的版本,因為LILO2.21及早期版本對硬碟大小有限制,如果安裝LILO到1023磁軌以外即8G的空間以外,LILO就無法啟動。

jzoj5913. NOIP2018模擬10.19風氣(樹形dp)

5913. 【NOIP2018模擬10.19】林下風氣 Description 裡口福因有林下風氣,帶領全國各地高校掀起了一股AK風,大家都十分痴迷於AK。裡口福為了打擊大家的自信心,出了一道自以為十分困難的題目。 裡口福有一棵樹,第i個節點上有點權ai,他的問

搞定Jvm面試 面試官:談談 JVM 類載入過程是怎樣的?

類載入過程 Class 檔案需要載入到虛擬機器中之後才能執行和使用,那麼虛擬機器是如何載入這些 Class 檔案呢? 系統載入 Class 型別的檔案主要三步:載入->連線->初始化。連線過程又可分為三步:驗證->準備->解析。 載入 類載入過程的第一步,主要完成下面3件事情:

小白學PyTorch19 TF2模型的儲存與載入

【新聞】:機器學習煉丹術的粉絲的人工智慧交流群已經建立,目前有目標檢測、醫學影象、時間序列等多個目標為技術學習的分群和水群嘮嗑的總群,歡迎大家加煉丹兄為好友,加入煉丹協會。微信:cyx645016617. 參考目錄: [TOC] 本文主要講述TF2.0的模型檔案的儲存和載入的多種方法。主要分成兩型別:模型

Recyclerview 列表中 使用Glide載入圖片, 或者 載入資料,圖片閃 爍 ,

剛開始以為是 Recyclerview的快取複用問題,隨找了幾個方法試試,在Recyclerview adapter裡邊 設定tag,,然並卵; 禁止 glide 預設載入動畫,沒卵用,並沒有解決,

經驗零基礎如何學插畫?

零基礎如何學動漫插畫?如何快速學好畫畫?這是不可能的!學畫畫是一個持久戰,基礎不行,就得老老實實打基礎,目前有不少培訓班,為了快速出效果,忽略基礎,帶著學生追求表面的華麗,也許這是學生所需要的,也許這是一種自欺欺人! 繪畫練習無時無刻,閒的時候都不能放棄練一下