基于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推荐16GB10GB可用磁盘空间开发工具Android Studio 2023.2.1Java Development Kit 17Android 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) CallImageResponse 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 ListImageData images; public ListImageData 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 CallbackNanoBananaService.ImageResponse() { Override public void onResponse(CallNanoBananaService.ImageResponse call, ResponseNanoBananaService.ImageResponse response) { if (response.isSuccessful() response.body() ! null) { ListNanoBananaService.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(CallNanoBananaService.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) CallVideoResponse 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 CallbackOmniFlashService.VideoResponse() { Override public void onResponse(CallOmniFlashService.VideoResponse call, ResponseOmniFlashService.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(CallOmniFlashService.VideoResponse call, Throwable t) { callback.onError(网络请求失败: t.getMessage()); } }); } private String buildVideoPrompt(String playerName, String position) { MapString, 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 MapString, 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 MapString, Object getStats() { return stats; } public void setStats(MapString, 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 ListPlayer 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 ListPlayer 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 ListPlayer getPlayers() { return players; } public void setPlayers(ListPlayer 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:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical android:backgroundcolor/background com.google.android.material.appbar.MaterialToolbar android:idid/toolbar android:layout_widthmatch_parent android:layout_height?attr/actionBarSize android:backgroundcolor/primary android:themestyle/ThemeOverlay.Material3.Dark.ActionBar / ScrollView android:layout_widthmatch_parent android:layout_height0dp android:layout_weight1 android:padding16dp LinearLayout android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationvertical !-- 球队信息 -- LinearLayout android:layout_widthmatch_parent android:layout_heightwrap_content android:orientationhorizontal android:gravitycenter_vertical android:paddingBottom16dp ImageView android:idid/team_logo android:layout_width48dp android:layout_height48dp android:srcdrawable/ic_team_default / TextView android:idid/team_name android:layout_width0dp android:layout_heightwrap_content android:layout_weight1 android:layout_marginStart16dp android:textSize24sp android:textStylebold android:textColorcolor/text_primary / TextView android:idid/formation android:layout_widthwrap_content android:layout_heightwrap_content android:textSize16sp android:backgrounddrawable/formation_badge android:padding8dp android:textColorcolor/white / /LinearLayout !-- 阵容展示区域 -- TextView android:layout_widthmatch_parent android:layout_heightwrap_content android:text首发阵容 android:textSize20sp android:textStylebold android:paddingBottom8dp / androidx.recyclerview.widget.RecyclerView android:idid/players_recycler android:layout_widthmatch_parent android:layout_heightwrap_content android:layout_marginBottom16dp / !-- 生成按钮 -- com.google.android.material.button.MaterialButton android:idid/generate_button android:layout_widthmatch_parent android:layout_heightwrap_content android:text生成阵容展示 android:textSize16sp stylestyle/Widget.Material3.Button / /LinearLayout /ScrollView /LinearLayout4.2 球员列表适配器实现创建RecyclerView适配器来展示球员列表// 文件路径app/src/main/java/com/worldcup/lineup/adapters/PlayerAdapter.java public class PlayerAdapter extends RecyclerView.AdapterPlayerAdapter.PlayerViewHolder { private final ListPlayer players; private final OnPlayerClickListener listener; public PlayerAdapter(ListPlayer 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 { ListPlayer 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 LruCacheString, 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 LruCacheString, 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

相关新闻

OpenCV 图像分割:全套方法、原理、适用场景、作用详解

OpenCV 图像分割:全套方法、原理、适用场景、作用详解

OpenCV 图像分割:全套方法、原理、适用场景、作用详解 一、图像分割核心作用 核心定义 图像分割 = 把一张完整图像,按语义 / 灰度 / 边缘 / 颜色划分成多个互不重叠的区域,每个区域内像素具备相同特征(灰度、颜色、纹理、梯度),区域间特征差异明显。 实际工程作用 目…

2026/7/10 19:23:19阅读更多 →
抖音视频批量下载终极指南:douyin-downloader工具完全教程

抖音视频批量下载终极指南:douyin-downloader工具完全教程

抖音视频批量下载终极指南:douyin-downloader工具完全教程 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback s…

2026/7/10 19:23:19阅读更多 →
终极指南:用Balena Etcher轻松安全地烧录系统镜像

终极指南:用Balena Etcher轻松安全地烧录系统镜像

终极指南:用Balena Etcher轻松安全地烧录系统镜像 【免费下载链接】etcher Flash OS images to SD cards & USB drives, safely and easily. 项目地址: https://gitcode.com/GitHub_Trending/et/etcher 你是否曾经因为制作系统启动盘而感到头疼&#xff…

2026/7/10 19:23:19阅读更多 →
《Java 100 天进阶之路》第54篇:AQS抽象队列同步器(2026版)

《Java 100 天进阶之路》第54篇:AQS抽象队列同步器(2026版)

第54篇:AQS抽象队列同步器(2026版) 📌 系列导航:《Java 100 天进阶之路》完整目录 | ⬅️ 上一篇:第53篇:volatile与JMM | ➡️ 下一篇:第55篇:线程池ThreadPoolExecutor…

2026/7/10 20:28:23阅读更多 →
为什么Nemotron-Labs-Audex-30B-A3B能在保持文本智能的同时实现音频理解?深度技术解析

为什么Nemotron-Labs-Audex-30B-A3B能在保持文本智能的同时实现音频理解?深度技术解析

为什么Nemotron-Labs-Audex-30B-A3B能在保持文本智能的同时实现音频理解?深度技术解析 【免费下载链接】Nemotron-Labs-Audex-30B-A3B 项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/Nemotron-Labs-Audex-30B-A3B Nemotron-Labs-Audex-30B-A3B是一款…

2026/7/10 20:28:23阅读更多 →
终极指南:使用llama-nemotron-embed-vl-1b-v2构建高效的视觉文档检索应用 [特殊字符]

终极指南:使用llama-nemotron-embed-vl-1b-v2构建高效的视觉文档检索应用 [特殊字符]

终极指南:使用llama-nemotron-embed-vl-1b-v2构建高效的视觉文档检索应用 🚀 【免费下载链接】llama-nemotron-embed-vl-1b-v2 项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/llama-nemotron-embed-vl-1b-v2 在当今AI驱动的信息时代&…

2026/7/10 20:28:23阅读更多 →
掌握三指拖拽:在Windows上解锁macOS级触控板操作体验

掌握三指拖拽:在Windows上解锁macOS级触控板操作体验

掌握三指拖拽:在Windows上解锁macOS级触控板操作体验 【免费下载链接】ThreeFingersDragOnWindows Enables macOS-style three-finger dragging functionality on Windows Precision touchpads. 项目地址: https://gitcode.com/gh_mirrors/th/ThreeFingersDragOnW…

2026/7/10 20:28:23阅读更多 →
如何使用CST2026版本的无参优化器进行拓扑优化

如何使用CST2026版本的无参优化器进行拓扑优化

作者 | Ma Bin 在CST2026版本中,无参数优化器进行了大量的优化。其中最大的变化就是CST已经将Tosca的求解器集成在CST安装包内。这意味着之前因为某种神秘原因而无法使用ToscaCST组合进行无参数优化的用户,现在可以不安装Tosca直接使用CST完成无参优化的…

2026/7/10 20:28:23阅读更多 →
革命性统一音频-文本大语言模型:深入解析Nemotron-Labs-Audex-30B-A3B的核心架构

革命性统一音频-文本大语言模型:深入解析Nemotron-Labs-Audex-30B-A3B的核心架构

革命性统一音频-文本大语言模型:深入解析Nemotron-Labs-Audex-30B-A3B的核心架构 【免费下载链接】Nemotron-Labs-Audex-30B-A3B 项目地址: https://ai.gitcode.com/hf_mirrors/nvidia/Nemotron-Labs-Audex-30B-A3B Nemotron-Labs-Audex-30B-A3B是一款突破性…

2026/7/10 20:23:22阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/10 12:10:00阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/10 12:29:21阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/10 4:59:05阅读更多 →
浏览器缓存行为深度解析:Chrome/Firefox/Safari 对 304 响应的 5 种触发场景对比

浏览器缓存行为深度解析:Chrome/Firefox/Safari 对 304 响应的 5 种触发场景对比

浏览器缓存行为深度解析:Chrome/Firefox/Safari 对 304 响应的 5 种触发场景对比当你在浏览器地址栏敲入一个网址时,背后可能隐藏着一场关于"要不要重新下载资源"的精密博弈。这场博弈的裁判是HTTP缓存机制,而304状态码则是这场博弈…

2026/7/10 0:00:01阅读更多 →
RoboWits:面向创造性问题求解的双臂机器人认知推理基准

RoboWits:面向创造性问题求解的双臂机器人认知推理基准

1. 项目概述:这不是又一个机器人抓取数据集,而是一次对“思考力”的压力测试 RoboWits——这个名字里藏着两个关键信号:“Robo”直指物理世界中的具身智能体,“Wits”则毫不掩饰地指向人类最核心的认知能力:机敏、判断…

2026/7/10 0:00:01阅读更多 →
5分钟完全指南:如何使用TegraRcmGUI图形化工具解锁Switch无限可能

5分钟完全指南:如何使用TegraRcmGUI图形化工具解锁Switch无限可能

5分钟完全指南:如何使用TegraRcmGUI图形化工具解锁Switch无限可能 【免费下载链接】TegraRcmGUI C GUI for TegraRcmSmash (Fuse Gele exploit for Nintendo Switch) 项目地址: https://gitcode.com/gh_mirrors/te/TegraRcmGUI TegraRcmGUI是一款专为Windows…

2026/7/10 0:00:01阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/10 13:39:09阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/9 15:50:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/10 17:29:22阅读更多 →