【问题标题】:Sending Data from Activity to Fragment Using Arguments (Kotlin/Android)使用参数将数据从 Activity 发送到 Fragment (Kotlin/Android)
【发布时间】:2018-02-20 18:34:20
【问题描述】:

我正在尝试将数据从 Activity 发送到它的子片段。我有一个活动首先像这样加载所有片段:

    view_pager.adapter = TabsAdapter(supportFragmentManager)
    tab_layout.setupWithViewPager(view_pager)

TabsAdapter 这里只是一个标签轮播的 if 条件,它支持滑动和点击标签标题来改变标签。

加载标签后,我调用 Fuel http 请求从 https://openweathermap.org 获取天气数据,这是一个非常简单的 API。我成功检索了我当地的天气,并可以将输出记录到控制台。我卡住的地方是将数据发送到 Fragment。

这个问题有很多 stackoverflow 的答案。我的问题是我需要做两件事:

1) 将数据发送到片段,最好使用参数,因为这看起来最简单 和

2) 将片段中的一些 textViews 设置为天气值(显示“天气晴朗!”)

这是我目前尝试解决问题的方法:

在我的活动中,数据从 http 请求返回后,我将数据放在一个包中,并将包发送到我的片段类中的一个方法:

 println("success from json request to weatherAPI")
 val weatherArray = result.get().obj().getJSONArray("weather").get(0) as JSONObject
 println("value of main in weatherArray:" + weatherArray["main"])
 println("value of description in weatherArray:" + weatherArray["description"])
 println("value of icon in weatherArray: " + weatherArray["icon"])

 val args = Bundle()
 args.putString("weatherurl",  "http://openweathermap.org/img/w/"+weatherArray["icon"]+".png")
 args.putString("weatherdescription", weatherArray["description"].toString())
 args.putString("weathermain", weatherArray["main"].toString())

 val homeFrag = HomeFragment()
 homeFrag.getWeather(args)

这是我正在使用的片段。所以在onCreateView 中,我将weathermainViewweatherdescriptionView 设置为片段布局中的项目。我必须在onCreateView 中执行此操作,因为这是唯一可以使用view 的地方。我将这些作为公共变量,因为我需要在我的getWeather 函数中访问它们。

我的getWeather 函数尝试获取捆绑参数并将它们设置为等于 textViews,然而无论我做什么,weathermainViewweatherdescriptionView 文本值都是空的,它们确实如此不显示在我的模拟器页面中。我原本以为我在创建选项卡和在活动中获取数据之间遇到了竞争条件,所以我设置了一个超时。但是,这也不能解决问题。

这里是片段代码:

class HomeFragment : Fragment() {
     var weathermainView: TextView?=null
     var weatherdescriptionView:  TextView?=null

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        val view = inflater!!.inflate(R.layout.fragment_home, container, false)

        weathermainView = view.findViewById<TextView>(R.id.textWeatherMain)
        weatherdescriptionView = view.findViewById<TextView>(R.id.textWeatherDescription)
        println("!!!after setting weathermainView in onCreateView!!!")
        println("value of weathermainView " + weathermainView?.text.toString())

        return inflater!!.inflate(R.layout.fragment_home, container, false)
    }

    fun getWeather(args: Bundle){
        val weatherMain = args.getString("weathermain")
        val weatherDescription = args.getString("weatherdescription")
        val timer = Timer()
        timer.schedule(timerTask {
            println("value of weathermainView BEFORE SETTING" + weathermainView?.text.toString())
            println("value of weatherdescriptionView BEFORE SETTING" + weatherdescriptionView?.text.toString())
            weathermainView?.text =  args.getString("weathermain")
            weatherdescriptionView?.text = args.getString("weatherdescription")
            println("value of weathermainView " + weathermainView?.text.toString())
            println("value of weatherdescriptionView " + weatherdescriptionView?.text.toString())
        }, 10000)

        println("Value of weatherMain: " + weatherMain)
        println("Value of weatherDescription: " + weatherDescription)

    }
}

这是我看到的命令行输出:

I/System.out: !!!after setting weathermainView in onCreateView!!!
I/System.out: value of weathermainView hello there home fragment
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 1
W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
D/OpenGLRenderer: Swap behavior 0
D/EGL_emulation: eglCreateContext: 0x9ab28d60: maj 3 min 0 rcv 3
D/EGL_emulation: eglMakeCurrent: 0x9ab28d60: ver 3 0 (tinfo 0x99bf5790)
E/eglCodecCommon: glUtilsParamSize: unknow param 0x00008cdf
E/eglCodecCommon: glUtilsParamSize: unknow param 0x00008cdf
E/eglCodecCommon: glUtilsParamSize: unknow param 0x00008824
E/eglCodecCommon: glUtilsParamSize: unknow param 0x00008824
D/EGL_emulation: eglMakeCurrent: 0x9ab28d60: ver 3 0 (tinfo 0x99bf5790)
I/System.out: success from json request to weatherAPI
I/System.out: value of main in weatherArray:Rain
I/System.out: value of description in weatherArray:light rain
I/System.out: value of icon in weatherArray: 10d
I/System.out: Value of weatherMain: Rain
I/System.out: Value of weatherDescription: light rain
I/System.out: value of weathermainView BEFORE SETTING: null
I/System.out: value of weatherdescriptionView BEFORE SETTING: null
I/System.out: value of weathermainView null
I/System.out: value of weatherdescriptionView null

注意:!!!after setting weathermainView in onCreateView!!! 是第一个输出,因此必须在从 http 请求返回响应并进行 getWeather 调用之前实例化选项卡。 并且在活动中设置天气变量和片段的onCreateView都有效。 但是

I/System.out: value of weathermainView BEFORE SETTING: null
I/System.out: value of weatherdescriptionView BEFORE SETTING: null
I/System.out: value of weathermainView null
I/System.out: value of weatherdescriptionView null

那么为什么这些为空?

没有竞态条件,我可以成功设置onCreatView。我什至设置了超时。我很困惑,任何提示都会有所帮助。

编辑:

这是我的 TabsAdapter:

class TabsAdapter(manager: FragmentManager) : FragmentPagerAdapter(manager) {
    // Title for tabs
    private val fragmentTitles = arrayOf("Home", "Music", "Map")
    // Return the Fragment associated with a specified position.
    override fun getItem(position: Int): Fragment {
        return when (position) {
            0 -> HomeFragment()
            1 -> MusicFragment()
            2 -> MapFragment()
            else -> HomeFragment()
        }
    }
    // Return the number of views/fragments available.
    override fun getCount(): Int = 3 // in our case 3 fragments
    // Return title based on position
    override fun getPageTitle(position: Int): CharSequence {
        return fragmentTitles[position]
    }
}

【问题讨论】:

  • onActivityCreatedonViewCreated 中写入bundle 相关代码,您可能不会收到错误消息。如果有效,请更新..
  • 嗯....不知道我知道怎么做。我尝试从这样的活动中传递参数:`homeFrag.arguments.putString(someweatherstring). In onViewCreated` 我有val weatherurl = arguments.getString("weatherurl"),但这给出了错误java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.os.Bundle.getString(java.lang.String)' on a null object reference。片段中的arguments应该是什么修饰符?
  • 首先你需要得到像getArguments()这样的参数
  • 记住,这是在 Kotlin 中,所以 get/setArguments() 现在是参数。它的语法略有不同。
  • 我也有类似的问题。你的解决了吗?你能分享一下出了什么问题吗?

标签: java android android-fragments kotlin race-condition


【解决方案1】:

代码中的以下行为您提供了一个新的 HomeFragment 实例:

val homeFrag = HomeFragment()

此实例未附加到Activity,因此无需执行onCreateView()。因此,TextViews 都是null

您希望在已附加到TabLayoutFragment 中显示新数据。所以你需要在某个变量中保留一个句柄 - 我们称之为 homeFragment - 然后写

homeFragment.getWeather(args);

查看您的TabsAdapter 实现,我认为将args 传递给适配器也是一个好主意,这样适配器就可以将数据传递给HomeFragment 以备下次使用是要显示的。为了实现这一点,HomeFragment 需要一个静态的 instance() 方法,该方法将采用 Bundle 参数并返回一个 HomeFragment 实例。那你就可以用这个方法代替构造函数了。

这个post 展示了如何在 Kotlin 中做到这一点。

【讨论】:

  • 这是一个非常好的观点。我已经发布了我的 TabsAdapter。也许我应该在那里编写一个函数来以某种方式获取 Fragment 类....但不确定。我不得不承认我有点迷失了,我还没有看到很多关于如何处理 TabAdapter 中的片段如此深入的 SO 帖子。
  • @Peter Weyand - 就像我编辑的答案所说:你使用 HomeFragment.instance(args) 而不是 HomeFragment() 我必须承认这是普通的旧 Java,不知道在 Kotlin 中怎么说.但我会尽力找出并回来
  • @Peter Weyand - 在 Stack Overflow 上找到了一些东西,我希望它有用
  • 我很欣赏这种洞察力。仍然有麻烦 - 我现在遇到的问题是异步问题。因此,如果我在我的休息电话中调用TabsAdapter.newInstanceHome("weatherurl", "http://openweathermap.org/img/w/"+weatherArray["icon"]+".png"),并且在onCreateView 中我有val bdl: Bundle = arguments 参数将为空并引发错误。为什么?因为onCreateView 在其余调用完成之前触发。我可以在片段中执行其他生命周期方法,但我只是希望片段满足竞争条件。
  • @Peter Weyand - 在刷新时显示旧数据或“加载...”不是更好吗?一旦有了新数据,就发布它们?您的 TabsAdapter 可能有一个方法 refresh(position: Int, args: Bundle)。然后要么 HomeFragment 显示,你可以简单地调用 getWeather() 或者它不是,然后下次应该显示你使用 args 和 instance() 方法
猜你喜欢
  • 2021-06-07
  • 1970-01-01
  • 1970-01-01
  • 2015-05-15
  • 2021-02-07
  • 2013-09-27
  • 1970-01-01
  • 1970-01-01
  • 2018-04-14
相关资源
最近更新 更多