使用参数将数据从活动发送到片段(Kotlin / Android)

时间:2018-02-20 18:34:20

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

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

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

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

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

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

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

2)在我的片段中设置一些textViews作为天气的值(显示“它是晴天!”)

以下是我到目前为止试图解决问题的方法:

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

 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文本值为null,它们不会在我的页面中显示在模拟器中。我原本以为我在创建选项卡和获取活动中的数据之间遇到了竞争条件,因此我暂停了。但是,这也没有解决问题。

这是片段代码:

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]
    }
}

1 个答案:

答案 0 :(得分:1)

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

val homeFrag = HomeFragment()

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

您希望在Fragment中显示已附加到TabLayout的新数据。所以你需要在一些变量中保持它的句柄 - 让我们称之为 homeFragment - 然后写

homeFragment.getWeather(args);

查看您的TabsAdapter实现,我认为将 args 传递给适配器也是一个好主意,因此适配器又可以将数据传递给HomeFragment下一次显示它。为实现此目的,HomeFragment需要一个静态instance()方法,该方法将获取Bundle参数并返回HomeFragment实例。然后,您可以使用此方法而不是构造函数。

post显示了如何在Kotlin中执行此操作。