I'm trying to create a to-do list. first click menu item (add) and alertdialog write edittext and save but I'm trying to get editable text with alarm box but i get always same number.
number photo
i write bread but same number generate photo
here is a menu code
R.id.add -> {
val mDialogView = LayoutInflater.from(this).inflate(R.layout.dialog_add_todo, null)
AlertDialog.Builder(this).setView(mDialogView).setTitle("ADD TODO").setPositiveButton("Save"){
dialogInterface, i ->
val todoTitle = R.id.et_dialog_add.toString()
if(todoTitle.isNotEmpty()) {
val todo = Todo(todoTitle)
todoAdapter.addTodo(todo)
alert dialog:
<EditText
android:id="#+id/et_dialog_add"
android:hint="Buy a Bread"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</EditText>
here is adapters:
fun addTodo(todo: Todo) {
todos.add(todo)
notifyItemInserted(todos.size -1)
}
This is your problem:
val todoTitle = R.id.et_dialog_add.toString()
This is not how you get the text value from an EditText field. You need to first use findViewById() to get a reference to the EditText, and then you can use its text property to get what the user entered:
val todoTitleView = mDialogView.findViewById<EditText>(R.id.et_dialog_add)
val todoTitle = todoTitleView.text
Related
I'm kinda new with Kotlin but i'm trying to make my TextView visibility gone or visible based on the live input on Edit Text. Basically, When the user start to input something, i want this text to visible and when they delete until empty, this text is dissapear. I tried to fix it but i still couldn't found any good results. Anyway, thank you.
this is my code:
val Email = findViewById<EditText>(R.id.EmailBox)
val sEmail = Email.text.toString()
val Password = findViewById<EditText>(R.id.PasswordBox)
val sPassword = Password.text.toString()
val emailtext = findViewById<TextView>(R.id.EmailText)
val passwordtext = findViewById<TextView>(R.id.PasswordText)
if(sEmail.isEmpty()) {
emailtext.visibility = View.VISIBLE
} else if (sPassword.isEmpty()) {
passwordtext.visibility = View.VISIBLE
} else {return
}
}
You need to set a listener to check for text changes, rather than getting the current value of the EditText once during setup.
You can do this using the doOnTextChanged method. This would look like:
val email = findViewById<EditText>(R.id.EmailBox)
val emailtext = findViewById<TextView>(R.id.EmailText)
// This will hide "emailtext" when "email" is empty, and show
// it when it is not empty
email.doOnTextChanged { newText, _, _, _ ->
emailtext.visibility = if (newText.isNullOrEmpty()) View.INVISIBLE
else View.VISIBLE
}
I am trying to get the text of a textInputEditText, which is in a dialog (to be exact the onCreateDialog method in the set positive button section), but it doesn't work. I've read many questions which already are on StackOverflow, but none helped me.
The variable inputAddTunnel in which the text should be saved, is always just equal to "":
private fun getInput(): String {
val inputAddTunnel = this.layoutInflater.inflate(R.layout.dialog_add_vpn_tunnel,null).findViewById<TextInputEditText>(R.id.input_add_tunnel).getText().toString()
return inputAddTunnel
}
The Xml for the dialog is:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/input_add_tunnel_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:hint="#string/name_of_vpn">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/input_add_tunnel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"/>
</com.google.android.material.textfield.TextInputLayout>
Edit: Here is the onCreateDialog() method
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = androidx.appcompat.app.AlertDialog.Builder(it)
// Get the layout inflater
val inflater = requireActivity().layoutInflater
// 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.dialog_add_vpn_tunnel, null))
// Add action buttons
.setPositiveButton(R.string.add_vpn_tunnel,
DialogInterface.OnClickListener { dialog, id ->
// Add VPN tunnel
addVpn()
})
.setNegativeButton(R.string.cancel,
DialogInterface.OnClickListener { dialog, id ->
getDialog()?.cancel()
})
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
And this is the addVpn method, which is called in onCreateDialog():
private fun addVpn() {
// TODO test function addVpn(); count doesn't work properly
val count = activity?.getPreferences(AppCompatActivity.MODE_PRIVATE)?.getInt("countTunnels", -1)
val inputAddTunnel = getInput()
// If inputAddTunnel is not equal to "", put it in getPreferences
if(inputAddTunnel != "") { //TODO test if !== is needed, not !=
activity?.getPreferences(AppCompatActivity.MODE_PRIVATE)?.
edit()?.putString("tunnel$count", inputAddTunnel).apply { }
}
// Edit the count in getPreferences to the incremented count
count?.plus(1)
if (count != null) {
activity?.getPreferences(AppCompatActivity.MODE_PRIVATE)?.getInt("countTunnels", count)
}
}
You need to get editext object from dialog view and call getText() method
Edittext edittext= getDialog().getView().findViewbyId(R.id.editText);
edittext.getText(); // for getting textvalue from editetxt
You are re-inflating a brand new view, not querying from the one you already set for your dialog.
You should call findViewById on the root dialog view that you initially inflated. Please share your onCreateDialog method for more help.
I faced the following problem: I need to implement the solution for the case when the phone should be entered to the EditText. This phone should have non-removable part and last four numbers should be filled with underscore at the beginning and then when user typing them underscores should be changed to numbers, like:
+12345____ -> typing 6 -> +123456___
I implemented the non-removable part. Here's the way how I did it:
binding.etPhoneNumber.filters = arrayOf(InputFilter.LengthFilter(args.phoneNumber?.length ?: 0))
binding.etPhoneNumber.doAfterTextChanged {
val symbolsLeft = it?.toString()?.length ?: 0
if (symbolsLeft < phoneNumberUi.length) {
binding.etPhoneNumber.setText(phoneNumberUi)
binding.etPhoneNumber.setSelection(symbolsLeft + 1)
}
}
But now I do not understand, how to handle the logic with underscores. I tried to append the underscores in doAfterTextChanged, like if ((args.phoneNumber?.length ?: 0) > (it?.toString()?.length ?: 0)) append n underscores, where n is the difference between length, but in this case I cannot add new symbols as EditText is filled and underscores are not removed. So, how can I solve the problem? Thanks in advance for any help!
You can remove the LengthFilter and check the length in doAfterTextChanged :
val phoneNumberUi = "+12345"
val length = 10
binding.etPhoneNumber.doAfterTextChanged {
when {
it == null -> {
}
// missing / incomplete prefix
it.length < phoneNumberUi.length -> {
it.replace(0, it.length, phoneNumberUi)
}
// prefix was edited
!it.startsWith(phoneNumberUi) -> {
it.replace(0, phoneNumberUi.length, phoneNumberUi)
}
// too short
it.length < length -> {
it.append("_".repeat(length - it.length))
}
// too long
it.length > length -> {
it.replace(length, it.length, "")
}
// set the cursor at the first _
it.indexOf("_") >= 0 -> {
binding.etPhoneNumber.setSelection(it.indexOf("_"))
}
}
}
Note : This uses a when because every change triggers immediately a recursive call to doAfterTextChanged
This approach has the following conditional branches
The place where the user adds their input (non-removable part or the changeable part)
The entered char (number or backspace)
And works by getting the entered char (number/backspace) within the onTextChanged() and its index (second parameter), and set the new EditText value upon the values of both of them.
Also the value of the EditText is tracked by currentText variable. So that we can only replace one character at a time which is the input done by the user to avoid the burden of manipulating the entire text.
You can find the rest of the explanation by the below comments through the code:
attachTextWatcher(findViewById(R.id.edittext))
fun attachTextWatcher(editText: EditText) {
// set the cursor to the first underscore
editText.setSelection(editText.text.indexOf("_"))
var currentText = editText.text.toString() // which is "+12345____"
val watcher: TextWatcher = object : TextWatcher {
override fun onTextChanged(
s: CharSequence,
newCharIndex: Int, // "newCharIndex" is the index of the new entered char
before: Int,
count: Int
) {
// New entered char by the user that triggers the TextWatcher callbacks
val newChar = s.subSequence(newCharIndex, newCharIndex + count).toString().trim()
/* Stop the listener in order to programmatically
change the EditText Without triggering the TextWatcher*/
editText.removeTextChangedListener(this)
// Setting the new text of the EditText upon examining the user input
currentText =
if (newChar.isEmpty()) { // User entered backspace to delete a char
if (newCharIndex in 0..5) { // The backspace is pressed in the non-removable part
"+12345" + currentText.substring(6)
} else { // The backspace is pressed in the changeable part
val sb = StringBuilder(currentText)
// replace the the number at which backspace pressed with underscore
sb.setCharAt(newCharIndex, '_')
sb.toString()
}
} else { // User entered a number
if (newCharIndex in 0..5) { // The number is entered in the non-removable part
// replace the first underscore with the entered number
val sb = StringBuilder(currentText)
sb.setCharAt(sb.indexOf("_"), newChar[0])
sb.toString()
} else { // The number is entered in the changeable part
if (newCharIndex < 10) { // Avoid ArrayOutOfBoundsException as the number length should not exceed 10
val sb = StringBuilder(currentText)
// replace the the number at which the number is entered with the new number
sb.setCharAt(newCharIndex, newChar[0])
sb.toString()
} else currentText
}
}
// Set the adjusted text to the EditText
editText.setText(currentText)
// Set the current cursor place
if (editText.text.contains("_"))
editText.setSelection(editText.text.indexOf("_"))
else
editText.setSelection(editText.text.length)
// Re-add the listener, so that the EditText can intercept the number by the user
editText.addTextChangedListener(this)
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun afterTextChanged(s: Editable) {
}
}
editText.addTextChangedListener(watcher)
}
This is the layout I'm testing with:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="#+id/edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:maxLength="11"
android:text="+12345____"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Note: make sure to survive the value of the currentText on configuration change.
preview
I think that PhúcNguyễn had a good idea to marry a TextView with an EditText to produce what you are looking for. You could place these as separate fields in a layout or place them in a composite view. Either way the effect will be the same and you could achieve what you want.
You have already figured out how to handle the static text at the beginning of the field. What I am presenting below is how to handle the underscores so characters that are entered appear to overwrite the underscores.
For the demo, I have place a TextView with the static text beside a custom EditText. It is the custom EditText that is really of any interest. With the custom view, the onDraw() function is overridden to write the underscores as part of the background. Although these underscores will appear like any other character in the field, they cannot be selected, deleted, skipped over or manipulated in any way except, as the user types, the underscores are overwritten one-by-one. The end padding of the custom view is manipulated to provide room for the underscores and text.
Here is the custom view:
EditTextFillInBlanks.kt
class EditTextFillInBlanks #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : androidx.appcompat.widget.AppCompatEditText(context, attrs, defStyleAttr) {
// Right padding before we manipulate it
private var mBaseRightPadding = 0
// Width of text that has been entered
private var mTextWidth = 0f
// Mad length of data that can be entered in characters
private var mMaxLength = 0
// The blanks (underscores) that we will show
private lateinit var mBlanks: String
// MeasureSpec for measuring width of entered characters.
private val mUnspecifiedWidthHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
init {
mBaseRightPadding = paddingRight
doOnTextChanged { text, _, _, _ ->
measure(mUnspecifiedWidthHeight, mUnspecifiedWidthHeight)
mTextWidth = measuredWidth.toFloat() - paddingStart - paddingEnd
updatePaddingForBlanks(text)
}
setText("", BufferType.EDITABLE)
}
/*
Make sure that the end padding is sufficient to hold the blanks that we are showing.
The blanks (underscores) are written into the expanded padding.
*/
private fun updatePaddingForBlanks(text: CharSequence?) {
if (mMaxLength <= 0) {
mMaxLength = determineMaxLen()
check(mMaxLength > 0) { "Maximum length must be > 0" }
}
text?.apply {
val blanksCount = max(0, mMaxLength - length)
mBlanks = "_".repeat(blanksCount).apply {
updatePadding(right = mBaseRightPadding + paint.measureText(this).toInt())
}
}
}
/*
Draw the underscores on the canvas. They will appear as characters in the field but
cannot be manipulated by the user.
*/
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
if (mBlanks.isNotEmpty()) {
canvas?.withSave {
drawText(mBlanks, paddingStart + mTextWidth, baseline.toFloat(), paint)
}
}
}
fun setMaxLen(maxLen: Int) {
mMaxLength = maxLen
}
private fun determineMaxLen(): Int {
// Before Lollipop, we can't get max for InputFilter.LengthFilter
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return 0
return filters.firstOrNull { it is InputFilter.LengthFilter }
?.let {
it as InputFilter.LengthFilter
it.max
} ?: 0
}
}
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/holo_blue_light"
tools:context=".MainActivity">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:paddingStart="8dp"
android:paddingTop="8dp"
android:text="+12345"
android:textColor="#android:color/black"
android:textSize="36sp"
app:layout_constraintBaseline_toBaselineOf="#id/editableSuffix"
app:layout_constraintEnd_toStartOf="#+id/editableSuffix"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="#+id/guideline2" />
<com.example.edittextwithblanks.EditTextFillInBlanks
android:id="#+id/editableSuffix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/edittext_background"
android:inputType="number"
android:maxLength="#integer/blankFillLen"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:textColor="#android:color/black"
android:textSize="36sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/textView"
app:layout_constraintTop_toTopOf="parent"
tools:text="____">
<requestFocus />
</com.example.edittextwithblanks.EditTextFillInBlanks>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="92dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val mStaticStart = "+12345"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (BuildConfig.VERSION_CODE < Build.VERSION_CODES.P) {
val maxLen = resources.getInteger(R.integer.blankFillLen)
findViewById<EditTextFillInBlanks>(R.id.editableSuffix).setMaxLen(maxLen)
}
}
}
It is likely that you can incorporate your static text handling into the custom view for a complete solution.
I have multiple option select and I need to get array of selected options but all I get is latest option selected.
Code
class PublishActivity : AppCompatActivity() {
var selectedTags: List<String>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_publish)
pTags.setOnClickListener {
var tagIds = ArrayList<String>()
val tagOptions = ArrayList<String>()
for (i in tags) {
tagOptions.add(i.title)
tagIds.add(i.id)
}
var checkedItems = ArrayList<Int>()
checkedItems.forEach{ index -> tagIds[index + 1] }
MaterialAlertDialogBuilder(this)
.setTitle(resources.getString(R.string.project_tags))
.setMultiChoiceItems(tagOptions.toTypedArray(), null) { dialog, which, checked ->
if (checked) {
checkedItems.add(which)
} else if (checkedItems.contains(which)) {
checkedItems.remove(Integer.valueOf(which))
}
// Respond to item chosen
pTags.setText("${checkedItems.size} tags selected")
}
.setPositiveButton(resources.getString(R.string.ok)) { dialog, which ->
for (i in checkedItems) {
Log.d("eeee1", tagOptions[i])
selectedTags = listOf(tagOptions[i])
}
}
.setNeutralButton(resources.getString(R.string.clear)) { dialog, which ->
pTags.text = null
pTags.hint = "0 tag selected"
if (checkedItems.size > 0) {
checkedItems.clear()
}
}
.show()
}
}
}
Log.d("eeee1", tagOptions[i]) returns such data in logcat
D/eeee1: 3D Printing
D/eeee1: 3D Architecture
D/eeee1: .NET/Mono
D/eeee1: ActionScript
but in my selectedTags I get only D/eeer1: [ActionScript]
It supposed to give me something like this D/eeer1: ["3D Printing", "3D Architecture", ".NET/Mono", "ActionScript"]
PS: what I'm actually look to achieve here is to get id of those selected items instead of their names that's why I have var tagIds = ArrayList<String>() but if that's not possible to achieve as long as it just return array of all names (like sample above) it's fine by me as well.
Any idea?
The following code sets your variable to a list with a single item. So you just overwrite your variable over and over again
selectedTags = listOf(tagOptions[i])
you need:
//Declaration
var selectedTags: MutableList<String> = mutableListOf()
...
// In loop
selectedTags.add(tagOptions[i])
You could also do it with a more functional approach:
//Declaration
var selectedTags: List<String>? = listOf()
...
// Skip the loop and use the map function
.setPositiveButton(resources.getString(R.string.ok)) { dialog, which ->
selectedTags = checkedItems.map{ tagOptions[it] }
}
To get the Id's instead of the titles you should just be able to use your tagIds instead of tagOptions. Just make sure that you get your typing right. The selectedTags list needs to be of the same type as tag.id.
You are getting only last inserted value because you are creating fresh list when ok button is clicked and assigning it to selectedTags. Problem at selectedTags = listOf(tagOptions[i]) line of your code.
Solution:
Declare a single list and put selected values into it. Like :
val selectedTags = arrayListOf<String>()
then use below code inside ok button click:
.setPositiveButton("Ok") { dialog, which ->
for (i in checkedItems) {
//selectedTags = listOf(tagOptions[i])
selectedTags.add(tagOptions[i])
}
}
I would like to make the message arguments text bold given the following string file.how can i make it?
code is given blow:
<string name="meter_reading_dialog_message">Current Reading: %s m³ \nPrevious Reading: %s m³ \nConsumption Reading: %s m³</string>
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val toCurrentReading =
requireArguments().getString(CURRENT_READING)
val toPreviousReading =
requireArguments().getString(PREVIOUS_READING)
val toConsumption =
requireArguments().getString(CONSUMPTION)
return AlertDialog.Builder(requireContext())
.setTitle(getString(R.string.confirmation_reading))
.setMessage(
getString(
R.string.meter_reading_dialog_message,
toCurrentReading,
toPreviousReading,
toConsumption
)
)
.setPositiveButton(R.string.deactivate_save) { _, _ ->
targetFragment?.onActivityResult(targetRequestCode, Activity.RESULT_OK, null)
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
targetFragment?.onActivityResult(targetRequestCode, Activity.RESULT_CANCELED, null)
}
.create()
}
I think you can use this for your situation.
you first define a string with it.
val mystring = getString(
R.string.meter_reading_dialog_message,
toCurrentReading,
toPreviousReading,
toConsumption
)
After that, you can set bold value like in this example.
How to set part of text to bold when using AlertDialog.setMessage() in Android?
You can use this method.
fun boldText(text: String) : String {
return Html.fromHtml(text)
}
println(boldText("<b>This text bold.</b> This text normal."))
output=> This text bold. This text normal.