我正在开发新闻应用程序,并且已经在片段类中实现了ViewModel并在片段类中获取了数据,但是它正在加载进度条,仅不显示来自服务器的数据
在我的MainViewModel.kt下面
def inner_product(vec_1, vec_2):
# Requirement 1
if len(vec_1) != len(vec_2):
return None
# Requirement 2
elif vec_1 == [] or vec_2 == []:
return 0
# Define a list to hold your values until you return it
counter = []
# range() takes a beginning integer (index in this case)
# and an ending one. However, the range is exclusive of the last number
# so even though len(vec_1) == 3, range will stop at 2
for i in range(0, len(vec_2)):
# Append the output to your counter array on each pass
counter.append(vec_1[i]*vec_2[i])
# Once the for loop is done, return the value
return counter
# These should all return True
assert inner_product([], []) == 0
assert inner_product([1,2,3], [1,2]) is None
assert inner_product([1,2,3], [3,4,5]) == [3,8,15]
在适配器类之下
class MainViewModel(newsRepository: Any?) : ViewModel(), CoroutineScope {
// Coroutine's background job
val job = Job()
val sportNewsInterface: SportNewsInterface? = null
// Define default thread for Coroutine as Main and add job
override val coroutineContext: CoroutineContext = Dispatchers.Main + job
val showLoading = MutableLiveData<Boolean>()
val sportList = MutableLiveData <List<Article>>()
val showError = SingleLiveEvent<String>()
fun loadNews(
) {
// Show progressBar during the operation on the MAIN (default) thread
showLoading.value = true
// launch the Coroutine
launch {
// Switching from MAIN to IO thread for API operation
// Update our data list with the new one from API
val result = withContext(Dispatchers.IO) {
sportNewsInterface?.getNews()
}
// Hide progressBar once the operation is done on the MAIN (default) thread
showLoading.value = false
when (result) {
is UseCaseResult.Success<*> -> {
sportList.value = result.data as List<Article>
}
is Error -> showError.value = result.message
}
}
}
override fun onCleared() {
super.onCleared()
// Clear our job when the linked activity is destroyed to avoid memory leaks
job.cancel()
}
}
在实现了ViewModel的TopHeadlinesFragment下方
class TopHeadlinesAdapter(val context: Context) :
RecyclerView.Adapter<TopHeadlinesAdapter.MyViewHolder>() {
private var articleList: List<Article> by Delegates.observable(emptyList()) { _, _, _ ->
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.news_list, parent, false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return articleList.size
}
@SuppressLint("NewApi")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.articleTitle.text = articleList.get(position).title
holder.articleSourceName.text = articleList.get(position).source.name
Picasso.get().load(articleList.get(position).urlToImage).into(holder.image)
val input = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX")
val output = SimpleDateFormat("dd/MM/yyyy")
var d = Date()
try {
d = input.parse(articleList[5].publishedAt)
} catch (e: ParseException) {
try {
val fallback = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
fallback.timeZone = TimeZone.getTimeZone("UTC")
d = fallback.parse(articleList[5].publishedAt)
} catch (e2: ParseException) {
// TODO handle error
val formatted = output.format(d)
val timelinePoint = LocalDateTime.parse(formatted)
val now = LocalDateTime.now()
var elapsedTime = Duration.between(timelinePoint, now)
println(timelinePoint)
println(now)
elapsedTime.toMinutes()
holder.articleTime.text = "${elapsedTime.toMinutes()}"
}
}
}
fun updateData(newList: List<Article>) {
articleList = newList
}
@SuppressLint("NewApi")
fun example() {
}
class MyViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) {
val image: ImageView = itemView!!.findViewById(R.id.imageView)
val articleTitle: TextView = itemView!!.findViewById(R.id.articleTitle)
val articleSourceName: TextView = itemView!!.findViewById(R.id.articleSourceName)
val imageCategory: ImageView = itemView!!.findViewById(R.id.imageCategory)
val articleTime: TextView = itemView!!.findViewById(R.id.articleTime)
}
}
AppModules.kt下面
class TopHeadlinesFragment : Fragment() {
private var viewModel: MainViewModel? = null
private lateinit var topHeadlinesAdapter: TopHeadlinesAdapter
//3
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(
R.layout.fragment_top_headlines
, container, false
)
val recyclerView = view.findViewById(R.id.recyclerView) as RecyclerView
val pb = view.findViewById(R.id.pb) as ProgressBar
topHeadlinesAdapter = TopHeadlinesAdapter(recyclerView.context)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = topHeadlinesAdapter
initViewModel()
return view
}
private fun initViewModel() {
viewModel?.sportList?.observe(this, Observer { newList ->
topHeadlinesAdapter.updateData(newList)
})
viewModel?.showLoading?.observe(this, Observer { showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
viewModel?.showError?.observe(this, Observer { showError ->
(showError)
})
viewModel?.loadNews()
})
}
}
fragment_top_headlines.xml以下
const val BASE_URL = "https://newsapi.org/"
val appModules = module {
// The Retrofit service using our custom HTTP client instance as a singleton
single {
createWebService<SportNewsInterface>(
okHttpClient = createHttpClient(),
factory = RxJava2CallAdapterFactory.create(),
baseUrl = BASE_URL
)
}
// Tells Koin how to create an instance of CatRepository
factory<NewsRepository> { (NewsRepositoryImpl(sportNewsInterface = get())) }
// Specific viewModel pattern to tell Koin how to build MainViewModel
viewModel { MainViewModel (newsRepository = get ()) }
}
/* Returns a custom OkHttpClient instance with interceptor. Used for building Retrofit service */
fun createHttpClient(): OkHttpClient {
val client = OkHttpClient.Builder()
client.readTimeout(5 * 60, TimeUnit.SECONDS)
return client.addInterceptor {
val original = it.request()
val requestBuilder = original.newBuilder()
requestBuilder.header("Content-Type", "application/json")
val request = requestBuilder.method(original.method, original.body).build()
return@addInterceptor it.proceed(request)
}.build()
}
/* function to build our Retrofit service */
inline fun <reified T> createWebService(
okHttpClient: OkHttpClient,
factory: CallAdapter.Factory, baseUrl: String
): T {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addCallAdapterFactory(factory)
.client(okHttpClient)
.build()
return retrofit.create(T::class.java)
}
低于news_list.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
NewsRepository.kt以下
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="85dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:contentDescription="bbc"
tools:background="@color/colorPrimary" />
<TextView
android:id="@+id/articleTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="@id/imageView"
android:layout_toRightOf="@id/imageView"
android:ellipsize="end"
android:lines="3"
android:maxLines="3"
android:text="1\n2\n3\n" />
<ImageView
android:id="@+id/imageCategory"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_below="@id/articleTitle"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_toEndOf="@id/imageView"
android:layout_toRightOf="@id/imageView"
android:src="@drawable/ic_espn"
tools:background="@color/colorPrimary" />
<TextView
android:id="@+id/articleSourceName"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_below="@id/articleTitle"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_toEndOf="@id/imageCategory"
android:layout_toRightOf="@id/imageCategory"
android:gravity="center|start"
android:text="Onefootbal" />
<TextView
android:id="@+id/articleTime"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_below="@id/articleTitle"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="@id/articleSourceName"
android:layout_toRightOf="@id/articleSourceName"
android:gravity="center|start"
android:text="- 1h"
android:textColor="@android:color/darker_gray"
tools:ignore="NotSibling" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
在SportInterface.kt下面,我正在获得终点
interface NewsRepository {
// Suspend is used to await the result from Deferred
suspend fun getNewsList(): UseCaseResult<Deferred<List<SportNewsResponse>>>
}
class NewsRepositoryImpl(private val sportNewsInterface: SportNewsInterface) : NewsRepository {
override suspend fun getNewsList(): UseCaseResult<Deferred<List<SportNewsResponse>>> {
/*
We try to return a list of cats from the API
Await the result from web service and then return it, catching any error from API
*/
return try {
val result = sportNewsInterface.getNews()
UseCaseResult.Success(result) as UseCaseResult<Deferred<List<SportNewsResponse>>>
} catch (ex: Exception) {
UseCaseResult.Error(ex)
}
}
}
SportNewsResponse.kt以下
interface SportNewsInterface {
@GET("v2/top-headlines?country=us&apiKey=da331087e3f3462bb534b3b0917cbee9")
suspend fun getNews(): List<SportNewsResponse>
@GET("/v2/top-headlines?sources=espn&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getEspn(): Deferred<List<SportNewsResponse>>
@GET("/v2/top-headlines?sources=football-italia&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getFootballItalia(): Deferred<List<SportNewsResponse>>
@GET("/v2/top-headlines?sources=bbc-sport&apiKey=da331087e3f3462bb534b3b0917cbee9")
fun getBBCSport(): Deferred<List<SportNewsResponse>>
}
Article.kt以下
data class SportNewsResponse(
val articles: List<Article>,
val status: String,
val totalResults: Int
)
答案 0 :(得分:0)
您的initViewModel
看起来不太正常。尝试在下面使用:
private fun initViewModel() {
viewModel?.sportList?.observe(this, Observer { newList ->
topHeadlinesAdapter.updateData(newList)
})
viewModel?.showLoading?.observe(this, Observer { showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
})
viewModel?.showError?.observe(this, Observer { showError ->
(showError)
})
viewModel?.loadNews()
}
答案 1 :(得分:0)
首先 viewModel不应为null,在创建片段时,请使用lateinit
并初始化viewModel。
第二,因为您使用的是Kotlin,所以不需要使用findViewById
。
因此,您可以将所有初始视图移至onViewCreated
而不是onCreateView
第三通过viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
初始化viewModel
或只是viewModel = MainViewModel()
。这使您的viewModel可以工作。
毕竟,我不知道您的MainModel中的newsRepository
的含义。您永远不会使用它,也许可以删除它。
class TopHeadlinesFragment : Fragment() {
private lateinit var viewModel: MainViewModel
private lateinit var topHeadlinesAdapter: TopHeadlinesAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(
R.layout.fragment_top_headlines
, container, false
)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
topHeadlinesAdapter = TopHeadlinesAdapter(context)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = topHeadlinesAdapter
initViewModel()
viewModel.loadNews()
}
private fun initViewModel() {
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
viewModel?.sportList?.observe(this, Observer { newList ->
topHeadlinesAdapter.updateData(newList)
})
viewModel?.showLoading?.observe(this, Observer { showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
viewModel?.showError?.observe(this, Observer { showError ->
(showError)
})
})
}
}
希望这会有所帮助。