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

如何在Android新闻App中用Fragment替代Activity展示新闻详情

新闻App转Fragment实现方案及学习指南

我开发了一款新闻App,使用Retrofit调用API,通过RecyclerView结合CardView展示新闻列表;用户点击新闻卡片时,会跳转至新的Activity,通过WebView展示完整新闻内容。现在我希望改用Fragment实现这一功能,但我对Fragment完全陌生,缺乏相关知识,恳请提供实现思路及Fragment的学习资源。以下是我的现有代码:

现有代码

MainActivity.kt

class MainActivity : AppCompatActivity() {
    lateinit var adapter: NewsAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        getNews()
    }

    private fun getNews() {
        val news :Call<News> = NewsSeversis.newsIntence.getHeadlines("in",1)
        news.enqueue(object :Callback<News>{
            override fun onResponse(call: Call<News>, response: Response<News>) {
                //here response is a News response
                val news:News?=response.body()
                if (news!=null){
                    Log.d("response",news.toString())
                    adapter= NewsAdapter(news.articles)
                    newsList.adapter=adapter
                    newsList.layoutManager=LinearLayoutManager(this@MainActivity)
                    Log.d("itemcount","${(newsList.layoutManager as LinearLayoutManager).itemCount}")
                    Log.d("itemcount2", "${(newsList.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()}")
                }
            }
            override fun onFailure(call: Call<News>, t: Throwable) {
                Log.d("response","error occur",t)
            }
        })
    }
}

NewsAdapter.kt

class NewsAdapter (val artical: List<Artical>):RecyclerView.Adapter<NewsAdapter.articalViewHolder>(){
   inner class articalViewHolder(itemView: View):RecyclerView.ViewHolder(itemView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): articalViewHolder {
        val view:View=LayoutInflater.from(parent.context).inflate(R.layout.item_layout,parent,false)
        return articalViewHolder(view)
    }

    override fun onBindViewHolder(holder: articalViewHolder, position: Int) {
        val articals:Artical=artical[position]
        holder.itemView.apply {
            newsTitle.text=articals.title
            newsDescription.text=articals.description
            Glide.with(context).load(articals.urlToImage).into(newsImage)
            holder.itemView.setOnClickListener {
                val intent=Intent(context,DetailsWebview::class.java)
                intent.putExtra("EXTRA_URL",articals.url)
                Log.d("url",articals.url)
                context.startActivity(intent)
            }
        }
    }
    override fun getItemCount(): Int {
       return artical.size
    }
}

详情页WebView Activity代码

class DetailsWebview : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_details_webview)
        val url :String?=intent.getStringExtra("EXTRA_URL")
        if (url !=null){
            Log.d("DataFlow", "URL received: $url")
            detailsWebview.settings.javaScriptEnabled=true
            detailsWebview.settings.userAgentString="Mozilla/5.0 (Linux; Android 9; CPH1909) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36"
            detailsWebview.webViewClient = object : WebViewClient() {
                override fun onPageFinished(view: WebView?, url: String?) {
                    progressBar.visibility=View.GONE
                    detailsWebview.visibility=View.VISIBLE
                }
            }
            detailsWebview.loadUrl(url)
        }else  Log.d("DataFlow", "No URL received")
    }
}

转Fragment实现思路

1. 拆分页面为两个Fragment

  • 新闻列表Fragment:创建NewsListFragment,将原MainActivity中获取新闻、初始化RecyclerView的逻辑全部迁移到该Fragment中,布局复用原activity_main的内容(单独抽成fragment_news_list.xml)。
  • 新闻详情Fragment:创建NewsDetailFragment,复用原DetailsWebview的布局逻辑,将布局改为fragment_news_detail.xml,通过Fragment的arguments传递URL参数,替代原Activity的Intent传参。

2. 改造主Activity作为Fragment容器

  • 修改activity_main.xml,替换为FragmentContainerView作为Fragment的承载容器。
  • 主Activity仅负责在启动时加载NewsListFragment,不再处理业务逻辑。

3. 修改Adapter的点击事件逻辑

  • 给NewsAdapter添加点击回调Lambda,当用户点击卡片时,通过FragmentManager执行事务,切换到NewsDetailFragment并传递URL,同时将事务加入回退栈,保证返回键能回到列表页。

关键代码修改示例

NewsListFragment.kt

class NewsListFragment : Fragment() {
    lateinit var adapter: NewsAdapter

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_news_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        getNews()
    }

    private fun getNews() {
        val news: Call<News> = NewsSeversis.newsIntence.getHeadlines("in", 1)
        news.enqueue(object : Callback<News> {
            override fun onResponse(call: Call<News>, response: Response<News>) {
                val newsData: News? = response.body()
                newsData?.let {
                    adapter = NewsAdapter(it.articles) { url ->
                        val detailFragment = NewsDetailFragment().apply {
                            arguments = Bundle().apply {
                                putString("EXTRA_URL", url)
                            }
                        }
                        parentFragmentManager.beginTransaction()
                            .replace(R.id.fragment_container, detailFragment)
                            .addToBackStack(null)
                            .commit()
                    }
                    newsList.adapter = adapter
                    newsList.layoutManager = LinearLayoutManager(requireContext())
                }
            }

            override fun onFailure(call: Call<News>, t: Throwable) {
                Log.d("response", "error occur", t)
            }
        })
    }
}

改造后的NewsAdapter.kt

class NewsAdapter(
    val articles: List<Artical>,
    private val onItemClick: (String) -> Unit
) : RecyclerView.Adapter<NewsAdapter.ArticleViewHolder>() {
    inner class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_layout, parent, false)
        return ArticleViewHolder(view)
    }

    override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
        val article = articles[position]
        holder.itemView.apply {
            newsTitle.text = article.title
            newsDescription.text = article.description
            Glide.with(context).load(article.urlToImage).into(newsImage)
            setOnClickListener { onItemClick(article.url) }
        }
    }

    override fun getItemCount() = articles.size
}

NewsDetailFragment.kt

class NewsDetailFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_news_detail, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val url = arguments?.getString("EXTRA_URL")
        url?.let { loadNewsUrl(it) } ?: Log.d("DataFlow", "No URL received")
    }

    private fun loadNewsUrl(url: String) {
        Log.d("DataFlow", "URL received: $url")
        detailsWebview.apply {
            settings.javaScriptEnabled = true
            settings.userAgentString = "Mozilla/5.0 (Linux; Android 9; CPH1909) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36"
            webViewClient = object : WebViewClient() {
                override fun onPageFinished(view: WebView?, loadedUrl: String?) {
                    progressBar.visibility = View.GONE
                    detailsWebview.visibility = View.VISIBLE
                }
            }
            loadUrl(url)
        }
    }
}

简化后的MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.fragment_container, NewsListFragment())
                .commit()
        }
    }
}

Fragment核心学习要点

  1. 生命周期:重点掌握onCreateView、onViewCreated、onDestroyView等方法,理解Fragment和宿主Activity生命周期的联动关系。
  2. 事务管理:熟练使用FragmentManager和FragmentTransaction的replace、addToBackStack等方法,掌握Fragment的添加、替换、移除逻辑。
  3. 数据传递:学习通过arguments传递简单数据,通过ViewModel实现Fragment与Activity、Fragment间的复杂数据共享,避免内存泄漏。
  4. 上下文获取:在Fragment中优先使用requireContext()、requireActivity()获取上下文,避免使用可能为空的context、activity。
  5. 状态保存:了解onSaveInstanceState的用法,处理屏幕旋转等场景下的Fragment状态恢复。

内容的提问来源于stack exchange,提问作者Sumita Das

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 04:32:01