你的位置:首页 > 操作系统

[操作系统]Android自学历程—ListView由简入深


前段时间学习了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实例),

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.

 

Using a Custom ArrayAdapter(准备数据模版,视图模版)

当我们想要在List里展示你一系列的自定义的列表,我们需要给每一个Items使用,我们自定义的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

下一步,我们需要创建一个

<LinearLayout ="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/