ListView以及RecyclerView的学习以及电影排行榜的实现
ListView学习
适配器作用与选择
数组数据无法直接给 ListView,需借助适配器。Android 中常用ArrayAdapter
,通过泛型指定适配数据类型,构造函数传入要适配数据 。其有多个构造函数,需依实际选,示例因数据是字符串,泛型指定为String
。
ArrayAdapter
构造参数
构造ArrayAdapter
时依次传入:当前上下文、ListView 子项布局 id、要适配的数据data。
调用 ListView 的setAdapter()
方法,传入构建好的适配器对象,完成 ListView 与数据的关联 。
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" };//配置data
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1, data);//使用ArrayAdapter并用泛型指定String,构造函数传入context,ListView的布局id,以及构建好的data
ListView listView = (ListView) findViewById(R.id.list_view);//绑定
listView.setAdapter(adapter);//通过setAdapter方法传入构建好的适配器对象,完成ListView与数据的关联
}
}
现在就实现了一个只有文字的list界面,下一步给每一个子项都加上图片
首先定义一个实体类Fruit并给他们加上两个属性name以及imageId用来匹配图片,然后为ListView的子项指定一个我们自定义的布局,在layout下新建fruit_item.xml
<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>
下面需要我们自定义一个适配器,它继承自ArrayAdapter,并且把泛型指定为Fruit
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}//构造方法
public View getView(int position, View convertView, ViewGroup parent) {
// 获取当前位置的Fruit实例
Fruit fruit = getItem(position);
// 使用布局加载器将布局资源转换为View对象
// parent参数用于提供布局参数,false表示不立即将View添加到父容器
View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
// 从布局中查找ImageView和TextView控件
ImageView fruitImage = view.findViewById(R.id.fruit_image);
TextView fruitName = view.findViewById(R.id.fruit_name);
// 设置水果图片和名称
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
// 返回配置好的视图
return view;
}
- 构造函数与方法重写:
FruitAdapter
重写父类构造函数,用于传递上下文、ListView 子项布局 id 和数据;重写getView()
方法,该方法在子项滚动到屏幕内时调用。 getView()
方法流程:先通过getItem()
获取当前项Fruit
实例,再用LayoutInflater
加载传入布局,inflate()
方法第三个参数设为false
(让父布局中声明的layout
属性生效,且不为View
添加父布局,避免无法加入ListView
) 。- 控件设置与返回:调用
findViewById()
获取ImageView
和TextView
实例,分别用setImageResource()
、setText()
设置图片和文字,最后返回布局,完成自定义适配器。
接下来配置一下MainActivity
package com.example.listviewtest;
import android.os.Bundle;
import android.widget.ListView;
import androidx.appcompat.app.AppCompatActivity;
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 = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initFruits() {
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.mango_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);
}
}
}
用一个initFruits方法来初始化所有水果,然后循环往list添加了两遍
下面是展示图
ListView的优化方案:
在getView中有个参数conventView,这个参数用于将之前加载好的布局进行缓存,以便重用。
if(convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false)
}else{
view = convertView;
}
修改getView中的小部分代码,在getView中进行判断,若没有缓存则直接使用layoutInflater加载布局,若有缓存则直接对converView进行重用。
虽然现在已经不会再去重复加载布局,但是在getView的方法中还是会调用View的findViewById()方法来获取一次控件的实例,可以接著一个ViewHolder来对这部分性能进行优化
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHolder); // 将 ViewHolder 存储在 View 中
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag(); // 重新获取 ViewHolder
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder {
ImageView fruitImage;
TextView fruitName;
}
}
ViewHolder
作用:新增内部类ViewHolder
,用于缓存控件实例,避免重复通过findViewById()
获取控件,提升效率。- 缓存逻辑:
convertView
为null
时,创建ViewHolder
,存控件实例,并用View
的setTag()
存储ViewHolder
;convertView
不为null
时,用getTag()
取出ViewHolder
。
RecyclerView学习
我们用上面的图片和fruit_item来重新构筑一个项目,加入这些控件我就不在描述
先给RecyclerView准备一个适配器,FruitAdapter,这个适配器来自RecyclerView.Adapter,并且将泛型指定为FruitAdapter.ViewHolder,其中,ViewHolder是我们在FruitAdapter中定义的一个内部类。
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{
private List<Fruit> mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder{
View fruitView;
ImageView fruitImage;
TextView fruitName;
public ViewHolder(@NonNull View itemView) {
super(itemView);
fruitView = itemView;
fruitImage = (ImageView) itemView.findViewById(R.id.fruit_image);
fruitName = (TextView) itemView.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> fruitList){
mFruitList = fruitList;
}
@NonNull
@Override
public 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);
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(),"u clicked view "+fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
holder.fruitImage.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
int position = holder.getLayoutPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(),"u clicked image "+fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
return holder;
}
@Override
public void onBindViewHolder(@NonNull FruitAdapter.ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
@Override
public int getItemCount() {
return mFruitList.size();
}
}
-
ViewHolder
定义- 是
RecyclerView.Adapter
中的内部类,需继承RecyclerView.ViewHolder
。 - 构造函数接收
View
参数(通常是RecyclerView
子项最外层布局 ),可通过findViewById
获取子项内ImageView
、TextView
等控件实例 。
- 是
-
FruitAdapter
构造函数:用于传入要展示的数据源,赋值给全局变量(如mFruitList
),后续操作基于该数据源 。 -
必须重写的方法
onCreateViewHolder
:创建ViewHolder
实例,加载子项布局(如fruit_item
),创建ViewHolder
并传入布局,返回该实例 。onBindViewHolder
:为RecyclerView
子项数据赋值,子项滚动到屏幕内时执行;通过position
获取当前Fruit
实例,将数据设置到ViewHolder
控件中 。getItemCount
:告知RecyclerView
子项总数,直接返回数据源长度 。
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 = (RecyclerView) findViewById(R.id.recycler_view); LinearLayoutManager layoutManager = new LinearLayoutManager(this); 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("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.mango_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); } } }
RecyclerView配置流程(onCreate` 方法内)
- 获取核心控件实例:先拿到
RecyclerView
在布局里对应的实例,这是操作RecyclerView
的基础 - 设置布局管理器
- 创建
LinearLayoutManager
对象,它的作用是指定RecyclerView
的布局展示形式 - 将创建好的
LinearLayoutManager
设置给RecyclerView
,这里用LinearLayoutManager
能实现和ListView
类似的线性列表展示效果
- 创建
- 适配器相关操作
- 实例化
FruitAdapter
,并把之前初始化好的水果数据,通过构造函数传递给FruitAdapter
,让适配器能拿到要展示的数据 - 调用
RecyclerView
的setAdapter()
方法,把FruitAdapter
和RecyclerView
关联起来,这样数据就能通过适配器渲染到RecyclerView
上,完成数据与RecyclerView
展示的关联搭建
- 实例化
RecyclerView的点击事件
@Override public 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); 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(),"u clicked view "+fruit.getName(),Toast.LENGTH_SHORT).show(); } }); holder.fruitImage.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v){ int position = holder.getLayoutPosition(); Fruit fruit = mFruitList.get(position); Toast.makeText(v.getContext(),"u clicked image "+fruit.getName(),Toast.LENGTH_SHORT).show(); } });
一、代码实现逻辑
- ViewHolder 改造:在
ViewHolder
里新增fruitView
变量,用于保存子项最外层布局实例,为注册点击事件做准备 - 点击事件注册(
onCreateViewHolder
方法中)- 给最外层布局(通过
fruitView
)注册点击事件 - 给
ImageView
(fruitImage
)也注册点击事件 - 事件处理流程:
- 先通过
holder.getAdapterPosition()
获取点击项在适配器里的位置position
- 再用
position
从数据源mFruitList
中拿到对应的Fruit
实例 - 最后用
Toast
弹出不同内容,区分是布局点击还是ImageView
点击(提示分别为you clicked view + 水果名
、you clicked image + 水果名
)
- 先通过
- 给最外层布局(通过
二、RecyclerView
优势体现
RecyclerView
可轻松给子项里任意控件(如这里的最外层布局、ImageView
)或布局注册点击事件,实现精细的交互控制,这是它强大功能的体现
三、效果验证
ImageView
点击效果:重新运行代码,点击香蕉图片部分,会触发ImageView
的点击事件,弹出对应Toast
提示- 未注册控件点击的处理:点击菠萝文字部分时,因为
TextView
没注册点击事件,点击会被子项最外层布局的点击事件捕获,也会弹出布局点击对应的Toast
提示 ,体现了事件的传递与处理机制
实现电影排行榜
列表展示一个电影列表页面,每一项显示评分、电影名称、上映时间、主演名字、和海报图片
大概要求:
1、选择Top20的电影展示
2、点击列表中主演的名字,会弹框显示主演的基本信息
3、点击列表中的海报图片,会弹框显示放大的海报图片
4、写两个Activity 分别使用 RecyclerView 和ListView 实现同样的显示功能
5、点击RecyclerView 的Activity标题跳转到ListView界面,同样点击ListView 的Activity标题跳转到RecyclerView 界面
首先创建各项布局
<?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="vertical">
<TextView
android:id="@+id/title_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:padding="16dp"
android:text="电影列表 (ListView)"
android:textColor="@android:color/white"
android:textSize="18sp"
android:clickable="true"/>
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
一个标题,下面ListView
package com.example.movielist;
import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import com.bumptech.glide.Glide;
import java.util.List;
public class MovieListViewAdapter extends BaseAdapter {
private Context context;
private List<Movie> movieList;
private LayoutInflater inflater;
public MovieListViewAdapter(Context context, List<Movie> movieList) {
this.context = context;
this.movieList = movieList;
this.inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return movieList.size();
}
@Override
public Object getItem(int position) {
return movieList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_movie, parent, false);
holder = new ViewHolder();
holder.tvTitle = convertView.findViewById(R.id.tv_title);
holder.tvRating = convertView.findViewById(R.id.tv_rating);
holder.tvReleaseDate = convertView.findViewById(R.id.tv_release_date);
holder.tvActors = convertView.findViewById(R.id.tv_actors);
holder.ivPoster = convertView.findViewById(R.id.iv_poster);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Movie movie = movieList.get(position);
// 设置电影信息
holder.tvTitle.setText(movie.getTitle());
holder.tvRating.setText("评分: " + movie.getRating());
holder.tvReleaseDate.setText("上映时间: " + movie.getReleaseDate());
// 设置演员信息
StringBuilder actorsSb = new StringBuilder("主演: ");
for (int i = 0; i < movie.getActors().size(); i++) {
actorsSb.append(movie.getActors().get(i).getName());
if (i < movie.getActors().size() - 1) {
actorsSb.append(", ");
}
}
holder.tvActors.setText(actorsSb.toString());
// 加载海报图片
Glide.with(context)
.load(movie.getPosterUrl())
.placeholder(R.drawable.ic_launcher_background)
.into(holder.ivPoster);
// 保存当前电影对象到Tag,用于点击事件
convertView.setTag(R.id.tag_movie, movie);
// 海报点击事件
View finalConvertView = convertView;
holder.ivPoster.setOnClickListener(v -> {
Movie m = (Movie) finalConvertView.getTag(R.id.tag_movie);
showLargePoster(m.getPosterUrl());
});
// 演员点击事件
View finalConvertView1 = convertView;
holder.tvActors.setOnClickListener(v -> {
Movie m = (Movie) finalConvertView1.getTag(R.id.tag_movie);
showActorInfoDialog(m.getActors());
});
return convertView;
}
static class ViewHolder {
TextView tvTitle, tvRating, tvReleaseDate, tvActors;
ImageView ivPoster;
}
// 显示放大的海报
private void showLargePoster(String posterUrl) {
Dialog dialog = new Dialog(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
dialog.setContentView(R.layout.dialog_poster);
ImageView ivLargePoster = dialog.findViewById(R.id.iv_large_poster);
Glide.with(context)
.load(posterUrl)
.placeholder(R.drawable.ic_launcher_background)
.into(ivLargePoster);
dialog.show();
ivLargePoster.setOnClickListener(v -> dialog.dismiss());
}
// 显示演员信息弹窗
private void showActorInfoDialog(List<Actor> actors) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
View dialogView = inflater.inflate(R.layout.dialog_actor_info, null);
Actor firstActor = actors.get(0);
((TextView) dialogView.findViewById(R.id.tv_actor_name)).setText(firstActor.getName());
((TextView) dialogView.findViewById(R.id.tv_actor_age)).setText("年龄: " + firstActor.getAge());
((TextView) dialogView.findViewById(R.id.tv_actor_nationality)).setText("国籍: " + firstActor.getNationality());
((TextView) dialogView.findViewById(R.id.tv_actor_biography)).setText("简介: " + firstActor.getBiography());
builder.setView(dialogView)
.setPositiveButton("确定", (dialog, which) -> dialog.dismiss())
.show();
}
}
因为其中多数的用法都在上面提到,就不仔细写了
item_movie.xml
<?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"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:id="@+id/iv_poster"
android:layout_width="100dp"
android:layout_height="150dp"
android:scaleType="centerCrop"
android:clickable="true"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/tv_rating"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="14sp"/>
<TextView
android:id="@+id/tv_release_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="14sp"/>
<TextView
android:id="@+id/tv_actors"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="14sp"
android:textColor="@color/colorPrimary"
android:clickable="true"/>
</LinearLayout>
</LinearLayout>
演员弹窗的布局
dialog_actor_info.xml
<?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"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tv_actor_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/tv_actor_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"/>
<TextView
android:id="@+id/tv_actor_nationality"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"/>
<TextView
android:id="@+id/tv_actor_biography"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"/>
</LinearLayout>
电影海报的弹窗dialog_poster.xml
<?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"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tv_actor_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/tv_actor_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"/>
<TextView
android:id="@+id/tv_actor_nationality"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"/>
<TextView
android:id="@+id/tv_actor_biography"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"/>
</LinearLayout>
<?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="vertical">
<TextView
android:id="@+id/title_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:padding="16dp"
android:text="电影列表 (RecyclerView)"
android:textColor="@android:color/white"
android:textSize="18sp"
android:clickable="true"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
上面是activity_recycler_view.xml
package com.example.movielist;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
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 com.bumptech.glide.Glide;
import java.util.List;
public class MovieRecyclerViewAdapter extends RecyclerView.Adapter<MovieRecyclerViewAdapter.MovieViewHolder> {
private Context context;
private List<Movie> movieList;
private LayoutInflater inflater;
public MovieRecyclerViewAdapter(Context context, List<Movie> movieList) {
this.context = context;
this.movieList = movieList;
this.inflater = LayoutInflater.from(context);
}
@NonNull
@Override
public MovieViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.item_movie, parent, false);
return new MovieViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MovieViewHolder holder, int position) {
Movie movie = movieList.get(position);
// 设置电影信息
holder.tvTitle.setText(movie.getTitle());
holder.tvRating.setText("评分: " + movie.getRating());
holder.tvReleaseDate.setText("上映时间: " + movie.getReleaseDate());
// 设置演员信息
StringBuilder actorsSb = new StringBuilder("主演: ");
for (int i = 0; i < movie.getActors().size(); i++) {
actorsSb.append(movie.getActors().get(i).getName());
if (i < movie.getActors().size() - 1) {
actorsSb.append(", ");
}
}
holder.tvActors.setText(actorsSb.toString());
// 加载海报图片(这里使用Glide库)
Glide.with(context)
.load(movie.getPosterUrl())
.placeholder(R.drawable.ic_launcher_background)
.into(holder.ivPoster);
// 海报点击事件 - 显示大图
holder.ivPoster.setOnClickListener(v -> showLargePoster(movie.getPosterUrl()));
// 演员点击事件 - 显示演员信息
holder.tvActors.setOnClickListener(v -> showActorInfoDialog(movie.getActors()));
}
@Override
public int getItemCount() {
return movieList.size();
}
public static class MovieViewHolder extends RecyclerView.ViewHolder {
TextView tvTitle, tvRating, tvReleaseDate, tvActors;
ImageView ivPoster;
public MovieViewHolder(@NonNull View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.tv_title);
tvRating = itemView.findViewById(R.id.tv_rating);
tvReleaseDate = itemView.findViewById(R.id.tv_release_date);
tvActors = itemView.findViewById(R.id.tv_actors);
ivPoster = itemView.findViewById(R.id.iv_poster);
}
}
// 显示放大的海报
private void showLargePoster(String posterUrl) {
Dialog dialog = new Dialog(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
dialog.setContentView(R.layout.dialog_poster);
ImageView ivLargePoster = dialog.findViewById(R.id.iv_large_poster);
Glide.with(context)
.load(posterUrl)
.placeholder(R.drawable.ic_launcher_background)
.into(ivLargePoster);
dialog.show();
// 点击大图关闭弹窗
ivLargePoster.setOnClickListener(v -> dialog.dismiss());
}
// 显示演员信息弹窗
private void showActorInfoDialog(List<Actor> actors) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
View dialogView = inflater.inflate(R.layout.dialog_actor_info, null);
// 这里简化处理,只显示第一位演员信息
Actor firstActor = actors.get(0);
((TextView) dialogView.findViewById(R.id.tv_actor_name)).setText(firstActor.getName());
((TextView) dialogView.findViewById(R.id.tv_actor_age)).setText("年龄: " + firstActor.getAge());
((TextView) dialogView.findViewById(R.id.tv_actor_nationality)).setText("国籍: " + firstActor.getNationality());
((TextView) dialogView.findViewById(R.id.tv_actor_biography)).setText("简介: " + firstActor.getBiography());
builder.setView(dialogView)
.setPositiveButton("确定", (dialog, which) -> dialog.dismiss())
.show();
}
}
ListViewActivity
package com.example.movielist;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class ListViewActivity extends AppCompatActivity {
private ListView listView;
private MovieListViewAdapter adapter;
private List<Movie> movieList;
private TextView titleTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
// 初始化视图
listView = findViewById(R.id.list_view);
titleTextView = findViewById(R.id.title_list);
// 初始化数据
initMovieData();
// 设置ListView
adapter = new MovieListViewAdapter(this, movieList);
listView.setAdapter(adapter);
// 标题点击事件 - 跳转到RecyclerViewActivity
titleTextView.setOnClickListener(v -> {
Intent intent = new Intent(ListViewActivity.this, RecyclerViewActivity.class);
startActivity(intent);
});
}
// 初始化电影数据(与RecyclerViewActivity相同)
private void initMovieData() {
movieList = new ArrayList<>();
// 添加示例数据
for (int i = 1; i <= 20; i++) {
List<Actor> actors = new ArrayList<>();
actors.add(new Actor("演员" + i, 30 + i, "中国", "这是演员" + i + "的简介信息..."));
actors.add(new Actor("演员" + (i+100), 25 + i, "美国", "这是演员" + (i+100) + "的简介信息..."));
String imageUrl = String.format("https://picsum.photos/300/450?random=%d", i);
movieList.add(new Movie(
"电影标题" + i,
8.0 + (i % 10) * 0.1,
"2023-" + (i % 12 + 1) + "-" + (i % 28 + 1),
actors,
imageUrl // 占位图片URL
));
}
}
}
RecyclerViewActivity
package com.example.movielist;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class RecyclerViewActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private MovieRecyclerViewAdapter adapter;
private List<Movie> movieList;
private TextView titleTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_view);
// 初始化视图
recyclerView = findViewById(R.id.recycler_view);
titleTextView = findViewById(R.id.title_recycler);
// 初始化数据
initMovieData();
// 设置RecyclerView
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new MovieRecyclerViewAdapter(this, movieList);
recyclerView.setAdapter(adapter);
// 标题点击事件 - 跳转到ListViewActivity
titleTextView.setOnClickListener(v -> {
Intent intent = new Intent(RecyclerViewActivity.this, ListViewActivity.class);
startActivity(intent);
});
}
// 初始化电影数据(Top20电影)
private void initMovieData() {
movieList = new ArrayList<>();
// 添加示例数据
for (int i = 1; i <= 20; i++) {
List<Actor> actors = new ArrayList<>();
actors.add(new Actor("演员" + i, 30 + i, "中国", "这是演员" + i + "的简介信息..."));
actors.add(new Actor("演员" + (i+100), 25 + i, "美国", "这是演员" + (i+100) + "的简介信息..."));
String imageUrl = String.format("https://picsum.photos/300/450?random=%d", i);
movieList.add(new Movie(
"电影标题" + i,
8.0 + (i % 10) * 0.1,
"2023-" + (i % 12 + 1) + "-" + (i % 28 + 1),
actors,
imageUrl // 占位图片URL
));
}
}
}
展示效果图: