I want to send an editText string from a dialogFragment popup to the activity screen behind it. I'm not sure how to do this in Kotlin since most resources online are in Java. I've tried to implement: How to send data from dialog to my activity kotlin?. However, I couldn't quite get it work so I'm not sure what method to use from here.
Here is the SelectAPScreen.kt (the activity I want the string to go to)
package com.wcsng.dlocapp
import android.annotation.SuppressLint
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.ViewGroup
import android.widget.*
import androidx.annotation.RequiresApi
import androidx.lifecycle.LifecycleEventObserver
import androidx.navigation.findNavController
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import kotlinx.android.synthetic.main.imu_data.*
import java.io.BufferedReader
import java.io.InputStreamReader
class SelectAPScreen : AppCompatActivity(), OnButtonClick {
#SuppressLint("ResourceAsColor")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_select_apscreen)
val directoryName = findViewById<EditText>(R.id.dir_field)
val pingIP = findViewById<EditText>(R.id.ip_field)
val rpiIP = findViewById<EditText>(R.id.rpiField)
val qtnIPs = {}
val addQtn = findViewById<Button>(R.id.addQtn)
addQtn.setOnClickListener {
var dialog = AddQtnFragment()
dialog.show(supportFragmentManager, "Add Qtn Popup")
}
}
}
Here is the DialogFragment Code:
package com.wcsng.dlocapp
import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.annotation.NonNull
import androidx.fragment.app.DialogFragment
import androidx.navigation.fragment.findNavController
import com.wcsng.dlocapp.R
class AddQtnFragment: DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = AlertDialog.Builder(it)
// Get the layout inflater
val inflater = requireActivity().layoutInflater
val qtn_ip = view?.findViewById<EditText>(R.id.qtn_ip)?.text.toString()
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(inflater.inflate(R.layout.add_qtn_popup, null))
.setPositiveButton("Save", DialogInterface.OnClickListener{dialog, id ->
// TODO Return the qtn_ip to SelectAPScreen
// Close the dialog and return back to the parent activity
dialog.dismiss()
})
.setNegativeButton("Cancel", DialogInterface.OnClickListener{dialog, id ->
dialog.dismiss()
})
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}
you can use getActivity() or requireActivity() methods for obtaing managing Activity, then you may cast it to proper class, e.g. (requireActivity() as SelectAPScreen) and now you can call methods of it
(requireActivity() as SelectAPScreen).selectAPScreenMethod()
selectAPScreenMethod() is a custom method placed inside SelectAPScreen class
Related
I'm a relative beginner to android programming and I want to have it so my fragments display the layout files which are assigned to them through a view model. I'll be providing only one fragment and view model pair as the 3 others which I have are practically the same except some names and text is swapped around.
Here's my TimetableFragment.kt
package com.example.intranetapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
class TimetableFragment : Fragment() {
private lateinit var timetableViewModel: TimetableViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
timetableViewModel =
ViewModelProvider(this).get(TimetableViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_news, container, false)
val textView: TextView = root.findViewById(R.id.text_news)
timetableViewModel.text.observe(viewLifecycleOwner, Observer {
textView.text = it
})
return root
}
}
Here's my TimetableViewModel.kt
package com.example.intranetapp
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class TimetableViewModel : ViewModel() {
private val _text = MutableLiveData<String>().apply {
value = "This is timetable Fragment"
}
val text: LiveData<String> = _text
}
And here's my MainActivity.kt
package com.example.intranetapp
import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(setOf(
R.id.timetableFragment, R.id.dueworkFragment, R.id.newsFragment, R.id.gradesFragment))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
I'm not getting any errors when running the application, i'm more just wondering how i can make it so my XML file Timetable.XML is shown when creating the viewmodel rather than text in the middle of the screen which says "This is Timetable fragment"
I'm sorry if i come across as a dumb coder, i'm just struggling to fix this issue and this was the first place to come to mind as google searches aren't helping
The problem is here:
private val _text = MutableLiveData<String>().apply {
value = "This is timetable Fragment"
}
Setting the value happens when ViewModel is initializing. But you start observing this LiveData after initializing, so you loss this value.
You should add method in your viewModel:
fun setTextValue() {
_text.value = "This is timetable Fragment"
}
and call it after timetableViewModel.text.observe()
I've encountered a small issue when creating an Alert dialog in a Fragment. The following is the error thrown in the Logcat:
2020-07-26 11:21:47.425 20063-20063/com.example.MyApp E/WindowManager: android.view.WindowLeaked: Activity com.example.MyApp.MainActivity has leaked window DecorView#b9684c5[MainActivity] that was originally added here
at android.view.ViewRootImpl.<init>(ViewRootImpl.java:769)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:440)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:95)
at android.app.Dialog.show(Dialog.java:473)
at com.example.MyApp.ScannerFragment.onCreateView(ScannerFragment.kt:44)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:8125)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)
I've tried referring to this question which explained that this error may appear possibly due to showing a dialog after an Activity was exited, not calling dismiss() before exiting, or another possibility.
In my context, however, I'm not sure on how to apply the solutions suggested in my context. I'm showing the Alert dialog on a Fragment, ScannerFragment, that is related to an Activity, MainActivity, through a Bottom Navigation menu. The following includes the code for the related files:
ScannerFragment.kt
package com.example.MyApp
import android.Manifest
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityCompat.requestPermissions
import androidx.fragment.app.Fragment
class ScannerFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
val builder: AlertDialog.Builder? = this.let {
AlertDialog.Builder(requireActivity())
}
builder
?.setMessage(R.string.camera_unauthorisedMessage)
?.setTitle(R.string.camera_unauthorisedTitle)
?.apply {
setPositiveButton("OK",
DialogInterface.OnClickListener { dialog, _ ->
requestPermissions(requireActivity(), arrayOf(Manifest.permission.CAMERA), 1111)
dialog.dismiss()
})
}
builder?.create()?.show() // Line 44 - error occurs here
} else {
Log.e("DB", "PERMISSION GRANTED")
}
return inflater.inflate(R.layout.fragment_scanner, container, false)
}
}
MainActivity.kt
package com.example.MyApp
import android.Manifest
import android.content.DialogInterface
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Sets up the bottom navigation bar for usage
initialiseBottomNav()
}
// Overrides the back button to prevent going back to StartActivity
override fun onBackPressed() {}
private fun initialiseBottomNav() {
val navController = findNavController(R.id.main_navhostfragment)
main_bottomnav.setupWithNavController(navController)
}
}
If I were to refer to the question above, what are the steps that I would need to do to resolve this error? All help is greatly appreciated, thank you!
This circumstance means your activity is finishing or your app is crashing somewhere else in your code, but then the open dialog leads to leak window. A tip can be checking your log (typically lines above the log you have shared) and see if there is any other exception/error happening earlier.
Firstly, I would suggest you to add your AlertDialog code in onViewCreated method of your fragment.
Second, you can add the following checking just before showing your AlertDialog:
if(!requireActivity().isFinishing)
builder?.create()?.show()
This happening because dialog associated with the activity context
use application context
val builder: AlertDialog.Builder? = activity?.applicationContext?.let { AlertDialog.Builder(it) }
if(builder != null) {
builder
.setMessage("")
?.setTitle("R.string.camera_unauthorisedTitle")
?.apply {
setPositiveButton("OK",
DialogInterface.OnClickListener { dialog, _ ->
requestPermissions(requireActivity(), arrayOf(Manifest.permission.CAMERA), 1111)
dialog.dismiss()
})
}
builder.create().show() // Line 44 - error occurs here
}
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.widget.*
import android.os.Bundle
import android.R
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val activity_btn = mainbtn
activity_btn.setOnClickListener(View.OnClickListener {
val intent = Intent(this, activity_main2::class.java)
startActivity(intent)
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left)
})
}
}
I have tried clean project,reopening project,rebuild project etc but none of these have worked.
It says Unresolved reference:activity_main,slide_in_right,slide_out_left etc.
remove line import android.R; from your code and rebuild again
Im learning Kotlin and Mvvm for Android. I am using a recycler view, and when i try to set the adapter i cant import the Adapter class, I dont know if the problem is in the code because Android Studio let me import ViewHolder class inside the Adapter class but not Adapter class
FrontPageActivity.kt
package com.jmyp.resport.view
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.jmyp.resport.model.New
import com.jmyp.resport.R
import com.jmyp.resport.viewmodel.NewViewModel
class FrontPageActivity : AppCompatActivity() {
lateinit var adapter : NewsAdapter // I can not import this but i can
// NewsAdapter.NewViewHolder
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_front_page)
var viewModelNews = ViewModelProviders.of(this).get(NewViewModel::class.java)
viewModelNews.getNews().observe(this, Observer<ArrayList<New>> { news ->
adapter = NewsAdapter(this, news)
})
}
}
NewsAdapter.kt
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.jmyp.resport.model.New
import com.jmyp.resport.R
import com.jmyp.resport.view.FrontPageActivity
import kotlinx.android.synthetic.main.row_front_page.view.*
class NewsAdapter(private val context: FrontPageActivity, private val news : ArrayList<New>) : RecyclerView.Adapter<NewsAdapter.NewViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder {
return NewViewHolder(LayoutInflater.from(context).inflate(R.layout.row_front_page,parent,false))
}
override fun getItemCount(): Int {
return news.size
}
override fun onBindViewHolder(holder: NewViewHolder, position: Int) {
holder.title.text = news.get(position).titulo
}
class NewViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val cover = itemView.iv_cover
val title = itemView.tv_title
}
}
You seem to be missing package in your Adapter class. Add it and try again :)
Can anyone tell me what is the problem. This is the code:
package com.mohdjey.user.inflate
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.strictmode.WebViewMethodCalledOnWrongThreadViolation
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.mohdjey.user.inflate.R.id.root_layout
import com.mohdjey.user.inflate.R.layout.activity_main
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main.view.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(activity_main)
var inflater: LayoutInflater? = null
var view: View? = null
// inflater.inflate(R.layout.child_layout_to_merge, parent_layout, true);
view = inflater?.inflate(R.layout.another_view , null)
val layout = findViewById<LinearLayout>(R.id.root_layout)
layout.addView(layout)
} }
I am practicing layout inflate.
I don't Know what to write.
You're trying to add the LinearLayout with the ID root_layout as its own child here:
layout.addView(layout)
Perhaps you meant to add your newly inflated View as its child?
layout.addView(view)
Your entire code block should just be
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(activity_main)
val root = findViewById<LinearLayout>(R.id.root_layout)
val view = layoutInflater.inflate(R.layout.another_view, root, false)
root.addView(view)
}
That being said, it's not clear why you don't just include your R.layout.another_view directly in your main layout.