基于Nano Banana 2 Lite和Gemini Omni Flash的世界杯阵容App开发
最近在开发世界杯主题应用时,发现传统静态阵容展示已经无法满足用户需求。球迷们不仅想看到首发阵容名单,更希望直观感受球员风采和战术布局。本文将结合最新的Nano Banana 2 Lite和Gemini Omni Flash技术,打造一个能够动态生成球员形象和战术动画的世界杯首发阵容App。
这个项目适合有一定Android开发基础,想要探索AI图像生成和视频编辑技术的开发者。通过本文,你将掌握如何将两种AI模型无缝集成到移动应用中,实现从文本描述到生动视频的完整创作流程。
1. 技术背景与核心概念
1.1 Nano Banana 2 Lite技术解析
Nano Banana 2 Lite是Google最新推出的轻量级图像生成模型,专门为移动端和高并发场景优化。该模型基于Gemini 3.1 Flash Lite Image架构,在保持高质量图像生成的同时,显著降低了计算资源和响应时间。
在实际测试中,Nano Banana 2 Lite生成一张1024x1024分辨率图像仅需4秒左右,成本为每千张图像0.034美元。这种性能表现使其非常适合实时交互应用,比如我们的世界杯阵容App需要快速生成多个球员形象。
与之前的版本相比,Nano Banana 2 Lite在提示词遵循度和字符一致性方面有显著提升。这意味着当我们用"生成梅西穿着阿根廷队服的比赛照片"这样的提示词时,模型能够准确理解足球相关的专业术语和视觉特征。
1.2 Gemini Omni Flash视频生成能力
Gemini Omni Flash是多模态视频生成模型,支持文本、图像和视频的混合输入。在我们的世界杯App中,它将负责把静态的球员图像转化为生动的比赛片段。
该模型的核心优势在于对话式视频编辑能力。用户可以用自然语言指令如"让梅西带球突破防守"来指导视频生成过程,模型会基于对足球运动的理解生成相应的动画效果。
目前Omni Flash支持最长10秒的视频生成,正好适合展示球员的典型技术动作。每秒钟视频生成成本为0.10美元,对于短片段展示来说成本可控。
1.3 技术组合的价值主张
将Nano Banana 2 Lite和Gemini Omni Flash结合使用,可以创建完整的多媒体内容生产流水线。这种组合特别适合需要快速原型开发和内容迭代的场景。
在我们的世界杯App中,工作流程将是:首先用Nano Banana 2 Lite根据球员信息生成标准肖像,然后使用Gemini Omni Flash为每个球员创建特色技术动作视频,最后将所有内容整合成交互式阵容展示。
2. 开发环境准备
2.1 基础开发环境配置
为了确保项目顺利进行,需要准备以下开发环境:
操作系统要求:
- Windows 10/11 或 macOS 12.0+ 或 Ubuntu 20.04+
- 至少8GB RAM,推荐16GB
- 10GB可用磁盘空间
开发工具:
- Android Studio 2023.2.1+
- Java Development Kit 17+
- Android SDK API 34+
依赖管理: 使用Gradle进行依赖管理,确保网络连接稳定以便下载相关SDK。
2.2 API密钥获取与配置
访问Google AI Studio获取必要的API密钥:
- 前往 Google AI Studio
- 使用Google账户登录
- 在控制台创建新项目
- 启用Gemini API服务
- 生成API密钥
在项目的 local.properties 文件中安全存储API密钥:
gemini.api.key=你的API密钥
nanobanana.api.key=你的Nano Banana API密钥
2.3 项目基础结构搭建
创建新的Android项目,选择Empty Activity模板。配置app级别的 build.gradle 文件:
android {
compileSdk 34
defaultConfig {
applicationId "com.worldcup.lineup"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.10.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// 网络请求库
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
// 图像加载库
implementation 'com.github.bumptech.glide:glide:4.15.1'
// 视频播放库
implementation 'com.google.android.exoplayer:exoplayer:2.19.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
3. 核心功能实现
3.1 Nano Banana 2 Lite图像生成集成
首先创建API服务接口用于调用Nano Banana 2 Lite:
// 文件路径:app/src/main/java/com/worldcup/lineup/api/NanoBananaService.java
public interface NanoBananaService {
@POST("v1/images:generate")
Call<ImageResponse> generateImage(@Body ImageRequest request);
@Headers("Content-Type: application/json")
class ImageRequest {
private final ImagePrompt prompt;
private final int width;
private final int height;
public ImageRequest(ImagePrompt prompt, int width, int height) {
this.prompt = prompt;
this.width = width;
this.height = height;
}
}
class ImagePrompt {
private final String text;
public ImagePrompt(String text) {
this.text = text;
}
}
class ImageResponse {
private List<ImageData> images;
public List<ImageData> getImages() {
return images;
}
class ImageData {
private String base64Data;
public String getBase64Data() {
return base64Data;
}
}
}
}
实现球员图像生成工具类:
// 文件路径:app/src/main/java/com/worldcup/lineup/utils/PlayerImageGenerator.java
public class PlayerImageGenerator {
private static final String BASE_URL = "https://generativelanguage.googleapis.com/";
private final NanoBananaService service;
private final String apiKey;
public PlayerImageGenerator(Context context) {
this.apiKey = getApiKey(context);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(createHttpClient())
.build();
this.service = retrofit.create(NanoBananaService.class);
}
public void generatePlayerImage(String playerName, String team, String position,
ImageGenerationCallback callback) {
String prompt = buildPlayerPrompt(playerName, team, position);
NanoBananaService.ImageRequest request =
new NanoBananaService.ImageRequest(
new NanoBananaService.ImagePrompt(prompt), 512, 512);
service.generateImage(request).enqueue(new Callback<NanoBananaService.ImageResponse>() {
@Override
public void onResponse(Call<NanoBananaService.ImageResponse> call,
Response<NanoBananaService.ImageResponse> response) {
if (response.isSuccessful() && response.body() != null) {
List<NanoBananaService.ImageResponse.ImageData> images =
response.body().getImages();
if (images != null && !images.isEmpty()) {
String base64Image = images.get(0).getBase64Data();
callback.onSuccess(base64Image);
return;
}
}
callback.onError("图像生成失败");
}
@Override
public void onFailure(Call<NanoBananaService.ImageResponse> call, Throwable t) {
callback.onError("网络请求失败: " + t.getMessage());
}
});
}
private String buildPlayerPrompt(String playerName, String team, String position) {
return String.format(
"生成%s队%s球员%s的专业足球比赛照片。穿着正式比赛队服,在绿茵场上,展现足球运动员的专业姿态。背景为体育场,光线自然,图像清晰。",
team, position, playerName
);
}
public interface ImageGenerationCallback {
void onSuccess(String base64Image);
void onError(String errorMessage);
}
}
3.2 Gemini Omni Flash视频生成集成
创建视频生成服务接口:
// 文件路径:app/src/main/java/com/worldcup/lineup/api/OmniFlashService.java
public interface OmniFlashService {
@POST("v1/videos:generate")
Call<VideoResponse> generateVideo(@Body VideoRequest request);
class VideoRequest {
private final VideoPrompt prompt;
private final int durationSeconds;
public VideoRequest(VideoPrompt prompt, int durationSeconds) {
this.prompt = prompt;
this.durationSeconds = durationSeconds;
}
}
class VideoPrompt {
private final String text;
private final String imageReference;
public VideoPrompt(String text, String imageReference) {
this.text = text;
this.imageReference = imageReference;
}
}
class VideoResponse {
private VideoData video;
public VideoData getVideo() {
return video;
}
class VideoData {
private String videoUri;
private int duration;
public String getVideoUri() {
return videoUri;
}
}
}
}
实现球员动作视频生成器:
// 文件路径:app/src/main/java/com/worldcup/lineup/utils/PlayerVideoGenerator.java
public class PlayerVideoGenerator {
private static final String BASE_URL = "https://generativelanguage.googleapis.com/";
private final OmniFlashService service;
private final String apiKey;
public PlayerVideoGenerator(Context context) {
this.apiKey = getApiKey(context);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(createHttpClient())
.build();
this.service = retrofit.create(OmniFlashService.class);
}
public void generatePlayerVideo(String playerName, String position,
String base64Image, VideoGenerationCallback callback) {
String prompt = buildVideoPrompt(playerName, position);
OmniFlashService.VideoRequest request =
new OmniFlashService.VideoRequest(
new OmniFlashService.VideoPrompt(prompt, base64Image), 5);
service.generateVideo(request).enqueue(new Callback<OmniFlashService.VideoResponse>() {
@Override
public void onResponse(Call<OmniFlashService.VideoResponse> call,
Response<OmniFlashService.VideoResponse> response) {
if (response.isSuccessful() && response.body() != null) {
OmniFlashService.VideoResponse.VideoData video =
response.body().getVideo();
if (video != null) {
callback.onSuccess(video.getVideoUri());
return;
}
}
callback.onError("视频生成失败");
}
@Override
public void onFailure(Call<OmniFlashService.VideoResponse> call, Throwable t) {
callback.onError("网络请求失败: " + t.getMessage());
}
});
}
private String buildVideoPrompt(String playerName, String position) {
Map<String, String> positionActions = new HashMap<>();
positionActions.put("前锋", "展示射门和突破动作");
positionActions.put("中场", "展示传球和组织进攻动作");
positionActions.put("后卫", "展示抢断和防守动作");
positionActions.put("守门员", "展示扑救动作");
String action = positionActions.getOrDefault(position, "展示足球技术动作");
return String.format("让%s在足球比赛中%s。画面流畅自然,展现专业足球运动员的技术特点。",
playerName, action);
}
public interface VideoGenerationCallback {
void onSuccess(String videoUri);
void onError(String errorMessage);
}
}
3.3 数据模型设计
定义球员和阵容的数据结构:
// 文件路径:app/src/main/java/com/worldcup/lineup/models/Player.java
public class Player {
private String id;
private String name;
private String team;
private String position;
private int jerseyNumber;
private String imageUrl;
private String videoUrl;
private boolean isCaptain;
private Map<String, Object> stats;
public Player(String name, String team, String position, int jerseyNumber) {
this.name = name;
this.team = team;
this.position = position;
this.jerseyNumber = jerseyNumber;
this.stats = new HashMap<>();
}
// Getter和Setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getTeam() { return team; }
public void setTeam(String team) { this.team = team; }
public String getPosition() { return position; }
public void setPosition(String position) { this.position = position; }
public int getJerseyNumber() { return jerseyNumber; }
public void setJerseyNumber(int jerseyNumber) { this.jerseyNumber = jerseyNumber; }
public String getImageUrl() { return imageUrl; }
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
public String getVideoUrl() { return videoUrl; }
public void setVideoUrl(String videoUrl) { this.videoUrl = videoUrl; }
public boolean isCaptain() { return isCaptain; }
public void setCaptain(boolean captain) { isCaptain = captain; }
public Map<String, Object> getStats() { return stats; }
public void setStats(Map<String, Object> stats) { this.stats = stats; }
}
// 文件路径:app/src/main/java/com/worldcup/lineup/models/Lineup.java
public class Lineup {
private String team;
private String formation;
private List<Player> players;
private Date matchDate;
private String opponent;
public Lineup(String team, String formation) {
this.team = team;
this.formation = formation;
this.players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player);
}
public List<Player> getPlayersByPosition(String position) {
return players.stream()
.filter(player -> position.equals(player.getPosition()))
.collect(Collectors.toList());
}
// Getter和Setter方法
public String getTeam() { return team; }
public void setTeam(String team) { this.team = team; }
public String getFormation() { return formation; }
public void setFormation(String formation) { this.formation = formation; }
public List<Player> getPlayers() { return players; }
public void setPlayers(List<Player> players) { this.players = players; }
public Date getMatchDate() { return matchDate; }
public void setMatchDate(Date matchDate) { this.matchDate = matchDate; }
public String getOpponent() { return opponent; }
public void setOpponent(String opponent) { this.opponent = opponent; }
}
4. 用户界面设计与实现
4.1 主界面布局设计
创建主Activity的布局文件,展示球队阵容:
<!-- 文件路径:app/src/main/res/layout/activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/background">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primary"
android:theme="@style/ThemeOverlay.Material3.Dark.ActionBar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- 球队信息 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingBottom="16dp">
<ImageView
android:id="@+id/team_logo"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_team_default" />
<TextView
android:id="@+id/team_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="@color/text_primary" />
<TextView
android:id="@+id/formation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:background="@drawable/formation_badge"
android:padding="8dp"
android:textColor="@color/white" />
</LinearLayout>
<!-- 阵容展示区域 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="首发阵容"
android:textSize="20sp"
android:textStyle="bold"
android:paddingBottom="8dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/players_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp" />
<!-- 生成按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/generate_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="生成阵容展示"
android:textSize="16sp"
style="@style/Widget.Material3.Button" />
</LinearLayout>
</ScrollView>
</LinearLayout>
4.2 球员列表适配器实现
创建RecyclerView适配器来展示球员列表:
// 文件路径:app/src/main/java/com/worldcup/lineup/adapters/PlayerAdapter.java
public class PlayerAdapter extends RecyclerView.Adapter<PlayerAdapter.PlayerViewHolder> {
private final List<Player> players;
private final OnPlayerClickListener listener;
public PlayerAdapter(List<Player> players, OnPlayerClickListener listener) {
this.players = players;
this.listener = listener;
}
@NonNull
@Override
public PlayerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_player, parent, false);
return new PlayerViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull PlayerViewHolder holder, int position) {
Player player = players.get(position);
holder.bind(player, listener);
}
@Override
public int getItemCount() {
return players.size();
}
static class PlayerViewHolder extends RecyclerView.ViewHolder {
private final ImageView playerImage;
private final TextView playerName;
private final TextView playerPosition;
private final TextView jerseyNumber;
private final ImageView captainBadge;
public PlayerViewHolder(@NonNull View itemView) {
super(itemView);
playerImage = itemView.findViewById(R.id.player_image);
playerName = itemView.findViewById(R.id.player_name);
playerPosition = itemView.findViewById(R.id.player_position);
jerseyNumber = itemView.findViewById(R.id.jersey_number);
captainBadge = itemView.findViewById(R.id.captain_badge);
}
public void bind(Player player, OnPlayerClickListener listener) {
playerName.setText(player.getName());
playerPosition.setText(player.getPosition());
jerseyNumber.setText(String.valueOf(player.getJerseyNumber()));
if (player.isCaptain()) {
captainBadge.setVisibility(View.VISIBLE);
} else {
captainBadge.setVisibility(View.GONE);
}
// 加载球员图像
if (player.getImageUrl() != null) {
Glide.with(itemView.getContext())
.load(player.getImageUrl())
.placeholder(R.drawable.ic_player_placeholder)
.into(playerImage);
}
itemView.setOnClickListener(v -> {
if (listener != null) {
listener.onPlayerClick(player);
}
});
}
}
public interface OnPlayerClickListener {
void onPlayerClick(Player player);
}
}
4.3 球员详情页面实现
创建球员详情页面,展示生成的图像和视频:
// 文件路径:app/src/main/java/com/worldcup/lineup/ui/PlayerDetailActivity.java
public class PlayerDetailActivity extends AppCompatActivity {
private static final String EXTRA_PLAYER = "extra_player";
private Player player;
private ImageView playerImageView;
private VideoView playerVideoView;
private ProgressBar loadingProgress;
private Button playVideoButton;
public static void start(Context context, Player player) {
Intent intent = new Intent(context, PlayerDetailActivity.class);
intent.putExtra(EXTRA_PLAYER, player);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player_detail);
initViews();
loadPlayerData();
setupVideoPlayer();
}
private void initViews() {
playerImageView = findViewById(R.id.player_image);
playerVideoView = findViewById(R.id.player_video);
loadingProgress = findViewById(R.id.loading_progress);
playVideoButton = findViewById(R.id.play_video_button);
// 设置工具栏
MaterialToolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
private void loadPlayerData() {
player = getIntent().getParcelableExtra(EXTRA_PLAYER);
if (player == null) {
finish();
return;
}
setTitle(player.getName());
// 显示球员图像
if (player.getImageUrl() != null) {
Glide.with(this)
.load(player.getImageUrl())
.into(playerImageView);
}
// 如果有视频URL,显示播放按钮
if (player.getVideoUrl() != null) {
playVideoButton.setVisibility(View.VISIBLE);
} else {
playVideoButton.setVisibility(View.GONE);
}
}
private void setupVideoPlayer() {
playVideoButton.setOnClickListener(v -> {
if (player.getVideoUrl() != null) {
playVideo(player.getVideoUrl());
}
});
}
private void playVideo(String videoUrl) {
loadingProgress.setVisibility(View.VISIBLE);
playerVideoView.setVisibility(View.VISIBLE);
MediaController mediaController = new MediaController(this);
mediaController.setAnchorView(playerVideoView);
playerVideoView.setMediaController(mediaController);
playerVideoView.setVideoURI(Uri.parse(videoUrl));
playerVideoView.setOnPreparedListener(mp -> {
loadingProgress.setVisibility(View.GONE);
playerVideoView.start();
});
playerVideoView.setOnErrorListener((mp, what, extra) -> {
loadingProgress.setVisibility(View.GONE);
Toast.makeText(this, "视频播放失败", Toast.LENGTH_SHORT).show();
return true;
});
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}
5. 核心业务逻辑整合
5.1 阵容生成管理器
创建阵容生成的核心管理类,协调图像和视频生成流程:
// 文件路径:app/src/main/java/com/worldcup/lineup/core/LineupGenerator.java
public class LineupGenerator {
private final Context context;
private final PlayerImageGenerator imageGenerator;
private final PlayerVideoGenerator videoGenerator;
private final ExecutorService executorService;
public LineupGenerator(Context context) {
this.context = context.getApplicationContext();
this.imageGenerator = new PlayerImageGenerator(context);
this.videoGenerator = new PlayerVideoGenerator(context);
this.executorService = Executors.newFixedThreadPool(3);
}
public void generateCompleteLineup(Lineup lineup, GenerationCallback callback) {
executorService.execute(() -> {
try {
List<Player> players = lineup.getPlayers();
CountDownLatch latch = new CountDownLatch(players.size());
AtomicInteger successCount = new AtomicInteger(0);
AtomicInteger errorCount = new AtomicInteger(0);
for (Player player : players) {
generatePlayerContent(player, new PlayerGenerationCallback() {
@Override
public void onSuccess(Player updatedPlayer) {
successCount.incrementAndGet();
latch.countDown();
checkCompletion(players.size(), successCount.get(),
errorCount.get(), callback);
}
@Override
public void onError(String error) {
errorCount.incrementAndGet();
latch.countDown();
checkCompletion(players.size(), successCount.get(),
errorCount.get(), callback);
}
});
}
// 设置超时时间
boolean completed = latch.await(5, TimeUnit.MINUTES);
if (!completed) {
callback.onError("生成超时");
}
} catch (InterruptedException e) {
callback.onError("生成过程被中断");
}
});
}
private void generatePlayerContent(Player player, PlayerGenerationCallback callback) {
// 首先生成图像
imageGenerator.generatePlayerImage(
player.getName(),
player.getTeam(),
player.getPosition(),
new PlayerImageGenerator.ImageGenerationCallback() {
@Override
public void onSuccess(String base64Image) {
// 保存图像
String imageUrl = saveImage(base64Image, player.getName());
player.setImageUrl(imageUrl);
// 然后生成视频
generatePlayerVideo(player, base64Image, callback);
}
@Override
public void onError(String errorMessage) {
callback.onError("图像生成失败: " + errorMessage);
}
}
);
}
private void generatePlayerVideo(Player player, String base64Image,
PlayerGenerationCallback callback) {
videoGenerator.generatePlayerVideo(
player.getName(),
player.getPosition(),
base64Image,
new PlayerVideoGenerator.VideoGenerationCallback() {
@Override
public void onSuccess(String videoUri) {
player.setVideoUrl(videoUri);
callback.onSuccess(player);
}
@Override
public void onError(String errorMessage) {
// 视频生成失败不影响整体流程,只记录错误
Log.w("LineupGenerator", "视频生成失败: " + errorMessage);
callback.onSuccess(player); // 仍然认为成功,只是没有视频
}
}
);
}
private String saveImage(String base64Image, String playerName) {
try {
byte[] imageData = Base64.decode(base64Image, Base64.DEFAULT);
File imageFile = new File(context.getFilesDir(),
"player_" + playerName + "_" + System.currentTimeMillis() + ".jpg");
try (FileOutputStream fos = new FileOutputStream(imageFile)) {
fos.write(imageData);
}
return imageFile.getAbsolutePath();
} catch (IOException e) {
Log.e("LineupGenerator", "保存图像失败", e);
return null;
}
}
private void checkCompletion(int total, int success, int error,
GenerationCallback callback) {
if (success + error == total) {
if (success > 0) {
callback.onSuccess(success, error);
} else {
callback.onError("所有球员内容生成失败");
}
}
}
public interface GenerationCallback {
void onSuccess(int successCount, int errorCount);
void onError(String errorMessage);
}
public interface PlayerGenerationCallback {
void onSuccess(Player player);
void onError(String error);
}
}
5.2 主Activity业务逻辑
实现主Activity的完整业务逻辑:
// 文件路径:app/src/main/java/com/worldcup/lineup/ui/MainActivity.java
public class MainActivity extends AppCompatActivity {
private LineupGenerator lineupGenerator;
private PlayerAdapter playerAdapter;
private Lineup currentLineup;
private RecyclerView playersRecycler;
private MaterialButton generateButton;
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
setupLineupGenerator();
loadSampleData();
setupRecyclerView();
}
private void initViews() {
playersRecycler = findViewById(R.id.players_recycler);
generateButton = findViewById(R.id.generate_button);
generateButton.setOnClickListener(v -> generateLineupContent());
// 设置工具栏
MaterialToolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
private void setupLineupGenerator() {
lineupGenerator = new LineupGenerator(this);
}
private void loadSampleData() {
// 创建示例阵容数据
currentLineup = new Lineup("阿根廷", "4-3-3");
currentLineup.setOpponent("法国");
currentLineup.setMatchDate(new Date());
// 添加示例球员
currentLineup.addPlayer(new Player("梅西", "阿根廷", "前锋", 10));
currentLineup.addPlayer(new Player("迪玛利亚", "阿根廷", "前锋", 11));
currentLineup.addPlayer(new Player("阿尔瓦雷斯", "阿根廷", "前锋", 9));
currentLineup.addPlayer(new Player("德保罗", "阿根廷", "中场", 7));
currentLineup.addPlayer(new Player("恩佐", "阿根廷", "中场", 24));
currentLineup.addPlayer(new Player("麦卡利斯特", "阿根廷", "中场", 20));
currentLineup.addPlayer(new Player("莫利纳", "阿根廷", "后卫", 26));
currentLineup.addPlayer(new Player("罗梅罗", "阿根廷", "后卫", 13));
currentLineup.addPlayer(new Player("奥塔门迪", "阿根廷", "后卫", 19));
currentLineup.addPlayer(new Player("阿库尼亚", "阿根廷", "后卫", 8));
currentLineup.addPlayer(new Player("马丁内斯", "阿根廷", "守门员", 23));
// 设置队长
currentLineup.getPlayers().get(0).setCaptain(true);
updateUI();
}
private void setupRecyclerView() {
playersRecycler.setLayoutManager(new LinearLayoutManager(this));
playerAdapter = new PlayerAdapter(currentLineup.getPlayers(),
new PlayerAdapter.OnPlayerClickListener() {
@Override
public void onPlayerClick(Player player) {
PlayerDetailActivity.start(MainActivity.this, player);
}
});
playersRecycler.setAdapter(playerAdapter);
}
private void generateLineupContent() {
if (currentLineup == null) return;
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("正在生成球员图像和视频...");
progressDialog.setCancelable(false);
progressDialog.show();
lineupGenerator.generateCompleteLineup(currentLineup,
new LineupGenerator.GenerationCallback() {
@Override
public void onSuccess(int successCount, int errorCount) {
runOnUiThread(() -> {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
String message = String.format(
"生成完成!成功:%d个,失败:%d个", successCount, errorCount);
Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
updateUI();
});
}
@Override
public void onError(String errorMessage) {
runOnUiThread(() -> {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
Toast.makeText(MainActivity.this,
"生成失败: " + errorMessage, Toast.LENGTH_LONG).show();
});
}
});
}
private void updateUI() {
if (playerAdapter != null) {
playerAdapter.notifyDataSetChanged();
}
// 更新球队信息
TextView teamName = findViewById(R.id.team_name);
TextView formation = findViewById(R.id.formation);
if (teamName != null) {
teamName.setText(currentLineup.getTeam());
}
if (formation != null) {
formation.setText(currentLineup.getFormation());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
}
6. 性能优化与最佳实践
6.1 图像和视频缓存策略
实现高效的缓存机制来提升用户体验:
// 文件路径:app/src/main/java/com/worldcup/lineup/cache/MediaCache.java
public class MediaCache {
private static final long MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100MB
private final LruCache<String, Bitmap> memoryCache;
private final File cacheDir;
public MediaCache(Context context) {
// 计算可用的内存缓存大小
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8; // 使用1/8的可用内存
memoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
cacheDir = new File(context.getCacheDir(), "media");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
}
public void putBitmap(String key, Bitmap bitmap) {
if (getBitmap(key) == null) {
memoryCache.put(key, bitmap);
}
}
public Bitmap getBitmap(String key) {
return memoryCache.get(key);
}
public File getVideoFile(String videoUrl) {
String filename = "video_" + hashKeyForDisk(videoUrl) + ".mp4";
return new File(cacheDir, filename);
}
public boolean isVideoCached(String videoUrl) {
File videoFile = getVideoFile(videoUrl);
return videoFile.exists();
}
private String hashKeyForDisk(String key) {
String cacheKey;
try {
MessageDigest digest = MessageDigest.getInstance更多推荐


所有评论(0)