ListView
滑动屏幕控件
<ListView
android:id="@+id/lv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
public class MainActivity extends AppCompatActivity {
//测试数组
private String[] data={"Apple","Banana","Orange","Lemon","Potato","Tomato","Gabbage",
"Cocacola","Pepsi","Lemonade","Vegetable","Juice"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建适配器,用于向ListView中传递数据,ArrayAdapter类型的适配器可以通过泛型指定数据类型
//第一个参数当前上下文,第二个参数是ListView子项布局的ID,第三个参数是要适配的数据
//这里采用的simple_list_item_1是Android内置的布局文件,可用于显示一段TextView
ArrayAdapter<String> adapter=new ArrayAdapter<String>(
MainActivity.this, android.R.layout.simple_list_item_1,data);
//绑定控件
ListView listView=findViewById(R.id.lv_text);
//传入适配器
listView.setAdapter(adapter);
}
}
定制界面
资源类
主要存储每个子项布局中需要用到的资源,图片ID,文本内容等等
package cn.ywrby.listviewtext;
//定义Fruit类,里边包含了ListView中每一行需要包含的所有元素
public class Fruit {
private String text;
private int imageID;
public Fruit(String text, int imageID) {
this.text = text;
this.imageID = imageID;
}
public String getText() {
return text;
}
public int getImageID() {
return imageID;
}
}
子项布局
ListView中每行的布局方式
<?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="wrap_content">
<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/fruit_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"/>
</LinearLayout>
适配器
主要负责将数据类中的数据传入子项布局中
package cn.ywrby.listviewtext;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.List;
//定义适配器,负责向控件中传送数据
//继承自ArrayAdapter
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int textViewResourceID, @NonNull List<Fruit> objects) {
super(context, textViewResourceID, objects);
resourceId=textViewResourceID;
}
//重写getView方法,方法会在每个子项划入屏幕时被调用
//第一个参数表示当前项的ID,第二个表示要绑定的控件,第三个表示该控件的父控件
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Fruit fruit=getItem(position); //获取当前需要写入的数据类
View view= LayoutInflater.from(getContext()).inflate(resourceId,parent,false); //获取当前要操作控件
ImageView imageView=view.findViewById(R.id.fruit_image);
TextView textView=view.findViewById(R.id.fruit_id);
imageView.setImageResource(fruit.getImageID()); //写入图片数据
textView.setText(fruit.getText()); //文本数据
return view; //返回已经操作完成的控件
}
}
MainActivity
package cn.ywrby.listviewtext;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;
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);
initFruits();
FruitAdapter adapter= new FruitAdapter(
MainActivity.this, R.layout.fruit_item,fruitList);
//绑定控件
ListView listView=findViewById(R.id.lv_text);
//传入适配器
listView.setAdapter(adapter);
}
//初始化数组内容
private void initFruits(){
for(int i=0;i<5;i++){
Fruit apple=new Fruit("Apple",R.drawable.apple);
fruitList.add(apple);
Fruit lemon=new Fruit("Lemon",R.drawable.lemon);
fruitList.add(lemon);
Fruit banana=new Fruit("Banana",R.drawable.banana);
fruitList.add(banana);
Fruit cola=new Fruit("CokeCola",R.drawable.cola);
fruitList.add(cola);
}
}
}
性能优化
如果不进行任何性能优化,我们每次有子项布局划入屏幕都会重新加载资源,哪怕它刚刚已经被加载过了
所以,为了避免这种问题,可以对其进行优化
在每次重新加载控件前,都会对第二个参数进行一次判断,第二个参数是将之前的参数进行缓存,以方便以后重用,如果这个参数不为空,就可以省去加载控件的时间,直接复用以前的布局
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Fruit fruit=getItem(position); //获取当前需要写入的数据类
View view;
if(convertView==null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
}
else {
view=convertView;
}
ImageView imageView=view.findViewById(R.id.fruit_image);
TextView textView=view.findViewById(R.id.fruit_id);
imageView.setImageResource(fruit.getImageID()); //写入图片数据
textView.setText(fruit.getText()); //文本数据
return view; //返回已经操作完成的控件
}
上述方法优化了布局加载时间,但每次调用findViewByID还是会重新获取一次控件实例,所以还可以借助ViewHolder继续优化ListView性能
ListView点击事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fruit fruit=fruitList.get(position);
Toast.makeText(MainActivity.this,fruit.getText(),Toast.LENGTH_SHORT).show();
}
});
RecyclerView
重写适配器类
package cn.ywrby.recyclerviewtext;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{
//存储每个子布局所需资源的集合
private List<Fruit> mFruitList;
//内部类ViewHolder,缓存布局方式
static class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView textView;
//构造方法
public ViewHolder(@NonNull View itemView) {
super(itemView);
imageView=itemView.findViewById(R.id.fruit_image);
textView=itemView.findViewById(R.id.fruit_id);
}
}
//构造方法
public FruitAdapter(List<Fruit> mFruitList) {
this.mFruitList = mFruitList;
}
//onCreateViewHolder负责创建ViewHolder
@NonNull
@Override
public FruitAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//获取布局方式
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
ViewHolder holder=new ViewHolder(view); //创建ViewHolder对象
return holder;
}
//将资源与对应的子布局绑定
//在每次对应子布局进入屏幕时调用
@Override
public void onBindViewHolder(@NonNull FruitAdapter.ViewHolder holder, int position) {
Fruit fruit=mFruitList.get(position); //获取对应位置资源
holder.imageView.setImageResource(fruit.getImageID()); //将对应资源写入
holder.textView.setText(fruit.getText());
}
//长度
@Override
public int getItemCount() {
return mFruitList.size();
}
}
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);
initFruits(); //初始化资源集合
RecyclerView recyclerView=findViewById(R.id.recycler_view); //找到RecyclerView对象
LinearLayoutManager layoutManager=new LinearLayoutManager(this); //表示RecyclerView对象的布局方式,这里采用线性布局
recyclerView.setLayoutManager(layoutManager); //绑定布局方式
FruitAdapter fruitAdapter=new FruitAdapter(fruitList);
recyclerView.setAdapter(fruitAdapter); //将资源导入
}
//初始化数组内容
private void initFruits(){
for(int i=0;i<5;i++){
Fruit apple=new Fruit("Apple",R.drawable.apple);
fruitList.add(apple);
Fruit lemon=new Fruit("Lemon",R.drawable.lemon);
fruitList.add(lemon);
Fruit banana=new Fruit("Banana",R.drawable.banana);
fruitList.add(banana);
Fruit cola=new Fruit("CokeCola",R.drawable.cola);
fruitList.add(cola);
}
}
}
RecyclerView实现横向滚动
修改fruit_item
<?xml version="1.0" encoding="utf-8"?>
<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/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
<TextView
android:id="@+id/fruit_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"/>
</LinearLayout>
修改MainActivity中的布局方式
RecyclerView recyclerView=findViewById(R.id.recycler_view); //找到RecyclerView对象
LinearLayoutManager layoutManager=new LinearLayoutManager(this); //表示RecyclerView对象的布局方式,这里采用线性布局
recyclerView.setLayoutManager(layoutManager); //绑定布局方式
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); //设置布局排列方式,默认纵向,这里设置为横向
同样的通过设置布局方式,还可以很轻松的设置为瀑布流布局等排列方式
RecyclerView点击事件
RecyclerView并不提供类似ListView中setOnItemClickListener的注册监听器方法,每个监听器需要手动绑定,并且可以轻松控制如何绑定,是绑定整个子布局还是其中某个控件,都可以轻松实现
package cn.ywrby.recyclerviewtext;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{
//存储每个子布局所需资源的集合
private List<Fruit> mFruitList;
//内部类ViewHolder,缓存布局方式
static class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView textView;
private View fruitView;
//构造方法
public ViewHolder(@NonNull View itemView) {
super(itemView);
fruitView=itemView;
imageView=itemView.findViewById(R.id.fruit_image);
textView=itemView.findViewById(R.id.fruit_id);
}
}
//构造方法
public FruitAdapter(List<Fruit> mFruitList) {
this.mFruitList = mFruitList;
}
//onCreateViewHolder负责创建ViewHolder
@NonNull
@Override
public FruitAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//获取布局方式
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
final ViewHolder holder=new ViewHolder(view); //创建ViewHolder对象
//绑定整个布局
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.getText(),Toast.LENGTH_SHORT).show();
}
});
//绑定图片控件
holder.imageView.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.getText(),Toast.LENGTH_SHORT).show();
}
});
return holder;
}
//将资源与对应的子布局绑定
//在每次对应子布局进入屏幕时调用
@Override
public void onBindViewHolder(@NonNull FruitAdapter.ViewHolder holder, int position) {
Fruit fruit=mFruitList.get(position); //获取对应位置资源
holder.imageView.setImageResource(fruit.getImageID()); //将对应资源写入
holder.textView.setText(fruit.getText());
}
//长度
@Override
public int getItemCount() {
return mFruitList.size();
}
}
上文中通过修改内部类成员变量以及onCreateViewHolder实现了每个布局的点击事件,并且是分别为图片和整个子布局添加响应事件