ListView
ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
在MainActivity中
public class MainActivity extends AppCompatActivity {
private String[] data = {"Apple","Banana","Orange","Watermelon","Pear",
"Grape","Pineapple","Strawberry","Cherry","Mango",
"Apple","Banana","Orange","Watermelon","Pear",
"Grape","Pineapple","Strawberry","Cherry","Mango",};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.list_view);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_list_item_1,data);
listView.setAdapter(adapter);
}
}
● data 是ListView中要显示的数据,可以自定义,也可以从后台获取
● ArrayAdapter 数据无法直接传给ListView,需要借助适配器来完成。而ArrayAdapter是Android中提供的适配器实现类,可以通过泛型来指定要适配的数据类型。
● ArrayAdapter 构造函数参数一:当前上下文;参数二:ListView子项布局的id;参数三:要适配的数据。
● android.R.layout.simple_list_item_1 是一个Android内置的布局,里面只有一个TextView,可用于简单地显示一段文本。
● listView.setAdapter() 通过该方法将构建好的适配器对象传递进去,这样ListView和数据之间就建立
定制ListView的界面
ListView的子项布局正常情况下不止一段文本,我们可以对ListView进行界面的定制
第一,定义一个实体类,作为ListView适配器的适配对象
新建类Fruit
public class Fruit
{
private String name;
private int imageId;
public Fruit(String _Name,int _Id) {
Name = _Name;
Id = _Id;
}
public String getName() {return this.Name;}
public int getId(){return this.Id;}
}
name表示水果的名字,imageId表示水果对应图片的资源id。
第二 自定义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="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/fruitimage"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
</ImageView>
<TextView
android:id="@+id/fruittext"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</LinearLayout>
第三 自定义适配器
这个适配器继承ArrayAdapter,并将泛型指定为Fruit类。
public class FruitAdapter extends ArrayAdapter<Fruit>{
private int resourceId;
public FruitAdapter(Context cotext,int textViewResourceId,List<Fruit> objects)
{
super(context,textViewResourceId,object);
resourceId=textViewResourceId;
}
public View getView(int position,View converView,ViewGroup parent)
{
Fruit fruit=getItem(position);
View view=LayoutInflater.from(getContext()).inflate(resourId,parent,false);
ImageView fruiteImage= view.findViewById(R.id.fruit_image);
TextView fruitName= view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId);
furitName.setText(fruit.getName());
return view;
}
}
● FruitAdapter 重写父类个构造函数,将上下文、ListView子项布局id和数据传递进来
● 重写getView()方法。每个子项滚动到屏幕内时都会调用这个方法。
● getItem()方法得到当前项的Fruit实例
● LayoutInflater 为子项加载我们传入的布局
● inflate()方法的参数三要传入false,表示只让我们在父布局中声明的layout生效,但不会为这个View添加父布局,因为View一旦有了父布局,就不能将它添加到ListView中
● 调用View的findViewById()方法分别获取到ImageView和TextView的实例,然后分别调用setImageResource()和setText()来设置显示图片和文字
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
ListView listView = (ListView) findViewById(R.id.list_view);
FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, mFruitList);
listView.setAdapter(adapter);
//注册监听器
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fruit fruit = mFruitList.get(position);
Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
}
RecyclerView
ListView只能实现数据纵向滚动效果,无法实现横向滚动,且效率低下。因此Android提供了一个增强版的ListView——RecyclerView。
RecyclerView属于新增的控件,为了让RecyclerView在所有的版本上都能适用,Android团队采用同样的方式,将RecyclerView定义在了support库中.
implementation 'androidx.recyclerview:recyclerview:1.0.0'
由于RecyclerView并不是内置在系统SDK当中的,所以需要把完整的包路径写出来。
main_activity.xml
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
Fuit和frui_item_xml参考ListView
下来准备RecyclerView适配器。
1.创建适配器类继承·RecyclerView.Adapter并且把泛型指定为FruitAdapter.ViewHolder。
2.创建内部类ViewHolder即RecyclerView.ViewHolder的子类
3.重写RecyclerView.Adapter的各种方法
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>
{
private List<Fruit> mFruitList;
static class ViewHolder extends RecyclerView.Holder
{
ImageView fruitImage;
ImageView fruitName;
public ViewHolder(View view)
{
super(view);
fruitImage=(ImageView)view.findViewById(R.id.furit_image);
fruitName=(TextView)view.findViewById(R.id.furit_name);
}
}
public FruitAdapter(List<Fruit> fruitList)
{
mFruitList=fruitList;
}
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;
}
public void onBindViewHolder(ViewHolder holder,int position)
{
Fruit fruit=mFruitList.from(positon);
holder.furitImage.setImageResource(fruit.getImageId());
holder.furitName.setText(furit.getName());
}
public int getItemCount()
{
return mFruitList.size();
}
}
重写onCreateHolder():创建Holder,并把布局加载到构造函数中,再把viewHolder返回;
onBindViewHolder():对子项数据进行赋值,并在子项被滚入屏幕内执行,position获得当前实例
getItemCount:RecyclerView子项的数目。
适配器准备好以后可以使用RecyclerView,修改MainActivity的代码
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList = new ArrayList<>();
private FruitAdapter fruitAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.activity_main);
initData();
fruitAdapter = new FruitAdapter(fruitList);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
//默认为垂直方向
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(fruitAdapter);
}
public void initData() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit("Apple", R.drawable.apple_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 watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
fruitList.add(mango);
}
}
}
实现横向滚动和瀑布流布局
首先对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/image"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal">
</ImageView>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp">
</TextView>
</LinearLayout>
然后修改MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.activity_main);
initData();
FruitAdapter fruitAdapter = new FruitAdapter(fruitList);
RecyclerView recyclerView = findViewById(R.id.recycler_h);
//设置方向水平 LinearLayoutManager.HORIZONTAL
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setAdapter(fruitAdapter);
recyclerView.setLayoutManager(layoutManager);
}
LIstView不能实现水平滑动的原因:
ListView的布局排列是由自身去管理的
RecyclerView的布局排列是由LayoutManager去控制,LayoutManager中制定了一套可扩展的布局排列接口,子类只要按照接口的规范来实现,就能定制出各种不同排列方式的布局。
RecycleView还提供给我们GridLayoutManager和StaggeredGridLayoutManager这两种内置的布局排列方式。前者实现网格布局后者实现瀑布流布局
瀑布流布局
除了LinearLayoutManager之外,RecyclerView还提供了GridLayoutManager和StaggerdGridLayoutManager这两种内置的布局排列方式。
● GridLayoutManager可以用于实现网格布局,
● StaggerdGridLayoutManager可以用于实现瀑布流布局。
- 修改fruit_item.xml文件
因为要实现瀑布流效果,所以需要对上面的fruit_item.xml文件进行修改:
<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>
- 使用StaggerdGridLayoutManager
public class MainActivity extends AppCompatActivity {
private List<Fruit> mFruitList = 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);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(mFruitList);
recyclerView.setAdapter(adapter);
}
private void initFruits() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);
mFruitList.add(apple);
Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);
mFruitList.add(banana);
Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);
mFruitList.add(orange);
Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);
mFruitList.add(watermelon);
Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);
mFruitList.add(pear);
Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);
mFruitList.add(grape);
Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);
mFruitList.add(pineapple);
Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);
mFruitList.add(strawberry);
Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);
mFruitList.add(cherry);
Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);
mFruitList.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();
}
}
StaggeredGridLayoutManager的构造函数接受两个参数,参数一:用于指定布局的列数,传入3表示会把布局分为3列;参数二用于指定布局的排列方向。
RecyclerView的点击事件
·RecyclerView并没有提供类似于setOnItemClickListener()这样的注册监听器方法,而是需要我们自己给子项具体的View去注册点击事件。
其实ListView的setOnItemClickListener()方法注册的是子项的点击事件,如果想点击子项里面具体的某一个按钮,ListView实现起来就有点复杂。为此,RecyclerView干脆直接摒弃了子项点击事件的监听器,所有的点击事件都由具体的View去注册。
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> mFruitList;
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);
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();
}
});
//给子项中的ImageView注册点击事件
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;
}
//对RecyclerView子项的数据进行赋值
@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();
}
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView fruitImage;
TextView fruitName;
View fruitView; //子项最外层布局实例
public ViewHolder(View itemView) {
super(itemView);
fruitView = itemView;
fruitImage = ((ImageView) itemView.findViewById(R.id.fruit_image));
fruitName = ((TextView) itemView.findViewById(R.id.fruit_name));
}
}
}