Unresolved reference: search_src_text - android

For reference
This is a branch showing the issue (note the project is incomplete). Run it and you'll see the compile time error Unresolved reference: search_src_text.
https://github.com/mitchtabian/Clean-Notes/blob/searchview-issue/notes/src/main/java/com/codingwithmitch/cleannotes/notes/framework/presentation/notelist/NoteListFragment.kt
What I'm trying to do:
I want to submit text to my ViewModel from the SearchView. When the text is submitted I execute a query using that text.
What you might be thinking:
"Just use an OnQueryTextListener."
An OnQueryTextListener will not submit text if the text is null/empty. I am using the null/empty case to search everything in the database. So that doesn't work.
The work-around I tried:
In the past I've manually searched for the id of the SearchAutoComplete view inside the SearchView. You can find it like this:
val searchPlate: SearchView.SearchAutoComplete? = search_view.findViewById(R.id.search_src_text)
Then you just attach an OnEditorActionListener and listen for the IME actions and you're good to go:
searchPlate.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_UNSPECIFIED
|| actionId == EditorInfo.IME_ACTION_SEARCH ) {
val searchQuery = v.text.toString()
viewModel.setQuery(searchQuery)
viewmodel.executeSearch()
}
true
}
Usually this works fine. The only difference (that I can see) is this time I'm using a Dynamic Feature Module. When trying to access R.id.search_src_text it throws this compile time error:
Unresolved reference: search_src_text
And yes, the layout is in the dynamic feature module /res/ directory.
The code
1. SearchView in layout
<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:background="#color/app_background_color"
>
<androidx.appcompat.widget.SearchView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:iconifiedByDefault="false"
android:queryHint="#string/text_Search"
android:layout_centerHorizontal="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
2. Get reference to search_src_text
val searchPlate: SearchView.SearchAutoComplete? = search_view.findViewById(R.id.search_src_text)
Running the project will yield the error Unresolved reference: search_src_text.
Another weird thing:
I looped through the View hierarchy until I found the SearchAutoComplete, then attached the listener and it works just fine...
for((index1, child) in search_view.children.withIndex()){
printLogD("ListFragment", "${index1}, ${child}")
for((index2, child2) in (child as ViewGroup).children.withIndex()){
printLogD("ListFragment", "T2: ${index2}, ${child2}")
if(child2 is ViewGroup){
for((index3, child3) in (child2 as ViewGroup).children.withIndex()){
printLogD("ListFragment", "T3: ${index3}, ${child3}")
if(child3 is ViewGroup){
for((index4, child4) in (child3 as ViewGroup).children.withIndex()){
printLogD("ListFragment", "T4: ${index4}, ${child4}")
if(child4 is SearchView.SearchAutoComplete){
child4.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_UNSPECIFIED
|| actionId == EditorInfo.IME_ACTION_SEARCH ) {
val searchQuery = v.text.toString()
printLogD("NoteList", "SearchView: (keyboard or arrow) executing search...: ${searchQuery}")
viewModel.setQuery(searchQuery).let{
viewModel.loadFirstPage()
}
}
true
}
break
}
}
}
}
}
}
}
Any insights would be greatly appreciated.

val searchPlate: SearchView.SearchAutoComplete? = search_view.findViewById(androidx.appcompat.R.id.search_src_text)

Related

Just mark 1 checkbox of each question and save answer of each position - kotlin

Good morning, community, I have the following case, I have 1 list of questions with their respective YES/NO answers, which are the checkboxes, what is complicating me is how I can apply 1 validation that only allows marking 1 answer (yes or no), in turn save that answer with its respective position and then save it in a DB.
this is my adapter(preusoadapter.kt)
class preusoadapter(
private val context : Context,
private val listpreguntaspreuso: ArrayList<epreguntas>
) : RecyclerView.Adapter<preusoadapter.PreUsoViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PreUsoViewHolder {
val layoutInflater = LayoutInflater.from(context)
return PreUsoViewHolder(layoutInflater.inflate(R.layout.activity_estructura_listapreuso, parent, false)
)
}
override fun onBindViewHolder(holder: PreUsoViewHolder, position: Int) {
val item = listpreguntaspreuso[position]
holder.render(item)
holder.displayChecked(item.answer)
if (position == 2) {
holder.displayAnswers(setOf(Answer.IVSS, Answer.DSS))
} else if (position == 6) {
holder.displayAnswers(setOf(Answer.FRESERV, Answer.FREDMANO))
} else if (position == 14) {
holder.displayAnswers(setOf(Answer.NA, Answer.SI, Answer.NO))
} else if (position == 18) {
holder.displayAnswers(setOf(Answer.NA, Answer.SI, Answer.NO))
} else if (position == 22) {
holder.displayAnswers(setOf(Answer.NA, Answer.SI, Answer.NO))
} else if (position == 25) {
holder.displayAnswers(setOf(Answer.NA, Answer.SI, Answer.NO))
}
}
override fun getItemCount(): Int = listpreguntaspreuso.size
//CLASE INTERNA PREUSOVIEWHOLDER//
inner class PreUsoViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = ActivityEstructuraListapreusoBinding.bind(view)
private val idpregunta = view.findViewById<TextView>(R.id.txtidpregunta)
private val numeropregunta = view.findViewById<TextView>(R.id.txtnumeropregunta)
private val pregunta = view.findViewById<TextView>(R.id.txtpreguntas)
private val imgestado = view.findViewById<ImageView>(R.id.icosemaforo)
fun render (epreguntas: epreguntas){
idpregunta.text = epreguntas.id_pregunta
numeropregunta.text = epreguntas.num_pregunta
pregunta.text = epreguntas.pregunta
Glide.with(imgestado.context).load(epreguntas.icono_estado).into(imgestado)
}
private val checkboxAnswers = mapOf(
binding.chbksi to Answer.SI,
binding.chbkno to Answer.NO,
binding.chbkna to Answer.NA,
binding.chbkIVSS to Answer.IVSS,
binding.chbkDSS to Answer.DSS,
binding.chbkFSERV to Answer.FRESERV,
binding.chbkfmano to Answer.FREDMANO
)
init {
// set the listener on all the checkboxes
checkboxAnswers.keys.forEach { checkbox ->
checkbox.setOnClickListener { handleCheckboxClick(checkbox) }
}
}
// A function that handles all the checkboxes
private fun handleCheckboxClick(checkbox: CheckBox) {
// get the item for the position the VH is displaying
val item = listpreguntaspreuso[adapterPosition]
// update the item's checked state with the Answer associated with this checkbox
// If it's just been -unchecked-, then that means nothing is checked
checkboxAnswers[checkbox]?.let { answer ->
item.answer = if (!checkbox.isChecked) null else answer
// remember to notify the adapter (so it can redisplay and uncheck any other boxes)
notifyItemChanged(adapterPosition)
}
}
fun displayChecked(answer: Answer?) {
// set the checked state for all the boxes, checked if it matches the answer
// and unchecked otherwise.
// Setting every box either way clears any old state from the last displayed item
checkboxAnswers.forEach { (checkbox, answerType) ->
checkbox.isChecked = answerType == answer
}
}
fun displayAnswers(answers: Collection<Answer>) {
// iterate over each checkbox/answer pair, hiding or displaying as appropriate
checkboxAnswers.forEach { (checkbox, answerType) ->
checkbox.visibility = if (answerType in answers) View.VISIBLE else View.GONE
}
}
}
}
and this is my class epreguntas.kt
class epreguntas(
var id_pregunta: String,
var num_pregunta: String,
var pregunta : String,
var icono_estado: String,
var checkvalor: Boolean = false,
var answer: Answer? = null
) {
}
this is my structure i use for my recylcview
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:animateLayoutChanges="true"
app:cardCornerRadius="2dp"
app:cardElevation="4dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/icosemaforo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="#drawable/ic_android" />
</RelativeLayout>
<LinearLayout
android:id="#+id/contenedor_categoria1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:orientation="vertical"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:id="#+id/txtnumeropregunta"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_alignParentTop="true"
android:text="N°"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="#+id/txtpreguntas"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="20dp"
android:layout_toStartOf="#+id/contenedorcheck"
android:textSize="14sp"
android:layout_toEndOf="#+id/txtnumeropregunta"
android:textStyle="bold"
android:text="Preguntas" />
<LinearLayout
android:id="#+id/contenedorcheck"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:paddingEnd="20dp"
>
<CheckBox
android:id="#+id/chbksi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="#string/check_si" />
<CheckBox
android:id="#+id/chbkIVSS"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:visibility="gone"
android:text="#string/check_IVSS" />
<CheckBox
android:id="#+id/chbkFSERV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:visibility="gone"
android:text="#string/check_freserv" />
<CheckBox
android:id="#+id/chbkno"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:text="#string/check_no" />
<CheckBox
android:id="#+id/chbkfmano"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginEnd="10dp"
android:text="#string/check_fredmano" />
<CheckBox
android:id="#+id/chbkDSS"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginEnd="10dp"
android:text="#string/check_DSS" />
<CheckBox
android:id="#+id/chbkna"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginEnd="10dp"
android:text="#string/check_na" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
It complies with selecting 1 box and unchecking the other, but I realize that when I check the box for question 1 (yes) another question overlaps and my question 1 is hidden, and I see that in some questions the YES/NO/ NA up to IVSS and DSS
example
Like Tenfour04 says, RadioButtons in a RadioGroup would handle the "only one thing can be selected" functionality. (You'd need a different listener to handle its button id has been selected callbacks.)
But since you're already storing the checked state and displaying it, you can handle that yourself - you just need a way to ensure when one checkbox is checked, the others are stored as unchecked.
An easy way to do that is to store an Int which represents the checkbox that's selected. Say 0 for the first, 1 for the second, and a negative number for none. Because each number represents one checked item, by changing the number you're "unchecking" the others.
If you want to store that in your data object (like with checkvaloryes) you can just do:
class epreguntas(
var id_pregunta: String,
...
var checkvaloryes: Boolean = false,
var id_answer: Int
)
Then your checkbox click listeners just have to store the appropriate value, and in onBindHolder you enable the selected checkbox and disable all the others.
This is basically what using a RadioGroup involves too - you get a button ID when the selection changes (-1 for no selection), you store that, and when you display it you fetch that stored ID and set it on the RadioGroup.
The automatic deselection of other buttons (in single-selection mode) is nice and convenient, but setting the current button in onBindViewHolder will trigger the OnCheckedChangedListener and you can get stuck in a loop, so you'd need to avoid that. One way is to only update your stored value (and notify the adapter of the change) if the current selection ID is different to the stored one.
But you can also use checkboxes and click listeners like you already are, you just have to handle their state yourself. Here's a bit of a long explanation of one way to do it, but it's partly about solving other problems you're probably going to run into.
I'm gonna complicate things a bit, because I feel like it will help you out once you understand what's happening - you have a few things to wrangle here. I'm just posting this as one way to approach that store and display problem.
First, I think it would be a good idea to define your options somewhere. Since you have a fixed set of checkboxes, there's a fixed set of answers, right? You could define those with an enum:
enum class Answer {
SI, NO, IVSS, FRESERV, FREDMANO, DSS, NA
}
And you could store that in your data:
class epreguntas(
...
var answer: Answer? = null // using null for 'no answer selected'
)
(You can use that enum elsewhere in your app too - it means your answer data is in a useful data structure, and it's not tied to some detail about your list display, like what order the checkboxes happen to be added in the layout. If you need to turn these enum constants into an Int, e.g. for storage, you can use their ordinal property)
Now you can connect those responses to the checkboxes that represent them. We could do that in the ViewHolder class:
class PreUsoViewHolder(view: View) : RecyclerView.ViewHolder(view) {
...
// create an answer type lookup for each checkbox in the ViewHolder instance
// I'm going to use your binding object since you have it!
val checkboxAnswers = mapOf(
binding.chbksi to SI,
binding.chbkno to NO,
...
)
That checkboxAnswers map acts as two things - it's a lookup that links each CheckBox in the layout to a specific answer type, and the keys act as a collection of all your CheckBox views, so you can easily do things to all of them together.
Now you can create a click listener that checks which View was clicked, get the matching Answer, and set it:
// I've made this an -inner- class, and it need to be nested inside your Adapter class
// This gives the ViewHolder access to stuff inside the adapter, i.e. listpreguntaspreuso
inner class PreUsoViewHolder(view: View) : RecyclerView.ViewHolder(view) {
init {
...
// set the listener on all the checkboxes
checkboxAnswers.keys.forEach { checkbox ->
checkbox.setOnClickListener { handleCheckboxClick(checkbox) }
}
// A function that handles all the checkboxes
private fun handleCheckboxClick(checkbox: CheckBox) {
// get the item for the position the VH is displaying
val item = listpreguntaspreuso[adapterPosition]
// update the item's checked state with the Answer associated with this checkbox
// If it's just been -unchecked-, then that means nothing is checked
checkboxAnswers[checkbox]?.let { answer ->
item.answer = if (!checkbox.isChecked) null else answer
// remember to notify the adapter (so it can redisplay and uncheck any other boxes)
notifyItemChanged(adapterPosition)
}
}
This relies on you using a click listener, not a checkedChanged listener, because setting checked state in onBindViewHolder (when you're clearing checkboxes) will trigger that checkedChanged listener. A click listener only fires when the user is the one checking or unchecking a box.
So now you have a click listener that sets the appropriate Answer value for an item. To display it, we could put another function in the ViewHolder:
fun displayChecked(answer: Answer?) {
// set the checked state for all the boxes, checked if it matches the answer
// and unchecked otherwise.
// Setting every box either way clears any old state from the last displayed item
checkboxAnswers.forEach { (checkbox, answerType) ->
checkbox.isChecked = answerType == answer
}
}
And now you can call that from onBindViewHolder:
override fun onBindViewHolder(holder: PreUsoViewHolder, position: Int) {
val item = listpreguntaspreuso[position]
holder.render(item)
holder.displayChecked(item.answer)
The other reason for doing things this way, is I think your code to make stuff visible/invisible is broken:
else if (position == 14){
holder.itemView.chbkna.visibility = View.VISIBLE
}
This kind of thing won't work - all you're doing is saying "for item 14, make this box visible" - it says nothing about which of the other boxes should be visible, and which should be hidden. You'll have stuff randomly shown or hidden depending on which item happened to be displayed in that ViewHolder before. You need to explicitly say what should be displayed, every time onBindViewHolder runs.
You can do that with a similar function to the displayChecked one we just wrote:
inner class PreUsoViewHolder(view: View) : RecyclerView.ViewHolder(view) {
...
// provide a list of the answers that should be shown
fun displayAnswers(answers: Collection<Answer>) {
// iterate over each checkbox/answer pair, hiding or displaying as appropriate
checkboxAnswers.forEach { (checkbox, answerType) ->
checkbox.visibility = if (answerType in answers) VISIBLE else GONE
}
}
Now you can easily update your displayed boxes in onBindViewHolder:
else if (position == 14){
holder.displayAnswers(setOf(NA, SI, NO))
}
And even better, you could create groups of answer types associated with the question in the data. So instead of hardcoding by position, you can just pull the required types out of the item itself
class epreguntas(
...
answerTypes: Set<Answer>
// onBindViewHolder
holder.displayAnswers(item.answerTypes)
You could even create preset lists for different types of question, like val yesNo = setOf(SI, NO) (or an enum) and reuse those when defining your questions - there are probably only a few combos you're using anyway!
I hope that wasn't too complicated, and the organisation ideas (and the benefits they can give you) make sense. A RadioGroup is simpler, but with the other stuff you'll probably have to deal with, I feel like this is a useful general approach

EditText field never gets submitted

I got a RecyclerView with multiple EditText-fields. When I try to edit one of the EditText-fields and click enter on the virtual keyboard, the focus shifts down to the next EditText-field, something I don't want to happen. I want to submit the changes I made in the first EditText-field and then close the keyboard. I managed to turn off this focus-shifting by adding the following to my .xml file:
android:focusable="true"
android:focusableInTouchMode="true"
But the problem still persists, now the changes just never get submitted as my listener never gets called. If I remove all items except from one in my RecyclerView everything works like I want. How can I make that happen with more items in myRecyclerView too?
My bind function inside my UserCardItem.kt file;
override fun bind(viewHolder: ViewHolder, position: Int) {
...
viewHolder.itemView.creditcard_nickname.setOnEditorActionListener{ _, actionId, _ ->
if(actionId == EditorInfo.IME_ACTION_DONE){
saveNickname(viewHolder)
true
} else {
false
}
}
private fun saveNickname(viewHolder : ViewHolder){
val nickname = viewHolder.itemView.creditcard_nickname.text.toString()
userCreditcard.nickname = nickname
UserCardStore().updateNickname(userCreditcard)
}
Add android:imeOptions="actionDone" to your EditText in your layout XML.

Selected Item on RecyclerView Horizontal Layout keeps hidden behind start of Layout on Android

I'm using a RecyclerView with LinearLayout Manager and Horizontal orientation.
It's to be used on a TV app, so it needs to be navigated with the dpad. But whenever I go do the end and come back it get stuck on the last one. See photo:
The selected ItemView is the first, but it won't go to the center. if you go to the right and the come back it will show fully.
the code:
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="320dp"
app:layout_constraintBottom_toTopOf="#+id/tv_mini_player_view_placeholder"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/carrier_image_card"
app:layout_constraintVertical_bias="0.10">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="#dimen/margin_start_tv_home"
android:background="#null"
android:clipToPadding="false"
android:orientation="horizontal"
android:padding="#dimen/margin_medium_big"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:context=".android.screens.auth.SelectCarrierActivity"
tools:listitem="#layout/imagecardview_station" />
</androidx.core.widget.NestedScrollView>
One little thing I observed is the layout World Hits didn't adjust the label size. When selected these layouts show a description which makes the transparent grey box larger to acommodate. It's a "View.GONE" change on the description label. So the last selected layout the description disappeared but the layout didn't readjusted. The code for the View Holder part is below:
inner class StationViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val mainImageView: ImageView by lazy { itemView.findViewById<ImageView>(R.id.main_image) }
val titleView: TextView by lazy { itemView.findViewById<TextView>(R.id.title_text) }
val descriptionView: TextView by lazy { itemView.findViewById<TextView>(R.id.description_text) }
init {
val frameLayout = itemView.findViewById<FrameLayout>(R.id.frame_layout)
val cardView = itemView.findViewById<FrameLayout>(R.id.cardview)
cardView.apply {
isFocusable = true
isFocusableInTouchMode = true
onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus ->
if (hasFocus) {
AbstractCardPresenter.animateScaleUp(cardView as View)
frameLayout.background = itemView.context.resources.getDrawable(R.drawable.card_border)
descriptionView.visibility = View.VISIBLE
} else {
AbstractCardPresenter.animateScaleDown(cardView as View)
frameLayout.background = null
descriptionView.visibility = View.GONE
}
}
}
itemView.onAttachStateChangeListener {
onViewAttachedToWindow {
if (adapterPosition == 0) {
cardView.requestFocus()
}
}
}
}
}
U need to get rid of NestedScrollView
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="#dimen/margin_start_tv_home"
android:background="#null"
android:clipToPadding="false"
android:orientation="horizontal"
android:padding="#dimen/margin_medium_big"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:context=".android.screens.auth.SelectCarrierActivity"
tools:listitem="#layout/imagecardview_station" />
I found the offending bit of code!! I was trying to get focus on the first ViewHolder of the RecyclerView upon load. That was causing the problem. Just need to find another way to do it now! This bit of code right there:
itemView.onAttachStateChangeListener {
onViewAttachedToWindow {
if (adapterPosition == 0) {
cardView.requestFocus()
}
}
}

EditText.selectAll() doesnt select text

First of all, if anything can be better in my question, please let me know, it's my first one here.
I am building an app for android.
I want my users to be able to enter a time in an edittext field quickly. There usually already is a time in there, which should be selected so any input from keyboard will result in it being overwritten, while just hitting "next" (or enter on keyboard" will leave it the way it is and jump to the next field.
For all my fields this works nicely, but for some reason I cannot grasp, the text in this specific field (and its twin) won't get selected no matter what I tried.
I putandroid:selectAllOnFocus="true" in the res file, and when that didn't work I also put v.selectAll() in its onFocusChangedListener.
flighttOutStringField?.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus ->
if (hasFocus) {
with(v as TextInputEditText) {
previousText = this.text.toString()
v.selectAll()
}
} else {
with(v as TextInputEditText) {
val currentText = this.text.toString()
if (!(("([01]\\d|2[0-3]):[0-5]\\d".toRegex().containsMatchIn(currentText) && currentText.length == 5)
|| ("([01]\\d|2[0-3])[0-5]\\d".toRegex().containsMatchIn(
currentText.padStart(
4,
'0'
)
) && currentText.length <= 4))
|| (currentText == "")
)
this.setText(previousText) // not a valid new entry, so no update on flight
else {
this.setText(
if (currentText.length < 5) "${currentText.padStart(
4,
'0')
.slice(0..1)}:${currentText.padStart(4, '0').slice(2..3)}" else currentText)
flight = flight.copy (tOutString = "${date?.format(DateTimeFormatter.ISO_DATE)}T${this.text}:00", changed = 1)
}
}
}
updating the "flight" var will refill all fields through its setter.
<android.support.design.widget.TextInputLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="#+id/flighttOutStringWrapper"
app:layout_constraintStart_toEndOf="#+id/flighttOutSelector" android:layout_marginTop="4dp"
app:layout_constraintTop_toBottomOf="#+id/flightOrigWrapper"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintEnd_toStartOf="#+id/flighttInStringWrapper">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="time"
android:ems="10"
android:id="#+id/flighttOutStringField"
android:textAlignment="center"
android:includeFontPadding="false"
android:hint="#string/timeOut"
android:nextFocusForward="#id/flighttInStringField" android:imeOptions="actionNext"
android:selectAllOnFocus="true"/>
</android.support.design.widget.TextInputLayout>
I would think that upon selecting the TextInputEditText, all text would be selected, but it isn't.
Then, I thought the v.selectAll() would do that for me, but it also doesn't.
Try this
v.setOnFocusChangeListener(null)
v.clearFocus();
v.requestFocus();
v.selectAll()
v.setOnFocusChangeListener(listener)

Can't write text on forced keyboard Android Xamarin

I'm doing my own custom rich text editor in Android using Xamarin, with possibilities of adding/removing bold, underline, italic, color (etc) attributes on the selection. I was losing focus every time I clicked on a button that set one of these attributes so I found the solution with the soft keyboard.
But with the soft keyboard that opens
I can't write more text
even though I have forced the focus on the EditText. The keys pressed do nothing.
Here's the code I'm using to force the soft keyboard to show :
_myEditText.FocusChange += _MyEditText_OnFocusChanged;
and
private void _editor_FocusChange(object sender, FocusChangeEventArgs e)
{
_myEditText.RequestFocus();
InputMethodManager imm = (InputMethodManager)Context.GetSystemService(Context.InputMethodService);
imm.ToggleSoftInput(ShowFlags.Implicit, 0);
}
Is there a way to keep the keyboard open and being able to write new text in the EditText? I've tried with the ShowSoftInput() and the ShowFlags.Forced tag and even the ShowSoftInputOnFocus property on _myEditText but results are the same.
EDIT
I made a sample in Native Android with Android Studio and the keyboard works perfectly OK even without the use of the InputMethodManager ... what am I missing. Is there something in Xamarin that's not working properly ? Because in native it has the exact behaviour that I'm unable to make in xamarin.
EDIT 2
It seems related to the fact I'm using the AppCompat.ViewRenderer of Xamarin.Android and not the basic ViewRenderer. On my way to test it.
EDIT 3
After a long time testing every environment and possibilities, I've come to a conclusion that the code IS working the same way as in a native application but with a custom renderer,
ONLY if my clicks on the buttons are at least ~0.5s long.
If a do an instant click, I lose the focus of my EditText, but if I stay pressed on the button a little longer, the button action is fired, the keyboard stays open and the selection is impacted by the style changement.
Here is some basic code to reproduce the problem as easy as possible.
Just create a basic forms app. And then add the 3 following class
My forms custom view : (empty)
public class RichTextEditor : View
{
}
My android renderer :
using Android.App;
using TestFocusApplicationForms.CustomViews;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(RichTextEditor), typeof(TestFocusApplicationForms.Droid.RichTextEditorRenderer_Droid))]
namespace TestFocusApplicationForms.Droid
{
public class RichTextEditorRenderer_Droid : Xamarin.Forms.Platform.Android.ViewRenderer<RichTextEditor, Android.Views.View>
{
protected override void OnElementChanged(ElementChangedEventArgs<RichTextEditor> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
return;
}
if (Control == null)
{
Activity activity = this.Context as Activity;
var view = activity.LayoutInflater.Inflate(Resource.Layout.TestFocus_Layout, this, false);
SetNativeControl(view);
}
}
}
}
And the TestFocus_Layout.axml file corresponding :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:orientation="horizontal"
android:layout_gravity="top|end">
<Button
android:layout_gravity="end"
android:layout_width="44dp"
android:layout_height="44dp"
android:text="B"
android:id="#+id/boldButton" />
<Button
android:layout_gravity="end"
android:layout_width="44dp"
android:layout_height="44dp"
android:text="I"
android:id="#+id/italicButton" />
</LinearLayout>
<EditText
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top"
android:hint="Texte riche"
android:layout_gravity="bottom"
android:id="#+id/myEditText"
android:singleLine="false"
android:imeOptions="actionNone" />
</LinearLayout>
I've found a solution, it might not be the best in term of long term design but it works.
I made my own VisualElementRenderer for my RichTextEditor Class, and removed all unnecessary code and more important, removed code overriding the default focus behaviour of the element.
Also, it seems the fact that inflating the layout from a .axml file would cause different behaviour as creating the views programmatically, so I made a RichTextEditor_Droid class containing simple conversion of my .axml in C#. And I removed the FocusChange eventHandler on my EditText.
Here is my renderer :
public class RichTextEditorRenderer_Droid :
Xamarin.Forms.Platform.Android.VisualElementRenderer<RichTextEditor>
{
public Android.Views.View NativeView { get; private set; }
public RichTextEditorRenderer_Droid(Context context) : base(context)
{
}
public RichTextEditorRenderer_Droid()
{
}
protected override void OnElementChanged(
ElementChangedEventArgs<RichTextEditor> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
return;
}
if (NativeView == null)
{
Xamarin.Forms.Application.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
var activity = Context as Activity;
var textPad = new RichTextEditor_Droid(this.Context);
var textPadLayoutParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MatchParent,
ViewGroup.LayoutParams.MatchParent);
activity.SetContentView(textPad, textPadLayoutParams);
NativeView = textPad;
}
}
bool _disposed;
protected override void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
if (NativeView != null && ManageNativeControlLifetime)
{
RemoveView(NativeView);
NativeView.Dispose();
NativeView = null;
}
_disposed = true;
}
base.Dispose(disposing);
}
}
Now I've got another problem cause I have no more AppBar when this view is rendered on Android. But that is out of context. So thank you #YorkShen-MSFT for helping.

Categories

Resources