Activity之滚动控件Recycler
基本用法
RecyclerView 不仅实现了和 ListView 同样的效果,而且还优化了 ListView 存在的各种不足。
RecyclerView 也是新增的控件,以必须先在项目的 build.gradle 中添加相应的依赖库才能使用:
compile 'com.android.support:recyclerview-v7:24.2.1'
修改布局文件:
<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("猫1", R.drawable.cat1));
cats.add(new Cat("猫2", R.drawable.cat2));
cats.add(new Cat("猫3", R.drawable.cat3));
cats.add(new Cat("猫4", R.drawable.cat4));
cats.add(new Cat("猫5", R.drawable.cat5));
cats.add(new Cat("猫6", R.drawable.cat6));
cats.add(new Cat("猫7", R.drawable.cat7));
cats.add(new Cat("猫8", R.drawable.cat8));
cats.add(new Cat("猫9", R.drawable.cat9));
}
}
这里创建了 LinearLayoutManager
的线性布局对象,传递给了 recyclerView.setLayoutManager()
方法。
横向滚动
把 cat_item 中的元素变为垂直排列:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
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 的宽度设为 100dp,即固定的值。用 match_parent ,会让子项就占满了整个屏幕。把 ImageView
与 TextView
都设置为水平居中,用 android:layout_marginTop
,让文字与图片保持了一段距离。
修改活动类的代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
...
}
横向排列
ListView 的布局排列是由自身来管理的,所以存在一定的局限性;而 RecyclerView 把布局的工作交给了 LayoutManager,LayoutManager 制定了一系列可扩展的布局排列接口。
瀑布流布局
使用 StaggeredGridLayoutManager 实现瀑布流布局
修改 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 改为居左对齐。
修改活动类的代码:
@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 的实例,它的构造函数接受两个参数,第一个参数用于指定布局的列数,第二个参数用于指定布局的排列方向。
点击事件
RecyclerView 没有像 ListView 一样的 setOnItemClickListener() 事件,所以需要自己给子项具体的 View 注册点击事件。
ListView 的 setOnItemClickListener()
注册的是子项的点击事件,但如果想要注册点击的是子项里具体的某个按钮时,使用 ListView 实现起来就比较麻烦。所以 RecyclerView 直接摈弃了子项点击事件的监听器,把所有的点击事件都交给具体的 View 去注册实现。
修改适配器:
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;
}