MateralDesign-RecyclerView
ListView功能十分强大,同时在Android开发中可以说贡献卓越,直到今天仍然还有很多不计其数的程序再继续使用着ListView.不过ListView并不是完全没有缺点,比如说如果我们不使用一些技巧来提升它的运行效率,那么ListView的性能很差。还有,ListView的扩展性也不够很好,它只能实现数据纵向滚动的效果,如果想让实现横向滚动的话,ListView很难实现。
为此,Android提供了一个强大的滚动控件----RecycleView。它是增强版的ListView,不仅可以轻松实现和ListView同样的效果,还优化了ListView中存在的不足之处,目前Android官方推荐使用RecycleView,之后的开发中更多地程序逐渐从ListView转变到RecycleView.
下来我简单的创建一个RecycleViewTest项目,简述一下RecycleView的简单用法。
首先我们需要打开app/build.gradle文件,在dependencies闭包中添加如下的内容:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:recyclerview-v7:24.2.1'
testCompile 'junit:junit:4.12'
}
添加完之后Sync Now 进行同步
布局很简单
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
使用RecycleView和ListView步骤基本一致,还需要创建Item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
>
<ImageView
android:id="@+id/iv_fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/banana_pic"
android:layout_gravity="center_horizontal"
/>
<TextView
android:id="@+id/tv_fruit_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:text="水果信息"
/>
</LinearLayout>
Ok,到此基本上布局就搞定了。接下来我们创建一个Adapter,新建一个FruitAdapter类,让这个适配器继承自RecycleView.Adapter,并且泛型指定为FruitAdapter.ViewHolder,其中ViewHolder是我们在FruitAdapter中定义的一个的同步类,代码如下所示:
/**
* Description:
* Copyright : Copyright (c) 2016
* Author : yangfang
*/
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> mFruitList;
public FruitAdapter(List<Fruit> fruitList) {
mFruitList = fruitList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fruit_item, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitInfo.setText(fruit.getName());
}
@Override
public int getItemCount() {
return mFruitList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private ImageView fruitImage;
private TextView fruitInfo;
public ViewHolder(View itemView) {
super(itemView);
fruitImage = (ImageView) itemView.findViewById(R.id.iv_fruit_image);
fruitInfo = (TextView) itemView.findViewById(R.id.tv_fruit_info);
}
}
}
简单解释一下先定义一个内部类ViewHolder,ViewHolder要继承自RecycleView.ViewHolder,,然后ViewHolder的构造函数中要传入View参数,此View是RecyleView子项目的最外层布局,然后通过findViewById()方法来获取到布局中的ImagesView和TextView。
FruitAdapter中有一个构造函数,这个方法用于把展示的数据源传进来,变成一个全局变量mFruitAdapter.
FruitAdapter继承自RecycleView.Adapter必须重写onCreateViewHolder(),onBindViewHolder()和getItemCount().
onCreateViewHolder()用于创建ViewHolder实例,将对象item布局加载进来,并且把加载出俩的布局传入到构造函数中,最后ViewHolder的实例返回。
onBindViewHolder()用于对RecycleView子项的数据进行赋值,会在每个子项滚动到屏幕内的时候执行,通过position参数得到当前项Fruit实例,然后再将数据设置到ViewHolder的ImageView和TextView当中。
getItemCount()告诉recycleView一共加载多少项。
适配器创建好之后,接下来需要在MainActivity调用。代码如下
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruit();
RecyclerView recycleView = (RecyclerView) findViewById(R.id.recycle_view);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
recycleView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recycleView.setAdapter(adapter);
}
private void initFruit() {
for (int i = 0; i < 5 ; i++) {
Fruit apple = new Fruit("Apple", R.drawable.pineapple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
fruitList.add(orange);
Fruit pear = new Fruit("pear", R.drawable.pear_pic);
fruitList.add(pear);
Fruit cherry = new Fruit("cherry", R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit grape = new Fruit("grape", R.drawable.grape_pic);
fruitList.add(grape);
Fruit mango = new Fruit("mango", R.drawable.mango_pic);
fruitList.add(mango);
Fruit strawberry = new Fruit("strawberry", R.drawable.strawberry_pic);
fruitList.add(strawberry);
}
}
}
到这里使用RecycleView实现ListView样式基本实现。
在此处需要关注关于Adapter相关的一些问题:Adapter的作用是数据和界面的桥梁,通过适配器至ListView(如调用了ListView的setAdapter(adapter))列表的每一项显示至页面,其实当列表中的每一项显示到页面中都会调用Adapter的getView返回一个View。
CurorAdapter提供了两个抽象方法
newView(context,cursor,viewGroup)
bindView(view,context,cursor)
newView()和bindView()细化了getView()中的功能实现,均可由getview()实现。
接下我们试着用RecycleView实现一个横向滚动和瀑布流
ListView扩展性能不好,它只能实现纵向滚动的效果,如果想实现横向的滚动,那么就需要使用RecycleView去实现。
RecyclerView recycleView = (RecyclerView)findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recycleView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recycleView.setAdapter(adapter);
为什么ListView很难或者根本无法实现效果在RecyclerView上这么轻松实现呢?
因为ListView的布局排列是由自身去管理的,而RecyclerView则将这个工作交给LayoutManager,LayoutManager中制定了一套可扩展的布局排列接口,子类只要按照接口的规范来实现,就能定制出各类不同排列方式的布局了。
LinearLayoutManager之外,RecyclerView还给我们提供了GridLayoutManager和StaggeredGridLayoutManager这两种内置的布局排列方式。GridLayoutManager可以用于实现网格布局,StaggerGridLayoutManager可以用于实现瀑布流布局。
接下来简单的实现一下瀑布流
简单的布局布局如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp" >
<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="10dp" />
</LinearLayout>
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();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
StaggeredGridLayoutManager layoutManager = new
StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
private void initFruits() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);
fruitList.add(mango);
}
}
private String getRandomLengthName(String name) {
Random random = new Random();
int length = random.nextInt(20) + 1;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(name);
}
return builder.toString();
}
}
RecyclerView点击事件
和ListView一样,RecyclerView也必须要能相应点击事件才可以,下面我们介绍一下如何在RecyclerView中注册点击事件,修改FruitAdapter中的代码
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
final ViewHolder holder = new ViewHolder(view);
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(), "you clicked view " + fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
holder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(), "you clicked image " + fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
return holder;
}