文章目录
要走好明天的路,必须记住昨天走过的路,思索今天正在走着的路。
ListView,一种在垂直滚动列表中显示条目的视图;RecyclerView,一种在局限的窗口呈现大数据集合的灵活视图。RecyclerView 部件是 ListView 的一种更高级且更灵活的版本。
以上描述来自官网。
移动设备屏幕空间有限,导致在屏幕上一次性显示的内容也是有限的。当需要显示大量的数据时,设想有这样的控件,可以帮助用户只通过手指上下滑动,就可以让屏幕外的数据滚动到屏幕内,同时,屏幕上原有的数据会滚动出屏幕。如此,便可以优雅地解决在局限的屏幕上显示大量数据的问题。ListView 和 RecyclerView 便适用于此。
不过,自 Android 5.0 推出以来,RecyclerView 在很多地方都在逐步取代 ListView,这也是官方推崇的。“江山代有才人出,各领风骚数百年”。如今,已要来到 Android 7.0 的时代,RecyclerView 的使用也很普遍了,或许,ListView 正逐步从 Android 的大舞台退出,RecyclerView 即将独领风骚。
下面浅析从 ListView 到 RecyclerView 的用法。
ListView 的用法
ListViewDemo 地址: ListViewDemo,学习总结自郭霖的《第一行代码》。
新建一个 ListViewDemo 项目,默认持续下一步直至完成。然后,删掉 activity_main.xml 中默认建好的 TextView,建立一个 ListView 如下:
12345 |
<ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent"></ListView> |
接着,新建一个实体类 Fruit,作为 ListView 适配器的适配类型:
123456789101112131415161718 |
public class { private String name; private int imageId; public (String name, int imageId) { this.name = name; this.imageId = imageId; } public String getName() { return name; } public int getImageId() { return imageId; }} |
name 是水果的名字,imageId 是水果对应图片的资源 id。然后,为 ListView 的子项自定义一个布局,在 layout 目录下新建 fruit_item.xml 如下:
1234567891011 |
<ImageView android:id="@+id/fruit_image" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginLeft="10dp"/> |
ImageView 用于显示水果的图片,TextView 用于显示水果的名称。
由于数据无法直接传递给 ListView,需要借助适配器完成。Android 中提供了很多适配器的实现类,这里使用 ArrayAdapter,通过泛型指定适配的数据类型,再在构造函数中传入适配的数据。自定义一个适配器 FruitAdapter,继承自 ArrayAdapter,泛型指定为 Fruit 类。
同时,优化 ListView 的使用效率:
一. FruitAdapter 中的 getView() 方法每次都将布局加载一遍,这样,快速滚动的话会影响性能。故利用好 convertView 参数,缓存之前加载好的布局,再重用。
二. 使用 ViewHolder 模式,其充分利用 ListView 的视图缓存机制,避免每次调用 getView() 时通过 findViewById() 实例化控件。
代码如下:
12345678910111213141516171819202122232425262728293031323334 |
public class FruitAdapter extends ArrayAdapter<Fruit> { private int resourceId; public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) { super(context, textViewResourceId, objects); resourceId = textViewResourceId; } public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); View view; ViewHolder viewHolder; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate(resourceId, null); viewHolder = new ViewHolder(); viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image); viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name); view.setTag(viewHolder); } else { view = convertView; viewHolder = (ViewHolder) view.getTag(); //重新获取 ViewHolder, 找到缓存的布局. } viewHolder.fruitImage.setImageResource(fruit.getImageId()); viewHolder.fruitName.setText(fruit.getName()); return view; } class ViewHolder { ImageView fruitImage; TextView fruitName; }} |
FruitAdapter 重写父类的构造函数,将上下文、ListView 子项布局的 id 和数据传递进来。同时,重写了 getView() 方法,每个子项滚动进屏幕内时调用该方法。其中,getItem() 方法获取当前项的 Fruit 实例。
增加判断与一个内部类 ViewHolder,利用视图缓存机制进行缓存。
若 convertView 为空,则创建一个 ViewHolder 对象,将控件的实例存放在 ViewHolder 里。然后,调用 View 的 setTag() 方法,将 ViewHolder 对象存储在 View 中。若 convertView 不为空,则调用 View 的 getTag() 方法,重新取出 ViewHolder。如此,所有控件的实例都缓存在 ViewHolder 里。
最后,修改并完善 MainActivity 中的代码:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 |
public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<Fruit>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits(); FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList); ListView listView = (ListView) findViewById(R.id.list_view); listView.setAdapter(adapter); } private void initFruits() { Fruit apple = new Fruit("Apple", R.mipmap.ic_launcher); fruitList.add(apple); Fruit banana = new Fruit("Banana", R.mipmap.ic_launcher); fruitList.add(banana); Fruit orange = new Fruit("Orange", R.mipmap.ic_launcher); fruitList.add(orange); Fruit watermelon = new Fruit("Watermelon", R.mipmap.ic_launcher); fruitList.add(watermelon); Fruit pear = new Fruit("Pear", R.mipmap.ic_launcher); fruitList.add(pear); Fruit grape = new Fruit("Grape", R.mipmap.ic_launcher); fruitList.add(grape); Fruit pineapple = new Fruit("Pineapple", R.mipmap.ic_launcher); fruitList.add(pineapple); Fruit strawberry = new Fruit("Strawberry", R.mipmap.ic_launcher); fruitList.add(strawberry); Fruit cherry = new Fruit("Cherry", R.mipmap.ic_launcher); fruitList.add(cherry); Fruit mango = new Fruit("Mango", R.mipmap.ic_launcher); fruitList.add(mango); Fruit peach = new Fruit("Peach", R.mipmap.ic_launcher); fruitList.add(peach); Fruit lemon = new Fruit("Lemon", R.mipmap.ic_launcher); fruitList.add(lemon); Fruit pitaya = new Fruit("Pitaya", R.mipmap.ic_launcher); fruitList.add(pitaya); Fruit durin = new Fruit("Durin", R.mipmap.ic_launcher); fruitList.add(durin); } } |
添加了 initFruits() 方法,初始化所有的水果数据。将水果的名字和对应的图片 id 传入 Fruit 类的构造函数中,再把创建好的对象添加到水果列表中。而后,在 onCreate() 方法里创建了 FruitAdapter 对象,将 FruitAdapter 作为适配器传递给 ListView。
至此,ListView 的简单综合应用浅析完毕,运行程序,如下:
更多如动态修改 ListView、使 ListView 具有弹性、自动显示与隐藏布局 ListView、模仿微信聊天布局 ListView、动态改变点击 Item 布局的 ListView 等,详见 GitHub。
RecyclerView 的用法
RecyclerViewDemo 地址: RecyclerViewDemo,学习总结自徐宜生的《 Android 群英传》。
首先,在 gradle 中引入compile 'com.android.support:recyclerview-v7:23.4.0'
的依赖。由于 RecyclerView 封装好了 ViewHolder,再实现其功能。新建一个 RecyclerViewDemo 如上所示,同时添加了 Spinner 和按钮:
12345678910111213141516171819202122232425262728293031323334 |
<android.support.v7.widget.RecyclerView android:id="@+id/rc_list" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="9" android:scrollbars="vertical"/> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <Spinner android:id="@+id/spinner" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:entries="@array/recycler_list"/> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:onClick="addRecycler" android:text="Add"/> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:onClick="delRecycler" android:text="Delete"/> </LinearLayout> |
在 strings.xml 中新建 Spinner 中用到的 array:
1234 |
<string-array name="recycler_list"> <item>LinearLayoutManager</item> <item>GridLayoutManager</item></string-array> |
与 ListView 的用法一样,使用一个合适的数据适配器来加载数据,自定义一个适配器 RecyclerAdapter:
1 |