Fragment实例精讲——新闻(购物)类App列表Fragment的简单实现

感谢你提出“Fragment实例精讲——新闻(购物)类App列表Fragment的简单实现”。结合你之前对Android四大组件(ActivityServiceBroadcastReceiverContentProvider)、Intent、复杂数据传递以及Fragment底部导航栏(方法1:BottomNavigationView,方法2:ViewPager2,方法3:Jetpack Navigation)的探讨,我推测你希望通过一个具体的Fragment实例,学习如何实现一个新闻或购物类App的列表界面,集成到类似底部导航栏的结构中。你提到“简单实现”,可能希望聚焦核心功能(如列表展示、点击交互),并可能结合复杂数据传递(如新闻或商品对象)或四大组件(如Service记录点击、ContentProvider获取数据)。由于你提到“新闻(购物)类”,我将以新闻类App为例,实现一个Fragment展示新闻列表,支持点击查看详情,并结合Intent传递复杂数据(Parcelable新闻对象),启动Service记录浏览历史,通过BroadcastReceiver通知更新,结合DocumentsProvider加载新闻图片(可选)。如果你更倾向于购物类App或有特定需求(如与底部导航栏集成、Room数据库、ViewModel),请进一步说明,我可以调整方案。

以下我将以通俗易懂的中文,详细讲解如何实现一个新闻类App的列表Fragment,包含完整代码、步骤解析和注意事项,集成到简单的BottomNavigationView结构中(类似方法1),并支持复杂数据传递和其他组件交互。


一、新闻类App列表Fragment概述

1. 目标场景

  • 功能
  • NewsListFragment:展示新闻列表(标题、摘要、发布时间),使用RecyclerView
  • NewsDetailActivity:点击新闻项跳转,显示新闻详情(传递Parcelable对象)。
  • LogService:记录用户点击的新闻标题。
  • CustomReceiver:接收广播,通知新闻列表更新。
  • DocumentsProvider(可选):加载新闻图片URI。
  • 结构MainActivity包含BottomNavigationView,管理NewsListFragment和其他Fragment(如HomeFragmentProfileFragment)。
  • 数据
  • 新闻数据:News对象(标题、摘要、时间、图片URI),实现Parcelable
  • 列表:ArrayList<News>,支持动态更新。
  • 交互
  • 点击新闻项,通过Intent传递News对象到NewsDetailActivity
  • 记录点击历史到LogService
  • 发送广播通知CustomReceiver
  • (可选)通过DocumentsProvider选择图片。

2. 技术要点

  • Fragment:使用androidx.fragment.app.Fragment,展示新闻列表。
  • RecyclerView:高效显示列表,支持点击事件。
  • Parcelable:传递News对象,参考“Intent之复杂数据传递”。
  • BottomNavigationView:简单导航框架,参考方法1。
  • 四大组件:结合IntentServiceBroadcastReceiverDocumentsProvider

3. 与底部导航栏的关系

  • NewsListFragment嵌入MainActivity,通过BottomNavigationView切换。
  • 其他Fragment(HomeFragmentProfileFragment)作为占位,保持导航结构。

二、实现步骤

步骤1:添加依赖

app/build.gradle中添加AndroidX、Material Design和RecyclerView依赖:

dependencies { implementation ‘androidx.appcompat:appcompat:1.6.1’ implementation ‘com.google.android.material:material:1.9.0’ implementation ‘androidx.fragment:fragment:1.5.7’ implementation ‘androidx.recyclerview:recyclerview:1.3.2’ }

步骤2:定义复杂数据类(Parcelable)

创建News类,表示新闻数据,支持Parcelable


import android.os.Parcel;
import android.os.Parcelable;

public class News implements Parcelable {
private String title;
private String summary;
private String publishTime;
private String imageUri; // 可选,图片URI

public News(String title, String summary, String publishTime, String imageUri) {
    this.title = title;
    this.summary = summary;
    this.publishTime = publishTime;
    this.imageUri = imageUri;
}

protected News(Parcel in) {
    title = in.readString();
    summary = in.readString();
    publishTime = in.readString();
    imageUri = in.readString();
}

public static final Creator<News> CREATOR = new Creator<News>() {
    @Override
    public News createFromParcel(Parcel in) {
        return new News(in);
    }

    @Override
    public News[] newArray(int size) {
        return new News[size];
    }
};

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(title);
    dest.writeString(summary);
    dest.writeString(publishTime);
    dest.writeString(imageUri);
}

@Override
public int describeContents() {
    return 0;
}

public String getTitle() {
    return title;
}

public String getSummary() {
    return summary;
}

public String getPublishTime() {
    return publishTime;
}

public String getImageUri() {
    return imageUri;
}

}

步骤3:创建NewsListFragment

实现NewsListFragment,使用RecyclerView展示新闻列表。


import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;

public class NewsListFragment extends Fragment {
private ArrayList newsList = new ArrayList<>();
private NewsAdapter adapter;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_news_list, container, false);
    RecyclerView recyclerView = view.findViewById(R.id.news_recycler_view);
    recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

    // 初始化新闻数据(模拟)
    if (newsList.isEmpty()) {
        newsList.add(new News("新闻标题1", "这是第一条新闻的摘要", "2025-09-23", null));
        newsList.add(new News("新闻标题2", "这是第二条新闻的摘要", "2025-09-23", null));
        newsList.add(new News("新闻标题3", "这是第三条新闻的摘要", "2025-09-23", null));
    }

    // 设置适配器
    adapter = new NewsAdapter(newsList, news -> {
        // 点击新闻,跳转详情
        Intent intent = new Intent(getActivity(), NewsDetailActivity.class);
        intent.putExtra("news", news);
        startActivity(intent);

        // 启动Service记录
        Intent serviceIntent = new Intent(getActivity(), LogService.class);
        serviceIntent.putExtra("log_message", "查看新闻: " + news.getTitle());
        getActivity().startService(serviceIntent);

        // 发送广播
        Intent broadcastIntent = new Intent("com.example.CUSTOM_ACTION");
        broadcastIntent.putExtra("news", news);
        getActivity().sendBroadcast(broadcastIntent);
    });
    recyclerView.setAdapter(adapter);

    return view;
}

// 更新新闻列表(例如收到广播后)
public void addNews(News news) {
    newsList.add(news);
    adapter.notifyDataSetChanged();
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelableArrayList("news_list", newsList);
}

@Override
public void onViewStateRestored(Bundle savedInstanceState) {
    super.onViewStateRestored(savedInstanceState);
    if (savedInstanceState != null) {
        newsList = savedInstanceState.getParcelableArrayList("news_list");
        adapter.notifyDataSetChanged();
    }
}

}

步骤4:创建RecyclerView适配器

实现NewsAdapter显示新闻列表。


import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class NewsAdapter extends RecyclerView.Adapter {
private List newsList;
private OnNewsClickListener listener;

public interface OnNewsClickListener {
    void onNewsClick(News news);
}

public NewsAdapter(List<News> newsList, OnNewsClickListener listener) {
    this.newsList = newsList;
    this.listener = listener;
}

@Override
public NewsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item_news, parent, false);
    return new NewsViewHolder(view);
}

@Override
public void onBindViewHolder(NewsViewHolder holder, int position) {
    News news = newsList.get(position);
    holder.titleText.setText(news.getTitle());
    holder.summaryText.setText(news.getSummary());
    holder.timeText.setText(news.getPublishTime());
    holder.itemView.setOnClickListener(v -> listener.onNewsClick(news));
}

@Override
public int getItemCount() {
    return newsList.size();
}

static class NewsViewHolder extends RecyclerView.ViewHolder {
    TextView titleText, summaryText, timeText;

    NewsViewHolder(View itemView) {
        super(itemView);
        titleText = itemView.findViewById(R.id.news_title);
        summaryText = itemView.findViewById(R.id.news_summary);
        timeText = itemView.findViewById(R.id.news_time);
    }
}

}

步骤5:创建NewsDetailActivity

显示新闻详情,接收News对象。


import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class NewsDetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_detail);

    TextView titleText = findViewById(R.id.detail_title);
    TextView summaryText = findViewById(R.id.detail_summary);
    TextView timeText = findViewById(R.id.detail_time);

    News news = getIntent().getParcelableExtra("news");
    if (news != null) {
        titleText.setText(news.getTitle());
        summaryText.setText(news.getSummary());
        timeText.setText(news.getPublishTime());
    }
}

}

步骤6:创建其他Fragment(占位)

为保持导航结构,创建简单的HomeFragmentProfileFragment


import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;

public class HomeFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
TextView welcomeText = view.findViewById(R.id.welcome_text);
welcomeText.setText(“欢迎使用新闻App!”);
return view;
}
}


import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;

public class ProfileFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_profile, container, false);
TextView profileText = view.findViewById(R.id.profile_text);
profileText.setText(“个人中心”);
return view;
}
}

步骤7:创建MainActivity

使用BottomNavigationView管理Fragment。


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import com.google.android.material.bottomnavigation.BottomNavigationView;

public class MainActivity extends AppCompatActivity {
private NewsListFragment newsListFragment;
private BroadcastReceiver newsReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation);

    // 初始化默认Fragment
    if (savedInstanceState == null) {
        newsListFragment = new NewsListFragment();
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.fragment_container, newsListFragment)
                .commit();
    } else {
        newsListFragment = (NewsListFragment) getSupportFragmentManager()
                .findFragmentById(R.id.fragment_container);
    }

    // 设置导航监听
    bottomNav.setOnItemSelectedListener(item -> {
        Fragment selectedFragment = null;
        int itemId = item.getItemId();
        if (itemId == R.id.nav_home) {
            selectedFragment = new HomeFragment();
        } else if (itemId == R.id.nav_news) {
            selectedFragment = newsListFragment != null ? newsListFragment : new NewsListFragment();
        } else if (itemId == R.id.nav_profile) {
            selectedFragment = new ProfileFragment();
        }

        if (selectedFragment != null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, selectedFragment)
                    .addToBackStack(null)
                    .commit();
        }
        return true;
    });

    // 注册广播接收器
    newsReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            News news = intent.getParcelableExtra("news");
            if (news != null && newsListFragment != null) {
                newsListFragment.addNews(news); // 动态添加新闻
            }
        }
    };
    IntentFilter filter = new IntentFilter("com.example.CUSTOM_ACTION");
    registerReceiver(newsReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(newsReceiver);
}

}

步骤8:复用Service和BroadcastReceiver

复用之前的LogServiceCustomReceiver


import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class LogService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String message = intent.getStringExtra(“log_message”);
if (message != null) {
try {
File file = new File(getFilesDir(), “app_log.txt”);
FileWriter writer = new FileWriter(file, true);
writer.append(message).append(“\n”);
writer.close();
Log.d(“LogService”, “记录日志: ” + message);
} catch (IOException e) {
Log.e(“LogService”, “日志记录失败”, e);
}
}
return START_STICKY;
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

}


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class CustomReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
News news = intent.getParcelableExtra(“news”);
String message = news != null ? “查看新闻: ” + news.getTitle() : “无效新闻”;
Log.d(“CustomReceiver”, “收到广播: ” + message);
}
}

步骤9:复用DocumentsProvider(可选)

若需要为新闻加载图片,可复用之前的TextDocumentsProvider(参考“ContentProvider再探”)。在NewsListFragment中添加按钮打开文件选择器:

// 在NewsListFragment.java的onCreateView中添加
Button addImageBtn = view.findViewById(R.id.add_image_btn);
addImageBtn.setOnClickListener(v -> {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("image/*");
    startActivityForResult(intent, 1);
});

// 处理文件选择结果
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1 && resultCode == RESULT_OK && data != null) {
        String imageUri = data.getData().toString();
        newsList.add(new News("新图片新闻", "图片新闻摘要", "2025-09-23", imageUri));
        adapter.notifyDataSetChanged();
    }
}

修改布局fragment_news_list.xml):

步骤10:注册组件 在AndroidManifest.xml中声明: 步骤11:运行与测试 部署应用,启动MainActivity,验证底部导航栏是否显示“首页”“新闻”“个人”。 点击“新闻”,检查NewsListFragment是否显示新闻列表。 点击新闻项,验证: 是否跳转到NewsDetailActivity,显示新闻详情。 LogService是否记录日志(检查app_log.txt)。 CustomReceiver是否在Logcat打印广播(包含News对象)。 (可选)点击“添加图片新闻”,验证是否打开文件选择器并添加新新闻。 测试屏幕旋转,检查新闻列表是否恢复。 三、实现的关键点 1. NewsListFragment RecyclerView: 使用LinearLayoutManager实现垂直列表。 NewsAdapter支持点击事件,传递News对象。 状态管理outState.putParcelableArrayList("news_list", newsList); 动态更新:通过广播接收新新闻,调用addNews更新列表。 2. 复杂数据传递 ParcelableNews对象通过Intent传递到NewsDetailActivity广播:发送News对象,CustomReceiver接收。 注意事项: 确保Parcelable实现正确,避免序列化错误。 检查Intent数据大小,超1MB需优化。 3. BottomNavigationView 使用方法1的简单切换方式,动态替换Fragment。 缓存NewsListFragment实例,避免重复创建: newsListFragment = (NewsListFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_container); 4. 与四大组件结合 Intent:传递News对象,启动Service和广播。 Service:异步记录浏览历史。 BroadcastReceiver:动态更新新闻列表。 DocumentsProvider(可选):加载图片URI。 5. 性能与优化 性能: 使用RecyclerView优化列表性能。 缓存NewsListFragment实例,减少创建开销。 用户体验: 添加列表项动画:
java recyclerView.setItemAnimator(new DefaultItemAnimator()); 优化点击反馈,使用RippleDrawable或自定义背景。 状态管理:保存和恢复新闻列表,防止屏幕旋转丢失。 6. 常见问题 列表不显示:检查RecyclerView的布局和适配器初始化。 数据丢失:确保onSaveInstanceState正确保存newsList广播未接收:验证IntentFilterregisterReceiver。 四、购物类App的变体 若实现购物类App,可替换NewsProduct(包含名称、价格、描述、图片URI),其他逻辑类似: ProductFragment:展示商品列表。 ProductDetailActivity:显示商品详情。 Service:记录购买或浏览历史。 BroadcastReceiver:通知库存更新。 五、学习与实践资源(中文) 官方文档Android开发者 – RecyclerView Android开发者 – Fragment 书籍:《Android编程权威指南》(中文版,Big Nerd Ranch出品) 课程:B站或慕课网,搜索“Android RecyclerView”。 实践: 添加图片加载(使用Glide或Coil)。 结合Room存储新闻数据。 六、下一步 为了更精准地讲解,请告诉我: 你更倾向于新闻类还是购物类App? 你希望聚焦的功能(列表优化、图片加载、ViewModel、与ViewPager2集成)? 当前技术水平(熟悉RecyclerView、需要高级功能)? 是否需要扩展功能(如Room、ViewModel、滑动切换)? 是否结合其他方法(如ViewPager2或Navigation)? 你可以基于示例扩展功能(如添加图片加载或数据库),或提出具体问题,我会提供更深入的代码或分析!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注