ListView
一、定制ListView界面
1、定义实体类Fruit类
public class Fruit {
private String name; //水果名字段
private int imageId; //水果对应图片字段
public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public int getImageId() {
return imageId;
}
}
2、新建fruit_item.xml,为ListView子项指定自定义布局。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<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_vertical"
android:layout_marginLeft="10dp"
/>
</LinearLayout>
3、创建自定义适配器,该适配器继承自ArrayAdapter,并将泛型指定为Fruit类。
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
// FruitAdapter重写了一组构造器,将上下文,ListView子项布局的id与数据传递进来
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
//重写getView()方法,该方法在每个子项滚动到屏幕内时被调用。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position); //通过getItem()方法获得Fruit实例
//为子项加载布局
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
//第一个参数:加载的布局文件的id。
//第二个参数:给待加载的布局添加一个父布局。
//第三个参数:一般为false,表示只让父布局中声明的layout属性生效,但不会为该View添加父布局。
TextView fruitName = view.findViewById(R.id.fruit_name);
ImageView fruitImage = view.findViewById(R.id.fruit_image);
fruitName.setText(fruit.getName());
fruitImage.setImageResource(fruit.getImageId());
return view;
}
}
4、在MainActivity中使用ListView,把待适配的数据传入给Adapter,最后调用setAdapter()方法将构建好的适配器传入ListView,完成数据与ListView间的关联。
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitsList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits(); //初始化数据
//一般使用ListView时,数据无法直接传递给ListView,所以要借助适配器Adapter完成。
FruitAdapter adapter = new
FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitsList);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initFruits() {
Fruit apple = new Fruit("Apple",R.drawable.chair);
fruitsList.add(apple);
…… //添加数据,此处省略
}
}
二、提升ListView的运行效率
使用ListView时,其运行效率很低。在FruitAdapter的getView()方法中,每次都会将布局加载一遍。当ListView快速滚动时,性能就会成为问题。
1、使用getView()中convertView参数对加载好的布局进行缓存。
2、每次在getView()方法中还是会调用View的findViewById()方法来获取一次实例,所以借助ViewHolder对控件实例进行缓存。
三、设定ListView的点击事件
MainActivity中添加:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Fruit fruit = fruitsList.get(position);
Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_LONG).show();
}
});
setOnItemClickListener()方法为ListView注册了一个监听,当用户点击ListView中任何一个Item时,就会调用onItemClick()方法。
RecyclerView
RecyclerView与ListView相比性能进行了优化,同时扩展性增强,如可以实现横向滚动。
ListView的布局排列是由自身去管理,而RecyclerView中是运用LayoutManager管理,LayoutManager中制定了一套可扩展的布局排列接口,通过实现不同接口可以定制不同排列方式。
一、定制RecyclerView界面
1、在app/build.gradle文件中,dependencies闭包中添加:
compile 'com.android.support:recyclerview-v7:26.0.0-alpha1'
2、activity_main.xml文件中运用完整包路径添加RecyclerView:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
3、Fruit实体类与fruit_item.xml同ListView
4、新建适配器类FruitAdapter类,继承自RecyclerView.Adapter,泛型为FruitAdapter.ViewHolder,其中FruitAdapter为一个内部类。
public class FruitAdapter extends RecyclerView.Adapter <FruitAdapter.ViewHolder>{
private List<Fruit> mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder{
ImageView fruitImage;
TextView fruitName;
//RecyclerView子项的最外层布局,通过其可获得ImageView与TextView
public ViewHolder(View view) {
super(view);
fruitImage = view.findViewById(R.id.fruit_image);
fruitName = view.findViewById(R.id.fruit_name);
}
}
//用于获取展示的数据源
public FruitAdapter(List<Fruit> fruitList) {
mFruitList = fruitList;
}
//用于创建ViewHolder实例
@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;
}
//用于对RecyclerView子项进行赋值,当前子项滚动到屏幕时,会通过position得到当前子项的Fruit实例,然后设置到ImageView与TextView中。
@Override
public void onBindViewHolder(ViewHolder holder, int position){
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
//告诉RecyclerView一共有多少子项
@Override
public int getItemCount() {
return mFruitList.size();
}
}
5、在MainActivity中使用RecyclerView,用法与ListView类似
public class MainActivity extends AppCompatActivity {
private List<Fruit>fruitsList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits(); //初始化数据源
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
// LayoutManager用于指定RecyclerView的布局方式
LinearLayoutManager layoutManager = new
LinearLayoutManager (this);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitsList);
recyclerView.setAdapter(adapter);
}
private void initFruits() {
Fruit apple = new Fruit("Apple",R.drawable.chair);
fruitsList.add(apple);
……. 添加数据,此处省略
}
}
二、实现横向滚动
1、修改fruit_item.xml文件中的布局
2、
LinearLayoutManager layoutManager = new LinearLayoutManager (this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
三、实现瀑布流布局
1、修改fruit_item.xml文件中的布局
2、
//第一个参数:指定布局列数
//第二个参数:指定布局排列方向
StaggeredGridLayoutManager layoutManager = new
StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
四、RecyclerView的点击事件
ListView的点击事件缺陷:ListView通过setOnItemClickListener()为子项注册点击事件,无法为子项中具体的空间分别指定点击事件。
RecyclerView摒弃了这种缺陷,所有的点击事件都必须由每个Item中具体的View去注册监听。可以轻松实现子项中任意控件或布局的点击事件。
@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);
//为子项最外层的布局实例fruitView注册监听
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
//为子项中的图片fruitImage注册监听
holder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
return holder;
}