• 12.6 列表与卡片" level="2">12.6 列表与卡片
    • 12.6.1 RecyclerView" level="3">12.6.1 RecyclerView
    • 12.6.2 CardView" level="3">12.6.2 CardView

    12.6 列表与卡片" class="reference-link">12.6 列表与卡片

    12.6.1 RecyclerView" class="reference-link">12.6.1 RecyclerView

    在Android5.X中将使用了很久的ListView做了升级,增加了一个使用更方便、效率更高的控件——RecyclerView。RecyclerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,它同样拥有item回收复用的功能,但是RecyclerView可以直接把ViewHolder的实现封装起来,用户只要实现自己的ViewHolder就可以了,该组件会自动帮你回收复用每一个item。

    要使用RecyclerView,首先需要在项目中引入com.android.support:recyclerview- v7:21.0.2的依赖。在布局中使用RecyclerView与使用ListView基本类似,同样需要使用一个类似List item的布局,在Material Design中,通常与CardView配合使用,后面我们会详细讲解CardView的使用方法。

    使用RecyclerView的重点与使用ListView一样,需要使用一个合适的数据适配器来加载数据,RecyclerView中需要重写的很多方法都似曾相识,不过RecyclerView更加先进的是,它已经封装好了ViewHolder,只要实现功能就可以了,而使用上仍然是跟在ListView中使用ViewHolder一样,代码如下所示。

    1. package com.xys.myapplication;
    2.  
    3. import android.support.v7.widget.RecyclerView;
    4. import android.view.LayoutInflater;
    5. import android.view.View;
    6. import android.view.ViewGroup;
    7. import android.widget.TextView;
    8. import java.util.List;
    9.  
    10. public class RecyclerAdapter
    11. extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
    12.  
    13. private List<String> mData;
    14.  
    15. public RecyclerAdapter(List<String> data) {
    16. mData = data;
    17. }
    18.  
    19. public OnItemClickListener itemClickListener;
    20.  
    21. public void setOnItemClickListener(
    22. OnItemClickListener itemClickListener) {
    23. this.itemClickListener = itemClickListener;
    24. }
    25.  
    26. public interface OnItemClickListener {
    27. void onItemClick(View view, int position);
    28. }
    29.  
    30. public class ViewHolder extends RecyclerView.ViewHolder
    31.  
    32. implements View.OnClickListener {
    33.  
    34. public TextView textView;
    35.  
    36. public ViewHolder(View itemView) {
    37. super(itemView);
    38. textView = (TextView) itemView;
    39. textView.setOnClickListener(this);
    40. }
    41.  
    42. //通过接口回调来实现RecyclerView的点击事件
    43. @Override
    44. public void onClick(View v) {
    45. if (itemClickListener != null) {
    46. itemClickListener.onItemClick(v, getPosition());
    47. }
    48. }
    49. }
    50.  
    51. @Override
    52. public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    53. //将布局转化为View并传递给RecyclerView封装好的ViewHolder
    54. View v = LayoutInflater.from(viewGroup.getContext()).inflate(
    55. R.layout.rc_item, viewGroup, false);
    56. return new ViewHolder(v);
    57. }
    58.  
    59. @Override
    60. public void onBindViewHolder(ViewHolder viewHolder, int i) {
    61. //建立起ViewHolder中视图与数据的关联
    62. viewHolder.textView.setText(mData.get(i) + i);
    63. }
    64.  
    65. @Override
    66.  
    67. public int getItemCount() {
    68. return mData.size();
    69. }
    70. }

    上面就是一个非常简单却典型的RecyclerView,通过onCreateViewHolder将List item的布局转化为View,并传递给RecyclerView封装好的ViewHolder,就可以将数据与视图关联起来了。但是有一点要注意的是,Android并没有给RecyclerView增进点击事件,所以我们需要自己使用接口回调机制,创建一个点击事件的接口,代码如下所示。

    1. public OnItemClickListener itemClickListener;
    2.  
    3. public void setOnItemClickListener(OnItemClickListener itemClickListener) {
    4. this.itemClickListener = itemClickListener;
    5. }
    6. public interface OnItemClickListener {
    7. void onItemClick(View view, int position);
    8. }

    类似ListView的List Item视图如下所示。

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <TextView xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:orientation="vertical"
    4. android:textSize="40sp"
    5. android:gravity="center"
    6. android:layout_width="match_parent"
    7. android:background="#bebebe"
    8. android:layout_margin="3dp"
    9. android:layout_height="match_parent">
    10.  
    11. </TextView>

    当然,仅仅是优化性能也是不够的,让开发者能够更加方便地使用也是非常重要的。Google在RecyclerView中定义了LayoutManager来帮助开发者更加方便地创建不同的布局,下面的例子就演示了如何创建简单的水平和竖直两种布局方式。当然,你也可以通过自定义LayoutManager来创建自己的布局,核心代码如下所示。

    1. mRcList.setLayoutManager(new LinearLayoutManager(RecyclerTest.this));
    2. mRcList.setLayoutManager(new GridLayoutManager(RecyclerTest.this, 3));

    完整代码如下所示。

    1. package com.xys.myapplication;
    2.  
    3. import android.animation.Animator;
    4. import android.animation.AnimatorListenerAdapter;
    5. import android.app.Activity;
    6. import android.os.Bundle;
    7. import android.support.v7.widget.DefaultItemAnimator;
    8. import android.support.v7.widget.GridLayoutManager;
    9. import android.support.v7.widget.LinearLayoutManager;
    10. import android.support.v7.widget.RecyclerView;
    11. import android.view.View;
    12. import android.widget.AdapterView;
    13. import android.widget.Spinner;
    14.  
    15. import java.util.ArrayList;
    16. import java.util.List;
    17.  
    18. public class RecyclerTest extends Activity {
    19.  
    20. private RecyclerView mRcList;
    21. private RecyclerAdapter mAdapter;
    22. private RecyclerView.LayoutManager mLayoutManager;
    23.  
    24. private Spinner mSpinner;
    25.  
    26. private List<String> mData = new ArrayList<String>();
    27.  
    28. @Override
    29. protected void onCreate(Bundle savedInstanceState) {
    30. super.onCreate(savedInstanceState);
    31. setContentView(R.layout.recycler);
    32.  
    33. mRcList = (RecyclerView) findViewById(R.id.rc_list);
    34. mLayoutManager = new LinearLayoutManager(this);
    35. mRcList.setLayoutManager(mLayoutManager);
    36. mRcList.setHasFixedSize(true);
    37. //设置显示动画
    38. mRcList.setItemAnimator(new DefaultItemAnimator());
    39.  
    40. mSpinner = (Spinner) findViewById(R.id.spinner);
    41. mSpinner.setOnItemSelectedListener(
    42. new AdapterView.OnItemSelectedListener() {
    43. @Override
    44. public void onItemSelected(AdapterView<?> parent,
    45. View view,
    46. int position,
    47. long id) {
    48. if (position == 0) {
    49. mRcList.setLayoutManager(
    50. //设置为线性布局
    51. new LinearLayoutManager(
    52. RecyclerTest.this));
    53. } else if (position == 1) {
    54. mRcList.setLayoutManager(
    55. //设置为表格布局
    56. new GridLayoutManager(
    57. RecyclerTest.this, 3));
    58. } else if (position == 2) {
    59. }
    60. }
    61.  
    62. @Override
    63. public void onNothingSelected(AdapterView<?> parent) {
    64. }
    65. });
    66. //增加测试数据
    67. mData.add("Recycler");
    68. mData.add("Recycler");
    69. mData.add("Recycler");
    70. mAdapter = new RecyclerAdapter(mData);
    71. mRcList.setAdapter(mAdapter);
    72. mAdapter.setOnItemClickListener(
    73. new RecyclerAdapter.OnItemClickListener() {
    74. @Override
    75. public void onItemClick(final View view, int position) {
    76. //设置点击动画
    77. view.animate()
    78. .translationZ(15F).setDuration(300)
    79. .setListener(new AnimatorListenerAdapter() {
    80. @Override
    81. public void onAnimationEnd(Animator animation) {
    82. super.onAnimationEnd(animation);
    83. view.animate()
    84. .translationZ(1f)
    85. .setDuration(500).start();
    86. }
    87. }).start();
    88. }
    89. });
    90. }
    91.  
    92. public void addRecycler(View view) {
    93. mData.add("Recycler");
    94. int position = mData.size();
    95. if (position > 0) {
    96.  
    97. mAdapter.notifyDataSetChanged();
    98. }
    99. }
    100.  
    101. public void delRecycler(View view) {
    102. int position = mData.size();
    103. if (position > 0) {
    104. mData.remove(position - 1);
    105. mAdapter.notifyDataSetChanged();
    106. }
    107. }
    108. }

    在程序中,使用Spinner来选择线性布局管理器还是表格布局管理器,并给按钮增加了点击动画效果,整个程序运行效果如图12.12、图12.13、图12.14所示,图12.12展示了RecyclerView的线性布局,图12.13、图12.14展示了RecyclerView的表格布局。

    12.6 列表与卡片 - 图1 12.6 列表与卡片 - 图2 12.6 列表与卡片 - 图3
    图12.12 RecyclerView纵向布局 图12.13 RecyclerView横向布局 图12.14 RecyclerView表格布局

    12.6.2 CardView" class="reference-link">12.6.2 CardView

    CardView曾经开始流行在Google+上,后来越来越多的App也引入了Card这样一种布局方式。因此在Android 5.X上,Google索性就提供了CardView的控件,方便大家使用这种布局。说到底,CardView也是一个容器类布局,只是它提供了卡片这样一种形式。开发者可以定义卡片的大小与视图高度,并设置圆角的角度。不过使用CardView的方式与RecyclerView还是有区别的,首先同样是需要在项目中引入com.android.support:cardview-v7:21.+的依赖。其次在布局文件中使用CardView的时候需要引入一个新的名字空间——在Android Studio中使用xmlns:card_view=http://schemas.android. com/apk/res-auto来添加。这样才可以通过自定义的名字空间来引用它的两个属性。

    1. card_view:cardBackgroundColor="@color/cardview_background"
    2. card_view:cardCornerRadius="8dp"

    这两个属性非常简单,第一个是设置背景颜色,第二个是设置圆角的角度。我们以一个例子来演示一下CardView的使用,XML代码如下所示。

    1. <?xml version="1.0" encoding="utf-8"?>
    2.  
    3. <android.support.v7.widget.CardView xmlns:android="http://schemas.
    4.  
    5. android.com/apk/res/android"
    6. xmlns:card_view="http://schemas.android.com/apk/res-auto"
    7. android:id="@+id/cardview"
    8. android:layout_width="fill_parent"
    9. android:layout_height="wrap_content"
    10. android:layout_marginTop="40dp"
    11. card_view:cardBackgroundColor="@color/cardview_initial_background"
    12. card_view:cardCornerRadius="30dp"
    13. android:elevation="10dp"
    14. android:layout_marginLeft="@dimen/margin_large"
    15. android:layout_marginRight="@dimen/margin_large">
    16.  
    17. <TextView
    18. android:layout_width="wrap_content"
    19. android:layout_height="100dp"
    20. android:gravity="center"
    21. android:layout_gravity="center"
    22.  
    23. android:textSize="30sp"
    24. android:layout_margin="@dimen/margin_medium"
    25. android:text="I am a CardView" />
    26. </android.support.v7.widget.CardView>

    显示效果如图12.15所示。

    12.6 列表与卡片 - 图4 图12.15 CardView