Android自學曆程—ListView由簡入深,androidlistview
前段時間學習了RecyclerView,發現對ListView有更加明顯的感覺,於是決定把之前理清點思路的ListView,整理整理畢竟在5.0普及之前,ListView還是有用武之地的。一如既往的我們從簡單的開始。——站在巨人的肩膀之上
譯自:https://github.com/codepath/android_guides/wiki/Using-an-ArrayAdapter-with-ListView#using-a-basic-arrayadapter,可能本人理解不深,翻譯偏生硬,帶我多敲幾遍,再來幾篇供大家分享。
Using an ArrayAdapter with ListView
在android的開發中,我們經常會使用ListView來展示一組垂直滾動列表的需求,這時我們會使用Adapter填充資料。最簡單的Adaper是Arrayadapter,因為這個適配器把ArrayList的對象轉變為View列表,從而被載入到ListView的容器中。
Row View Recycling
當使用Adapter和ListView時,我們要確保明白,View的回收機制是怎樣工作的。
當你的ListView與adapter進行聯絡是,adapter將會執行個體每一行列表,直到ListView被充足的items填充(items填充ListView的總高度),在這種情況下,不會有多餘的行列表的對象再在記憶體當中建立。
取而代之的是,當使用者向下滾動列表的時候,離開螢幕的Items將會儲存到記憶體當中供之後使用,進而,每一個新的行列表進入螢幕時,將會複用儲存在記憶體當中的行列表。如此,即使有1000個列表的List,僅僅需要大約7個行列表的視圖需要被執行個體化,或者儲存在記憶體中,這是回收的直觀概述:
這是另一張回收視圖相關圖。
請參閱另一個本指南的ListView(個人覺得有更好的選擇,以後會寫部落格),看看這是如何工作來最佳化你的列表的效能。
Using a Basic ArrayAdapter
為了使用基本的 ArrayAdaper,我們僅僅需要去執行個體化一個adapter,並且將該適配器串連到ListView。第一步,我們初始化一個adapter。
Adapter<String> itemsadapter = new ArrayAdapter<String> (this,android.R.layout.simple_list_item_1,items);
ArrayAdapter需要去聲明item當被轉換為View時的類型(a String in this case),進而接收三個參數:context(activity執行個體),XML item layout,and the array date。注意我們選擇了
simple_list_item_1,她是一個簡單用TextView作為布局,為每一個Items。
現在,我們需要這個適配器串連到ListView進行填充,
ListView listView = (ListView) findViewById(R.id.lvItems);listView.setAdapter(itemsAdapter);
By default, this will now convert each item in the data array into a view by callingtoString on the item and then assigning the result as the value of a TextView(simple_list_item_1.xml) that is displayed as the row for that data item。(實在翻譯不來,自己意會吧,求好心人指導翻譯)。如果你的應用程式需要更加複雜的轉變在Item和View之中,那我們需要建立自訂的ArrayAdapter來取代之。
Using a Custom ArrayAdapter(準備資料模版,視圖模版)
當我們想要在List裡展示你一系列的自訂的列表,我們需要給每一個Items使用,我們自訂的XML layout檔案。為了完成這一步,我們要建立自訂的ArrayAdapter的類。 See this repo for the source code.
第一步,我們經常會定義一個模版,去描述List裡每一個Item所需要的資料。
Defining the Model
建立一個Java對象,來定義某些欄位,,例如, User class
public class User { public String name; public String hometown; public User(String name, String hometown) { this.name = name; this.hometown = hometown; }}
Creating the View Template
下一步,我們需要建立一個XML 布局檔案,來代表每一個Item的模版,res/layout/item_user.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/tvName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Name" /> <TextView android:id="@+id/tvHome" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="HomeTown" /></LinearLayout>
Defining the Adapter
下一步,我們需要去定義Adapter並且去描述,Java對象轉變為View的過程(在 getView 方法中)。稚嫩的方法如下(沒有任何緩衝):
public class UsersAdapter extends ArrayAdapter<User> { public UsersAdapter(Context context, ArrayList<User> users) { super(context, 0, users); } @Override public View getView(int position, View convertView, ViewGroup parent) { // Get the data item for this position User user = getItem(position); // Check if an existing view is being reused, otherwise inflate the view if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_user, parent, false); } // Lookup view for data population TextView tvName = (TextView) convertView.findViewById(R.id.tvName); TextView tvHome = (TextView) convertView.findViewById(R.id.tvHome); // Populate the data into the template view using the data object tvName.setText(user.name); tvHome.setText(user.hometown); // Return the completed view to render on screen return convertView; }}
Adapter有一個 構造和getView()的方法去描述 資料項目和視圖之間的轉變。
getView()是返回在特定位置用作排在ListView內的實際視圖的方法。
Attaching the Adapter to a ListView
現在,我們可以在Activity裡使用那個Adapter,去展示Items的數組嵌入到ListView中:
// Construct the data sourceArrayList<User> arrayOfUsers = new ArrayList<User>();// Create the adapter to convert the array to viewsUsersAdapter adapter = new UsersAdapter(this, arrayOfUsers);// Attach the adapter to a ListViewListView listView = (ListView) findViewById(R.id.lvItems);listView.setAdapter(adapter);
此刻,ListView控制項現在已經成功綁定到使用者數組資料。
Populating Data into ListView
一旦適配器串連,Items都將自動填滿到ListView中,基於數組中的內容。
我們可以添加新的Item到適配器中:
// Add item to adapterUser newUser = new User("Nathan", "San Diego");adapter.add(newUser);// Or even append an entire new collection// Fetching some data, data has now returned// If data was JSON, convert to ArrayList of User objects.JSONArray jsonArray = ...;ArrayList<User> newUsers = User.fromJson(jsonArray)adapter.addAll(newUsers);
這將會追加新的Items到List中。我們也可以清除所有的List在任何時候,只要一句:
adapter.clear();
現在我們可以添加,,刪除,修改使用者並且ListView中的Items會自動的做出相應的反射。
Constructing Models from External Source(從外部源構建模型)
為了建立模型執行個體,我們可能從外部源載入資料(即資料庫或者REST JSON API),所以我們應該在每個模型中建立2個額外的方法,允許構建一個List或者但單一的Item如果資料是從JSON API而來的。
public class User { // Constructor to convert JSON object into a Java class instance public User(JSONObject object){ try { this.name = object.getString("name"); this.hometown = object.getString("hometown"); } catch (JSONException e) { e.printStackTrace(); } } // Factory method to convert an array of JSON objects into a list of objects // User.fromJson(jsonArray); public static ArrayList<User> fromJson(JSONArray jsonObjects) { ArrayList<User> users = new ArrayList<User>(); for (int i = 0; i < jsonObjects.length(); i++) { try { users.add(new User(jsonObjects.getJSONObject(i))); } catch (JSONException e) { e.printStackTrace(); } } return users; }}
For more details, check out our guide on converting JSON into a model. If you are not using a JSON source for your data, you can safely skip this step.
(暫時沒遇到過,感覺哼重要的樣子)
Improving Performance with the ViewHolder Pattern
To improve performance, we should modify the custom adapter by applying the ViewHolder pattern which speeds up the population of the ListView considerably by caching view lookups for smoother, faster item loading:
public class UsersAdapter extends ArrayAdapter<User> { // View lookup cache private static class ViewHolder { TextView name; TextView home; } public UsersAdapter(Context context, ArrayList<User> users) { super(context, R.layout.item_user, users); } @Override public View getView(int position, View convertView, ViewGroup parent) { // Get the data item for this position User user = getItem(position); // Check if an existing view is being reused, otherwise inflate the view ViewHolder viewHolder; // view lookup cache stored in tag if (convertView == null) { viewHolder = new ViewHolder(); LayoutInflater inflater = LayoutInflater.from(getContext()); convertView = inflater.inflate(R.layout.item_user, parent, false); viewHolder.name = (TextView) convertView.findViewById(R.id.tvName); viewHolder.home = (TextView) convertView.findViewById(R.id.tvHome); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } // Populate the data into the template view using the data object viewHolder.name.setText(user.name); viewHolder.home.setText(user.hometown); // Return the completed view to render on screen return convertView; }}
在這個栗子中中,我們使用了一個 private static class called ViewHolder。在實踐當中,調用FindViewById()是真的非常慢,如果你的adapter每次都調用他,為你的每一個行列都去調用她,你會很快發現將會出現效能問題。而ViewHolder這個類所做的事情就是緩衝調用FindViewById()這個方法。一旦你的ListView到達它可以在螢幕上顯示的行的最大數量,Android會非常聰明的回收這些行的View。我們測試一下,如果既if(convertView == Null)之後,一個View(Item的視圖)被回收。如果不是Null的話,我們就有可以使用的回收後的View,並且我們可以改變她的值,否則我們需要擦魂歸一個新的行的View。The magic behind this is the setTag() method which lets us attach an arbitrary object onto a View object, which is how we save the already inflated View for future reuse.(在這背後的魔法就是setTag()方法,這個方法能夠讓我們附屬任意的對象到View對象之上,這就是我們如何儲存已經執行個體化的View,供以後使用。
Beyond ViewHolders
Customizing Android ListView Rows by Subclassing describes a strategy for obtaining instances of child views using a similar approach as a ViewHolder but without the explicit ViewHolder subclass.
References
- http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
- http://www.doubleencore.com/2013/05/layout-inflation-as-intended/
- http://www.bignerdranch.com/blog/customizing-android-listview-rows-subclassing/
資料尋找部分:
http://www.cnblogs.com/xiangtailiang/p/3379543.html
http://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html
http://www.codeofaninja.com/2013/09/android-listview-with-adapter-example.html
http://stackoverflow.com/questions/4145602/how-to-implement-a-view-holder
http://stackvoid.com/