最近在开发世界杯主题应用时,发现传统静态阵容展示已经无法满足用户需求。球迷们不仅想看到首发阵容名单,更希望直观感受球员风采和战术布局。本文将结合最新的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密钥:

  1. 前往 Google AI Studio
  2. 使用Google账户登录
  3. 在控制台创建新项目
  4. 启用Gemini API服务
  5. 生成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
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐