android studio(NewsApiDemo)100%kotlin

api接口地址:https://newsapi.org/docs/get-started

项目成品地址:https://github.com/RushHan824/NewsApiDemo

项目效果展示:

MVVM数据流

UML图


本系列文章将带你从零实现一个新闻列表App,适合零基础读者。一步步来,力求让每个人都能跟着做出来。

第一步:分析API和JSON,写好数据类。

  1. 打开API网址,获取返回的JSON数据。
  1. 观察JSON结构,分析每个字段。

这里注意会有null的字段 所以kotlin中创建变量的时候需要注意

  1. 用Kotlin写出对应的数据类,为后续网络请求和数据展示打好基础。

创建一个数据类 data class

然后根据上图的json结构 把数据类一层一层构建出来 还是比较简单的 结果如下图

当然要注意上面说到的null字段问题 而kotlin中val搭配问好?比较好

比如:val title: String? 就表示可能会null 安全且规范


第二步:配置Retrofit,完成网络请求

1.添加依赖

在build.gradle中添加Retrofit和Gson等依赖。

如下图 注意是app的.gradle文件 依赖可以一起添加了

Kotlin 复制代码
//实现网络请求的第一步:加上retrofit和gson的依赖
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    //livedata viewmodel依赖
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
    //Glide 图片加载
    implementation("com.github.bumptech.glide:glide:4.16.0")
    annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")

2.定义api接口

创建接口,声明请求方法和返回类型。

Kotlin 复制代码
interface Top_ApiService {
    @GET("top-headlines")
    suspend fun get_topNews(
        @Query("country") country: String,
        @Query("apiKey") apiKey: String
    ):News_Items
}
//我们访问一个网页需要网址 组成可以是baseurl+路径+参数
//这里@GET就是一种请求方式 表示从网页里拿数据 括号里"top-headlines"可以表示具体的路径
//suspend挂起 和kotlin中协程一起搭配 不会阻塞主线程 执行网络请求这样的耗时操作的时候 可以避免界面卡顿
//接下去两个Query就是两个参数
//最后返回类型是数据类
//返回类型通常是我们根据JSON结构定义的数据类(如News_Items),Retrofit会自动帮我们把JSON解析成这个类的对象

3.配置RetrofitClient

Kotlin 复制代码
object RetrofitClient {//object关键字声明了一个单例对象 这样RetrofitClient在全局只有一个实例 方便全局调用 不用每次都新建
    private val client = OkHttpClient.Builder()//OkHttpClient 是Retrofit底层用来发网络请求的库
        .addInterceptor(Interceptor { chain ->
            val request = chain.request().newBuilder()
                .header("User-Agent", "Mozilla/5.0 (Android)")
                .build()
            chain.proceed(request)//表示继续执行请求
        })//这里用Builder()自定义了一个OkHttpClient 加了一个拦截器Interceptor
        //拦截器可以在请求发出前、响应返回后做一些统一处理
        //这里的拦截器给每个请求都加上了一个User-Agent请求头(模拟浏览器或App身份,有些API会校验这个) 对于现在用的api 不加这个拦截器不行
        .build()

    val api: Top_ApiService by lazy {//懒加载的属性 只有第一次用到时才会初始化 节省资源
        Retrofit.Builder()
            .baseUrl("https://newsapi.org/v2/")//设置api基础网址
            .addConverterFactory(GsonConverterFactory.create())//用gson把json自动转换成kotlin对象
            .client(client)
            .build()//创建retrofit对象
            .create(Top_ApiService::class.java)//创建API接口的实现对象
    }
}

第三步:MVVM架构与数据流转

1.Repository层

Kotlin 复制代码
class Repository(private val api: Top_ApiService){//数据仓库
    //作用是在这里写一下api调用方法的具体实现
    suspend fun getTopNews(country: String,apiKey: String):News_Items{
        return api.get_topNews(country,apiKey)
    }
}
//在MVVM架构中 Repository负责从网络或本地数据库获取数据 并将数据提供给ViewModel 这样可以让ViewModel只关注数据的展示逻辑 而不用关心数据是怎么来的
//getTopNews方法:调用API接口获取新闻数据,参数和返回值都和API接口保持一致。用suspend修饰,表示要在协程中调用,避免阻塞主线程

2.ViewModel层

Kotlin 复制代码
class NewsViewModel(private val repository: Repository):ViewModel(){
    private val _newsList = MutableLiveData<News_Items>()
    val newsList:LiveData<News_Items> = _newsList

    fun fetchTopNews(country:String,apikey:String){
        viewModelScope.launch{
            try{
                val result=repository.getTopNews(country,apikey)
                _newsList.value=result
            }catch(_:Exception){ }
        }
    }
}
//ViewModel:用于管理界面数据,保证数据在界面旋转等情况下不会丢失。
//      比如你在看新闻列表,突然手机横屏了,Activity会被销毁再重建。
//      如果你把数据直接写在Activity里,界面重建后,数据会丢失,需要重新请求。
//ViewModel的作用就是:即使界面重建,ViewModel里的数据还在,不会丢失,这样用户体验更好。

//LiveData/MutableLiveData:实现数据的观察者模式,界面可以自动感知数据变化并刷新。
//viewModelScope.launch:在ViewModel自带的协程作用域中启动协程,进行异步操作(如网络请求),不会阻塞主线程。
//异常捕获:防止网络请求失败导致程序崩溃,可以在catch里加上错误提示

3.Activity层

Kotlin 复制代码
class NewsFeedActivity : AppCompatActivity() {
    private lateinit var viewModel: NewsViewModel
    private lateinit var adapter: NewsAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_news_feed)

        val recyclerView = findViewById<RecyclerView>(R.id.newsRecyclerView)
        recyclerView.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)//设置布局管理器 瀑布流
        adapter = NewsAdapter()//创建适配器
        recyclerView.adapter = adapter//绑定适配器

        val repository = Repository(RetrofitClient.api)//创建数据仓库对象
        viewModel = NewsViewModel(repository)//延迟初始化viewmodel
        viewModel.newsList.observe(this) { newsItems ->
            adapter.submitList(newsItems.articles)
        }
//        通过 observe 订阅 newsList 数据
//        当新闻数据发生变化时 自动调用 lambda 表达式 把新数据提交给 Adapter 刷新界面
        viewModel.fetchTopNews("us", "08928b7fed414010820d9af990c05f89")
    }
}

第四步:RecyclerView展示新闻列表

1.编写Adapter

Kotlin 复制代码
class NewsAdapter : RecyclerView.Adapter<NewsAdapter.NewsViewHolder>() {
    private var articles: List<Article> = emptyList()

    fun submitList(list: List<Article>) {//提交
        articles = list
        notifyDataSetChanged()//通知recyclerview 数据发生变化 刷新一下
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_news, parent, false)
        return NewsViewHolder(view)
    }//复制粘贴

    override fun onBindViewHolder(holder: NewsViewHolder, position: Int) {
        holder.bind(articles[position])
    }//当前位置数据绑定

    override fun getItemCount(): Int = articles.size//告诉recyclerview有多少条数据

    class NewsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {//
        private val titleTextView: TextView = itemView.findViewById(R.id.newsTitle)
        private val imageView: ImageView = itemView.findViewById(R.id.newsImage)

        fun bind(article: Article) {//将article数据绑定到控件上
            titleTextView.text = article.title
            Glide.with(itemView.context).load(article.urlToImage).into(imageView)//Glide高效加载网络图片,防止界面卡顿
        }
    }
}

//NewsAdapter:是 RecyclerView 的"桥梁",负责把新闻数据展示到每一行 item 上
//onCreateViewHolder:每当需要一个新的 item 行时,加载 item_news.xml 布局,生成 ViewHolder
//onBindViewHolder:把对应位置的数据绑定到 ViewHolder 上
//NewsViewHolder:用来缓存和管理 item_news.xml 里的控件,避免重复查找

2.编写item布局

相关推荐
mobsmobs3 小时前
Flutter开发环境搭建与工具链
android·flutter·ios·android studio·xcode
CheungChunChiu3 小时前
深入理解 eMMC RPMB 与 OP-TEE 在 Linux 系统中的应用开发
android·linux·运维·服务器·op-tee
onthewaying4 小时前
CameraX:Android相机开发的现代化解决方案
android
ifengouy4 小时前
Android中compileSdk,minSdk,targetSdk的含义和区别
android
星空梦想plus4 小时前
Android Camera openCamera
android·相机
茉莉玫瑰花茶6 小时前
MySQL 表的操作
android·数据库·mysql
CYRUS_STUDIO7 小时前
一键反编译、签名、安装 APK!手把手带你玩转 ApkTool + 签名工具
android·apk·逆向
只可远观7 小时前
Android集成Google Map
android·前端
搬砖不得颈椎病7 小时前
Compose 中的 Side-effects
android·android jetpack
fatiaozhang95278 小时前
天邑TY1613_S905L3SB_安卓9-高安版和非高安版-线刷固件包
android·电视盒子·刷机固件·机顶盒刷机