文章修改時間︰2014/08/06 10:25
文章修改次數︰2
文章參考來源
1.RecyclerView example(國外網站)2.RecyclerView in Android: The basics(國外網站)
3.RecyclerViewExtensions on Github
4.Android Simple RecyclerView Widget Example(國外網站)
5.ANDROID L: RECYCLERVIEW TUTORIAL(國外網站)
6.RecyclerView | Android Developer
本篇適合
對Android ListView元件了解的開發者需要手機
Android 4.0 (Ice cream sandwich)以上本篇概要
1.學會在Android Studio 0.8.x版添加Google擴充套件2.學會使用RecyclerView
3.學會使用強大的載圖工具Picasso
一、前言
Google在I/O 2014發表了Android L,
同時也在該系統中推岀一個嶄新的顯示元件RecyclerView,
官方也宣稱該元件比原來的ListVIew更好用。
以下是該元件與原來的ListView不同的地方︰
- 比ListView更進階也具彈性
- ViewHolder變成強制性必須實作的類別(稍候看實例會理解)
- 回收的速度比以往更有效率
- 以前你只建立ListView和Adapter,現在你還多需要建立一個LayoutManager
- LayoutManager能幫你避免過多次呼叫findViewById的所造成的資源浪費
二、本文開始
新元件LayoutManager和ItemAnimator介紹
在技術開始前,先說說LayoutManager這個新元件。
LayoutManager屬於RecyclerView的內部元件之一,其目的用來決定RecyclerView當一個view不再顯示給使用者,要怎麼重新使用這些view資源。
無論是要重覆使用(reuse)或資源回收(recycle)一個view,LayoutManager都會從數據集(Dataset)去讀取岀需要的資料,並且取代原view來顯示給使用者。然用這種方式去更換view能避免創建一些不需要的view、也能增進使用findViewById找資源的效能。
目前RecyclerView提供了LinearLayoutManager這個實作類別,該類別繼承了LayoutManager,這個元件能顯示直向版面(vertical)或橫向版面(Horizontal)的列表滑動清單。如果你需要一個客製化的排版方式(譬如想做得像以前的GridLayout方格式版面),你就得自己繼承RecyclerView.LayoutManager這個類別來教RecyclerView怎麼排版。
題外話,RecyclerView還提供動畫特效的功能,可以讓每一筆view顯示時,有一些被新增或移除的特效。如果想要改變這些特效,那麼就得去繼承RecyclerView.ItemAnimator這個類別,並在RecyclerView被實體化時,呼叫RecyclerView.setItemAnimator()這個函式。
技術環境準備
在開始coding以前,您需要準備好以下環境
- Android Studio(0.8.x版以上)
- 安裝SDK Tools、Platform-tools、Build-tools
- 安裝Android Support Library和Android Support Repository
- 點擊[File]->[Project Structure]
- 點擊左側[Modules]->[app]
- 右側頁面選擇[Properties],並修改以下值Compile Sdk Version: API20 / Build Tools Version: API 20.0.0
- 右側頁面選擇[Flavors],並修改以下值Target SDK: API 20
- 右側頁面選擇[Dependencies],並按下頁面下方[+]號,選擇1.Library dependency
- 添加以下5個擴充套件
com.android.support:appcompat-v7:+
com.android.support:support-v4:+
com.android.support:palette-v7:+
com.android.support:recyclerview-v7:+
com.squareup.picasso:picasso:2.3.+
小註︰看到上面5個套件後面都顯示 .+ ,這個符號意謂著跟Gradle說「請幫我使用該套件的最新版本。
但因為現在(2014/08/05)support-v4最新版本僅提供Android L Preview執行使用,因此我們需要做接下來的修改讓非Android L的手機也能使用這些套件。 - 為避免專案編譯時遇到
- Error:Execution failed for task ':app:processDebugManifest'.
- > Manifest merger failed : uses-sdk:minSdkVersion 15 cannot be smaller than version L declared in library com.android.support:appcompat-v7:21.0.0-rc1
我們需要修改二個地方︰
1>>修改Gradle腳本app\build.gradle
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:+' compile 'com.android.support:support-v4:+' compile 'com.android.support:palette-v7:+' compile 'com.squareup.picasso:picasso:2.3.+' compile 'com.android.support:recyclerview-v7:+' }
請改為
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:20.0.0' compile 'com.android.support:support-v4:20.0.0' compile 'com.squareup.picasso:picasso:2.3.+' compile 'com.android.support:recyclerview-v7:+' compile 'com.android.support:palette-v7:+' }
這是build.gradle最後的樣子
apply plugin: 'com.android.application' android { compileSdkVersion 20 buildToolsVersion '20.0.0' defaultConfig { applicationId "recyclerview.android.com.testrecyclerview" minSdkVersion 15 targetSdkVersion 20 versionCode 1 versionName "1.0" } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:20.0.0' compile 'com.android.support:support-v4:20.0.0' compile 'com.squareup.picasso:picasso:2.3.+' compile 'com.android.support:recyclerview-v7:+' compile 'com.android.support:palette-v7:+' }
2>>添加以下3行code至app\src\AndroidManifest.xml
xmlns:tools="http://schemas.android.com/tools"
<uses-sdk tools:node="replace"></uses-sdk>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
這是AndroidManifest.xml裡最後的樣子 |
按下Run ,試看看專案是否能正常在手機執行,
這麼做可以讓我們確定我們在coding前的初步環境和所需套件是否皆已建置完畢。
如果套件皆引用成功,應該能看到這個完全空白的畫面 |
(3)添加或修改3個Java class
- 添加ItemData這個Object類
- 實作RecyclerView的Adapter-->MyAdapter.java
- 在主頁面MyActivity.java實例化RecyclerView
package recyclerview.android.hmkcode.com.recyclerview; /** * Created by lp43 on 2014/8/4. */ public class ItemData { private String title; private String imageUrl; public ItemData(String title,String imageUrl){ this.title = title; this.imageUrl = imageUrl; } public String getTitle() { return title; } public String getImageUrl() { return imageUrl; } public void setTitle(String title) { this.title = title; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } }
package recyclerview.android.hmkcode.com.recyclerview; 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 android.widget.Toast; import com.squareup.picasso.Picasso; /** * Created by lp43 on 2014/8/4. */ public class MyAdapter extends RecyclerView.Adapter{ private ItemData[] itemsData; public MyAdapter(ItemData[] itemsData) { this.itemsData = itemsData; } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { // create a new view View itemLayoutView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_layout, null); // create ViewHolder ViewHolder viewHolder = new ViewHolder(itemLayoutView); return viewHolder; } @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { // - get data from your itemsData at this position // - replace the contents of the view with that itemsData viewHolder.txtViewTitle.setText(itemsData[position].getTitle()); // viewHolder.imgViewIcon.setImageResource(itemsData[position].getImageUrl()); Picasso.with(viewHolder.imgViewIcon.getContext()).cancelRequest(viewHolder.imgViewIcon); Picasso.with(viewHolder.imgViewIcon.getContext()).load(itemsData[position].getImageUrl()).into(viewHolder.imgViewIcon); } // Return the size of your itemsData (invoked by the layout manager) @Override public int getItemCount() { return itemsData.length; } // inner class to hold a reference to each item of RecyclerView public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public TextView txtViewTitle; public ImageView imgViewIcon; public ViewHolder(View itemLayoutView) { super(itemLayoutView); itemLayoutView.setOnClickListener(this); txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title); imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon); } @Override public void onClick(View view) { Toast.makeText(view.getContext(), "position = " + getPosition(), Toast.LENGTH_SHORT).show(); } } }
package recyclerview.android.hmkcode.com.recyclerview; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; public class MyActivity extends ActionBarActivity { private String[] sources = { "http://lorempixel.com/600/250/", "http://lorempixel.com/600/250/sports", "http://lorempixel.com/600/200/sports/Dummy-Text", "http://lorempixel.com/600/200/nature", "http://lorempixel.com/600/200/food", }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); // 1. get a reference to recyclerView RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); // this is data fro recycler view ItemData[] itemsData = { new ItemData("Delete",sources[0]), new ItemData("Cloud",sources[1]), new ItemData("Favorite",sources[2]), new ItemData("Like",sources[3]), new ItemData("Rating",sources[4]), new ItemData("Delete",sources[0]), new ItemData("Cloud",sources[1]), new ItemData("Favorite",sources[2]), new ItemData("Like",sources[3]), new ItemData("Rating",sources[4]) }; // 2. set layoutManger recyclerView.setLayoutManager(new LinearLayoutManager(this)); // 3. create an adapter MyAdapter mAdapter = new MyAdapter(itemsData); // 4. set adapter recyclerView.setAdapter(mAdapter); // 5. set item animator to DefaultAnimator recyclerView.setItemAnimator(new DefaultItemAnimator()); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.my, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/border2_pressed" /> <item android:drawable="@drawable/border2" /> </selector>
border2_pressed.png |
border2.png |
(5)添加或修改layout資料夾的二個xml
- activity_my.xml
- item_layout.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" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MyActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="40dp" android:background="@drawable/border2_combine"> <!-- icon --> <ImageView android:id="@+id/item_icon" android:layout_width="64dp" android:layout_height="64dp" android:layout_alignParentLeft="true" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="1dp" android:layout_marginBottom="1dp" android:contentDescription="icon" android:src="@drawable/ic_launcher" /> <!-- title --> <TextView android:id="@+id/item_title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/item_icon" android:layout_alignBaseline="@+id/item_icon" android:textColor="@android:color/darker_gray" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="8dp" android:textSize="22dp" /> </RelativeLayout>
(6)修改styles.xml裡的主題風格
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> </style> </resources>
(7)編譯並執行
這是程式執行後的樣子 |
三、結論
撰寫這篇文章的時間是2014年8月5日,本來這個專案裡很多的套件都要在Android L Preview裡去執行,
但因為添加了
<uses-sdk tools:node="replace"></uses-sdk>這個屬性到AndroidManifest.xml裡,
所以我們能在Android 4.x就能搶先體驗RecyclerView這個元件帶來的強大功能。
試著滑看看,
RecyclerView是不是像官方說的一樣流暢?
我實測跑了3隻Android 4.x的手機顯示都是正常的哦!
如需本篇源碼請至GitHub下載。
感謝!!
ReplyDelete(我其實看不懂原文)
希望有幫到你
ReplyDelete