android - 无法将数据传递到 Android (Kotlin) 中的另一个片段
问题描述
我有问题。我正在为 MQTT 客户端的 Android 制作应用程序,我需要在不同的片段中为 MqttAndroidClient() 方法使用相同的参数。我已经尝试在包中传递它们,意图 putExtra(),制作类的对象。bundle 和 putExtra 将数据发送到另一个片段,它以调试模式显示,但在目标片段中我得到 nulls。当我试图接收实例化第一个片段的值时,它会向我发送没有任何值的 lateinit 变量。我不知道我还能做些什么。我考虑过使用 setter 和 getter 来获取它,但我不确定这是否是解决方案。
我发送数据的第一个片段:
连接片段.kt
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.dzichkovskii.mqttsrm.R
import kotlinx.android.synthetic.main.fragment_connect.*
import org.eclipse.paho.android.service.MqttAndroidClient
import org.eclipse.paho.client.mqttv3.*
import java.io.Serializable
class ConnectFragment : Fragment(){
companion object{
const val SUCCESS_TEXT = "Connection established successfully"
const val FAILURE_TEXT = "Connection wasn't established. Error happened."
const val BLANK_TEXT = "Your inputs cannot be empty. Please, write the correct address or ID."
const val CONNECTION_FAILURE = "Something went wrong. Probably you have no internet. Try later"
const val SENDING_NAME_ADDRESS = "mqttAndroidClientAddress"
const val SENDING_NAME_ID = "mqttAndroidClientId"
const val TAG = "ConnectFragment"
}
lateinit var mqttAndroidClient: MqttAndroidClient
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_connect, container, false)
//I will leave this values just in case I would need to test the connection
//val testClientId = MqttClient.generateClientId()
//val testAddress = "tcp://broker.hivemq.com:1883"
root.findViewById<Button>(R.id.btn_connect).setOnClickListener {
mqttAndroidClient = connect(context, view)
}
return root
}
private fun connect(context: Context?,
view: View?): MqttAndroidClient {
// val inputAddress = view?.findViewById(R.id.tv_broker_address_input) as EditText
// val inputId = view?.findViewById(R.id.tv_client_id_input) as EditText
// val inputPort = view.findViewById(R.id.tv_broker_port_input) as EditText
//
//Making the string the user needs to put more friendly
// val addressStringSimplification = "tcp://" + inputAddress.text.toString() +
// ":" + inputPort.text.toString()
val addressStringSimplification = "tcp://broker.hivemq.com:1883"
val testClientId = MqttClient.generateClientId()
mqttAndroidClient = MqttAndroidClient(context?.applicationContext, addressStringSimplification, testClientId/*inputId.text.toString()*/)
val intent = Intent(this.context, SubscribeFragment::class.java)
val bundle = Bundle()
bundle.putString("testBundle", addressStringSimplification) //bundle here
val subscribeFragment = SubscribeFragment()
subscribeFragment.arguments = bundle
intent.putExtra(SENDING_NAME_ADDRESS, addressStringSimplification) //Intents here
intent.putExtra(SENDING_NAME_ID, testClientId)
// if (inputAddress.isBlank() || inputId.isBlank()
// || inputPort.isBlank() || addressStringSimplification == "tcp://:"){
// displayErrorMessage(BLANK_TEXT, view, this)
// return
// }
// else {
try {
val token = mqttAndroidClient.connect()
token.actionCallback = object : IMqttActionListener {
override fun onSuccess(asyncActionToken: IMqttToken?) {
Log.d(TAG, "Connection is successful")
Toast.makeText(context, SUCCESS_TEXT, Toast.LENGTH_SHORT).show()
hideKeyboard()
return
}
override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
Log.d(TAG, "Connection didn't established")
Toast.makeText(context, FAILURE_TEXT, Toast.LENGTH_SHORT).show()
displayErrorMessage(FAILURE_TEXT, view, this@ConnectFragment)
return
}
}
} catch (e: MqttException) {
Log.d(TAG, "Exception caught")
displayErrorMessage(CONNECTION_FAILURE, view, this)
}
return mqttAndroidClient
}
// tv_error.visibility = View.INVISIBLE
}
/**
* This extension function makes strings look less ugly.
*/
private fun EditText.isBlank() = this.text.toString().isBlank()
//}
fun displayErrorMessage(errorString: String, view: View?, fragment: Fragment){
val errorTextView = view?.rootView?.findViewById(R.id.tv_error) as TextView
errorTextView.text = errorString
errorTextView.visibility = View.VISIBLE
fragment.hideKeyboard()
}
fun Fragment.hideKeyboard() {
view?.let { activity?.hideKeyboard(it) }
}
fun Context.hideKeyboard(view: View) {
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}
这是我接收数据的另一个片段:
订阅片段.kt
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.dzichkovskii.mqttsrm.R
import com.google.android.material.chip.Chip
import kotlinx.android.synthetic.main.fragment_subscribe.view.*
import org.eclipse.paho.android.service.MqttAndroidClient
import org.eclipse.paho.client.mqttv3.IMqttActionListener
import org.eclipse.paho.client.mqttv3.IMqttToken
import org.eclipse.paho.client.mqttv3.MqttException
class SubscribeFragment : Fragment() {
companion object {
const val TAG = "SubscribeFragment"
const val BLANK_TEXT = "Your inputs cannot be empty. Please, write the correct address or ID."
const val ON_SUCCESS = "You subscribed successfully."
const val ON_FAILURE = "You didn't subscribed to the topic. Probably this topic doesn't exist."
const val CONNECTION_ERROR = "The topic don't exist or you have connection problems. " +
"Check your internet connection or change the topic's name"
const val GETTING_NAME_ADDRESS = "mqttAndroidClientAddress"
const val GETTING_NAME_ID = "mqttAndroidClientId"
}
private var checkedOption: Int = 0 //Default value of qos
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_subscribe, container, false)
return root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.chip_group?.setOnCheckedChangeListener { _, checkedId: Int ->
val chip: Chip? = view.findViewById(checkedId)
val qos = chip?.text.toString().toInt()
checkedOption = qos
Log.d(TAG, "Checked option passed with value $checkedOption")
}
view.findViewById<Button>(R.id.btn_subscribe).setOnClickListener {
subscribe()
}
}
private fun subscribe(){
val connectFragment = ConnectFragment()
val mqttAndroidClient = connectFragment.mqttAndroidClient //Instantiation of the class here
val address = this@SubscribeFragment.arguments?.getString("testBundle") // Bundle here
val id = activity?.intent?.getStringExtra(GETTING_NAME_ID) // Intent string here
//val mqttAndroidClient = MqttAndroidClient(context, address, id)
val inputTopic = view?.findViewById(R.id.et_topic) as EditText
val topic = inputTopic.text.toString()
if (inputTopic.isBlank()){
displayErrorMessage(BLANK_TEXT, view, this)
}
try {
Log.d(TAG, "Checked option in subscribe method is $checkedOption")
mqttAndroidClient.subscribe(topic, checkedOption, null, object : IMqttActionListener {
override fun onSuccess(asyncActionToken: IMqttToken) {
Toast.makeText(context, ON_SUCCESS, Toast.LENGTH_SHORT).show()
Log.d(TAG, "Connected successfully")
}
override fun onFailure(
asyncActionToken: IMqttToken,
exception: Throwable
) {
Toast.makeText(context, ON_FAILURE, Toast.LENGTH_SHORT).show()
Log.d(TAG, "Didn't connected")
}
})
} catch (e: MqttException) {
displayErrorMessage(CONNECTION_ERROR, view, this)
}
}
private fun EditText.isBlank() = this.text.toString().isBlank()
}
谢谢回答!
解决方案
首先要创建一个片段,你应该有一个newInstance
方法,这个方法应该除了你想要传递给片段的参数之外,在你的情况下SubscriberFragment
应该有以下函数companion
object
class SubscribeFragment: Fragment() {
companion object {
const val SENDING_NAME_ADDRESS = "mqttAndroidClientAddress"
const val SENDING_NAME_ID = "mqttAndroidClientId"
// Use this function to create instance of your fragment
fun newInstance(addressStringSimplification: String,
testClientId: String): MyFragment {
val args = Bundle()
args.putString(SENDING_NAME_ADDRESS , addressStringSimplification)
args.putString(SENDING_NAME_ID , testClientId)
val fragment = SubscribeFragment()
fragment.arguments = args
return fragment
}
}
}
在此之后,当您的片段被加载时,参数被传递给onCreate
方法,您可以按如下方式提取它们。
@Override
public void onCreate(Bundle savedInstanceState) {
var sendingName = getArguments().getInt(SENDING_NAME_ADDRESS);
var sendingId = getArguments().getString(SENDING_NAME_ID);
}
现在到最重要的部分,您似乎认为这样做SubscriberFragment()
足以加载片段,但事实并非如此。当你想开始时SubscriberFragment
,你应该做以下事情
// create instance of SubscriberFragment with newInstance function and pass the argguments you want
var someFragment = SubscribeFragment.newInstance(addressStringSimplification,testClientId);
var transaction = getActivity().getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, someFragment); // give your fragment container id in first parameter
transaction.addToBackStack(null);
transaction.commit();
推荐阅读
- flutter - Flutter Push 和 Pop 在浏览类别列表和 Bloc 时返回错误屏幕
- php - Woocommerce 商店页面在 WPML 中不显示一个类别
- reactjs - 状态变化不反映子组件的 UI
- json - 从 JSON 中检索一个值有效,但对另一个值采用相同的方法失败
- microsoft-cognitive - 表单识别器预览 - 无法加载示例文档
- opencv - 如何使用 tensorflow.js 检测视频帧(或图片)内的特定图片
- ruby-on-rails - 部署 react-native 应用,但不在应用商店或 Play 商店中
- node.js - 如何使用 node-Redis 在 Redis 中加载 CSV 文件
- angular - 如何指定“异步为”变量的类型?
- java - 连接升级到 WebSocket