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
3P(PS、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