Невозможно передать данные другому фрагменту в Android (Kotlin) - PullRequest
1 голос
/ 25 января 2020

У меня проблема. Я подаю заявку на Android клиента MQTT и мне нужно использовать одни и те же параметры для метода MqttAndroidClient () в разных фрагментах. Я уже пытался передать их в комплекте с намерением putExtra (), делая объекты класса. Связка и putExtra отправляют данные в другой фрагмент, он показывает в режиме отладки , но в целевом фрагменте я получаю нули . Когда я пытаюсь получить значение, создающее экземпляр первого фрагмента, он отправляет мне переменную lateinit без какого-либо значения. Я понятия не имею, что я могу сделать больше. Я думал об использовании сеттеров и геттеров, чтобы получить его, но я не уверен, что это решение.

Первый фрагмент, куда я отправляю данные :

ConnectFragment.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)
} 

А вот еще один фрагмент, где я получаю данные :

SubscribeFragment.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()
    }

Спасибо за ответ!

1 Ответ

0 голосов
/ 25 января 2020

Прежде всего, для создания фрагмента у вас должен быть метод 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(); 
...