如何在 jetpack compose 中使用改造 API 创建列表视图/网格视图/回收器视图

Posted

技术标签:

【中文标题】如何在 jetpack compose 中使用改造 API 创建列表视图/网格视图/回收器视图【英文标题】:How to create list view/grid view/recycler view with retrofit API in jetpack compose 【发布时间】:2021-11-02 05:41:44 【问题描述】:

我已经创建了 Kotlin 代码,用于在列表视图/网格视图/回收器视图中通过改造来解析 API,我想知道如何使用 jetpack compose 来做同样的事情?我使用改造来使用不同的 ViewGroup 解析 GET API 响应。视图绑定用于与此屏幕上的视图进行交互。

代码

 import android.annotation.SuppressLint
                import android.app.AlertDialog
                import android.app.ProgressDialog
                import android.content.ActivityNotFoundException
                import android.content.DialogInterface
                import android.content.Intent
                import android.content.res.Configuration
                import android.os.Bundle
                import android.util.Log
                import android.view.*
                import android.view.inputmethod.EditorInfo
                import android.widget.*
                import androidx.activity.viewModels
                import androidx.appcompat.app.AppCompatActivity
                import androidx.databinding.DataBindingUtil
                import okhttp3.ResponseBody
                import org.json.JSONArray
                import org.json.JSONException
                import org.json.JSONObject
                import retrofit.Retrofit2
                import retrofit2.Call
                import retrofit2.Callback
                import retrofit2.Response
                import retrofit2.Retrofit
                import retrofit2.converter.gson.GsonConverterFactory
                import supports.*
                import viewmodel.SIViewModel
                import java.io.IOException
                import java.net.SocketTimeoutException
                import java.util.*
                
                
                class TestIndex : AppCompatActivity() 
                    var adapter: Adapter1? = null
                    var dialog: AlertDialog? = null
                    var builder: AlertDialog.Builder? = null
                    private val viewModel: SIViewModel? by viewModels()
                    var test_arr = ArrayList<TestModel>()
                    var binding: TestGridBinding? = null
               
                
                    @SuppressLint("CommitPrefEdits", "ClickableViewAccessibility", "SetTextI18n")
                    override fun onCreate(savedInstanceState: Bundle?) 
                        super.onCreate(savedInstanceState)
                        binding = DataBindingUtil.setContentView(this, R.layout.test_grid)
                        setSupportActionBar(binding?.view?.toolbarr)
                        supportActionBar!!.elevation = 0f
                
                        viewModel
                
                
                       
                        adapter = Adapter1(this@TestIndex, R.layout.row, test_arr)
                
                
                
        //binding ViewModel retrofit API with activity, here ID1 and ID2 coming from the previous screen.
                        viewModel!!.getList(this@TestIndex , ID1!!, ID2!!)
        
                        binding?.gvTest?.adapter = adapter
                
        
                        binding?.swipeRefreshLayout?.setOnRefreshListener 
                            binding?.swipeRefreshLayout?.isRefreshing = true
                 
                                if (ID1 != null && ID2 != null) 
        
        // getting same server response on swipe refresh widget
                                    getdata(ID1!!, ID2!!)
                                 else 
                                    builder = AlertDialog.Builder(MyApplication.instance)
                                    builder!!.setCancelable(false)
                                    builder!!.setTitle("Alert")
                                    builder!!.setNegativeButton("Cancel")  dialog: DialogInterface, which: Int ->
                                        dialog.dismiss()
                                        finish()
                                    
                                    builder!!.setPositiveButton("OK")  dialog: DialogInterface, which: Int -> dialog.dismiss() 
                                    dialog = builder!!.create()
                                    dialog?.show()
                                
                
                        
                
                
                
                        subscribeObservers()
                    
                
//this is checked on the dev portal but I don't know I could I use it //dynamically with adapters and ArrayList.
                    @Composable
        fun LazyRowItemsDemo() 
            LazyRow 
                items((1..title_arr.size).toList()) 
                    Text(text = "Item $it")
                
            
        
                
                
                    private fun getdata(id1: String, id2: String) 
                
                        val mProgressDialog = ProgressDialog(this@TestIndex)
                        mProgressDialog.isIndeterminate = true
                        mProgressDialog.setMessage(Keys.KEY_pre_msg)
                        if (!this.isFinishing) 
                            mProgressDialog.show()
                        
                        val retrofit = Retrofit.Builder()
                            .baseUrl(Keys.testURL)
                            .client(OkHttpClient().build())
                            .addConverterFactory(GsonConverterFactory.create())
                            .build()
                        val retrofitInterface = retrofit.create(
                            RetrofitInterface::class.java
                        )
                        val call = retrofitInterface.getTestdata(id1, id2)
                
                        call!!.enqueue(object : Callback<ResponseBody?> 
                            override fun onResponse(call: Call<ResponseBody?>, response: Response<ResponseBody?>) 
                                var remoteResponse: String? = null
                                if (response.code() == 200) 
                                    try 
                                        assert(response.body() != null)
                                        remoteResponse = response.body()!!.string()
                                     catch (e: Exception) 
                                        e.printStackTrace()
                                    
                                 else 
                                    try 
                                        if (response.errorBody() != null) 
                                            remoteResponse = response.errorBody()!!.string()
                                        
                                     catch (e: IOException) 
                                        e.printStackTrace()
                                    
                                
                                if (remoteResponse != null) 
            
            //getting response fields and parsing list view or grid view/recycler view in different screens
            
                                        adapter =
                                            Adapter1(this@TestIndex, R.layout.row, test_arr)
                                        binding!!.gvTest.adapter = adapter
                                        adapter!!.notifyDataSetChanged()
                
                                
                            
                
                            override fun onFailure(call: Call<ResponseBody?>, t: Throwable) 
                                Log.d(Keys.KEY_TAG, "onFailure: " + t.localizedMessage)
                            
                        )
                        if (mProgressDialog.isShowing) mProgressDialog.dismiss()
                    
                
                
        //subscribed the Observers here from view model
                    private fun subscribeObservers() 
                        viewModel!!.lifting.observe(this,  TestModel: List<TestModel>? ->
                            adapter!!.updateTests(TestModel)
                            binding!!.swipeRefreshLayout.isRefreshing = false
                    
                
                

请让我知道如何使用 jetpack compose 为列表视图、网格视图、回收器视图做同样的事情。谢谢。

【问题讨论】:

查看lazy lists in Compose 我在 Compose 中看到了一些惰性列表的演示,但无法根据我的需要进行更新。 显示您尝试了哪些撰写代码,哪些未按预期工作。也可以查看 compose view models,您可以在那里进行数据处理 先生,请检查我是否更新了 LazyRowItemsDemo 函数,我想用 arraylist 更新列表视图/网格视图/回收器视图,我不确定如何从 API 调用更新它并与适配器同步动态的。 【参考方案1】:

这是一个更一般的例子,没有改造。您可以在我的 getTestData 方法中实现数据提取。

首先,为了了解使用 Compose 的基本原理,我建议你学习compose tutorials。

Compose 使用视图模型来执行复杂的数据操作。我将使用基本版本,但您也可以查看Hilt 了解更复杂的架构。

为了改变对象的状态以导致重组,您可以使用:

    mutableStateObject - 这是一个专门为 compose 创建的容器,如果值发生变化,它将更新视图 您也可以使用LiveDataFlow,它们都可以转换为mutableStateObject。

请注意,如果您在其中传递复杂类,mutableStateObject 不会提醒您容器对象字段的更改。它只会在值本身发生变化时通知您,因此建议仅将其用于简单类型。

您也可以使用mutableStateListOf 来存储收藏。在我的示例中,您将看到两者:mutableStateListOf 可以方便地向集合添加/删除对象,而 mutableStateObjectList 位于内部更容易完全替换为新对象。

在 Composable 函数内部,您需要使用 remember 包装可变状态对象,以防止在每个组合上重新初始化它们,而在您的视图模型内部,您不需要这样做,因为它在任何情况下都不会被重新初始化。

SwipeRefresh 不是 compose 的一部分,它也是 compose 维护者创建的库。要安装它,请关注this instructions。

我在这里使用两列只是为了显示mutableStateOfmutableStateListOf 之间的区别,您可以删除RowLazyColumn 之一

class ScreenViewModel : ViewModel() 
    var list by mutableStateOf(emptyList<String>())
    var mutableList = mutableStateListOf<String>()
    var isRefreshing by mutableStateOf(false)

    init 
        refresh()
    

    fun refresh() 
        isRefreshing = true
        viewModelScope.launch 
            list = getTestData()
            mutableList.addAll(0, list)
            isRefreshing = false
        
    

    suspend fun getTestData(): List<String> 
        // emulate network call
        delay(1000)
        return List(100) 
            Random.nextInt(100).toString()
        
    


@Composable
fun TestView() 
    val viewModel: ScreenViewModel = viewModel()
    SwipeRefresh(
        state = rememberSwipeRefreshState(viewModel.isRefreshing),
        onRefresh = 
            viewModel.refresh()
        ,
    ) 
        Row 
            LazyColumn(
                modifier = Modifier.weight(1f) // this needed inly for two columns case
            ) 
                itemsIndexed(viewModel.list)  i, item ->
                    Text("$i $item")
                
            
            LazyColumn(
                modifier = Modifier.weight(1f) // this needed inly for two columns case
            ) 
                itemsIndexed(viewModel.mutableList)  i, item ->
                    Text("$i $item")
                
            
        
    

结果:

【讨论】:

感谢您的评论先生,但我想要一个使用 API 调用的更通用的解决方案! @Ramanjeet 为什么不复制自己的 API 调用代码而不是我的 getTestData 这就是我想知道的如何将我的 kotlin 代码使用改造 API 集成到 jetpack compose 中...我应该使用相同的代码而不做任何修改吗?? @Ramanjeet 基本上是的。我向你展示了 Jetpack Compose 的所有 UI 部分,如果你知道改造的工作原理,插入它应该没问题。

以上是关于如何在 jetpack compose 中使用改造 API 创建列表视图/网格视图/回收器视图的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Jetpack Compose 中使用小部件?

如何在 jetpack compose 中使用 MotionLayout、MotionScene、ConstraintSet?

Jetpack Compose ScrollableTabRow 如何调整最小宽度

如何在 Android Jetpack Compose 中使用字符串资源?

如何将 CameraView 与 Jetpack Compose 一起使用?

如何在 Android Jetpack Compose 中结合使用 LazyColumn stickyHeader 和 Paging?