1. 程式人生 > >RecyclerView的基本用法 (三)

RecyclerView的基本用法 (三)

Recyclerview,從它出生開始到現在也已經有很長時間了,但是對它的使用也僅僅停留在一般的使用上,頂多也就在本文的前兩個介紹裡的普通用法的程度,於是抱著對其深入研究的態度,看了不少的文章和專案,在應用層待得越久越想知道這些東西的原理但也越來越不想知道,本著不為程式碼而程式碼,不為技術而技術謀求生計便是如此,大把大把的時間投入到無限的無意義的需求裡也是沒有辦法的事。

philm是大神寫的,整個專案內容甚多重點在於MVP的架構,但本文不偏題,只挖出其中一個效果即頂部View的滾動縮排;於此相似的便是parallax-recyclerview。這種效果在不少的app可以看到,這裡作為recyclerview的一個鋪墊進行介紹。

效果圖:

parallax-recyclerview庫裡面只有兩個類,這裡我們先介紹ParallaxRecyclerAdapter:

public abstract class ParallaxRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final float SCROLL_MULTIPLIER = 0.5f;

    public static class VIEW_TYPES {
        public static final int NORMAL = 1;
        public static final int HEADER = 2;
        public static final int FIRST_VIEW = 3;
    }

    public abstract void onBindViewHolderImpl(RecyclerView.ViewHolder viewHolder,ParallaxRecyclerAdapter<T> adapter,  int i);

    public abstract RecyclerView.ViewHolder onCreateViewHolderImpl(ViewGroup viewGroup,ParallaxRecyclerAdapter<T> adapter, int i);

    public abstract int getItemCountImpl(ParallaxRecyclerAdapter<T> adapter);

    public interface OnClickEvent {
        /**
         * Event triggered when you click on a item of the adapter
         *
         * @param v        view
         * @param position position on the array
         */
        void onClick(View v, int position);
    }

    public interface OnParallaxScroll {
        /**
         * Event triggered when the parallax is being scrolled.
         *
         * @param percentage
         * @param offset
         * @param parallax
         */
        void onParallaxScroll(float percentage, float offset, View parallax);
    }

    private List<T> mData;
    private CustomRelativeWrapper mHeader;
    private OnClickEvent mOnClickEvent;
    private OnParallaxScroll mParallaxScroll;
    private RecyclerView mRecyclerView;
    private boolean mShouldClipView = true;

    /**
     * Translates the adapter in Y
     *
     * @param of offset in px
     */
    public void translateHeader(float of) {
        float ofCalculated = of * SCROLL_MULTIPLIER;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            mHeader.setTranslationY(ofCalculated);
        } else {
            TranslateAnimation anim = new TranslateAnimation(0, 0, ofCalculated, ofCalculated);
            anim.setFillAfter(true);
            anim.setDuration(0);
            mHeader.startAnimation(anim);
        }

        mHeader.setClipY(Math.round(ofCalculated));
        if (mParallaxScroll != null) {
            float left = Math.min(1, ((ofCalculated) / (mHeader.getHeight() * SCROLL_MULTIPLIER)));
            mParallaxScroll.onParallaxScroll(left, of, mHeader);
        }
    }

    /**
     * Set the view as header.
     *
     * @param header The inflated header
     * @param view   The RecyclerView to set scroll listeners
     */
    public void setParallaxHeader(View header, final RecyclerView view) {
        mRecyclerView = view;
        mHeader = new CustomRelativeWrapper(header.getContext(), mShouldClipView);
        mHeader.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        mHeader.addView(header, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        view.setOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (mHeader != null) {
                    translateHeader(mRecyclerView.computeVerticalScrollOffset());
                }
            }
        });
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int i) {
        if (i != 0 && mHeader != null) {
            onBindViewHolderImpl(viewHolder,this, i - 1);
        } else if (i != 0) {
            onBindViewHolderImpl(viewHolder, this, i);
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, final int i) {
        if (i == VIEW_TYPES.HEADER && mHeader != null)
            return new ViewHolder(mHeader);
        if (i == VIEW_TYPES.FIRST_VIEW && mHeader != null && mRecyclerView != null) {
            final RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForAdapterPosition(0);
            if (holder != null) {
                translateHeader(-holder.itemView.getTop());
            }
        }
        final RecyclerView.ViewHolder holder = onCreateViewHolderImpl(viewGroup, this, i);
        if (mOnClickEvent != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mOnClickEvent.onClick(v, holder.getAdapterPosition() - (mHeader == null ? 0 : 1));
                }
            });
        }
        return holder;
    }

    /**
     * @return true if there is a header on this adapter, false otherwise
     */
    public boolean hasHeader() {
        return mHeader != null;
    }

    public void setOnClickEvent(OnClickEvent onClickEvent) {
        mOnClickEvent = onClickEvent;
    }


    public boolean isShouldClipView() {
        return mShouldClipView;
    }

    /**
     * Defines if we will clip the layout or not. MUST BE CALLED BEFORE {@link #setParallaxHeader(android.view.View, android.support.v7.widget.RecyclerView)}
     *
     * @param shouldClickView
     */
    public void setShouldClipView(boolean shouldClickView) {
        mShouldClipView = shouldClickView;
    }

    public void setOnParallaxScroll(OnParallaxScroll parallaxScroll) {
        mParallaxScroll = parallaxScroll;
        mParallaxScroll.onParallaxScroll(0, 0, mHeader);
    }

    public ParallaxRecyclerAdapter(List<T> data) {
        mData = data;
    }

    public List<T> getData() {
        return mData;
    }

    public void setData(List<T> data) {
        mData = data;
        notifyDataSetChanged();
    }

    public void addItem(T item, int position) {
        mData.add(position, item);
        notifyItemInserted(position + (mHeader == null ? 0 : 1));
    }

    public void removeItem(T item) {
        int position = mData.indexOf(item);
        if (position < 0)
            return;
        mData.remove(item);
        notifyItemRemoved(position + (mHeader == null ? 0 : 1));
    }


    public int getItemCount() {
        return getItemCountImpl(this) + (mHeader == null ? 0 : 1);
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 1)
            return VIEW_TYPES.FIRST_VIEW;
        return position == 0 ? VIEW_TYPES.HEADER : VIEW_TYPES.NORMAL;
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View itemView) {
            super(itemView);
        }
    }

    static class CustomRelativeWrapper extends RelativeLayout {

        private int mOffset;
        private boolean mShouldClip;

        public CustomRelativeWrapper(Context context, boolean shouldClick) {
            super(context);
            mShouldClip = shouldClick;
        }

        @Override
        protected void dispatchDraw(Canvas canvas) {
            if (mShouldClip) {
                canvas.clipRect(new Rect(getLeft(), getTop(), getRight(), getBottom() + mOffset));
            }
            super.dispatchDraw(canvas);
        }

        public void setClipY(int offset) {
            mOffset = offset;
            invalidate();
        }
    }
}

這個類的意圖主要是解決這樣幾個問題:

1 onBindViewHolderImpl和onCreateViewHolderImpl將佈局定義和item賦值等類似的介面向外暴露,並且將點選事件和滑動事件介面也暴露出來使開發者的邏輯更直觀,易於維護,可以從以下的程式碼中可以看得到:

List<String> myContent = new ArrayList<String>(); // or another object list
ParallaxRecyclerAdapter<String> adapter = new ParallaxRecyclerAdapter<String>(content) {
            @Override
            public void onBindViewHolderImpl(RecyclerView.ViewHolder viewHolder, ParallaxRecyclerAdapter<String> adapter, int i) {
              // If you're using your custom handler (as you should of course) 
              // you need to cast viewHolder to it.
              ((MyCustomViewHolder) viewHolder).textView.setText(myContent.get(i)); // your bind holder routine.
            }

            @Override
            public RecyclerView.ViewHolder onCreateViewHolderImpl(ViewGroup viewGroup, final ParallaxRecyclerAdapter<String> adapter, int i) {
              // Here is where you inflate your row and pass it to the constructor of your ViewHolder
              return new MyCustomViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.myRow, viewGroup, false));
            }

            @Override
            public int getItemCountImpl(ParallaxRecyclerAdapter<String> adapter) {
              // return the content of your array
              return myContent.size();
            }
        };


2 新增頭部view的設計思路。首先從類開頭的列舉可以看出,作者將header作為一個viewtype區分出來了,並且在addItem、removeItem等等方法裡面都可以體現出來;然後就輪到CustomRelativeWrapper出場了,其名為wrapper也就是說,開發者要作為header的view傳入adapter然後被放進這個wrapper裡,其餘的就不用管了;接下來需要注意的就是該wrapper的內部重寫方法dtspatchDraw和setClipY,而上面截圖的縮排效果就是這兩句的共同作用,在滑動狀態下,不斷的裁剪畫布使其看上去像是被逐漸遮住一樣,而引起其不斷擷取的方法就是setClipY內不斷的invalidate,同樣在philm裡也可以找到擷取畫布的實現,而這就是截圖縮排效果的實現原理。

那麼通過該效果可以加深recyclerview使用方法的學習,同時可以看到其實該類裡面所有的方法以及實現都可以在ListView裡面能夠找到,也就是說到此為止ListView幾乎所有用法都可以在recyclerview實現了,只是很多細節處都需要自己寫罷了。那麼該庫的另外一個類HeaderLayoutManagerFixed是用來幹什麼的呢,它繼承自RecyclerView.LayoutManager,那麼到這裡我不得不重新審視LayoutManager,因為在此之前的使用基本上都是用的LinearLayoutManager或GridLayoutManager。

第一它的真正用途是什麼,第二它的真正使用場景是什麼,它內部有什麼特殊的地方。

相關推薦

RecyclerView基本用法

Recyclerview,從它出生開始到現在也已經有很長時間了,但是對它的使用也僅僅停留在一般的使用上,頂多也就在本文的前兩個介紹裡的普通用法的程度,於是抱著對其深入研究的態度,看了不少的文章和專案,在應用層待得越久越想知道這些東西的原理但也越來越不想知道,本著不為程式碼

python的基本用法字串常用函式

字串常用函式 # s='.abcd.'# new_s=s.strip('.')#預設去掉字串兩邊的空格和換行符,想去掉什麼括號中就寫什麼# print('s',s)# print('new_s',new_s)# t='.hhjDDDhjhj.'# print(t.rstrip('.'))# print(t.

RecyclerView基本用法

RecyclerView的最簡單用法 關於RView,首先是其基本的用法,要讓它執行起來需要處理哪些塊的東西,下面將詳細講解,這個例子簡化了grokkingandroid的例子A First Glance at Android’s RecyclerView。 1 引入v7

RecyclerView基本用法

這一次,我用一個極簡的程式碼段來揭開layoutmanager的神祕面紗,而這段程式碼也僅僅是拿出了基本骨架,而該骨架是在自定義layoutmanager必須要有的,卻只有這些骨架還遠遠不夠,看了很多

linux一些基本常識

sta oca 幫助文檔 spa onf mov 源碼安裝 依賴 undle acl:對本身權限的擴展 打包:zip 111.zip a.txt b.txt..... zip -r /etc/sysconfig/* (樣才能第歸所有內容0) 解寶:uzip

java中正則表達式基本用法

code ack acea print 表達式 劃線 跟著 以及 n) https://www.cnblogs.com/xhj123/p/6032683.html 正則表達式是一種可以用於模式匹配和替換的規範,一個正則表達式就是由普通的字符(例如字符a到z)以及特殊字符(元

Oracle數據庫基本操作 —— DQL相關內容說明及應用

保留 group gpo 個數字 轉義字符 ike 關鍵字 其他 單行函數   本文所使用的查詢表來源於oracle數據中scott用戶中的emp員工表和dept部門表。 一、基本語法   SQL語句的編寫順序: p.p1 { margin: 0.0px 0.0px 0.0

3PPS、PR、PDF編輯器Acrobat中的基基本操作

確認密碼 安全性 inf 選中 編輯器 png nbsp 打開 順序 本文介紹一些關於圖片、視頻、PDF的最常用操作: 圖像方面:旋轉、裁剪、拼接、水印(文字)、導出     軟件:Photoshop 視頻方面:剪切(拼接)、水印(文字、字幕)、導出    軟件:Premi

執行緒的基本操作

volatile關鍵字:   修飾變數,可以保證變數的可見性,但是無法保證對變數複合操作的原子性,如:        static volatile int = 0;        pub

線程的基本操作

string art 無法 rri 進行 但是 每次 pan 高並發 volatile關鍵字:   修飾變量,可以保證變量的可見性,但是無法保證對變量復合操作的原子性,如: static volatile int = 0; public stat

Linux基本命令

1、關機命令   Init 0 2、重啟命令 Init 6 3、管道符號  |    ---把前一個命令的輸出結果傳遞給後一個命令處理       以下命令支援放在管道符號後面:more 

JavaScript總結之DOM基本操作

  DOM(Document Object Model),即文件物件模型。當我們建立了一個網頁並把它載入到Web瀏覽器上,DOM就在幕後悄然而生,因為它把你所編寫的網頁文件轉換為一個文件物件。   我們可以這麼理解DOM,把DOM看做一棵節點樹,主要由元素節點、屬性節點、文字節點三種節點構成。例如下方的一行

安卓開發:RecyclerView的使用

其實我以前也有一個每天聊到深夜的人。   檢視文章: 安卓開發:RecyclerView的使用(一) 安卓開發:RecyclerView的使用(二) 安卓開發:RecyclerView的使用(三) RecyclerView的點選事件

ES6 Generator函式之基本用法2

Generator函式之基本用法(2) 上一篇文章中總結了Generator函式基本概念: yield表示式,與Iterator介面、for…of迴圈的關係,next方法,throw方法,return方法等內容。 這篇文章接著上一篇文章繼續總結Generator函式的基本用法 (1)

ES6 Generator函式之基本用法1

Generator函式之基本用法 (1)基本概念 Generator函式是ES6 提供的一種非同步程式設計解決方案,語法與傳統函式完全不同。 Generator函式與普通函式在寫法上的不同 1.function命令與函式名之間有一個星號(*)。 2.函式體內部使用yield語

Java——多執行緒基本使用 餓漢式和懶漢式的單例設計模式,多執行緒之間的通訊

這一則部落格主要寫的是單例設計模式,與實現多執行緒之間的通訊等等~ 1.單例設計模式:保證類在記憶體中只有一個物件 2.保證類在記憶體中只有一個物件            &

Spring的基本用法大全

Spring的簡介 基於依賴注入的核心機制、基於AOP的宣告式事務管理,與多種持久層技術的整合。使用Spring框架必須使用Spring Core Container只要由org.springframework.core、org.springframework.beans、o

django之ORM介紹與基本用法

  一、ORM介紹 1.什麼是ORM ORM 全拼Object-Relation Mapping. 中文意為 物件-關係對映. 在MVC/MVT設計模式中的Model模組中都包括ORM 2.ORM優勢 (1)只需要面

Tensorflow基本用法

介紹在官方文件中MNIST進階中用到的函式 1. tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None) shape:生成張量的形

複習之JavaScript基本語法——getElement[...]方法使用

JavaScript核心document.getElementById() document.getElementById()是根據id獲取標籤物件 //獲取標籤物件的值 //文字域和文字框都用value取值 document.getElementById("u