I want to validate text input, but how to show multiple edit text error simultaneously??
Here is my code
binding.btnCalculate.setOnClickListener() {
val panjang = binding.etPanjang.text
val lebar = binding.etLebar.text
val tinggi = binding.etTinggi.text
if (TextUtils.isEmpty(binding.etPanjang.text)) {
binding.etPanjang.setError("Field must be filled")
} else if (TextUtils.isEmpty(binding.etLebar.text)) {
binding.etLebar.setError("Field must be filled")
} else if (TextUtils.isEmpty(binding.etLebar.text)) {
binding.etTinggi.setError("Field must be filled")
} else {
val action = InputFragmentDirections.actionInputFragmentToOutputFragment(
panjang.toString().toInt(),
lebar.toString().toInt(),
tinggi.toString().toInt()
)
findNavController().navigate(
action
)
}
}
Result
I Want all edit text that empty is showing error, not only one edit text
You just need to change if .. else to if only for each condition so each condition can be checked and set error if input is not valid
replace your code with below
binding.btnCalculate.setOnClickListener() {
val panjang = binding.etPanjang.text
val lebar = binding.etLebar.text
val tinggi = binding.etTinggi.text
var isAnyError = false
if (TextUtils.isEmpty(binding.etPanjang.text)) {
binding.etPanjang.setError("Field must be filled")
isAnyError = true
}else{
binding.etPanjang.setError(null)
}
if (TextUtils.isEmpty(binding.etLebar.text)) {
binding.etLebar.setError("Field must be filled")
isAnyError = true
}else{
binding.etLebar.setError(null)
}
if (TextUtils.isEmpty(binding.etLebar.text)) {
binding.etTinggi.setError("Field must be filled")
isAnyError = true
}else{
binding.etTinggi.setError(null)
}
if(!isAnyError){
val action = InputFragmentDirections.actionInputFragmentToOutputFragment(
panjang.toString().toInt(),
lebar.toString().toInt(),
tinggi.toString().toInt()
)
findNavController().navigate(
action
)
}
}
Related
I'm trying to send a variable value from my ViewModel to my composable screen. I tried using the debugger to find out where it gets stuck. It seems like it sends the value but never actually receives it.
This is the code I'm using:
NewEvent.kt
#Composable
fun NewEvent(
viewModel: NewEventViewModel = viewModel(),
navController: NavController
){
val context = LocalContext.current
LaunchedEffect(context){
viewModel.newEventType.collect { eventType ->
Toast.makeText(context, eventType.toString(), Toast.LENGTH_SHORT).show()
}
}
}
changeEventType() gets called here
DropdownMenu(
expanded = menuExpanded,
onDismissRequest = { menuExpanded = false },
) {
eventTypeList.forEach {
if(it != viewModel.event.eventType && it != EventType.UNKNOWN) {
DropdownMenuItem(
onClick = { viewModel.changeEventType(it); menuExpanded = false },
text = { Text(stringResource(context.resources.getIdentifier(it.toString().lowercase(), "string", context.packageName))) }
)
}
}
}
NewEventViewModel.kt
private val newEventTypeChannel = Channel<EventType>()
val newEventType = newEventTypeChannel.receiveAsFlow()
fun changeEventType(newEventType: EventType){
viewModelScope.launch {
newEventTypeChannel.send(newEventType)
}
}
I downloaded a sample project from GitHub using this exact implementation and it worked, I'm not sure what I'm missing here.
If you want to display toast message, you don't have to create separate composable function for it. Since Toast is dynamic and does not need to be recomposed, you don't need composable function for it.
It would be more clear and better to implement it like this (in one composable function)
val context = LocalContext.current
LaunchedEffect(context){
viewModel.newEventType.collect { eventType ->
Toast.makeText(context, eventType.toString(), Toast.LENGTH_SHORT).show()
}
}
DropdownMenu(
expanded = menuExpanded,
onDismissRequest = { menuExpanded = false },
) {
eventTypeList.forEach {
if(it != viewModel.event.eventType && it != EventType.UNKNOWN) {
DropdownMenuItem(
onClick = { viewModel.changeEventType(it); menuExpanded = false },
text = { Text(stringResource(context.resources.getIdentifier(it.toString().lowercase(), "string", context.packageName))) }
)
}
}
}
i am working on QuizApp and I get a problem when I choose an answer and click submit it show me the correct and wrong answer and then I should click submit again to go to the next question, i want to disable that , I want when someone click submit he cant change the answer or choose another answer
here is my code
override fun onClick(v: View?) {
when (v?.id) {
R.id.tv_option_one -> {
selectedOptionView(tv_option_one, 1)
}
R.id.tv_option_two -> {
selectedOptionView(tv_option_two, 2)
}
R.id.tv_option_three -> {
selectedOptionView(tv_option_three, 3)
}
R.id.tv_option_four -> {
selectedOptionView(tv_option_four, 4)
}
R.id.btn_submit -> {
if (mSelectedOptionPosition == 0) {
mCurrentPosition++
when {
mCurrentPosition <= mQuestionsList!!.size -> {
setQuestion()
}
else -> {
// TODO (STEP 5: Now remove the toast message and launch the result screen which we have created and also pass the user name and score details to it.)
// START
val intent =
Intent(this, ResultActivity::class.java)
intent.putExtra(Constants.USER_NAME, mUserName)
intent.putExtra(Constants.CORRECT_ANSWERS, mCorrectAnswers)
intent.putExtra(Constants.TOTAL_QUESTIONS, mQuestionsList!!.size)
startActivity(intent)
finish()
// END
}
}
} else {
val question = mQuestionsList?.get(mCurrentPosition - 1)
// This is to check if the answer is wrong
if (question!!.correctAnswer != mSelectedOptionPosition) {
answerView(mSelectedOptionPosition, R.drawable.wrong_option_border_bg)
//
}
else {
mCorrectAnswers++
}
// This is for correct answer
answerView(question.correctAnswer, R.drawable.correct_option_border_bg)
if (mCurrentPosition == mQuestionsList!!.size) {
btn_submit.text = "FINISH"
} else {
btn_submit.text = "GO TO NEXT QUESTION"
}
mSelectedOptionPosition = 0
}
}
}
}
private fun setQuestion() {
val question = mQuestionsList!!.get(mCurrentPosition - 1) // Getting the question from the list with the help of current position.
defaultOptionsView()
if (mCurrentPosition == mQuestionsList!!.size) {
btn_submit.text = "FINISH"
} else {
btn_submit.text = "SUBMIT"
}
progressBar.progress = mCurrentPosition
tv_progress.text = "$mCurrentPosition" + "/" + progressBar.getMax()
tv_question.text = question.question
iv_image.setImageResource(question.image)
tv_option_one.text = question.optionOne
tv_option_two.text = question.optionTwo
tv_option_three.text = question.optionThree
tv_option_four.text = question.optionFour
}
/**
* A function to set the view of selected option view.
*/
private fun selectedOptionView(tv: TextView, selectedOptionNum: Int) {
defaultOptionsView()
mSelectedOptionPosition = selectedOptionNum
tv.setTextColor(
Color.parseColor("#363A43")
)
tv.setTypeface(tv.typeface, Typeface.BOLD)
tv.background = ContextCompat.getDrawable(
this,
R.drawable.selected_option_border_bg
)
}
/**
* A function to set default options view when the new question is loaded or when the answer is reselected.
*/
private fun defaultOptionsView() {
val options = ArrayList<TextView>()
options.add(0, tv_option_one)
options.add(1, tv_option_two)
options.add(2, tv_option_three)
options.add(3, tv_option_four)
for (option in options) {
option.setTextColor(Color.parseColor("#7A8089"))
option.typeface = Typeface.DEFAULT
option.background = ContextCompat.getDrawable(
this,
R.drawable.default_option_border_bg
)
}
}
/**
* A function for answer view which is used to highlight the answer is wrong or right.
*/
private fun answerView(answer: Int, drawableView: Int) {
when (answer) {
1 -> {
tv_option_one.background = ContextCompat.getDrawable(
this,
drawableView
)
}
2 -> {
tv_option_two.background = ContextCompat.getDrawable(
this,
drawableView
)
}
3 -> {
tv_option_three.background = ContextCompat.getDrawable(
this,
drawableView
)
}
4 -> {
tv_option_four.background = ContextCompat.getDrawable(
this,
drawableView
)
}
}
}
}
in your onclick, change the other views
view.isClickable = false
Here I wrote a function where when a particular textView is clicked it will sort the recycler view according to that property but the function only does sorting in descending order. I want functionality where when click the text the first time it sort in ascending order and in the second time it will sort in descending or vice versa. Can please anyone help?
private fun sortTheData(){
binding.statusTextTV.setOnClickListener {
statusAsc = if(statusAsc){
vehicleList.sortedBy {
it.status
}
false
}else{
vehicleList.sortByDescending {
it.status
}
true
}
}
binding.permitTextTV.setOnClickListener {
Log.e(TAG, "sortTheData: $permitAsc", )
permitAsc = if(permitAsc){
vehicleList.sortedBy { vehicle ->
vehicle.permit
}.forEach { println(it.permit) }
false
}else{
vehicleList.sortByDescending { vehicle ->
vehicle.permit
}
true
}
adapter.updateList(vehicleList)
}
binding.licenseTextTV.setOnClickListener {
licenseAsc = if(licenseAsc){
vehicleList.sortedBy {
it.license
}
adapter.updateList(vehicleList)
false
}else{
vehicleList.sortByDescending {
it.license
}
adapter.updateList(vehicleList)
true
}
}
binding.spaceTextTV.setOnClickListener {
spaceAsc = if(spaceAsc){
vehicleList.sortedBy {
it.space
}
vehicleList.forEach{
println(it.space)
}
false
}else{
vehicleList.sortByDescending {
it.space
}
vehicleList.forEach{
println(it.space)}
true
}
}
}
You need to keep a boolean flag and sort accordingly. Pseudo code:
var reversed = false
textView.setOnClickListener {
reversed = !reversed
list = vehicleList.sortedBy { it.license }
if (reversed) {
list = list.reversed()
}
}
You can use type varaible or enum
e.g
var sortType : Int = 1 // 1 or asc & 2 for desc. You can use enum as well
Your sort function will be
fun sort() {
if(sortType == 1) {
sortType = 2
vehicleList.sortByDescending {it.license}
} else {
sortType = 1
vehicleList.sortBy {it.license}
}
adapter.updateList(vehicleList)
}
I have a table with fifteen rows. Each row have three columns and a total column. I want to get the total per row, the grand total, and the overall average.
The user may not enter data for all rows, and the user may skip a row.
So the code checks if the user have entered data in one of three fields of each row.
If the row is blank, ignore it.
If some of the fields are filled-up, tell the user to fill up the rest of the row.
If all the fields in a row is filled up, sum all its fields and increment the divider.
I have only pasted the codes for Rows 1 & 2 for brevity, but it shows the gist of what I'm trying to achieve:
The code:
var a1 = 0
var a2 = 0
var total = 0
var divider = 0
// Row 1
if (b1p1.text.isNotEmpty() or b2p1.text.isNotEmpty() or b3p1.text.isNotEmpty()) {
var y = 0
listOf(b1p1, b2p1, b3p1).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p1, b2p1, b3p1).forEach {
a1 += it.text.toString().toInt()
}
total1.text = a1.toString()
total += a1
e2 = 1
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
// Row 2
if (b1p2.text.isNotEmpty() or b2p2.text.isNotEmpty() or b3p2.text.isNotEmpty()) {
var y = 0
listOf(b1p2, b2p2, b3p2).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p2, b2p2, b3p2).forEach {
a2 += it.text.toString().toInt()
}
total2.text = a2.toString()
total += a2
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
if (e2 == 1) {
grandTotalTextView.text = total.toString()
average = total.toDouble()/divider
val decimalFormatter = DecimalFormat("#,###.##")
averageTextView.text = decimalFormatter.format(average).toString()
cyeSingleton.anct3b = decimalFormatter.format(average).toString()
} else {
Toast.makeText(activity, "Error 2", Toast.LENGTH_SHORT).show()
}
The table:
This is the best I could come up with. Should there be no other suggestion, I will settle for this.
Thanks in advance!
**EDIT: Thanks to ** https://stackoverflow.com/users/3736955/jemshit-iskenderov
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
private fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>): TotalResult {
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews as List<EditText>, totalTextViews[index])
if(!rowResult.ignoreRow){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
Toast.makeText(
activity,
"$divider, $allTotal, $showError", Toast.LENGTH_SHORT)
.show()
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
private fun calculateRowResult(rowTextViews:List<EditText>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.text.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
fun main(){
val totalResult = calculateTotalResult(
allTextViews = listOf(
listOf(t11,t12,t13),
listOf(t21,t22,t23)
),
totalTextViews = listOf(totalView1, totalView2)
)
// single Toast error
if(totalResult.showError){
// showToast(error)
}
// use totalResult.divider, totalResult.allTotal
}
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>){
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews, totalTextViews[index])
if(!rowResult.ignore){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
fun calculateRowResult(rowTextViews:List<TextView>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
Extracted calculateTotalResult() and calculateRowResult() so multiple rows and columns do not need to repeat same code.
calculateRowResult() processes singlet row of TextViews. I had to iterate rowTextViews twice, one to calculate ignore, the other to show error on TextView if not ignore. We don't show Toast Error here yet.
calculateTotalResult() iterates through all rows and gets total result. We show only one Toast Error (if required) after this step.
Code is pseudo-code, not tested.
I am new to Android Development. I need to build a temperature converter app which would convert Celcius to Farenheit and Farenheit to Celcius. I have used an editText for user input. There are two buttons. One button is to convert the input and the other one is the mode button which would toggle between the two modes of conversion. When I launch the app the mode is in celcius to farenheit by default. By clicking on mode button I can change the mode to farenheit to celcius scale. The problem is that when I again click on the mode button it does not return to the celcius to farenheit conversion mode. I don't know how to do it. Can someone help me in this regard?
I have set the function getset() for the convert button and function mode() for the mode button.
fun getSet(view: View)
{
val convert = findViewById<Button>(R.id.button)
convert.setOnClickListener {
if(editText.length()==0)
{
editText.setError("Enter a Value")
}
else
{
val editxt = findViewById<EditText>(R.id.editText)
val msg = editxt.text.toString()
val txtview = findViewById<TextView>(R.id.textView2).apply {
val cel = msg.toDouble()
val far = (cel*1.8)+32
text = "Result: " + far.toString()
}
}
}
}
fun mode(view: View)
{
val convert = findViewById<Button>(R.id.button)
val heading = findViewById<TextView>(R.id.textView).apply {
val caption = "Farenheit to Celcius"
text = caption
}
convert.setOnClickListener {
if(editText.length()==0)
{
editText.setError("Enter a Value")
}
else
{
val editxt = findViewById<EditText>(R.id.editText)
val msg = editxt.text.toString()
val txtview = findViewById<TextView>(R.id.textView2).apply {
val far = msg.toDouble()
val cel = (far-32)*0.5555555556
text = "Result: " + cel.toString()
}
}
}
}
You need to store the "mode" in a global variable.
create a global variable
var isModeCelsius: Boolean = true
Now inside your onCreate() method in your activity, under the setContentView(R.layout.your_layout_name) line enter the below code.
//Initialize edittext and button
val convert = findViewById<Button>(R.id.button)
val heading = findViewById<TextView>(R.id.textView)
val modeButton = findViewById<Button>(R.id.id_of_button)
val editxt = findViewById<EditText>(R.id.editText)
val showResultTextView = findViewById<TextView>(R.id.textView2)
//You only need to assign the click listener once
modeButton.setOnClickListener {
if (isModeCelsius) {
isModeCelsius = false
} else {
isModeCelsius = true
}
//Or you can simply use
//isModeCelsius=!isModeCelsius
}
convert.setOnClickListener {
val msg = editxt.text.toString()
if(isModeCelsius){
val far = msg.toDouble()
val cel = (far-32)*0.5555555556
showResultTextView.text = "Result: " + cel.toString()
}else{
val cel = msg.toDouble()
val far = (cel*1.8)+32
showResultTextView.text = "Result: " + far.toString()
}
}