RecyclerView的基本用法
和百分比布局類似樟结,RecyclerView也屬于新增的控件狭吼,android團隊也采取同樣的方式殖妇,將RecyclerView定義在了support庫當(dāng)中。因此疲吸,想要用RecyclerView這個控件摘悴,首先需要在項目的build.gradle中添加相應(yīng)的依賴庫才行舰绘。打開app/build.gradle捂寿,在dependencies閉包中添加如下內(nèi)容:
dependencies {
????compile fileTree(dir:'libs',include: ['*.jar'])
????androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
????????excludegroup:'com.android.support',module:'support-annotations'
? ? })
????compile'com.android.support:appcompat-v7:24.2.1'
? ? compile'com.android.support:recyclerview-v7:24.2.1'
? ? testCompile'junit:junit:4.12'
}
添加完后記得點擊一下Sync Now來進行同步秦陋。然后修改activity_main.xml中的代碼,如下所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? android:id="@+id/activity_main"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent">
? ? <android.support.v7.widget.RecyclerView
? ? ? ? android:id="@+id/recycler_view"
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="match_parent"/>
</RelativeLayout>
先為RecyclerView指定一個id赤嚼,然后將寬度和高度都設(shè)置為match_parent,這樣RecyclerView也就占滿了整個布局的空間更卒。需要注意的是由于RecyclerView并不是內(nèi)置在系統(tǒng)SDK當(dāng)中,所以需要把完整的包路徑寫出來流济。
把之前用到的圖片,item_fruit.xml和Fruit類都復(fù)制過來雕憔。然后為RecyclerView準(zhǔn)備一個適配器斤彼,新建FruitAdapter類,讓這個適配器繼承自RecyclerView.Adapter,并將泛型指定為FruitAdapter.viewHolder嘲玫。其中去团,ViewHolder使我們在FruitAdapter中定義一個內(nèi)部類穷蛹,代碼如下:
public class FruitAdapterextends RecyclerView.Adapter {
????private ListmFruitList;
? ? static class ViewHolderextends RecyclerView.ViewHolder{
????????ImageViewfruitimage;
? ? ? ? TextViewfruitname;
? ? ? ? public ViewHolder(View view){
????????????super(view);
? ? ? ? ? ? fruitimage =(ImageView)view.findViewById(R.id.fruit_img);
? ? ? ? ? ? fruitname =(TextView)view.findViewById(R.id.fruit_name);
? ? ? ? }
}
public FruitAdapter(List fruitList){
????mFruitList=fruitList;
? ? }
@Override
? ? public ViewHolderonCreateViewHolder(ViewGroup parent, int viewType) {
????????View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
? ? ? ? ViewHolder viewHolder=new ViewHolder(view);
? ? ? ? return viewHolder;
? ? }
@Override
? ? public void onBindViewHolder(ViewHolder viewHolder,int position){
????????Fruit fruit=mFruitList.get(position);
? ? ? ? viewHolder.fruitimage.setImageResource(fruit.getImageid());
? ? ? ? viewHolder.fruitname.setText(fruit.getName());
? ? }
@Override
? ? public int getItemCount(){
????????return mFruitList.size();
? ? }
}
看著很長鬼雀,但是比ListView的適配器更容易理解蛙吏。我們先定義了一個內(nèi)部類ViewHolder,ViewHolder要繼承自RecyclerView.ViewHolder励烦。然后ViewHolder的構(gòu)造函數(shù)中要傳入一個View參數(shù)崩侠,這個參數(shù)通常是RecyclerView子項的最外層布局,那么我們可以通過findViewById()方法來獲取到布局中的ImageView和TextView的實例了改抡。
由于FruitAdapter是繼承自RecyclerView.Adapter的阿纤,那么就必須重寫onCreateViewHolder()夷陋、onBindViewHolder()和getItemCount()這三個方法骗绕。onCreateViewHolder()方法是用于創(chuàng)建ViewHolder實例的,我們在這個方法中將fruit_item布局加載進來荆忍,然后創(chuàng)建一個ViewHolder實例刹枉,并把加載出來的布局傳入到構(gòu)造函數(shù)當(dāng)中屈呕,最后將ViewHolder的實例返回虎眨。onBindViewHolder()方法是用于對RecyclerView子項的數(shù)據(jù)進行賦值的,會在每個子項被滾動到屏幕內(nèi)的時候執(zhí)行钟鸵,這里我們通過position參數(shù)得到當(dāng)前項的Fruit實例棺耍,然后再將數(shù)據(jù)設(shè)置到ViewHolder的ImageView和TextView當(dāng)中即可种樱。getItemCount()方法就非常簡單嫩挤,它用于高速RecyclerView一共有多少子項,直接返回數(shù)據(jù)源的長度就可以了以现。
適配器準(zhǔn)備好了邑遏,我們就可以開始RecyclerView,修改MainActivity中的代碼憎蛤,如下所示:
public class MainActivityextends AppCompatActivity {
????private ListfruitList=new ArrayList<>();
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
????????super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
????????//初始化水果數(shù)據(jù)
? ? ? ? initFruits();
? ? ? ? RecyclerView recyclerview=(RecyclerView)findViewById(R.id.recycler_view);
? ? ? ? LinearLayoutManager lm=new LinearLayoutManager(this);
? ? ? ? recyclerview.setLayoutManager(lm);
? ? ? ? FruitAdapter adapter=new FruitAdapter(fruitList);
? ? ? ? recyclerview.setAdapter(adapter);
? ? }
private void initFruits() {
????????for(int i=0;i<2;i++){
????????????Fruit apple =new Fruit("Apple",R.drawable.apple);
? ? ? ? ? ? fruitList.add(apple);
? ? ? ? ? ? Fruit banana=new Fruit("banana",R.drawable.banana);
? ? ? ? ? ? fruitList.add(banana);
? ? ? ? ? ? Fruit orange=new Fruit("orange",R.drawable.orange);
? ? ? ? ? ? fruitList.add(orange);
? ? ? ? ? ? Fruit water=new Fruit("watermelon",R.drawable.water);
? ? ? ? ? ? fruitList.add(water);
? ? ? ? ? ? Fruit pear=new Fruit("pear",R.drawable.pear);
? ? ? ? ? ? fruitList.add(pear);
? ? ? ? ? ? Fruit Str=new Fruit("Strawberry",R.drawable.stra);
? ? ? ? ? ? fruitList.add(Str);
? ? ? ? ? ? Fruit Cherry=new Fruit("Cherry",R.drawable.cherry);
? ? ? ? ? ? fruitList.add(Cherry);
? ? ? ? ? ? Fruit Mango=new Fruit("Mango",R.drawable.mango);
? ? ? ? ? ? fruitList.add(Mango);
? ? ? ? ? ? Fruit Grape=new Fruit("Grape",R.drawable.grape);
? ? ? ? ? ? fruitList.add(Grape);
? ? ? ? }
}
}
這里使用了一個同樣的initFruits()方法,用于初始化所以得水果數(shù)據(jù)棚辽。接著在onCreate()方法中我們先獲取到RecyclerView的實例巷疼,然后創(chuàng)建了一個LinearLayoutManager對象嚼沿,并將它設(shè)置到RecyclerView當(dāng)中瓷患。LayoutManager用于指定RecyclerView的布局方式擅编,這里使用的LinearLayoutManager是線性布局的意思,可以實現(xiàn)和ListView類似的效果谭贪。接下來我們創(chuàng)建了FruitAdapter的實例俭识,并將水果數(shù)據(jù)傳入到FruitAdapter的構(gòu)造函數(shù)中洞渔,最后調(diào)用RecyclerView的setAdapter()方法來完成適配器設(shè)置磁椒,這樣RecyclerView和數(shù)據(jù)之間的關(guān)聯(lián)就建立完成了。
現(xiàn)在運行以下程序本辐,看看效果吧慎皱。
實現(xiàn)橫向滾動和瀑布流布局
我們已經(jīng)知道ListView的擴展性并不好宝冕,它只能實現(xiàn)縱向滾動效果,如果想進行橫向滾動的話菊卷,ListView就做不到了洁闰。那么RecyclerView就能做得更好嗎万细?當(dāng)然可以赖钞,不僅能做得更好,還簡單弓千,那么我們先試試橫向滾動吧。
首先修改fruit_item.xml中的代碼撒妈,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? android:layout_width="wrap_content"
? ? android:layout_height="wrap_content"
? ? android:orientation="vertical">
? ? <ImageView
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:id="@+id/fruit_img"
? ? ? ? android:layout_gravity="center_horizontal"/>
? ? <TextView
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:id="@+id/fruit_name"
? ? ? ? android:layout_gravity="center_horizontal"
? ? ? ? android:layout_marginTop="10dp"/>
</LinearLayout>
我們將LinearLayout改成垂直方向排列。然后將ImageView和TextView都設(shè)置成了在布局中水平居中汁展。并且使用layout_marginTop屬性讓文字和圖片之間保持距離善镰。
接下來修改MainActivity中的代碼年枕,如下所示:
public class MainActivityextends AppCompatActivity {
????private ListfruitList=new ArrayList<>();
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
????????super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? initFruits();
? ? ? ? RecyclerView recyclerview=(RecyclerView)findViewById(R.id.recycler_view);
? ? ? ? LinearLayoutManager lm=new LinearLayoutManager(this);
? ? ? ? //調(diào)用LinearLayoutManager的setOrientation()方法來設(shè)置布局排列方式
? ? ? ? lm.setOrientation(LinearLayoutManager.HORIZONTAL);
? ? ? ? recyclerview.setLayoutManager(lm);
? ? ? ? FruitAdapter adapter=new FruitAdapter(fruitList);
? ? ? ? recyclerview.setAdapter(adapter);
? ? }
private void initFruits() {
????????for(int i=0;i<2;i++){
????????????Fruit apple =new Fruit("Apple",R.drawable.apple);
? ? ? ? ? ? fruitList.add(apple);
? ? ? ? ? ? Fruit banana=new Fruit("banana",R.drawable.banana);
? ? ? ? ? ? fruitList.add(banana);
? ? ? ? ? ? Fruit orange=new Fruit("orange",R.drawable.orange);
? ? ? ? ? ? fruitList.add(orange);
? ? ? ? ? ? Fruit water=new Fruit("watermelon",R.drawable.water);
? ? ? ? ? ? fruitList.add(water);
? ? ? ? ? ? Fruit pear=new Fruit("pear",R.drawable.pear);
? ? ? ? ? ? fruitList.add(pear);
? ? ? ? ? ? Fruit Str=new Fruit("Strawberry",R.drawable.stra);
? ? ? ? ? ? fruitList.add(Str);
? ? ? ? ? ? Fruit Cherry=new Fruit("Cherry",R.drawable.cherry);
? ? ? ? ? ? fruitList.add(Cherry);
? ? ? ? ? ? Fruit Mango=new Fruit("Mango",R.drawable.mango);
? ? ? ? ? ? fruitList.add(Mango);
? ? ? ? ? ? Fruit Grape=new Fruit("Grape",R.drawable.grape);
? ? ? ? ? ? fruitList.add(Grape);
? ? ? ? }
????}
}
MainActivity中只加入了一行代碼品洛,調(diào)用LinearLayoutManager的setOrientation()方法來設(shè)置布局的排列方向桥状,默認是縱向排列,傳入LinearLayoutManager.HORIZONTAL表示讓布局橫向排列转晰。
運行一下查邢,看看效果圖吧酵幕。
為什么ListView很難或者根本無法實現(xiàn)的效果在RecyclerView上這么輕松就能解決了呢芳撒?這主要得益于RecyclerView出色的設(shè)計笔刹。ListView的布局排列室友自身去管理的,而RecyclerView則將這個工作交給了LayoutManager门躯,LayoutManager中制定了一套可擴展的布局排列接口,子類只要按照接口的規(guī)范來實現(xiàn)山孔,就能制定出各種不同排列方式的布局了荷憋。
出了LinearLayoutManager之外勒庄,還提供了GridLayoutManager和StaggeredGridLayoutManager這兩種內(nèi)置的布局排列方式。GridLayoutManager可以用于實現(xiàn)網(wǎng)絡(luò)布局荡碾,StaggeredGridLayoutManager可以用于實現(xiàn)瀑布流布局坛吁。我們來試一下效果炫酷的瀑布流布局,網(wǎng)格布局就作為練習(xí)吧哆姻,自己查閱相關(guān)資料矛缨。
要使用StaggeredGridLayoutManager實現(xiàn)瀑布流布局帖旨,首先修改fruit_item.xml中的代碼碉就,如圖所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? android:layout_width="match_parent"
? ? android:layout_height="wrap_content"
? ? android:layout_margin="5dp"
? ? android:orientation="vertical">
? ??<ImageView
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:id="@+id/fruit_img"
? ? ? ? android:layout_gravity="center_horizontal"/>
<TextView
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:id="@+id/fruit_name"
? ? ? ? android:layout_gravity="left"
? ? ? ? android:layout_marginTop="10dp"/>
</LinearLayout>
至于上面的修改內(nèi)容或添加的內(nèi)容不在多做講解瓮钥,自己多花功夫去學(xué)習(xí)吧碉熄。
接下來,我們接著修改MainActivity中的代碼呀酸,如下所示:
public class MainActivityextends AppCompatActivity {
????private ListfruitList=new ArrayList<>();
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
????????super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? //初始化水果數(shù)據(jù)
? ? ? ? initFruits();
? ? ? ? RecyclerView recyclerview=(RecyclerView)findViewById(R.id.recycler_view);
? ? ? ? StaggeredGridLayoutManager layoutManager =new ????????StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
? ? ? ? recyclerview.setLayoutManager(layoutManager);
? ? ? ? FruitAdapter adapter=new FruitAdapter(fruitList);
? ? ? ? recyclerview.setAdapter(adapter);
? ? }
private void initFruits() {
????????for(int i=0;i<2;i++){
????????????Fruit apple =new Fruit(getRandomLengthName("Apple"),R.drawable.apple);
? ? ? ? ? ? fruitList.add(apple);
? ? ? ? ? ? Fruit banana=new Fruit(getRandomLengthName("banana"),R.drawable.banana);
? ? ? ? ? ? fruitList.add(banana);
? ? ? ? ? ? Fruit orange=new Fruit(getRandomLengthName("orange"),R.drawable.orange);
? ? ? ? ? ? fruitList.add(orange);
? ? ? ? ? ? Fruit water=new Fruit(getRandomLengthName("watermelon"),R.drawable.water);
? ? ? ? ? ? fruitList.add(water);
? ? ? ? ? ? Fruit pear=new Fruit(getRandomLengthName("pear"),R.drawable.pear);
? ? ? ? ? ? fruitList.add(pear);
? ? ? ? ? ? Fruit Str=new Fruit(getRandomLengthName("Strawberry"),R.drawable.stra);
? ? ? ? ? ? fruitList.add(Str);
? ? ? ? ? ? Fruit Cherry=new Fruit(getRandomLengthName("Cherry"),R.drawable.cherry);
? ? ? ? ? ? fruitList.add(Cherry);
? ? ? ? ? ? Fruit Mango=new Fruit(getRandomLengthName("Mango"),R.drawable.mango);
? ? ? ? ? ? fruitList.add(Mango);
? ? ? ? ? ? Fruit Grape=new Fruit(getRandomLengthName("Grape"),R.drawable.grape);
? ? ? ? ? ? fruitList.add(Grape);
? ? ? ? }
}
private StringgetRandomLengthName(String name) {
????????Random randrom =new Random();
? ? ? ? int length = randrom.nextInt(20)+1;
? ? ? ? StringBuilder builder =new StringBuilder();
? ? ? ? for(int i =0;i < length;i++){
????????????builder.append(name);
? ? ? ? }
????????return builder.toString();
? ? }
}
在onCreate()方法中茎杂,我們創(chuàng)建了一個StaggeredGridLayoutManager的實例。StaggeredGridLayoutManager的構(gòu)造函數(shù)接收兩個參數(shù)倾哺,第一個參數(shù)用于指定布局的列數(shù)羞海,傳入3表示把布局分為3列曲管;第二個參數(shù)用于指定布局的排列方式翘地,傳入StaggeredGridLayoutManager.VERTICAL表示讓布局縱向排列癌幕,最后再把創(chuàng)建好的實例設(shè)置到RecyclerView中就可以了勺远。
為了讓瀑布流布局更加明顯时鸵,我們使用了一個小技巧饰潜。這里我們看到getRandromLengthName()方法上彭雾,該方法使用了Random對象來創(chuàng)造一個1-20之間的隨機數(shù),然后將參數(shù)中傳入的字符串隨機重復(fù)半沽,然后再initFruits()方法中者填,每種水果的名字都改成getRandromLengthName()來生成做葵,保證每種水果名字的差距不太一樣了,因此高度也就不同了酿矢。
現(xiàn)在運行一下榨乎,看看效果吧。
RecyclerView的點擊事件
和ListView一樣瘫筐,RecyclerView也必須要能夠響應(yīng)點擊事件才可以谬哀,不然沒什么實際用途。不過不同于ListView的是严肪,RecyclerView并沒有提供類似于setOnItemClickListener()這樣的注冊監(jiān)聽器,而是需要我們自己給子項的具體的View去注冊點擊事件谦屑。相較于ListView要復(fù)雜。其實ListView點擊事件的處理并不人性化氢橙,setOnItemClickListener()方法注冊的是子項的點擊事件酝枢,如果我想點擊子項里的某一按鍵呢?雖然也能實現(xiàn)悍手,但ListView就比RecyclerView麻煩帘睦。所以RecyclerView干脆直接摒棄了子項點擊事件的監(jiān)聽器袍患,所有的點擊事件都由具體的View去注冊,就在沒有這個煩惱了竣付。
下面我們進入正題吧诡延,先修改FruitAdapter中的代碼,如下所示:
public class FruitAdapterextends RecyclerView.Adapter {
????private ListmFruitList;
? ? static class ViewHolderextends RecyclerView.ViewHolder{
????????View fruitview;
? ? ? ? ImageView fruitimage;
? ? ? ? TextView fruitname;
? ? ? ? public ViewHolder(View view){
????????????super(view);
? ? ? ? ? ? fruitview = view;
? ? ? ? ? ? fruitimage =(ImageView)view.findViewById(R.id.fruit_img);
? ? ? ? ? ? fruitname =(TextView)view.findViewById(R.id.fruit_name);
? ? ? ? }
}
public FruitAdapter(List fruitList){
????mFruitList=fruitList;
? ? }
@Override
? ? public ViewHolderonCreateViewHolder(ViewGroup parent, int viewType) {
????????View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
? ? ? ? final ViewHolder holder =new ViewHolder(view);
? ? ? ? holder.fruitview.setOnClickListener(new View.OnClickListener() {
????????????@Override
? ? ? ? ? ? public void onClick(View v) {
????????????????int position =holder.getAdapterPosition();
? ? ? ? ? ? ? ? Fruit fruit =mFruitList.get(position);
? ? ? ? ? ? ? ? Toast.makeText(v.getContext(), "you clicked view "+fruit.getName(), Toast.LENGTH_SHORT).show();
? ? ? ? ? ? }
});
? ? ? ? holder.fruitimage.setOnClickListener(new View.OnClickListener() {
? ??????????@Override
? ? ? ? ? ? public void onClick(View v) {
? ??????????????int position =holder.getAdapterPosition();
? ? ? ? ? ? ? ? Fruit fruit =mFruitList.get(position);
? ? ? ? ? ? ? ? Toast.makeText(v.getContext(), "you clicked view "+fruit.getName(), Toast.LENGTH_SHORT).show();
? ? ? ? ? ? }
});
? ? ? ? return holder;
? ? }
@Override
? ? public void onBindViewHolder(ViewHolder viewHolder,int position){
Fruit fruit=mFruitList.get(position);
? ? ? ? viewHolder.fruitimage.setImageResource(fruit.getImageid());
? ? ? ? viewHolder.fruitname.setText(fruit.getName());
? ? }
@Override
? ? public int getItemCount(){
return mFruitList.size();
? ? }
}
我們先是在ViewHolder中添加了fruitView變量來保存子項最外層布局的實例古胆,然后在onCreateViewHolder()方法中注冊點擊事件就可以了肆良。這里分為最外層布局和ImageView和TextView都注冊了點擊事件,RecyclerView的強大之處也在這里逸绎,它可以輕松實現(xiàn)子項中任意控件或布局的點擊事件惹恃。點擊事件都是先獲取用戶點擊的position,然后通過position拿到相應(yīng)的Fruit實例棺牧,在使用Toast分別彈出兩種不同的內(nèi)容以示區(qū)別巫糙。
運行一下吧,看看效果如何颊乘。