ListView 由于其强大的功能,在过去的 Andorid 开发中使用非常广泛。不过 ListView 需要优化来提升运行效率,就像我们之前所优化的那样,否则性能将很差。还有就是只能够纵向滚动,如果要想实现横向移动,用 ListView 是做不到的。
RecyclerView 可以说是一个增强版的 ListView 。它不仅实现了和 ListView 同样的效果,而且还优化了 ListView 存在的各种不足。 RecyclerView 现在可是官方推荐使用的滚动控件哦O(∩_∩)O~
1 基本用法
RecyclerView 也是新增的控件,所以必须先在项目的 build.gradle 中添加相应的依赖库才能使用:
compile 'com.android.support:recyclerview-v7:24.2.1'
注意不要拼错哦O(∩_∩)O~
添加后,记得点击 Sync Now 链接哦。
接着,修改布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
这里把宽度和高度都定义为 match_parent,这样 RecyclerView 就能占满整个屏幕。因为 RecyclerView 也不是系统内置的 SDK,所以这里引用的是完整的包路径。
然后为 RecyclerView 创建一个适配器(继承自 RecyclerView.Adapter<CatAdapter.ViewHolder> ):
public class CatAdapter extends RecyclerView.Adapter<CatAdapter.ViewHolder> { private List<Cat> cats; static class ViewHolder extends RecyclerView.ViewHolder { ImageView image; TextView name; public ViewHolder(View view) { super(view); image = (ImageView) view.findViewById(R.id.image); name = (TextView) view.findViewById(R.id.name); } } public CatAdapter(List<Cat> cats) { this.cats = cats; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cat_item, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder holder, int position) { Cat cat = cats.get(position); holder.image.setImageResource(cat.getImageId()); holder.name.setText(cat.getName()); } @Override public int getItemCount() { return cats.size(); } }
在代码中,我们先定义了一个内部类 ViewHolder,它继承自 RecyclerView.ViewHolder。然后在 ViewHolder 的构造函数中传入一个 View 参数,它是 RecyclerView 子项的最外层布局,所以我们可以通过它来取得布局中的 ImageView 和 TextView 的实例。
CatAdapter 的构造函数用于把要展示的数据源传递进来,并赋值给一个类变量 cats。
因为 CatAdapter 继承自 RecyclerView.Adapter,所以必须重写以下三个方法:
- onCreateViewHolder - 创建 ViewHolder 实例,我们把 cat_item 的布局加载进来,创建了一个 ViewHolder 实例。
- onBindViewHolder - 对 RecyclerView 的子项数据进行赋值,这个方法会在每个子项被滚动到屏幕内时进行。
- getItemCount - 返回 RecyclerView 的子项总数。
最后,我们在活动类中使用 RecyclerView :
public class MainActivity extends AppCompatActivity { private List<Cat> cats = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view); LinearLayoutManager layoutManager=new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); CatAdapter adapter=new CatAdapter(cats); recyclerView.setAdapter(adapter); } /** * 初始化数据 */ private void init() { cats.add(new Cat("暹罗猫", R.drawable.cat1)); cats.add(new Cat("布偶猫", R.drawable.cat2)); cats.add(new Cat("苏格兰折耳猫", R.drawable.cat3)); cats.add(new Cat("英国短毛猫", R.drawable.cat4)); cats.add(new Cat("波斯猫", R.drawable.cat5)); cats.add(new Cat("俄罗斯蓝猫", R.drawable.cat6)); cats.add(new Cat("美国短毛猫", R.drawable.cat7)); cats.add(new Cat("异国短毛猫", R.drawable.cat8)); cats.add(new Cat("挪威森林猫", R.drawable.cat9)); cats.add(new Cat("孟买猫", R.drawable.cat10)); cats.add(new Cat("缅因猫", R.drawable.cat11)); cats.add(new Cat("埃及猫", R.drawable.cat12)); } }
这里创建了 LinearLayoutManager 的线性布局对象,传递给了 recyclerView.setLayoutManager() 方法。
recyclerView 示例
我们使用了 recyclerView 创建出了 ListView 的效果,而且代码逻辑更清晰咯。
2 横向滚动
现在让我们把这些猫变为 “横向滚动” 吧。
把 cat_item 中的元素变为垂直排列:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="110dp" android:layout_height="wrap_content" android:orientation="vertical" > <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp" /> </LinearLayout>
这里还把 LinearLayout 的宽度设为 110dp,即固定的值。因为每种猫的文字长度不同,如果用 wrap_content 的话,可能造成子项的长短不一致;而如果用 match_parent 的话,又会导致一个子项就占满了整个屏幕。
我们把 ImageView 与 TextView 都设置为水平居中,而且用 android:layout_marginTop,让文字与图片保持了一段距离,这样更美观。
接下来,修改活动类的代码:
@Override protected void onCreate(Bundle savedInstanceState) { ... layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); ... }
把 LinearLayoutManager 的布局改为横向排列(默认是纵向排列)。
横向排列
我们可以在水平方向上滑动来查看屏幕外的 “猫” 啦。
ListView 的布局排列是由自身来管理的,所以存在一定的局限性;而 RecyclerView 把布局的工作交给了 LayoutManager,LayoutManager 制定了一系列可扩展的布局排列接口,所以我们只要按照接口的规范来实现,就能够定制出各种不同排列方式的布局啦O(∩_∩)O~
3 瀑布流布局
让我们使用 StaggeredGridLayoutManager 来实现酷炫的瀑布流布局吧O(∩_∩)O~
首先修改 cat_item 布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_margin="5dp" > <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left" android:layout_marginTop="10dp" /> </LinearLayout>
这里把 LinearLayout 的宽度改为 wrap_content,这样宽度会根据实际的布局列数自动适配。 还使用 layout_margin 让子项之间留出一定的间距。最后将 TextView 改为居左对齐,因为下面的说明文件内容可能会很长哟O(∩_∩)O~
修改活动类的代码:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); StaggeredGridLayoutManager layoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); CatAdapter adapter = new CatAdapter(cats); recyclerView.setAdapter(adapter); }
在此,我们创建了 StaggeredGridLayoutManager 的实例,它的构造函数接受两个参数,第一个参数用于指定布局的列数,第二个参数用于指定布局的排列方向。
瀑布流示例
4 点击事件
RecyclerView 没有像 ListView 一样的 setOnItemClickListener() 事件,所以需要我们自己给子项具体的 View 注册点击事件。
ListView 的 setOnItemClickListener() 注册的是子项的点击事件,但如果想要注册点击的是子项里具体的某个按钮时,使用 ListView 实现起来就比较麻烦。所以 RecyclerView 直接摈弃了子项点击事件的监听器,把所有的点击事件都交给具体的 View 去注册实现咯O(∩_∩)O
修改适配器:
static class ViewHolder extends RecyclerView.ViewHolder { View catView; ImageView image; TextView name; public ViewHolder(View view) { super(view); catView = view; image = (ImageView) view.findViewById(R.id.image); name = (TextView) view.findViewById(R.id.name); Log.d(TAG, "ViewHolder: image:" + image); Log.d(TAG, "ViewHolder: name:" + name); } } public CatAdapter(List<Cat> cats) { this.cats = cats; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cat_item, parent, false); final ViewHolder holder = new ViewHolder(view); holder.catView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = holder.getAdapterPosition(); Cat cat = cats.get(position); Toast.makeText(v.getContext(), "你点击了 View " + cat.getName(), Toast.LENGTH_SHORT).show(); } }); holder.image.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = holder.getAdapterPosition(); Log.d(TAG, "onClick: position:" + position); Cat cat = cats.get(position); Toast.makeText(v.getContext(), "你点击了图片 " + cat.getName(), Toast.LENGTH_SHORT).show(); } }); return holder; }
我们为最外层的布局与 ImageView 都注册了点击事件,这就是 RecyclerView 的灵活之处。
触发 RecyclerView 点击事件
如果点击了图片下方的文字,会触发 ImageView 的点击事件,因为事件会向外传播哦O(∩_∩)O~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持自由互联。