I implemented a "Don't show again" option to my Alert Dialog, but it's not working as intended:
All the other questions are 6-7 years old and in java, and since I am completely new to programming I only know the basics of kotlin as of right now..
fun showDialog() {
val dialogBuilder = AlertDialog.Builder(context)
val inflater = requireActivity().layoutInflater;
dialogBuilder.setView(inflater.inflate(R.layout.alert, null))
dialogBuilder.setMessage("Please always put the total on the bottom right corner. An example is shown below.")
dialogBuilder.setPositiveButton("Alright, got it!",
DialogInterface.OnClickListener { dialog, whichButton ->
pb.visibility = View.VISIBLE
checkPermission(Manifest.permission.CAMERA,CAMERA_PERMISSION_CODE)
startActivityForResult(receiptsViewModel.cameraIntent(requireActivity()),REQUEST_CODE_KAMERA)
})
val mainView: View = inflater.inflate(R.layout.alert, null)
checkBox = mainView.findViewById<View>(R.id.checkBox) as CheckBox
val b = dialogBuilder.create()
b.show()
checkBox.setOnCheckedChangeListener { compoundButton, b ->
if (compoundButton.isChecked) {
storeDialogStatus(true)
} else {
storeDialogStatus(false)
}
}
if (dialogStatus) {
b.hide()
} else {
b.show()
}
}
private fun storeDialogStatus(isChecked: Boolean) {
val mSharedPreferences = requireActivity().getSharedPreferences("CheckItem", AppCompatActivity.MODE_PRIVATE)
val mEditor = mSharedPreferences.edit()
mEditor.putBoolean("item", isChecked)
mEditor.apply()
}
private val dialogStatus: Boolean
private get() {
val mSharedPreferences = requireActivity().getSharedPreferences("CheckItem",
AppCompatActivity.MODE_PRIVATE
)
return mSharedPreferences.getBoolean("item", false)
}
The problem I see is that the checkbox that you're adding a listener to is for a layout that you're not using. You inflated a layout and set that as your dialog view. Then you inflate a second copy of the layout and set a listener on the checkbox in that unused second layout.
A few other tips, but these aren't things that are preventing it from working. They'll just make your code clearer:
You can chain Builder calls so you don't have to keep putting dialogBuilder. and you don't even have to store it in a variable.
findViewById<View> can be changed to findViewById<CheckBox> so you don't have to cast its result to CheckBox.
You can return early from the function if the dialog isn't needed rather than creating the dialog, showing it, and then immediately hiding it.
Instead of using if (someBoolean) doSomething(true) else doSomething(false) you can simplify to doSomething(someBoolean).
private fun startCamera() {
pb.visibility = View.VISIBLE
checkPermission(Manifest.permission.CAMERA,CAMERA_PERMISSION_CODE)
startActivityForResult(
receiptsViewModel.cameraIntent(requireActivity()),
REQUEST_CODE_KAMERA
)
}
fun showDialog() {
if (dialogStatus) {
startCamera()
return
}
val mainView = requireActivity().layoutInflater.inflate(R.layout.alert, null)
checkBox = mainView.findViewById<CheckBox>(R.id.checkBox)
checkBox.setOnCheckedChangeListener { compoundButton, b ->
storeDialogStatus(compoundButton.isChecked)
}
AlertDialog.Builder(context)
.setView(mainView)
.setMessage("Please always put the total on the bottom right corner. An example is shown below.")
.setPositiveButton("Alright, got it!") { _, _ -> startCamera() }
.create()
.show()
}
I also find it odd that you have a property for checkBox that couldn't possibly be useful outside this function. You should probably remove the property.
Also, you should look into using DialogFragment instead of a bare Dialog. There are issues with bare Dialogs, such as them disappearing after a screen rotation. The official documentation explains how to use DialogFragment. I will admit, though, that I think DialogFragment is kind of convoluted to use, especially for a new Android programmer.
Your logic seems to be faulty. This piece of code does not work as intended:
if (dialogStatus) {
b.hide()
} else {
b.show()
}
You should not create the dialog at all if the user has opted out. Please try this modified code:
fun showDialog() {
if (dialogStatus) {
return
}
val dialogBuilder = AlertDialog.Builder(context)
val inflater = requireActivity().layoutInflater
dialogBuilder.setView(inflater.inflate(R.layout.alert, null))
.setMessage("Please always put the total on the bottom right corner. An example is shown below.")
.setPositiveButton("Alright, got it!",
DialogInterface.OnClickListener { dialog, whichButton ->
pb.visibility = View.VISIBLE
checkPermission(Manifest.permission.CAMERA, CAMERA_PERMISSION_CODE)
startActivityForResult(
receiptsViewModel.cameraIntent(requireActivity()),
REQUEST_CODE_KAMERA
)
})
dialogBuilder.create().show()
val mainView: View = inflater.inflate(R.layout.alert, null)
checkBox = mainView.findViewById<CheckBox>(R.id.checkBox)
checkBox.setOnCheckedChangeListener { compoundButton, b ->
if (compoundButton.isChecked) {
storeDialogStatus(true)
} else {
storeDialogStatus(false)
}
}
}
Related
I have a chip group and have bunch of chips inside that particular group. I applied setOnCheckedChangeListener
private fun prefDialog(context: Context): Dialog {
val dialog = Dialog(context)
dialog.setContentView(R.layout.pref_dialog)
val chipGroup = dialog.findViewById<ChipGroup>(R.id.genre_chip_group)
chipGroup.setOnCheckedChangeListener { group, checkedId ->
when (checkedId) {
R.id.popChip -> Log.i(TAG, "onCheckedChanged: checked")
}
}
return dialog
}
but it's not being triggered. Kindly guide me how to achieve this
I have a custom dialog in my android project and it was working fine. After I removed kotlin extension from the project, I have modified my code as follows but there is some issue with the Views in the custom dialog. Codes etTitle.visibility = View.GONE and val newRequest = etDetail.text.toString() didn't work as I expected. It didn't hide the view etTitle and the value in the EditText etDetail is not picked also, it always returns emplty even when there is some value.
private lateinit var bindingDialogLayout: CustomDialogBinding
fun specialRequestDialog(currentRequest: String?) {
bindingDialogLayout = CustomDialogBinding.inflate(layoutInflater)
val dialogLayout = layoutInflater.inflate(R.layout.custom_dialog, null)
val etTitle = bindingDialogLayout.etTitle
val etDetail = bindingDialogLayout.etDetails
etTitle.visibility = View.GONE
etDetail.setText(currentRequest)
MaterialAlertDialogBuilder(this)
.setTitle("What is your special request?")
.setCancelable(false)
.setPositiveButton("Save") { dialog, which ->
val newRequest = etDetail.text.toString()
if (newRequest.isEmpty()) {
showErrorSnackBar("Type in if you have any special request, else hit cancel", true)
} else {
addButton.visibility = View.GONE
deleteButton.visibility = View.VISIBLE
}
}
.setNegativeButton("Cancel") { dialog, which ->
dialog?.dismiss()
}
.setView(dialogLayout)
.show()
}
You set the wrong view to the dialog.
Use this instead:
.setView(bindingDialogLayout.root)
#ExperimentalCoroutinesApi
class WeekdayTimesFragment #Inject constructor(viewModelFactory: SavedStateViewModelFactory.Factory)
:Fragment(R.layout.fragment_weekday_times), WeekdayAlarmTimesAdapter.OnWeekdayAlarmTimePressed, ActionModeListener {
private lateinit var weekdayAlarmTimesAdapter: WeekdayAlarmTimesAdapter
private val weekdayTimesViewModel
by viewModels<WeekdayTimesViewModel> { viewModelFactory.create(this) }
override val actionCallback = PrimaryActionCallback(this)
private var weekday: Int = 0
private val isActionModeActive: Boolean
get() = weekdayTimesViewModel.isActionModeActive
private val numSelected: Int
get() = weekdayTimesViewModel.selectedTimePositions.size
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
weekday = requireArguments().get("WEEKDAY") as Int
if(isActionModeActive) { //action mode may be active prior to rotation
startActionMode() //for some reason, starting action mode here does not work....
}
initRecyclerView() //will determine what recyclerview items are selected via the viewmodel
weekdayTimesViewModel.getTimesAndAlarmsForSelectedWeekday(Weekday[weekday])
.observe(viewLifecycleOwner) { times->
weekdayAlarmTimesAdapter.submitWeekdayTimes(times)
}
}
override fun alarmTimePressed(wasSelected: Boolean, position: Int, time: WeekdayTime) {
if(wasSelected) { //this item was selected prior to being selected, effectively unselecting it
weekdayTimesViewModel.selectedTimePositions.remove(position)
} else {
weekdayTimesViewModel.selectedTimePositions[position] =
Time(time.hour, time.minute, Weekday[weekday], time.alarm.alarmId, time.isDisabled, time.id)
}
if(numSelected == 0 && isActionModeActive) { //no more times are selected, turn off action mode
destroyActionMode() //need to de-select recyclerview items
} else {
if (!isActionModeActive) {
startActionMode()
} else {
actionCallback.changeTitle("$numSelected selected")
}
}
}
private fun startActionMode() {
actionCallback
.startActionMode(
requireView(),
R.menu.toolbar_contextual_menu,
"$numSelected selected")
//fragment hosting the viewpager
(requireParentFragment() as ActionModeListenerController).currentActionModeListener = this
weekdayTimesViewModel.isActionModeActive = true
}
override fun onActionItemClick(item: MenuItem?) {
item?.let {
when(it.itemId){
R.id.toolbar_disable_alarm-> {
requireContext().createGenericAlertDialog(
message = if(numSelected == 1) "Are you sure you want to disable this alarm?"
else "Are you sure you want to disable these alarms?",
positiveListener = { _, _ ->
weekdayTimesViewModel.disableSelectedTimes()
showUndoSnackbar(true)
}
).show()
}
R.id.toolbar_delete_alarms-> {
requireContext().createGenericAlertDialog(
message = if(numSelected == 1) "Are you sure you want to delete this alarm?"
else "Are you sure you want to delete these alarms?",
positiveListener = { _, _ ->
weekdayTimesViewModel.deleteSelectedTimes()
showUndoSnackbar(false)
}
).show()
}
}
}
}
//when we aren't destroying the view but need to end action mode (changing tab or after we confirm an action)
//we may want to do specific things specific to certain listeners, e.g. de-select certain recyclerview items.
override fun destroyActionMode() {
actionCallback.finishActionMode()
weekdayTimesViewModel.isActionModeActive = false
//also un-select all of the elements that have been selected
weekdayTimesViewModel.selectedTimePositions.clear()
//redraw recyclerview as well
rv_weekday_times.resetAdapterState()
}
private fun showUndoSnackbar(disablingTimes: Boolean){
val message =
if(disablingTimes) "Your alarm times have been disabled." else "Your alarm times have been deleted."
Snackbar.make(requireView(), message, Snackbar.LENGTH_LONG).setAction("UNDO"){
if(disablingTimes)
weekdayTimesViewModel.restoreDisabledTimes()
else
weekdayTimesViewModel.restoreDeletedTimes()
}.setActionTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary))
.setAnchorView(requireActivity().bottom_nav_view).show()
}
//when rotating screen or navigating we can just end action mode. The state of whether we are
//in action mode either doesn't matter (navigation) or is already persisted, along with the selected
//items in the viewmodel(rotation)
override fun onDestroyView() {
actionCallback.finishActionMode()
super.onDestroyView()
}
private fun initRecyclerView() {
weekdayAlarmTimesAdapter = WeekdayAlarmTimesAdapter(this, weekdayTimesViewModel.selectedTimePositions.keys)
rv_weekday_times.apply {
adapter = weekdayAlarmTimesAdapter
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
}
weekdayAlarmTimesAdapter.selectedItems.clear()
//clears selected items as these items no longer remain consistently for the lifecycle of the view
}
}
So my ActionMode works great when I get a callback (alarmTimePressed) which is through a click listener set in the recyclerview's OnBindViewHolder. I start my ActionMode and it works great, I save whether ActionMode is active in my viewmodel, which works well upon rotation, as if it is active prior to rotation, it still remains active. As you can see in my OnViewCreated, I call startActionMode() when ActionMode is meant to be active. The problem is that when I start it from there, the onCreateActionMode callback is never called. This is strange because even after rotating, if I select an item in the RecyclerView it correctly launches ActionMode. The view I am passing to startActionMode is the same in both cases. If you need the code for PrimaryActionCallback, it's here: https://hastebin.com/vepujuhefe.m. Didn't think it was very necessary but in case you felt like it was necessary to see :). Anyone know what's going on here?
Despite a lot of searches and trials, I can't avoid my AlertDialog to close if my EditText is empty... I saw that it was a current issue on StackOverflow with a lot of responses but when I try it, it doesn't work...
Here is my Kotlin code, thanks for your help...
import androidx.appcompat.app.AlertDialog
import android.content.DialogInterface
private fun showMyEditextDialog() {
val alertDialog = AlertDialog.Builder(this)
val view = layoutInflater.inflate(R.layout.my_layout, null)
val myedittext = view.findViewById(R.id.myedittext) as EditText
alertDialog.setTitle(getString(R.string.title))
alertDialog.setView(view)
alertDialog.setCancelable(false)
alertDialog.setPositiveButton(R.string.ok, DialogInterface.OnClickListener { dialogInterface, i ->
var isValid = true
if ( isEmpty(myedittext.text.toString()) ) {
isValid = false
Toast.makeText(this, "empty", Toast.LENGHT_SHORT).show()
}
if ( isValid ) {
Toast.makeText(this, "yeah!", Toast.LENGHT_SHORT).show() }
if ( isValid ) {
dialogInterface.dismiss()
}
})
}
My xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/question" />
<EditText
android:id="#+id/myedittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal" />
</LinearLayout>
If I enter a value, I've got my "yeah" message.
If I don't enter a value, I've got my "empty" message but my dialog dismiss...
Thanks for your help,
MT
Use onShow to handle this like below:
val dialog = alertDialog.create()
dialog.setOnShowListener {
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
var isValid = true
if ( isEmpty(myedittext.text.toString()) ) {
isValid = false
Toast.makeText(this#MainActivity, "empty", Toast.LENGTH_SHORT).show()
}
if ( isValid ) {
Toast.makeText(this#MainActivity, "yeah!", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
}
}
dialog.show()
I'm unsure if there's a way to bypass an alertdialog's buttons' functions. If there is, I've never found it. However, you could go with a much simpler solution. Just create your own "alert" view with an editText and buttons. Set the view to view.setVisibility(View.INVISIBLE) or (View.GONE), and any of its children views will be invisible or gone as well. I generally, when this is necessary, do this with a semi-transparent black background behind the view as the parent; and set that view's visibility back and forth. Then, you can customize the functionality of the buttons and the rest of the view in any way you want.
You need to get the positive button to override the method onclick
you can use this code:
private fun showMyEditextDialog() {
val alertDialog = AlertDialog.Builder(this)
val view = layoutInflater.inflate(R.layout.my_layout, null)
val myedittext = view.findViewById(R.id.myedittext) as EditText
alertDialog.setTitle(getString(R.string.title))
alertDialog.setView(view)
alertDialog.setCancelable(false)
alertDialog.setPositiveButton(R.string.ok, null)
alertDialog.create()
var dialog = alertDialog.create()
dialog.show()
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
var isValid = true
if ( isEmpty(myedittext.text.toString()) ) {
isValid = false
Toast.makeText(this, "empty", Toast.LENGTH_SHORT).show()
}
if ( isValid ) {
Toast.makeText(this, "yeah!", Toast.LENGTH_SHORT).show() }
if ( isValid ) {
dialog.dismiss()
}
}
}
ImageView in first alert dialog opens second dialog to change an imageResource of the ImageView in the first dialog. However I don't know how to make connection between two alert dialogs
Both have different xml layouts, so I assume that in second dialog I should make a reference to the layout of the first dialog
private fun editItemDialog() {
val dialogBuilder1 = AlertDialog.Builder(this)
val inflater = this.layoutInflater
val dialogView = inflater.inflate(R.layout.edit_dialog, null)
dialogBuilder1.setView(dialogView)
var editIconButton = dialogView.findViewById<View>(R.id.editIcon) as ImageView
editIconButton.setOnClickListener{
showIconDialog()
}
dialogBuilder1.setTitle("Edit mode")
dialogBuilder1.setPositiveButton("Save") { _, _ ->
//sth
}
dialogBuilder1.setNegativeButton("Cancel") { _, _ ->
//sth
}
val b = dialogBuilder1.create()
b.show()
}
private fun showIconDialog() {
val dialogBuilder = AlertDialog.Builder(this)
val inflater = this.layoutInflater
val dialogView = inflater.inflate(R.layout.icons, null)
dialogBuilder.setView(dialogView)
//examplary two icons to select
var travelRB = dialogView.findViewById<View>(R.id.travel) as RadioButton
var travRB = dialogView.findViewById<View>(R.id.travel) as RadioButton
dialogBuilder.setTitle("Icon dialog")
dialogBuilder.setMessage("Select an icon")
dialogBuilder.setPositiveButton("Save") { _, _ ->
//here I would like to change an icon of the ImageView, for example:
editIconButton.setImageResource(R.id.travel)
dialogBuilder.setNegativeButton("Cancel") { _, _ ->
//sth
}
val b = dialogBuilder.create()
b.show()
}
You can add a callback to the second dialog
fun showIconDialog(callback : (Drawable) -> Unit) {
//code
callback.invoke(someDrawable)
}
And on the first one you just do this:
showIconDialog() { someDrawable ->
//code to change the layout src icon
}