国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

深入解析Android中的RecyclerView組件

2019-12-12 06:07:20
字體:
來源:轉載
供稿:網友

前些日子,組里為了在目前的Android程序里實現基于ListView子項的動畫效果,希望將最新的RecyclerView引入到程序中,于是我便花了一些時間研究了一下RecyclerView的基本情況。本文算是對這些日子里了解的內容做一些匯總。

在網上關于RecyclerView的基本使用方式已經有了比較詳細介紹,而且其設計結構也類似于ListView,所以本文將不重點介紹如何使用,在文末的引用中都可以相關內容。這里主要是介紹RecyclerView的基本功能、設計理念,以及系統提供API的情況。

什么是RecyclerView
RecyclerView是在Android L(也就是后來的Lollipop)中新加入的一種ViewGroup。但因為它使以support-v7庫的形式加入到Android系統中,所以不僅僅是Lollipop版本以后的Android系統可以使用,只要系開發項目中引入這個庫就在任意API級別中使用。不過查閱其文檔,可以感受到RecyclerView是如此的強烈的“還在完善中”的感覺,因為對它的介紹只有短短的一句話:

A flexible view for providing a limited window into a large data set.

單看這句話,其實已經被經常使用的ListView和GridView基本也是滿足這句描述的。而且從單呈現效果來看RecyclerView和ListView、GridView并沒有大多的差別。

不過它的“flexible”并不單單指它可以在ListView和GridView之間隨意互相切換,更在于它可以創造出更多的復雜的可滾動的視圖,比如水平方向的ListView,或者是Web上很流行的瀑布流式布局(Masonry)。只是大部分布局系統都沒有提供,必須由開發者自己實現。

所以RecyclerView的“flexible”:什么都可以做,但什么都要自己做。

RecyclerView和ListView的主要區別
只要用Android提供的LieanerLayoutManager并配以VERTICAL模式,RecyclerView就可以完美達到ListView的基本效果。兩者的設計結構也都是數據(Dataset)與視圖(View)分離,然后通過適配器(Adapter)來連接的方式。

但RecyclerView相對ListView來說有以下幾大提升:

強制使用ViewHolder
在ListView性能優化方面,Android就推薦使用ViewHolder來減少findViewById()的使用以提高效率。不過對于ListView上的ViewHolder,Android只是建議而非強制使用。不過因為使用ViewHolder模式太有意義了,所以在RecyclerView中ViewHolder就變成了必須使用的模式,Adapter要求返回的也從普通的View變成了ViewHolder。不過如果實現時沒有自定義的一些View實際變量,ViewHolder也依然失去其意義。

沒有OnItemClickListener
ListView從它的父類AdapterView直接繼承了對子項目點擊的響應,開發者可以定義自己的OnItemClickListner來接受點擊事件。但這個設計也造成了一些問題,比如子項內部視圖如果設置了OnClickListener,那么子項目視圖本身并不會知道,從而可能會導致視圖點擊狀態沒有同步等問題。ReyclerView沒有提供簡便的響應子項目被點擊的監聽器,雖然它有一個OnItemTouchListener,但在這個接口方法中沒有任何關于那個子項目被點擊的信息,該接口只是幫助開發者截獲觸摸事件,對于如何處理,檢測被觸摸目標對象都留給了開發者去完成

視圖與布局分離
ListView做到了數據和視圖的分離,RecyclerView在視圖和布局之間再進一步分離,于是便有了LayoutManager。RecyclerView負責管理視圖的重復利用,然后將布局方式全權交給了LayoutManager,通過配置或者切換LayoutManager就可以獲得不同的布局效果。不像ListView被限制在垂直滾動布局。同時RecyclerView還提供了ItemDecoration,在已有的子視圖基礎上還可以添加額外的視圖。比如做一條分割線,在ListView就需要額外占用一個ViewType來提供視圖,現在則不需要在Adapter中加入這些與實際邏輯業務無關的輔助內容。

支持子項目層次的動畫效果
ListView也可以支持子項目層次的動畫效果,在Android Developers的DevBytes頻道里有很多關于這方面的介紹,不過在看過其實現之后就會發現其解決方案是多么的丑陋而冗長。很多時候都是在計算和分析子視圖的位置狀態。RecyclerView則帶來了非常簡潔的ItemAnimator接口。當Adapter中的數據發生“增刪改移”變化,通過調用Adapter相應的方法就可以激活動畫的產生。當然開發者還需要自己實現具體的ItemAnimator對象來完成所需的動畫效果,但是其清晰的結構和接口已比ListView有極大的進步。

RecyclerView的現狀
上邊提到RecyclerView的套件已經加入了豪華Support Library v7,而且是以單獨的庫放入,所以只要在Android Studio項目的Gradle編譯文件的dependencies下加入下邊的這句就可以開始使用RecyclerView了:

compile 'com.android.support:recyclerview-v7:21.0.+'
不過看過庫中提供的那些自帶對象實現,體現基本操作流程,就會發現RecyclerView能做的看起來很多,但是已經做到的實在太少。

RecyclerView.Adapter

RecyclerView提供了一個抽象Adapter類,然后就沒有了。沒有任何可以直接使用的子類,像ListAdapter那樣的ArrayAdapter、SimpleCursorAdapter現成的類都沒有。一切都留給開發者自己去實現定義。

仔細想想這也很挺正常,相信應該很少在實際產品中有使用ArrayAdapter的,因為大部分列表都不會是簡單的一行文字。對于CursorAdapter使用也往往會實現不同的繼承類來提供子視圖。再者RecyclerView的Adapter和ListAdapter在理念上還是一樣的,所以想實現個RecyclerViewCursorAdapter,直接從CursorAdapter取材便可。

RecyclerView的Adapter相對ListAdapter在接口上有幾處變化也值得注意。

首先其將getView()方法分拆成了createViewHolder()和bindViewHolder()兩個。不過這個沒有什么好緊張的,在CursorAdapter里就已經有見到過這個更加合理的設計。另外返回對象也從View變成了ViewHolder只需提一下。

最關鍵的注意點在于createViewHolder(ViewGroup parent, int viewType)第二參數雖然是整形,但是它并不是以往的當前子項的位置(position),而是調用getItemViewType()獲得的的子項的類別。似乎在創建ViewHolder時,RecyclerView故意在隱藏子項的詳細信息,希望開發者完全只依賴其類別來創建相應的View以及ViewHolder。

其次,RecyclerView的Adapter除了和ListAdapter一樣有notifyDataSetChanged()方法外,還有一堆會觸發動畫效果的通知數據改變的方法:

  • final void notifyItemChanged(int position);
  • final void notifyItemInserted(int position);
  • final void notifyItemMoved(int fromPosition, int toPosition);
  • final void notifyItemRangeChanged(int positionStart, int itemCount);
  • final void notifyItemRangeInserted(int positionStart, int itemCount);
  • final void notifyItemRangeRemoved(int positionStart, int itemCount);
  • final void notifyItemRemoved(int position);

調用這些方法就會激發ItemAnimator上對應的用于產生動畫的方法。

RecyclerView.ViewHolder

在ListView的年代里,其實已經在使用ViewHolder,只是那時的方法看起來比較討巧,要隱藏在View的tag里。現在RecyclerView強制使用ViewHolder,并且ViewHolder除了有對子視圖的引用,還有諸如ItemViewType和Position這些信息。

RecyclerView.LayoutManager

LayoutManager相對于ListView來說是一個新部分,通過繼承該類來實現自定義的布局方式,而不像ListView只有固定的布局方式。Support庫提供了兩個現成的子類:LinearLayoutManager和StaggeredGridLayoutManager。前者可以獲得和ListView一樣的布局,還可以是水平方向的;后者則提供了形如GridView的布局。所以應用程序中的基本日常所以都可以被滿足。

如果需要實現自定義的LayoutManager,就比較麻煩了,需要理解Recycle、Scrap、Dirty這些關于子項目視圖狀態的概念。本人還沒有嘗試過創建一個自定義的LayoutManager,但在文末的引用文檔中有部分介紹實現方式的。

RecyclerView.ItemAnimator

通過實現繼承實現ItemAnimator,然后創建對象設置到RecyclerView上就可以得到基于子項目的動畫效果。不過如何正確合理地創建一個ItemAnimator子類,卻沒有詳細的描述指南。

窺探庫中提供的唯一一個可用的子類DefaultItemAnimator,可以看出它的動畫效果是簡單的alpha漸變。同時也會發現其實現是如此的復雜,有很多對于動畫步驟的操作,還得注意動畫在中途被打斷的處理,在結束時也要重置視圖狀態以便重用。這也反過來說明ItemAnimator基本沒有提供任何關于如何實現和管理動畫的信息。另一方面因為DefaultItemAnimator的實現過于具體,因此它并不是合適作為自定義ItemAnimator的父類。

相信當RecyclerView越來越多的被應用到程序中時,更多關于這方面的合理設計會被提出來。目前在Github上也有不少參考了DefaultItemAnimator的實現方式,比如這個,還有這個。

關于ItemAnimator的使用,有幾點值得提醒的是:如果沒有提供ItemAnimator,RecyclerView默認會創建一個DefaultItemAnimator用于動畫,所以不需要顯示地設置DefaultItemAnimator對象到RecyclerView上;添加(add)和刪除(remove)是默認起效的,但是修改(change)的效果需要調用setSupportsChangeAnimations(boolean)來指定是否啟用,其默認是沒有修改的動畫。

總體而言,RecyclerView的功能非常強大,其結構設計也十分開放,這也造成它的上手使用相對比較困難。隨著越來越多的人開始嘗試使用這個部件,也會有越來越深刻的理解和設計實現。另外,閱讀RecyclerView的源碼可以幫助對其設計思想的了解,在以后設計其它的復用視圖時可以有更好的參照。

DEMO

說了這么多,我們來看一個RecyclerVIew實現圖片文字按鈕的混排的demo:

2016629161652878.jpg (250×404)

首先還是看我的工程結構吧。

2016629161719331.jpg (281×707)

首先還是貼出我的main_acitivy.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <!-- A RecyclerView with some commonly used attributes --><android.support.v7.widget.RecyclerView android:id="@+id/my_recycler_view" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="match_parent"/></LinearLayout>

其他幾個xml就不用貼了,很簡單的放了寫TextVIew,ImgeView之類。

然后我們就來看看代碼,首先是Bean里面的代碼。

package com.androidl.bob;public class Bean { public static final int Y_TYPE = 0; //view類型0 public static final int X_TYPE = 1; //view類型2 public static final int Z_TYPE = 2;//view 類型3 private int type; private String text; public Bean(int type, String text) {  super();  this.type = type;  this.text = text; } public int getType() {  return type; } public void setType(int type) {  this.type = type; } public String getText() {  return text; } public void setText(String text) {  this.text = text; }}

然后是Adapter里面的代碼:

package com.androidl.bob;import java.util.List;import com.example.androidl.R;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.RecyclerView.ViewHolder;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.Button;import android.widget.ImageButton;import android.widget.ImageView;import android.widget.TextView;import android.widget.Toast;/** * Date : 2014/7/15 *  * @author edsheng *  */public class RecycleAdapter extends RecyclerView.Adapter<ViewHolder> { private List<Bean> beans; public RecycleAdapter(List<Bean> beans) {  super();  this.beans = beans; } /**  * 內部TextHoler  *   * @author edsheng  *   */ public class TextHoler extends RecyclerView.ViewHolder {  public TextView textView;  public TextHoler(View textview) {   super(textview);   this.textView = (TextView) textview.findViewById(R.id.mytext);  } } /**  * iamgeHolder  *   * @author edsheng  *   */ public class ImageHoler extends RecyclerView.ViewHolder {  public ImageView Imageview;  public ImageHoler(View textview) {   super(textview);   this.Imageview = (ImageView) textview.findViewById(R.id.myiamge);  } } /**  * 按鈕的holder  *   * @author edsheng  *   */ public class ButtonHolder extends RecyclerView.ViewHolder {  public Button button;  public ButtonHolder(View textview) {   super(textview);   this.button = (Button) textview.findViewById(R.id.mybutton);  } } @Override public int getItemCount() {  // TODO Auto-generated method stub  return beans.size(); } /**  * 獲取消息的類型  */ @Override public int getItemViewType(int position) {  // TODO Auto-generated method stub  return beans.get(position).getType(); } /**  * 創建VIewHolder  */ @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewtype) {  // TODO Auto-generated method stub  View v = null;  ViewHolder holer = null;  switch (viewtype) {  case Bean.X_TYPE:   v = LayoutInflater.from(parent.getContext()).inflate(     R.layout.recylce_item_x, null);   holer = new TextHoler(v);   break;  case Bean.Y_TYPE:   v = LayoutInflater.from(parent.getContext()).inflate(     R.layout.recylce_item_y, null);   holer = new ButtonHolder(v);   break;  case Bean.Z_TYPE:   v = LayoutInflater.from(parent.getContext()).inflate(     R.layout.recylce_item_z, null);   holer = new ImageHoler(v);   break;  }  return holer; } /**  * 綁定viewholder  */ @Override public void onBindViewHolder(ViewHolder holder, int position) {  // TODO Auto-generated method stub  switch (getItemViewType(position)) {  case Bean.X_TYPE:   TextHoler textholer = (TextHoler) holder;   textholer.textView.setText(beans.get(position).getText());   break;  case Bean.Y_TYPE:   ButtonHolder buttonHolder = (ButtonHolder) holder;   buttonHolder.button.setText(beans.get(position).getText());   break;  case Bean.Z_TYPE:   ImageHoler imageHoler = (ImageHoler) holder;   // imageHoler.Imageview.setImageResource(android.R.drawable.checkbox_on_background);   break;  } }}

最后是activity的代碼。

package com.androidl.bob;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.os.Bundle;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import com.example.androidl.R;public class Mainactivity extends Activity {  @Override protected void onCreate(Bundle savedInstanceState) {  // TODO Auto-generated method stub  super.onCreate(savedInstanceState);  setContentView(R.layout.main_activity);   RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);// // improve performance if you know that changes in content// // do not change the size of the RecyclerView//  mRecyclerView.setHasFixedSize(true);   //創建布局管理器  LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);  mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);  mRecyclerView.setLayoutManager(mLayoutManager);  //初始化數據  List<Bean> myDataset = new ArrayList<Bean>();  myDataset.add(new Bean(Bean.Z_TYPE, "圖片"));  myDataset.add(new Bean(Bean.X_TYPE, "文字"));  myDataset.add(new Bean(Bean.Y_TYPE, "按鈕"));  myDataset.add(new Bean(Bean.Z_TYPE, "圖片"));  myDataset.add(new Bean(Bean.X_TYPE, "shit"));  myDataset.add(new Bean(Bean.X_TYPE, "我擦"));  myDataset.add(new Bean(Bean.Z_TYPE, "圖片"));  myDataset.add(new Bean(Bean.Y_TYPE, "按鈕"));  myDataset.add(new Bean(Bean.Y_TYPE, "按鈕"));  myDataset.add(new Bean(Bean.X_TYPE, "文字"));  //創建Adapter  RecycleAdapter mAdapter = new RecycleAdapter(myDataset);  mRecyclerView.setAdapter(mAdapter); }} 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 麦盖提县| 全州县| 松江区| 阳江市| 永清县| 龙岩市| 福泉市| 大港区| 平凉市| 绵竹市| 夏邑县| 广德县| 久治县| 奉化市| 边坝县| 蓬安县| 玉龙| 洞口县| 兴隆县| 丹巴县| 巧家县| 白水县| 黄山市| 无锡市| 介休市| 嘉峪关市| 炎陵县| 德令哈市| 始兴县| 色达县| 拉萨市| 弥勒县| 曲麻莱县| 余姚市| 凌海市| 河津市| 菏泽市| 焉耆| 泽库县| 茶陵县| 塘沽区|