//This is my scanner barcode code using kotlin
override fun receiveDetections(detections: Detector.Detections) {
val barcodes = detections.detectedItems
if (barcodes.size() == 1) {
scannedValue = barcodes.valueAt(0).rawValue
runOnUiThread {
cameraSource.stop()
Toast.makeText(this#InsertStockInActivity, "value- $scannedValue", Toast.LENGTH_SHORT).show()
finish()
}
}else
{
Toast.makeText(this#InsertStockInActivity, "value- else", Toast.LENGTH_SHORT).show()
}
}
//This is my input page
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentInputStockInBinding.inflate(inflater, container, false)
binding.btnScanBarcode.setOnClickListener{ nav.navigate(R.id.insertStockInActivity)}
return binding.root
}[enter image description here][1]
i thnk you should extract your toast text as a variable and pass it to another page ( i assume that your mean are to another fragment/activity or to another screen)
private lateinit var toastText:String
...
if (barcodes.size() == 1) {
...
toastText = scannedValue
...
} else {
toastText = "value- else"
}
Toast.makeText(this#InsertStockInActivity, toastText , Toast.LENGTH_SHORT).show()
}
and pass toastText to another page via Intent or safe-args if you are using Jetpack Navigation
You could use a view model, live data, or another (static) object to hold your results in a variable. Along the same lines, you can create a show toast function in another class and just pass the context of the fragment or activity that you are in. For example a fragment context could be requireContext().
fun showToast(context: Context?, message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
Related
newbie here! I'm having a hard time learning the life cycle of fragments and I'm stuck in this problem. If I run this on the emu, the fragment is showing on the activity but the button inside the fragment needs to be clicked twice to run the destination activity.
FragmentSetting.kt:
class FragmentSetting : Fragment(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view: View = inflater!!.inflate(R.layout.fragment_main_setting, container, false)
val btnLogout: Button = view.findViewById(R.id.btn_logout)
val btnArticle: Button = view.findViewById(R.id.btn_art)
btnLogout.setOnClickListener(this)
btnArticle.setOnClickListener(this)
return view
}
companion object {
fun newInstance(): FragmentSetting {
return FragmentSetting()
}
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btn_logout -> {
btn_logout.setOnClickListener {
Toast.makeText(getContext(), "Signed Out.", Toast.LENGTH_SHORT).show()
FirebaseAuth.getInstance().signOut()
val intent = Intent(activity, SignInActivity::class.java)
startActivity(intent)
}
}
R.id.btn_art -> {
btn_art.setOnClickListener {
Toast.makeText(getContext(), "Hello World", Toast.LENGTH_SHORT).show()
val intent = Intent(activity, ArticleActivity::class.java)
startActivity(intent)
}
}
}
}
}
You are setting the Fragment class itself as the listener to both buttons when the view is created. That would be an acceptable place to do it.
However, your listener function doesn't perform your desired action of your button. Instead, it is setting a new listener for each button. So the first time they are clicked, they get their new listener. It is only in that inner secondary listener that you are going to another activity, so that's why it's taking two clicks.
You need to directly do your action instead of wrapping the action inside setting another listener. By the way, the view passed to a click listener is never null, so you can remove the ?'s.
override fun onClick(v: View) {
when (v.id) {
R.id.btn_logout -> {
Toast.makeText(getContext(), "Signed Out.", Toast.LENGTH_SHORT).show()
FirebaseAuth.getInstance().signOut()
val intent = Intent(activity, SignInActivity::class.java)
startActivity(intent)
}
R.id.btn_art -> {
Toast.makeText(getContext(), "Hello World", Toast.LENGTH_SHORT).show()
val intent = Intent(activity, ArticleActivity::class.java)
startActivity(intent)
}
}
}
there are two different things number one if you are implement View.OnClickListener interface so defiantly you use onclick call back method they will fire every time when ever you click it mean in onclick method execute piece code write in onclick method so in your current code first click they will set setOnclicklistener and then next time you click then it will work as per expected.
so simple is that you directly execute code with in curls braces don't need to set setOnClickListener.
For Example :
R.id.btn_art -> {
Toast.makeText(getContext(), "Hello World", Toast.LENGTH_SHORT).show()
val intent = Intent(activity, ArticleActivity::class.java)
startActivity(intent)
}
second thing is that you set setOnClickOnClickListenre in your onCreateView and don't implement clicklistenere.
For Example:
btn_logout.setOnClickListener {
Toast.makeText(getContext(), "Signed Out.", Toast.LENGTH_SHORT).show()
FirebaseAuth.getInstance().signOut()
val intent = Intent(activity, SignInActivity::class.java)
startActivity(intent)
}
but they best practice you can handle click listener in onclick method as per view architecture.
I am in the process of creating a golf scorecard app.
Every hole's score is an empty textView, and that textView has a setOnClickListener to open the score picker dialog.
I want to get the score value from the score picker dialog.
Here is the dialog interface:
https://i.stack.imgur.com/VNVc1.png
Each button is corresponding to a score.
I know that each button will need a setOnClickListener, but my knowledge is limited about everything else afterward.
So my question is how to return that score value so I can display it in that specific the textView and add it to the player's total? Any suggestions would be very helpful.
Thank you for your time.
You can implement custom listner, that listner you need to
below code is for demo,
implement in diaglog fragment
//score for current hole dialog
class SomeDialog:DialogFragment() {
private var dl: CustomListener? = null
// interface to detect dialog close event
interface CustomListener {
fun closeEvent(id: String, valueToPass: Int)
}
fun customListerTrig(l: CustomListener) {
dl = l
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
button1.setOnClickListener {
dl?.closeEvent("golfDiag", 1)
this.dismiss()
}
button2.setOnClickListener {
dl?.closeEvent("golfDiag", 2)
this.dismiss()
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_some_dialog, container, false)
}
}
How i called and retrived button click event
val common = SomeDialog()
common.customListerTrig(object : CommonInfoDialog.CustomListener {
override fun closeEvent(id: String, buttonValueFromDialog: Int) {
// todo usebuttonValueFromDialog
}
})
//use fragment according where this dialog will be called.
common.show(this.childFragmentManager, "golfDiag")
May be this code is easy for you. Use Android dialog API to make your work easier.
/**
* e.g.
showPickScoreDialog(requireContext()) { score ->
Toast.makeText(context, "score[$score]", Toast.LENGTH_LONG).show()
}
*/
fun showPickScoreDialog(context: Context, callback: (Int) -> Unit) {
val defaultCheckedScore = -1
val scoreList = getScoreList()
MaterialAlertDialogBuilder(context)
.setTitle("Score For Current Hole:")
.setSingleChoiceItems(scoreList, defaultCheckedScore) { dialog: DialogInterface?, which: Int ->
val score = scoreList[which].toInt()
callback(score)
dialog?.dismiss()
}
.setNegativeButton("Cancel", null)
.show()
}
private fun getScoreList(): Array<String> {
val list = mutableListOf<String>()
for (i in 1..12) {
list.add(i.toString())
}
return list.toTypedArray()
}
So my question is how to return that score value so I can display it in that specific the textView and add it to the player's total? Any suggestions would be very helpful.
https://developer.android.com/guide/topics/ui/dialogs#PassingEvents
I'm trying to understand the concepts of MVVM but i'm having a hard time trying to understand how to communicate between The model class and UI (The fragment) in this case.
Here's the (shitty, be aware) code:
LoginFragment.kt
class LoginFragment: Fragment(), AuthListener {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = DataBindingUtil.inflate<CredentialsLoginFragmentBinding>(
inflater,
R.layout.credentials_login_fragment,
container,
false
)
val viewModel = ViewModelProviders.of(this).get(LoginViewModel::class.java)
val view: View = binding.root
val registerButton: Button = view.findViewById(R.id.register_button)
binding.viewModel = viewModel
viewModel.authListener = this
registerButton.setOnClickListener {
val transaction: FragmentTransaction? = fragmentManager?.beginTransaction()
transaction?.replace(R.id.fragment_container, SignupFragment())?.commit()
}
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val constraintRoot: MotionLayout = view.findViewById(R.id.sign_in_root)
ActivityUtils().switchLayoutAnimationKeyboard(constraintRoot = constraintRoot)
}
override fun onStarted() {
Toast.makeText(context, "Started", Toast.LENGTH_SHORT).show()
}
override fun onSuccess() {
Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show()
}
override fun onError(message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}}
LoginViewModel.kt
class LoginViewModel: ViewModel(){
var username: String? = null
var password: String? = null
var isCredentialsValid: Boolean = false
var authListener: AuthListener? = null
private val context: Context? = null
fun onLoginButtonClicked(view: View){
if(username.isNullOrEmpty() || password.isNullOrEmpty()){
authListener?.onError("Invalid username or password")
isCredentialsValid = false
return
}
if(!username.isNullOrEmpty() && password!!.length >= 8){
isCredentialsValid = true
authListener?.onSuccess()
}else{
authListener?.onError("Invalid")
}
}}
Lets assume now that I enter an username and password and both meet the criteria. Now i'd like to, when i click on the "Log in" button, the current fragment is replaced by a menu fragment, for example.
How could i achieve something like that ? I've tried to replace from the ViewModel class, but that doesn't work.
Should I take the result of "isCredentialsValid" from the VM class and respond accordingly in the LoginFragment class ?
Thank you.
You have to use live data for updating the data from viewModel to view. I will post the code how it should be, but make sure that you need to understand the concept of LiveData.
LoginViewModel.kt
class LoginViewModel: ViewModel(){
var username: String? = null
var password: String? = null
var isCredentialsValid: Boolean = false
var authListener: AuthListener? = null
private val context: Context? = null
// LiveData to udpate the UI
private val _isValidCredential = MutableLiveData<Boolean>()
val isValidCredential: LiveData<Boolean> = _isValidCredential
fun onLoginButtonClicked(view: View){
if(username.isNullOrEmpty() || password.isNullOrEmpty()){
authListener?.onError("Invalid username or password")
isCredentialsValid = false
return
}
if(!username.isNullOrEmpty() && password!!.length >= 8){
isCredentialsValid = true
// to update the value of live data wherever you need
_isValidCredential.value = true
authListener?.onSuccess()
}else{
authListener?.onError("Invalid")
// to update the value of live data wherever you need
_isValidCredential.value = false
}
}
}
Your Fragment should be
LoginFragment.kt
class LoginFragment: Fragment(), AuthListener {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = DataBindingUtil.inflate<CredentialsLoginFragmentBinding>(
inflater,
R.layout.credentials_login_fragment,
container,
false
)
val viewModel = ViewModelProviders.of(this).get(LoginViewModel::class.java)
val view: View = binding.root
val registerButton: Button = view.findViewById(R.id.register_button)
binding.viewModel = viewModel
viewModel.authListener = this
// This is the way you need to observe the value
viewModel.isValidCredential.observe(viewLifecycleOwner, Observer {
if(it){
// do your navigation stuff here
}else{
// do your stuff if not valid credential
}
})
registerButton.setOnClickListener {
val transaction: FragmentTransaction? =
fragmentManager?.beginTransaction()
transaction?.replace(R.id.fragment_container, SignupFragment())?.commit()
}
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val constraintRoot: MotionLayout = view.findViewById(R.id.sign_in_root)
ActivityUtils().switchLayoutAnimationKeyboard(constraintRoot = constraintRoot)
}
override fun onStarted() {
Toast.makeText(context, "Started", Toast.LENGTH_SHORT).show()
}
override fun onSuccess() {
Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show()
}
override fun onError(message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}}
A typical way of communicating back to the UI from the view model is using livedata. In your LoginViewModel, you would set your livedata to either true or false. Inside your view LoginFragment.kt you would have an observer. This observers job is to fire anytime a livedata's value has changed. That way you can have logic in your view that can either shows an error message liveData = false or launch the menu fragment = true.
Here is a good example using livedata to pass data to the view (fragment) this in the docs: https://developer.android.com/topic/libraries/architecture/viewmodel#implement
I have an activity with three fragments that are navigated with a ViewPager. The starting fragment has a button with a click event. When the fragment first appears the button works but when I swipe to the last fragment and back to the main fragment the button doesn't work. It only does this with the button nothing else... I know it's probably something fairly obvious be gentle folks lol!
button layout
<Button
android:id="#+id/login_button"
android:layout_width="match_parent"
android:textSize="#dimen/body_text_size"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/layout_margin"
android:layout_marginEnd="#dimen/layout_margin"
android:layout_marginBottom="#dimen/layout_margin"
android:focusable="false"
android:background="#drawable/button_pressed_state"
android:text="#string/login"
android:textColor="#color/white"
android:textStyle="bold" />
Fragment Code that it happens on.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.login_fragment, container, false)
viewModel = ViewModelProvider(this).get(LoginViewModel::class.java)
loginView = binding.root
initClickableLinks()
launch {
initButtonClick()
}
return loginView
}
private suspend fun initButtonClick(): String? {
val edittextSize = binding.emailAddressEditText.textSize
val textviewSize = binding.forgetPassTextview.textSize
var message: String? = ""
binding.loginButton.setOnClickListener {
fun onClick(view: View?) {
try {
viewModel.setEmailAddress(binding.emailAddressEditText.text.toString())
viewModel.setPassword(binding.passwordEditText.text.toString())
//if nothing is entered this will do nothing but update text
val invalidString = requireActivity().getString(R.string.invalid_combo)
binding.authTextView.text = ""
if (binding.emailAddressEditText.text.toString()
.isBlank() || binding.passwordEditText.text.toString().isBlank()
) {
binding.authTextView.text = invalidString
//exits listener because authentication failed
// return#setOnClickListener
}
binding.progressBar.visibility = View.VISIBLE
//background thread for IO
GlobalScope.launch(Dispatchers.IO) {
//call api
//UI Thread
withContext(Dispatchers.Main) {
val mess = viewModel.getMessage()
if (mess.equals("Successful")) {
val intent = Intent(activity, MemberActivity::class.java)
val loginfo = viewModel.getLoginResult().toString()
intent.putExtra("loginIno", loginfo)
activity?.startActivity(intent)
} else {
binding.authTextView.text = mess
Toast.makeText(activity!!.applicationContext, mess, Toast.LENGTH_LONG).show()
}
binding.progressBar.visibility = View.GONE
}
}
} catch (ex: Exception) {
println(ex)
}
}
}
return message
}
This only happens with the button
In part I found that the following only gets called once :
initButtonClick()
Removing the launch and changing the Private suspend function to a private function that still asynchronously calls the login service fixed the issue and now gets called multiple times.
the question is how to pass data from BottomSheetDialogFragment to Fragment or Activity and what would be the correct way ?
Here is my Fragment dialog that will be opened in my Frament and should save data from textview that is getting clicked on.
class BallTypeDialogFragment : BottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) =
inflater.inflate(R.layout.fragment_blood_type_dialog, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
text_view_ball_O.setOnClickListener {
text_view_ball_O.text
Toast.makeText(context, "O+", Toast.LENGTH_SHORT).show()
}
text_view_ball_A.setOnClickListener {
text_view_ball_A.text
Toast.makeText(context, "A+", Toast.LENGTH_SHORT).show()
}
text_view_ball_AA.setOnClickListener {
Toast.makeText(context, "AA+", Toast.LENGTH_SHORT).show()
}
text_view_blood_grop_minus.setOnClickListener {
text_view_blood_grop_minus.text
Toast.makeText(context, "-", Toast.LENGTH_SHORT).show()
}
text_view_ball_AAR.setOnClickListener {
text_view_ball_AAR.text
Toast.makeText(context, "R -", Toast.LENGTH_SHORT).show()
}
text_view_ball_AARS.setOnClickListener {
text_view_ball_AARS.text
Toast.makeText(context, "AARS -", Toast.LENGTH_SHORT).show()
}
text_view_ball_OO.setOnClickListener {
text_view_ball_OO.text
Toast.makeText(context, "OO -", Toast.LENGTH_SHORT).show()
}
}
}
And i Simply open it in my Fragment like this,even though I understand it is incorrect.
private fun showDialog() {
val dialog = BallTypeDialogFragment()
dialog.show(childFragmentManager, "BallTypeDialogFragment")
}
So here is how I solved the problem.
I created an interface in my BottomSheetDialogFragment with String variable for a class
private var ballType: String = ""
interface OnBallGroupSelectedListener {
fun onBalldGroupListener(ballType: String)
}
When I was selecting value in my Dialog I was setting value to a string and then using method to pass the values to my parent Fragment.
private fun getBloodGroupResults() {
val listener = targetFragment as OnBallGroupSelectedListener?
listener?.onBalldGroupListener(ballType)
dismiss()
}
Then in my parent Fragment simply implementing the Interface and creating String variable that will be set in the Interface
private var ballType: String? = ""
override fun onBallGroupListener(ballType: String) {
this.ballType = ballType
}
Since you're using kotlin, you might consider passing a lambda to your dialogfragment.
E.g.
BallTypeDialogFragment(onData: (String) -> Unit)
and then you pass it by
private fun showDialog(onData: (String) -> Unit)) {
val dialog = BallTypeDialogFragment(onData)
dialog.show(childFragmentManager, "BallTypeDialogFragment")
}
then in your DialogFragment you just do:
//something something
text_view_ball_O.setOnClickListener {
onData(text_view_ball_O.text)
Toast.makeText(context, "O+", Toast.LENGTH_SHORT).show()
}
//something something
1- Create an interface.
interface DialogActivityContract{
fun onPassDataRequsted(dataType: DataType)
}
2- Implement that interface to the activity:
class ActivityThatHoldsTheDialogActivity : SomeAppCompactActivity(),DialogActivityContract{
//other methods here
override public void onPassDataRequsted(DataType dataType){
//handle data here
}
}
And in your fragment:
lateinit var dialogActivityContract: DialogActivityContract
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialogActivityContract = (activity as ActivityThatHoldsTheDialogActivity)
}
// some methods here
fun whenYouNeedToSendThatData(){
dialogActivityContract.onPassDataRequsted(yourData)
}
Another way of doing it is using the EventBus library.
You can use listeners as follows
open class BallTypeDialogFragment<L: BallTypeDialogFragment.BottomSheetClickListener.SingleAction> : BottomSheetDialogFragment(){
open fun showWithListener(listener: L, fm : FragmentManager, tag : String) {
this.listener = listener
show(fm, tag)
}
interface BottomSheetClickListener {
interface SingleAction : BottomSheetClickListener {
fun passData(ballType: String)
}
}
}
}
And then in the calling Fragment.
activity?.supportFragmentManager?.let {
BallTypeDialog<LocationBottomSheet.BottomSheetClickListener.SingleAction>().showWithListener(object : BallTypeDialogFragment.BottomSheetClickListener.SingleAction {
override fun passData(data : String)
}, it, "test" )
}