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实例),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 source
ArrayList<User> arrayOfUsers = new ArrayList<User>();
// Create the adapter to convert the array to views
UsersAdapter adapter = new UsersAdapter(this, arrayOfUsers);
// Attach the adapter to a ListView
ListView listView = (ListView) findViewById(R.id.lvItems);
listView.setAdapter(adapter);

此刻,ListView控件现在已经成功绑定到用户数组数据。

Populating Data into ListView

一旦适配器连接,Items都将自动填充到ListView中,基于数组中的内容。

我们可以添加新的Item到适配器中:

// Add item to adapter
User 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://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/

时间: 2024-12-26 17:21:05

Android自学历程—ListView由简入深的相关文章

Android自学历程—回调函数的意思(源码+例子)

为什么会学习回调函数,那还要从线程说起.虽然以前写过一篇文章叫做“Android自学历程—多线程”,现在看看还是太年轻了. 在学习线程的时候,我试着看了看Handler的源码,于是对其中的Callback接口产生了兴趣.于是补自身不足,拿来学习学习.废话到此为止,整理思路,一起来学习. 下面这是一段Handler的源码 /** * Callback(回收) interface(界面) you can use when instantiating(例示) a Handler to avoid *

Android自学历程—RecyclerView的使用(2)

Introduction to RecyclerView RecyclerView在Android 5中被介绍,在 Support-V7的包中.她允许展示items在随意任何之处(可联想ListView),正如包名所说的,在API7以上均可使用(Android 22).   她的名字来自于其工作的方式,当一个Item被隐藏时,不是去destroyed她并且随后为每一个新new出来的对象去创建一个新的item,隐藏的item被回收:她们被重用,并且会有新的数据绑定她们. 一个RccyclerVie

Android自学历程—RecyclerView的使用

在网上看见有关RecyclerView的介绍,说是ListView的进阶版,官方推荐,便找来资料,耍耍. 首先挂上官方的教程,官方是最具权威和最让人信服的第一手资料. https://developer.android.com/training/material/lists-cards.html To create complex lists and cards with material design styles in your apps, you can use the RecyclerV

Android自学历程—通讯录开发

前段时间写了个通讯录,现在闲的正好回顾回顾. 其实对我的感触是 一:实现方式有很多,那就要重于积累.但难免有落后的,设计不合理的. 二:还有就是设计方面,整体的设计上.编程不就是逻辑嘛.参考别人的,形成自己的. 1.显示效果,很简单,直接Listview 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.an

Android自学历程—围住神经猫开发

学习核心内容: 1. 学会SurfaceView的绘图技巧. 2. 掌握神经猫的游戏逻辑设计. 第一阶段主要完成内容: 并且创建游戏所需的类,搭建基本的代码结构. 创建一个二维数组,用于保存游戏场景,并且将场景初始化. SurfaceView中根据场景数据,在界面中绘制点阵. 先上代码 1 package com.ryan.catchcrazycat; 2 3 /** 4 * Created by air on 15-8-1. 5 */ 6 public class Dot { 7 8 /* 9

Android自学历程—Surfaceview整理总结

这里借鉴了不少博主的劳动成果,先表示感谢.(一定要自己整理才能看的进去--) 其实这里还有个疑惑,SurfaceView与View的区别,以及如何选择使用. 涉及到画面更新的需求.一种是主动更新,另一种是被动更新.类似棋牌类游戏,需要被动的点击去触发它,完全可以采用View. 而比如罗盘,需要一直在旋转,这个时候需要一个线程去处理它.此种情况选用SurfaceView 先看看官方的解释 SurfaceView的API介绍 Provides a dedicated drawing surface

Android自学历程—OkHttp的基本使用

前段时间学习线程,想通过子线程获取网络图片,进而更新主UI界面的学习.发现大部分的Demo都是基于HttpClient的使用,但google似乎削弱了HttpClient,我甚至只能找到HttpURLConnection的相关类.没办法,只能寻找新的途径,这就是这篇译文的由来. 开始之前: 1.下载最新版本的OKHttp的Jar包,和Okio的Jar包. 2.在Android studio 中,导入Jar包. 最快捷的方式是:(但是我有时候我这样操作没用,不知道问什么,之后乖乖下载再导入Jar包

Android自学历程—Material Design的Tabs

好几天没写博客了,今天给大家带来一篇不错的译文.我照着练习了段时间,还有买了新书<android开发艺术探索>,如果好的话给大家分享分享. 用Android Material Design的方式处理Tabs Android Design Support Library这个类可以使我们更加向后兼容Androoid 2.1,去使用material design的组建.在Design support Library中,一些组建如:navigation drawer, floating action

Android自学历程—手把手教你使用OkHttp(基础篇)

安装 在使用OkHttp前,我们需要先导入OkHttp的第三方库. 我们可以在Github上找到她的项目地址: https://github.com/square/okhttp 我们可以在Android Studio中使用Gradle, 最后效果如下: 测试使用我们的OKHttp第三方库 1.第一步我们需要去创建一个 OKHttpClient 对象 OkHttpClient okHttpClient = new OkHttpClient(); 2.下一步我们还需要一个 Request 对象,她可