You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

其他潜在问题排查

  1. API密钥有效性:确认"Mykey"是已启用YouTube Data API v3的有效密钥,且配额充足。
  2. 网络权限:在AndroidManifest.xml中添加网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
  1. 线程异常捕获:为loadVideos添加全局异常捕获,避免线程崩溃无日志:
private void loadVideos() {
    try {
        // 原有初始化和请求逻辑
    } catch (Exception e) {
        Log.e("ListFragment", "Load videos failed: ", e);
    }
}

验证步骤

  1. 修复后运行App,查看Logcat是否有新异常。
  2. 检查YouTube API控制台的请求记录,确认请求是否成功。
  3. 在search方法中打印response.getItems()的大小,验证API是否返回数据。

内容的提问来源于stack exchange,提问作者John B Gilbert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 17:44:53