Android Studio中ListView运行正常但无法填充数据求助
问题:ListView无法加载YouTube视频列表,仅显示加载转圈
[截图:MainActivity界面显示加载转圈动画,无视频列表内容]
最近跟着旧教程做项目,要实现从YouTube拉取视频列表的ListView功能。代码运行无报错,但ListView无法填充数据,MainActivity只显示加载转圈,后台看似在运行但没有实际效果。以下是相关代码:
fragment_list.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/videoList" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="6dp" android:clipToPadding="false" android:scrollbars="vertical" tools:listitem="@layout/fragment_list"/> </FrameLayout>
ListFragment.java
package com.example.Myapp; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.youtube.YouTube; import com.google.api.services.youtube.model.SearchListResponse; import com.google.api.services.youtube.model.SearchResult; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ListFragment extends Fragment { private ListView mListView; public ListFragment(ListView mListView) { this.mListView = mListView; } @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container != null) { // You no longer need to override onCreateView unless you have custom logic. // Use onViewCreated to set up your UI. } return null; } private YouTube.Search.List mYouTubeList; private void loadVideos() { mYouTubeList.setKey("Mykey"); var mYoutube = new YouTube.Builder(new NetHttpTransport(), new GsonFactory(), hr -> { }).setApplicationName( getString(R.string.app_name)).build(); try { mYouTubeList = mYoutube.search().list(Collections.singletonList("id,snippet")); mYouTubeList.setType(Collections.singletonList("video")); mYouTubeList.setFields("items(id/videoId,snippet/title,snippet/ description,snippet/thumbnails/default/url)"); } catch (IOException e) { Log.d(this.getClass().toString(), "Could not initialize: " + e); } List<VideoItem> mVideos; VideoAdapter mAdapter; mVideos =search("Android development"); mAdapter = new VideoAdapter(getActivity(), R.layout.fragment_list_container, (ArrayList<VideoItem>) mVideos); assert getActivity() != null; getActivity().runOnUiThread(() -> { mListView.setAdapter(mAdapter); mAdapter.notifyDataSetChanged(); }); } @Override public void onActivityCreated(Bundle bundle){ super.onActivityCreated(bundle); new Thread(this::loadVideos).start(); } public List<VideoItem> search(String keywords) { mYouTubeList.setQ(keywords); try { SearchListResponse response = mYouTubeList.execute(); List<SearchResult> results = response.getItems(); List<VideoItem> items = getVideoItems(results); return items; } catch (IOException e) { Log.d("TEST", "Could not search: " + e); } return java.util.Collections.emptyList(); } @NonNull private static List<VideoItem> getVideoItems(List<SearchResult> results) { List<VideoItem> items = new ArrayList<>(); for (SearchResult result : results) { VideoItem item = new VideoItem(); item.setTitle(result.getSnippet().getTitle()); item.setDescription(result.getSnippet(). getDescription()); item.setThumbnailURL(result.getSnippet(). getThumbnails().getDefault().getUrl()); item.setId(result.getId().getVideoId()); items.add(item); } return items; } }
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_orange_light" android:id="@+id/main_container_for_list_fragment"> <ListView android:id="@+id/main_video_list_view" android:visibility="visible" android:padding="6dp" android:layout_marginTop="0dp" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView> </FrameLayout>
VideoAdapter.java
package com.example.MyApp; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; import java.util.ArrayList; public class VideoAdapter extends ArrayAdapter<VideoItem> { private Context mContext; private int mAdapterResourceId; public ArrayList<VideoItem> mVideos = null; static class ViewHolder { TextView videoId; TextView videoTitle; } @Override public int getCount() { super.getCount(); int count = mVideos != null ? mVideos.size() : 0; return count; } public VideoAdapter(Context context, int adapterResourceId, ArrayList<VideoItem> items) { super(context, adapterResourceId, items); this.mVideos = items; this.mContext = context; this.mAdapterResourceId = adapterResourceId; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater) this.getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(mAdapterResourceId, null); ViewHolder holder = new ViewHolder(); holder.videoId = (TextView) v.findViewById(R.id.adapter_video_id); holder.videoTitle = (TextView) v.findViewById(R.id.adapter_video_title); v.setTag(holder); } final VideoItem item = mVideos.get(position); if (item != null) { final ViewHolder holder = (ViewHolder) v.getTag(); holder.videoId.setText(item.getId()); holder.videoTitle.setText(item.getTitle()); } return v; } }
VideoItem.java
package com.example.Myapp; public class VideoItem { private String title; private String description; private String thumbnailURL; private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getThumbnailURL() { return thumbnailURL; } public void setThumbnailURL(String thumbnail) { this.thumbnailURL = thumbnail; } }
问题分析与修复方案
核心问题1:初始化顺序错误导致空指针
loadVideos方法先调用mYouTubeList.setKey("Mykey"),但此时mYouTubeList还未初始化,会直接抛出空指针异常,导致后续代码终止执行。
修复:
调整代码顺序,先初始化YouTube实例和mYouTubeList,再设置API密钥:
private void loadVideos() { try { var mYoutube = new YouTube.Builder(new NetHttpTransport(), new GsonFactory(), hr -> { }).setApplicationName( getString(R.string.app_name)).build(); mYouTubeList = mYoutube.search().list(Collections.singletonList("id,snippet")); mYouTubeList.setKey("Mykey"); // 移到初始化之后 mYouTubeList.setType(Collections.singletonList("video")); mYouTubeList.setFields("items(id/videoId,snippet/title,snippet/description,snippet/thumbnails/default/url)"); } catch (IOException e) { Log.d(this.getClass().toString(), "Could not initialize: " + e); return; // 初始化失败直接返回,避免后续空指针 } // 剩余代码不变 }
核心问题2:Fragment生命周期异常
ListFragment的onCreateView直接返回null,Fragment未创建有效视图,不符合生命周期规范,可能导致上下文或线程逻辑异常。
修复:
如果Fragment不需要自定义布局,返回一个空布局保证生命周期正常执行:
@Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 返回空布局,确保Fragment生命周期流程完整 return inflater.inflate(R.layout.fragment_empty, container, false); }
(需新建fragment_empty.xml,内容为一个空的FrameLayout)
核心问题3:Adapter计数逻辑不规范
VideoAdapter的getCount方法调用super.getCount()但未使用返回值,虽当前逻辑能返回正确数量,但不符合ArrayAdapter规范,可能引发内部状态异常。
修复:
简化计数逻辑:
@Override public int getCount() { return mVideos != null ? mVideos.size() : 0; }
其他潜在问题排查
- API密钥有效性:确认
"Mykey"是已启用YouTube Data API v3的有效密钥,且配额充足。 - 网络权限:在
AndroidManifest.xml中添加网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
- 线程异常捕获:为
loadVideos添加全局异常捕获,避免线程崩溃无日志:
private void loadVideos() { try { // 原有初始化和请求逻辑 } catch (Exception e) { Log.e("ListFragment", "Load videos failed: ", e); } }
验证步骤
- 修复后运行App,查看Logcat是否有新异常。
- 检查YouTube API控制台的请求记录,确认请求是否成功。
- 在
search方法中打印response.getItems()的大小,验证API是否返回数据。
内容的提问来源于stack exchange,提问作者John B Gilbert
相关产品推荐
相关产品推荐

