ListView、AdapterView、RecyclerView全面解析
原文鏈接:http://blog.csdn.net/carson_ho/article/details/51472640
AdapterView簡介
AdapterView本身是一個(gè)抽象類,AdapterView及其子類的繼承關(guān)系如下圖:
特征:
- AdapterView繼承自ViewGroup,本質(zhì)是個(gè)容器
- AdapterView可以包含多個(gè)“列表項(xiàng)”,并將這多個(gè)列表項(xiàng)以合適的形式展示
- AdapterView顯示的列表項(xiàng)內(nèi)容由Adapter提供
- 它派生的子類在用法上也基本相似,只是在顯示上有一定區(qū)別,因此把他們也歸為一類。
- 由AdapterView直接派生的三個(gè)類:
AbsListView、AbsSpinner、AdapterViewAnimator
都是抽象類,所以我們用的最多的也就是圖中第四行及以下的子類。
ListView簡介
1. 什么是ListView
即列表視圖,是Android開發(fā)中一種常用的視圖組件
2. ListView的作用
將所要展示的數(shù)據(jù)集合起來
以列表的形式展示到用戶界面上
3. 關(guān)于Adapter
- 定義:適配器
- 作用:作為View和數(shù)據(jù)之間的橋梁
由于ListView和所要展現(xiàn)的數(shù)據(jù)是分開的,不直接接觸,所以,Adapter的作用是把數(shù)據(jù)映射到ListView上,作為中介的作用,如下圖
3. ListView的工作原理
ListView、GridView、Spinner等AdapterView都只是容器,主要用于裝載要顯示的數(shù)據(jù)和顯示數(shù)據(jù),而Apdater負(fù)責(zé)提供容器的內(nèi)容
即AdapterView負(fù)責(zé)采用合適的方式顯示Adapter提供的內(nèi)容。
在運(yùn)行時(shí),當(dāng)需要顯示數(shù)據(jù)時(shí),ListView會(huì)針對數(shù)據(jù)項(xiàng)向Adapter取出數(shù)據(jù),從而加載到界面上。
試想下這么一個(gè)場景:如果把所有數(shù)據(jù)集合的信息都加載到View上,如果ListView要為每個(gè)數(shù)據(jù)都創(chuàng)建一個(gè)視圖,那么會(huì)占用非常多的內(nèi)存
從上面可知,ListView不會(huì)為每一個(gè)數(shù)據(jù)創(chuàng)建一個(gè)視圖,為了節(jié)省空間和時(shí)間,Android采用了一個(gè)叫Recycler的組件。
工作原理:當(dāng)屏幕需要顯示x個(gè)item時(shí),那么ListView只會(huì)創(chuàng)建x+1個(gè)視圖,當(dāng)?shù)谝粋€(gè)item離開屏幕時(shí),此item的view就會(huì)被拿來重用(用于顯示下一個(gè)item(即第x+1個(gè))的內(nèi)容)。
工作原理實(shí)例
假如屏幕只能顯示7個(gè)item,那么ListView只會(huì)創(chuàng)建(7+1)個(gè)item的視圖。當(dāng)?shù)?個(gè)item離開屏幕時(shí),此item的view就會(huì)被拿來重用(用于顯示第8個(gè)item的內(nèi)容)。原理如下圖顯示
ListView的使用
1. 生成方式
生成列表視圖(ListView)的方式主要有兩種:
- 直接用ListView進(jìn)行創(chuàng)建
- 讓Activity繼承ListActivity
2. xml文件配置信息
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFE1FF" android:orientation="vertical" > <ListView android:id="@+id/listView1" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>AbsListView的常用屬性和相關(guān)方法
| android:choiceMode | 列表的選擇行為,默認(rèn):none沒有選擇行為 | 選擇方式: none:不顯示任何選中項(xiàng) singleChoice:允許單選multipleChoice:允許多選multipleChoiceModal:允許多選 (把Activity里面adapter的第二個(gè)參數(shù)改成支持選擇的布局) |
| android:drawSelectorOnTop | 如果該屬性設(shè)置為true,選中的列表項(xiàng)將會(huì)顯示在上面 | |
| android:listSelector | 為點(diǎn)擊到的Item設(shè)置圖片 | 如果該屬性設(shè)置為true,選中的列表項(xiàng)將會(huì)顯示在上面 |
| android:fastScrollEnabled | 設(shè)置是否允許快速滾動(dòng) | 如果該屬性設(shè)置為true,將會(huì)顯示滾動(dòng)圖標(biāo),并允許用戶拖動(dòng)該滾動(dòng)圖標(biāo)進(jìn)行快速滾動(dòng)。 |
| android:listSelector | 指定被選中的列表項(xiàng)上繪制的Drawable | |
| android:scrollingCache | 滾動(dòng)時(shí)是否使用緩存 | 如果設(shè)置為true,則在滾動(dòng)時(shí)將會(huì)使用緩存 |
| android:stackFromBottom | 設(shè)置是否從底端開始排列列表項(xiàng) | |
| android:transcriptMode | 指定列表添加新的選項(xiàng)的時(shí)候,是否自動(dòng)滑動(dòng)到底部,顯示新的選項(xiàng)。 | disabled:取消transcriptMode模式;默認(rèn)的normal:當(dāng)接受到數(shù)據(jù)集合改變的通知,并且僅僅當(dāng)最后一個(gè)選項(xiàng)已經(jīng)顯示在屏幕的時(shí)候,自動(dòng)滑動(dòng)到底部。 alwaysScroll:無論當(dāng)前列表顯示什么選項(xiàng),列表將會(huì)自動(dòng)滑動(dòng)到底部顯示最新的選項(xiàng)。 |
Listview提供的XML屬性:
| android:divider | 設(shè)置List列表項(xiàng)的分隔條(可用顏色分割,也可用圖片(Drawable)分割 | 不設(shè)置列表之間的分割線,可設(shè)置屬性為@null |
| android:dividerHeight | 用于設(shè)置分隔條的高度 | |
| android:background屬性 | 設(shè)置列表的背景 | |
| android:entries | 指定一個(gè)數(shù)組資源,Android將根據(jù)該數(shù)組資源來生成ListView | |
| android:footerDividerEnabled | 如果設(shè)置成false,則不在footer View之前繪制分隔條 | |
| andorid:headerDividerEnabled | 如果設(shè)置成false,則不再header View之前繪制分隔條 |
Adapter介紹
Adapter本身是一個(gè)接口,Adapter接口及其子類的繼承關(guān)系如下圖:
Adapter接口派生了ListAdapter和SpinnerAdapter兩個(gè)子接口
其中ListAdapter為AbsAdapter提供列表項(xiàng),而SpinnerAdapter為AbsSpinner提供列表項(xiàng)
ArrayAdapter、SimpleAdapter、SimpleCursorAdapter、BaseAdapter都是常用的實(shí)現(xiàn)適配器的類
ArrayAdapter:簡單、易用的Adapter,用于將數(shù)組綁定為列表項(xiàng)的數(shù)據(jù)源,支持泛型操作
SimpleAdapter:功能強(qiáng)大的Adapter,用于將XML中控件綁定為列表項(xiàng)的數(shù)據(jù)源
SimpleCursorAdapter:與SimpleAdapter類似,用于綁定游標(biāo)(直接從數(shù)據(jù)數(shù)取出數(shù)據(jù))作為列表項(xiàng)的數(shù)據(jù)源
BaseAdapter:可自定義ListView,通用用于被擴(kuò)展。擴(kuò)展BaseAdapter可以對各個(gè)列表項(xiàng)進(jìn)行最大程度的定制。
常用適配器介紹
1. ArrayAdapter
定義:簡單、易用的Adapter,用于將數(shù)組綁定為列表項(xiàng)的數(shù)據(jù)源,支持泛型操作
步驟
1、 在xml文件布局上實(shí)現(xiàn)ListView
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.carson_ho.adapte_demo.MainActivity"><ListView android:id="@+id/list_item"android:layout_width="match_parent"android:layout_height="match_parent"android:divider="#f00"android:dividerHeight="1sp"android:headerDividersEnabled="false"></ListView> </RelativeLayout>2、 在MainActivity上定義一個(gè)鏈表,將所要展示的數(shù)據(jù)以存放在里面
3、 構(gòu)造ArrayAdapter對象,設(shè)置適配器
4、 將LsitView綁定到ArrayAdapter上
創(chuàng)建ArrayAdapter對象要指定三個(gè)參數(shù):
- context:代表方位Android應(yīng)用的接口
- textViewRseourceld:資源ID,代表一個(gè)TextView
- 數(shù)組:列表項(xiàng)展示的數(shù)據(jù)
5、 在xml文件布局添加資源文件TextView,該TextView組件將作列表項(xiàng)的組件
<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content">android:textSize="24sp" </TextView>最終效果圖
缺點(diǎn):ArrayAdapter較為簡單,易用,但每個(gè)列表項(xiàng)只能是TextView,功能實(shí)現(xiàn)的局限性非常大。
2. SimpleAdapter
定義:功能強(qiáng)大的Adapter,用于將XML中控件綁定作為列表項(xiàng)的數(shù)據(jù)源
特點(diǎn):可對每個(gè)列表項(xiàng)進(jìn)行定制(自定義布局),能滿足大多數(shù)開發(fā)的需求場景,靈活性較大
步驟
1、 在xml文件布局上實(shí)現(xiàn)ListView
2、 根據(jù)實(shí)際需求定制列表項(xiàng):實(shí)現(xiàn)ListView每行的xml布局(即item布局)
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><TextView android:id="@+id/name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:paddingLeft="14dp"android:textSize="17sp"/><TextView android:id="@+id/address"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/name"android:textSize="17sp"/><TextView android:id="@+id/lowerest_wholesale"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_toRightOf="@id/address"android:textSize="17sp"/><TextView android:id="@+id/price"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/address"android:textSize="17sp"/><ImageView android:id="@+id/picture"android:layout_width="115dp"android:layout_height="86dp"android:layout_alignParentRight="true"/> </RelativeLayout>3、 定義一個(gè)HashMap構(gòu)成的列表以鍵值對的方式存放數(shù)據(jù)
4、 構(gòu)造SimpleAdapter對象,設(shè)置適配器
5、 將LsitView綁定到SimpleAdapter上
結(jié)果顯示
BaseAdapter
定義:可自定義ListView,通用用于被擴(kuò)展。擴(kuò)展BaseAdapter可以對各個(gè)列表項(xiàng)進(jìn)行最大程度的定制
使用步驟:
- 定義主xml布局
- 根據(jù)需要定義ListView每行所實(shí)現(xiàn)的xml布局
- 定義一個(gè)Adapter類繼承BaseAdapter,重寫里面的方法。
- 定義一個(gè)HashMap構(gòu)成的列表,將數(shù)據(jù)以鍵值對的方式存放在里面。
- 構(gòu)造Adapter對象,設(shè)置適配器。
- 將LsitView綁定到Adapter上。
先定義一個(gè)Adapter類繼承BaseAdapter,并重寫里面的方法
使用BaseAdapter必須寫一個(gè)類繼承它,同時(shí)BaseAdapter是一個(gè)抽象類,繼承它必須實(shí)現(xiàn)它的方法。
class MyAdapter extends BaseAdapter {private LayoutInflater mInflater;//得到一個(gè)LayoutInfalter對象用來導(dǎo)入布局//構(gòu)造函數(shù)public MyAdapter(Context context,ArrayList<HashMap<String, Object>> listItem) {this.mInflater = LayoutInflater.from(context);this.listItem = listItem;}//聲明構(gòu)造函數(shù)@Overridepublic int getCount() {return listItem.size();}//這個(gè)方法返回了在適配器中所代表的數(shù)據(jù)集合的條目數(shù)@Overridepublic Object getItem(int position) {return listItem.get(position);}//這個(gè)方法返回了數(shù)據(jù)集合中與指定索引position對應(yīng)的數(shù)據(jù)項(xiàng)@Overridepublic long getItemId(int position) {return position;}//這個(gè)方法返回了在列表中與指定索引對應(yīng)的行id@Overridepublic View getView(int position, View convertView, ViewGroup parent) {return null;}//這個(gè)方法返回了指定索引對應(yīng)的數(shù)據(jù)項(xiàng)的視圖,還沒寫完 }這里主要講一下BaseAdapter里必須要重寫的4個(gè)方法
- BaseAdapter的靈活性就在于它要重寫很多方法,其中最重要的即為getView()方法。
- 我們結(jié)合上述重寫的4個(gè)方法了解下系統(tǒng)繪制ListView的原理:
當(dāng)系統(tǒng)開始繪制ListView的時(shí)候,首先調(diào)用getCount()方法。得到它的返回值,即ListView的長度。
系統(tǒng)調(diào)用getView()方法,根據(jù)這個(gè)長度逐一繪制ListView的每一行。(如果讓getCount()返回1,那么只顯示一行)。
getItem()和getItemId()則在需要處理和取得Adapter中的數(shù)據(jù)時(shí)調(diào)用。
那么getView()如何使用呢?如果有10000行數(shù)據(jù),就繪制10000次?這肯定會(huì)極大的消耗資源,導(dǎo)致ListView滑動(dòng)非常的慢,那應(yīng)該怎么做呢?可以使用BaseAdapter進(jìn)行優(yōu)化ListView的顯示。
以下將使用4種重寫方法來說明getView()的使用
重寫getView()的第一種方法
@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View item = mInflater.inflate(R.layout.item,null);ImageView img = (ImageView)item.findViewById(R.id.ItemImage);TextView title = (TextView)item.findViewById(R.id.ItemTitle);TextView test = (TextView)item.findViewById(R.id.ItemText);Button btn = (Button) item.findViewById(R.id.ItemBottom);img.setImageResource((Integer) listItem.get(position).get("ItemImage"));title.setText((String) listItem.get(position).get("ItemTitle"));test.setText((String) listItem.get(position).get("ItemText"));return item;}//這個(gè)方法返回了指定索引對應(yīng)的數(shù)據(jù)項(xiàng)的視圖這種方法每次getView()都要findViewById和重新繪制一個(gè)View,當(dāng)列表項(xiàng)數(shù)據(jù)量很大的時(shí)候會(huì)嚴(yán)重影響性能,造成下拉很慢,所以數(shù)據(jù)量大的時(shí)候不推薦用這種方式。
重寫getView()的第二種方法:使用convertView作為緩存進(jìn)行優(yōu)化
getView()返回值是一個(gè)View,把它作為輸入?yún)?shù)并放到getView()輸入?yún)?shù)里,形成反饋。這樣就形成了Adapter的itemView重用機(jī)制,減少了重繪View的次數(shù)。
這種方法和第一種相比減少了重繪View的次數(shù),但是還是每一次都要findViewById
重寫getView()第三種方法
通過convertView+ViewHolder來實(shí)現(xiàn)緩存進(jìn)而進(jìn)行優(yōu)化
convertView緩存了View,ViewHolder相當(dāng)于更加具體的緩存:View里的組件,即把View和View的組件一并進(jìn)行緩存,那么重用View的時(shí)候就不用再重繪View和View的組件(findViewById)
這種方法就既減少了重繪View,又減少了findViewById的次數(shù),所以這種方法是最能節(jié)省資源的,所以非常推薦大家使用通過convertView+ViewHolder來重寫getView()
利用convertView+ViewHolder來重寫getView()的實(shí)現(xiàn)BaseAdapter的具體實(shí)現(xiàn)代碼:
定義主xml的布局 activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#FFFFFF"android:orientation="vertical" ><ListView android:id="@+id/listView1"android:layout_width="match_parent"android:layout_height="match_parent" /> </LinearLayout>根據(jù)需要,定義ListView每行所實(shí)現(xiàn)的xml布局(item布局) item.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"><ImageView android:layout_alignParentRight="true"android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/ItemImage"/><Button android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按鈕"android:id="@+id/ItemBottom"android:focusable="false"android:layout_toLeftOf="@+id/ItemImage" /><TextView android:id="@+id/ItemTitle"android:layout_height="wrap_content"android:layout_width="fill_parent"android:textSize="20sp"/><TextView android:id="@+id/ItemText"android:layout_height="wrap_content"android:layout_width="fill_parent"android:layout_below="@+id/ItemTitle"/> </RelativeLayout>定義一個(gè)Adapter類繼承BaseAdapter,重寫里面的方法。 (利用convertView+ViewHolder來重寫getView())
MyAdapter.java
package scut.learnlistview;import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView;import java.util.ArrayList; import java.util.HashMap;/*** Created by yany on 2016/4/11.*/ class MyAdapter extends BaseAdapter {private LayoutInflater mInflater;//得到一個(gè)LayoutInfalter對象用來導(dǎo)入布局 ArrayList<HashMap<String, Object>> listItem;public MyAdapter(Context context,ArrayList<HashMap<String, Object>> listItem) {this.mInflater = LayoutInflater.from(context);this.listItem = listItem;}//聲明構(gòu)造函數(shù)@Overridepublic int getCount() {return listItem.size();}//這個(gè)方法返回了在適配器中所代表的數(shù)據(jù)集合的條目數(shù)@Overridepublic Object getItem(int position) {return listItem.get(position);}//這個(gè)方法返回了數(shù)據(jù)集合中與指定索引position對應(yīng)的數(shù)據(jù)項(xiàng)@Overridepublic long getItemId(int position) {return position;}//這個(gè)方法返回了在列表中與指定索引對應(yīng)的行id//利用convertView+ViewHolder來重寫getView()static class ViewHolder{public ImageView img;public TextView title;public TextView text;public Button btn;}//聲明一個(gè)外部靜態(tài)類@Overridepublic View getView(final int position, View convertView, final ViewGroup parent) {ViewHolder holder ;if(convertView == null){holder = new ViewHolder();convertView = mInflater.inflate(R.layout.item, null);holder.img = (ImageView)convertView.findViewById(R.id.ItemImage);holder.title = (TextView)convertView.findViewById(R.id.ItemTitle);holder.text = (TextView)convertView.findViewById(R.id.ItemText);holder.btn = (Button) convertView.findViewById(R.id.ItemBottom);convertView.setTag(holder);}else {holder = (ViewHolder)convertView.getTag();}holder.img.setImageResource((Integer) listItem.get(position).get("ItemImage"));holder.title.setText((String) listItem.get(position).get("ItemTitle"));holder.text.setText((String) listItem.get(position).get("ItemText"));holder.btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {System.out.println("你點(diǎn)擊了選項(xiàng)"+position);//bottom會(huì)覆蓋item的焦點(diǎn),所以要在xml里面配置android:focusable="false"}});return convertView;}//這個(gè)方法返回了指定索引對應(yīng)的數(shù)據(jù)項(xiàng)的視圖 }4、在MainActivity里:
- 定義一個(gè)HashMap構(gòu)成的列表,將數(shù)據(jù)以鍵值對的方式存放在里面。
- 構(gòu)造Adapter對象,設(shè)置適配器。
- 將LsitView綁定到Adapter上。
MainActivity.java
package scut.learnlistview;import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.SimpleAdapter;import java.util.ArrayList; import java.util.HashMap; import java.util.List;public class MainActivity extends AppCompatActivity {private ListView lv;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);lv = (ListView) findViewById(R.id.listView1);/*定義一個(gè)以HashMap為內(nèi)容的動(dòng)態(tài)數(shù)組*/ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();/*在數(shù)組中存放數(shù)據(jù)*/for (int i = 0; i < 100; i++) {HashMap<String, Object> map = new HashMap<String, Object>();map.put("ItemImage", R.mipmap.ic_launcher);//加入圖片map.put("ItemTitle", "第" + i + "行");map.put("ItemText", "這是第" + i + "行");listItem.add(map);}MyAdapter adapter = new MyAdapter(this, listItem);lv.setAdapter(adapter);//為ListView綁定適配器lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {System.out.println("你點(diǎn)擊了第" + arg2 + "行");//設(shè)置系統(tǒng)輸出點(diǎn)擊的行}});} }運(yùn)行結(jié)果
點(diǎn)擊輸出結(jié)果:
RecyclerView介紹
1. 定義
RecyclerView是Google推出用來代替ListView組件的,是一個(gè)強(qiáng)大的滑動(dòng)組件。
RecyclerView強(qiáng)制使用了ViewHolder,直接把viewholder的實(shí)現(xiàn)封裝起來,用戶只要實(shí)現(xiàn)自己的viewholder就可以了,該組件會(huì)自動(dòng)幫你回收復(fù)用每一個(gè)item。
2. 工作原理
當(dāng)屏幕需要顯示x個(gè)item時(shí),那么ListView只會(huì)創(chuàng)建x+1個(gè)視圖,當(dāng)?shù)谝粋€(gè)item離開屏幕時(shí),此item的view就會(huì)被拿來重用(用于顯示下一個(gè)item(即第x+1個(gè))的內(nèi)容)。
3. 工作原理實(shí)例
假如屏幕只能顯示7個(gè)item,那么ListView只會(huì)創(chuàng)建(7+1)個(gè)item的視圖。當(dāng)?shù)?個(gè)item離開屏幕時(shí),此item的view就會(huì)被拿來重用(用于顯示第8個(gè)item的內(nèi)容)。原理如下圖顯示
4. RecyclerView的重要概念介紹
RecyclerView.Adapter
和ListView一樣,RecyclerView一樣需要適配器,而且這個(gè)適配器強(qiáng)制要求了我們必須要用Viewholder,讓性能得到優(yōu)化,而且getView方法不需自己寫,我們只需要寫好Viewholder,View的復(fù)用已經(jīng)封裝好了。
LayoutManager
管理布局,設(shè)置為LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager可以輕易地實(shí)現(xiàn)ListView,GridView以及流式布局的列表效果。它還可以管理滾動(dòng)和循環(huán)利用。
ItemAnimator
這個(gè)類可以實(shí)現(xiàn)增刪動(dòng)畫,而且不想設(shè)置的話它的默認(rèn)效果已經(jīng)很好了。
5. 優(yōu)缺點(diǎn)
優(yōu)點(diǎn) : 有了ListView、GridView為什么還需要RecyclerView這樣的控件呢?優(yōu)點(diǎn)在于
- item復(fù)用性高
把ViewHolder的實(shí)現(xiàn)封裝起來,規(guī)范了ViewHolder,把item的view寫入ViewHolder中,可以通過復(fù)用ViewHolder來實(shí)現(xiàn)view的復(fù)用 - 靈活、可定制化高、可拓展性高
整體上看RecyclerView架構(gòu),提供了一種插拔式的體驗(yàn):高度的解耦,異常的靈活: - 控制其顯示的方式-通過布局管理器LayoutManager
- 控制Item間的間隔(可繪制)-通過ItemDecoration
- 控制Item增刪的動(dòng)畫- 通過ItemAnimator
問:相比較于ListView,RecyclerView基本需要上面一系列步驟進(jìn)行設(shè)置,而ListView可能只需要去設(shè)置一個(gè)adapter就能正常使用。那么為什么會(huì)添加這么多的步驟呢?
答:從名字上看RecyclerView,即回收循環(huán)視圖,也就是說RecyclerView只管回收與復(fù)用View,其他的你可以自己去設(shè)置,可以看出其高度的解耦,給予你充分的定制自由
缺點(diǎn): RecyclerView實(shí)現(xiàn)控制點(diǎn)擊、長按事件較為麻煩,需要自己寫
使用實(shí)例
使用RecyclerView的步驟:
- 定義主xml布局
- 根據(jù)需要定義RecyclerView每行所實(shí)現(xiàn)的xml布局
- 定義一個(gè)Adapter類繼承RecyclerView.Adapter,重寫里面的方法。
- 定義一個(gè)HashMap構(gòu)成的列表,將數(shù)據(jù)以鍵值對的方式存放在里面。
- 構(gòu)造Adapter對象,設(shè)置適配器。
- 將RecyclerView綁定到Adapter上。
Demo的源碼下載:https://github.com/Carson-Ho/RecyclerView
個(gè)人推薦先fork下來再對著下面的分析看,效果會(huì)更好哦!
步驟1. 定義主xml布局
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="${relativePackage}.${activityClass}" ><android.support.v7.widget.RecyclerView android:id="@+id/my_recycler_view"android:layout_width="match_parent"android:layout_height="match_parent"android:scrollbars="horizontal"/><!--設(shè)置一個(gè)RecyclerView--></RelativeLayout>在AndroidStudio1.5使用support-v7包:
- 右鍵文件目錄的app目錄進(jìn)入Moudle Setting
- 在Dependencies里面加入com.android.support:recyclerview-v7:23.1.1包。
步驟2. 根據(jù)需要定義RecyclerView每行所實(shí)現(xiàn)的xml布局(item布局)
list_cell.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent"android:layout_height="match_parent"><ImageView android:layout_alignParentRight="true"android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/ItemImage"/><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="New Text"android:id="@+id/Itemtitle" /><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="New Text"android:id="@+id/Itemtext"android:layout_below="@+id/Itemtitle"/> </RelativeLayout> </LinearLayout>步驟3. 定義一個(gè)Adapter類繼承 RecyclerView.Adapter,重寫里面的方法。
MyAdapter.Java
package scut.receiverview;import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView;import java.util.ArrayList; import java.util.HashMap;/*** Created by yany on 2016/4/11.*/ public class MyAdapter extends RecyclerView.Adapter {private LayoutInflater inflater;private ArrayList<HashMap<String, Object>> listItem;private MyItemClickListener myItemClickListener;public MyAdapter(Context context, ArrayList<HashMap<String, Object>> listItem) {inflater = LayoutInflater.from(context);this.listItem = listItem;}//構(gòu)造函數(shù),傳入數(shù)據(jù)//定義Viewholderclass Viewholder extends RecyclerView.ViewHolder {private TextView Title, Text;private ImageView ima;public Viewholder(View root) {super(root);Title = (TextView) root.findViewById(R.id.Itemtitle);Text = (TextView) root.findViewById(R.id.Itemtext);ima = (ImageView) root.findViewById(R.id.ItemImage);root.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (myItemClickListener != null)myItemClickListener .onItemClick(v,getPosition());}}//監(jiān)聽到點(diǎn)擊就回調(diào)MainActivity的onItemClick函數(shù));}public TextView getTitle() {return Title;}public TextView getText() {return Text;}public ImageView getIma() {return ima;}}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {return new Viewholder(inflater.inflate(R.layout.list_cell, null));}//在這里把ViewHolder綁定Item的布局@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {Viewholder vh = (Viewholder) holder;vh.Title.setText((String) listItem.get(position).get("ItemTitle"));vh.Text.setText((String) listItem.get(position).get("ItemText"));vh.ima.setImageResource((Integer) listItem.get(position).get("ItemImage"));}//在這里綁定數(shù)據(jù)到ViewHolder里面@Overridepublic int getItemCount() {return listItem.size();}//返回Item數(shù)目public void setOnItemClickListener(MyItemClickListener listener){myItemClickListener = listener;}//綁定MainActivity傳進(jìn)來的點(diǎn)擊監(jiān)聽器 }實(shí)現(xiàn)點(diǎn)擊事件:
MyItemClickListener.java接口:用來實(shí)現(xiàn)點(diǎn)擊事件
package scut.receiverview;import android.view.View;public interface MyItemClickListener {public void onItemClick(View view,int postion); }步驟4:在MainActicity.java里:
- 定義一個(gè)HashMap構(gòu)成的列表,將數(shù)據(jù)以鍵值對的方式存放在里面。
- 構(gòu)造Adapter對象,設(shè)置適配器
- 將RecyclerView綁定到Adapter上
MainActicity.java
package scut.receiverview;import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.Toast;import java.util.ArrayList; import java.util.HashMap;public class MainActivity extends AppCompatActivity implements MyItemClickListener {private RecyclerView Rv;private ArrayList<HashMap<String,Object>> listItem;private MyAdapter myAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initData();initView();}public void initData(){listItem = new ArrayList<HashMap<String, Object>>();/*在數(shù)組中存放數(shù)據(jù)*/for (int i = 0; i < 100; i++) {HashMap<String, Object> map = new HashMap<String, Object>();map.put("ItemTitle", "第" + i + "行");map.put("ItemText", "這是第" + i + "行");map.put("ItemImage",R.mipmap.ic_launcher);listItem.add(map);}}public void initView(){//為ListView綁定適配器myAdapter = new MyAdapter(this,listItem);myAdapter.setOnItemClickListener(this);Rv.setAdapter(myAdapter); Rv = (RecyclerView) findViewById(R.id.my_recycler_view);//使用線性布局LinearLayoutManager layoutManager = new LinearLayoutManager(this);Rv.setLayoutManager(layoutManager);Rv.setHasFixedSize(true);Rv.addItemDecoration(new DividerItemDecoration(this, layoutManager.getOrientation()));//用類設(shè)置分割線 //Rv.addItemDecoration(new DividerItemDecoration(this, R.drawable.list_divider)); //用已有圖片設(shè)置分割線}@Overridepublic void onItemClick(View view, int postion) {//點(diǎn)擊事件的回調(diào)函數(shù)System.out.println("點(diǎn)擊了第"+postion+"行");Toast.makeText(this, (String)listItem.get(postion).get("ItemText"), Toast.LENGTH_SHORT).show();}}如果自己畫了分割線就可以直接添上去,不需要寫這個(gè)類
DividerItemDecoration.java:
package scut.receiverview;import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.TypedValue; import android.view.View;public class DividerItemDecoration extends RecyclerView.ItemDecoration {/** RecyclerView的布局方向,默認(rèn)先賦值* 為縱向布局* RecyclerView 布局可橫向,也可縱向* 橫向和縱向?qū)?yīng)的分割想畫法不一樣* */private int mOrientation = LinearLayoutManager.VERTICAL ;/*** item之間分割線的size,默認(rèn)為1*/private int mItemSize = 1 ;/*** 繪制item分割線的畫筆,和設(shè)置其屬性* 來繪制個(gè)性分割線*/private Paint mPaint ;/*** 構(gòu)造方法傳入布局方向,不可不傳* @param context* @param orientation*/public DividerItemDecoration(Context context,int orientation) {this.mOrientation = orientation;if(orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL){throw new IllegalArgumentException("請傳入正確的參數(shù)") ;}mItemSize = (int) TypedValue.applyDimension(mItemSize, TypedValue.COMPLEX_UNIT_DIP,context.getResources().getDisplayMetrics());mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) ;mPaint.setColor(Color.BLUE);/*設(shè)置填充*/mPaint.setStyle(Paint.Style.FILL);}@Overridepublic void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {if(mOrientation == LinearLayoutManager.VERTICAL){drawVertical(c,parent) ;}else {drawHorizontal(c,parent) ;}}/*** 繪制縱向 item 分割線* @param canvas* @param parent*/private void drawVertical(Canvas canvas,RecyclerView parent){final int left = parent.getPaddingLeft() ;final int right = parent.getMeasuredWidth() - parent.getPaddingRight() ;final int childSize = parent.getChildCount() ;for(int i = 0 ; i < childSize ; i ++){final View child = parent.getChildAt( i ) ;RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();final int top = child.getBottom() + layoutParams.bottomMargin ;final int bottom = top + mItemSize ;canvas.drawRect(left,top,right,bottom,mPaint);}}/*** 繪制橫向 item 分割線* @param canvas* @param parent*/private void drawHorizontal(Canvas canvas,RecyclerView parent){final int top = parent.getPaddingTop() ;final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom() ;final int childSize = parent.getChildCount() ;for(int i = 0 ; i < childSize ; i ++){final View child = parent.getChildAt( i ) ;RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();final int left = child.getRight() + layoutParams.rightMargin ;final int right = left + mItemSize ;canvas.drawRect(left,top,right,bottom,mPaint);}}/*** 設(shè)置item分割線的size* @param outRect* @param view* @param parent* @param state*/@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {if(mOrientation == LinearLayoutManager.VERTICAL){outRect.set(0,0,0,mItemSize);}else {outRect.set(0,0,mItemSize,0);}} }效果輸出圖
總結(jié)
本文對ListView、AdapterView、RecyclerView進(jìn)行了全面整理,接下來我會(huì)介紹繼續(xù)介紹Android開發(fā)中的相關(guān)知識(shí),有興趣可以繼續(xù)關(guān)注Carson_Ho的安卓開發(fā)筆記
總結(jié)
以上是生活随笔為你收集整理的ListView、AdapterView、RecyclerView全面解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 依赖注入Dagger2详解
- 下一篇: RecyclerView