Why won't the AutoCompleteTextView.showDropDown() not trigger on call? - java

Problem: MyAutoCompleteTextView.**showDropDown()** will work when called by the onFocusChange but won't work when called by the onTextChanged. During my debug, the onTextChanged method gets called correctly when I want it to execute the showDropDown() according to the logging message I created, just nothing happens. I noticed there is a SpannableStringBuilder error that occurs prior, but as I recall (could be wrong) when I checked on this error in the past, this is a common error with an open ticket. I wasn't really sure it is the cause.
What I'm trying to do: ... is what everyone that asks this type of question wants to do, get the AutoCompleteTextView to show the full list anytime the AutoCompleteTextView is focused and empty (my first item is: "")
What I have tried: I've tried many "green checked" solutions on this forum, but none of them have worked at present EXCEPT when first focused. They will cause the full items list to present, but it is on the backspacing back to zero that it will not. I'm using a combination of change listeners as most suggestions have. I just really thought this would work based on the logging values and it being called at the proper time?
My logging statement: which writes when I want it to showing the method gets called when I like.
E/SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
E/OnTextChanged: showDropDown()
start: 0
before: 1
count: 0
length: 0
My current version and effort:
ArrayAdapter<String> topicsAdapter = DBQueryTools.captureDBTopics(this);
topic.setAdapter(topicsAdapter);
topic.setOnItemClickListener((parent, view, position, id) -> {
if(!topic.getText().toString().equals("")) {
question.setText("");
rListNotes.setAdapter(null);
customSearch.setText(null);
loadNotes(captureNotes(researchDatabase.getNotesDao().getNotesOnTopic(topic.getText().toString())));
}
});
topic.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(before == 1 && count == 0 && s.length()==0) {
topic.showDropDown();
Log.e("OnTextChanged", "showDropDown() " + s + "\n" + "start: " + start + "\nbefore: " + before + "\ncount: " + count + "\nlength: " + s.length());
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
topic.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
topic.showDropDown();
Log.e("HasFocus", "showDropDown");
}
}
});

I was able to get a modified version of the InstantAutoComplete view working (combining from here and here)
public class InstantAutoComplete extends AppCompatAutoCompleteTextView {
public InstantAutoComplete(Context context) {
super(context);
}
public InstantAutoComplete(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
public InstantAutoComplete(Context arg0, AttributeSet arg1, int arg2) {
super(arg0, arg1, arg2);
}
#Override
public boolean enoughToFilter() {
return true;
}
#Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (focused && getAdapter() != null) {
// This part was necessary to get it working when not
// inside a TextInputLayout and multiple per activity
if( !maybeShowSuggestions() ) {
post(this::maybeShowSuggestions);
}
}
}
private boolean maybeShowSuggestions() {
if( getWindowVisibility() == View.VISIBLE ) {
performFiltering(getText(), 0);
showDropDown();
return true;
}
else {
return false;
}
}
}
The XML for inserting it looks like (also worked without the TextInputLayout)
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/text_layout"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Type words here"
android:layout_margin="16dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/focus_stealer" >
<com.example.autocompletetextviewdemo.InstantAutoComplete
android:id="#+id/autocomplete_text_view"
android:layout_width="match_parent"
android:completionThreshold="0"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
and for setting it up with an adapter
private static final String[] WORDS = new String[] {
"Aardvark", "Apple", "Baby", "Boron", "Carbon"
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line, WORDS);
AutoCompleteTextView textView = findViewById(R.id.autocomplete_text_view);
textView.setAdapter(adapter);
}
This works with multiple InstantAutoComplete dropdowns per activity, and after navigating back and forth between activities. Without the extra changes to the custom class it didn't work with multiple instances in an activity that weren't in a TextInputLayout.

Related

Why is this code being executed when the user have not selected anything? [duplicate]

I created an Android application with a Spinner and a TextView. I want to display the selected item from the Spinner's drop down list in the TextView. I implemented the Spinner in the onCreate method so when I'm running the program, it shows a value in the TextView (before selecting an item from the drop down list).
I want to show the value in the TextView only after selecting an item from the drop down list. How do I do this?
Here is my code:
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
public class GPACal01Activity extends Activity implements OnItemSelectedListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Spinner spinner = (Spinner) findViewById(R.id.noOfSubjects);
// Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,R.array.noofsubjects_array, android.R.layout.simple_spinner_item);
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Apply the adapter to the spinner
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(this);
}
public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
TextView textView = (TextView) findViewById(R.id.textView1);
String str = (String) parent.getItemAtPosition(pos);
textView.setText(str);
}
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
}
spinner.setOnItemSelectedListener(this); // Will call onItemSelected() Listener.
So first time handle this with any Integer value
Example:
Initially Take int check = 0;
public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
if(++check > 1) {
TextView textView = (TextView) findViewById(R.id.textView1);
String str = (String) parent.getItemAtPosition(pos);
textView.setText(str);
}
}
You can do it with boolean value and also by checking current and previous positions. See here
Just put this line before setting the OnItemSelectedListener
spinner.setSelection(0,false)
This works because setSelection(int, boolean) calls setSelectionInt() internally so that when the listener is added, the item is already selected.
Beware that setSelection(int) won't work, because it calls setNextSelectedPositionInt() internally.
Beginning with API level 3 you can use onUserInteraction() on an Activity with a boolean to determine if the user is interacting with the device.
http://developer.android.com/reference/android/app/Activity.html#onUserInteraction()
#Override
public void onUserInteraction() {
super.onUserInteraction();
userIsInteracting = true;
}
As a field on the Activity I have:
private boolean userIsInteracting;
Finally, my spinner:
mSpinnerView.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View view, int position, long arg3) {
spinnerAdapter.setmPreviousSelectedIndex(position);
if (userIsInteracting) {
updateGUI();
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
As you come and go through the activity the boolean is reset to false. Works like a charm.
This worked for me
Spinner's initialization in Android is problematic sometimes
the above problem was solved by this pattern.
Spinner.setAdapter();
Spinner.setSelected(false); // must
Spinner.setSelection(0,true); //must
Spinner.setonItemSelectedListener(this);
Setting adapter should be first part and onItemSelectedListener(this) will be last when initializing a spinner. By the pattern above my OnItemSelected() is not called during initialization of spinner
haha...I have the same question.
When initViews() just do like this.The sequence is the key, listener is the last. Good Luck !
spinner.setAdapter(adapter);
spinner.setSelection(position);
spinner.setOnItemSelectedListener(listener);
To avoid calling spinner.setOnItemSelectedListener() during initialization
spinner.setSelection(Adapter.NO_SELECTION, true); //Add this line before setting listener
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
My solution:
protected boolean inhibit_spinner = true;
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int pos, long arg3) {
if (inhibit_spinner) {
inhibit_spinner = false;
}else {
if (getDataTask != null) getDataTask.cancel(true);
updateData();
}
}
You can do this by this way:
AdapterView.OnItemSelectedListener listener = new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
//set the text of TextView
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
yourSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
yourSpinner.setOnItemSelectedListener(listener);
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
At first I create a listener and attributed to a variable callback; then i create a second listener anonymous and when this is called at a first time, this change the listener
=]
The user interaction flag can then be set to true in the onTouch method and reset in onItemSelected() once the selection change has been handled. I prefer this solution because the user interaction flag is handled exclusively for the spinner, and not for other views in the activity that may affect the desired behavior.
In code:
Create your listener for the spinner:
public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {
boolean userSelect = false;
#Override
public boolean onTouch(View v, MotionEvent event) {
userSelect = true;
return false;
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (userSelect) {
userSelect = false;
// Your selection handling code here
}
}
}
Add the listener to the spinner as both an OnItemSelectedListener and an OnTouchListener:
SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
create a boolean field
private boolean inispinner;
inside oncreate of the activity
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (!inispinner) {
inispinner = true;
return;
}
//do your work here
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
Similar simple solution that enables multiple spinners is to put the AdapterView in a collection - in the Activities superclass - on first execution of onItemSelected(...) Then check to see if the AdapterView is in the collection before executing it. This enables one set of methods in the superclass and supports multiple AdapterViews and therefor multiple spinners.
Superclass ...
private Collection<AdapterView> AdapterViewCollection = new ArrayList<AdapterView>();
protected boolean firstTimeThrough(AdapterView parent) {
boolean firstTimeThrough = ! AdapterViewCollection.contains(parent);
if (firstTimeThrough) {
AdapterViewCollection.add(parent);
}
return firstTimeThrough;
}
Subclass ...
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (! firstTimeThrough(parent)) {
String value = safeString(parent.getItemAtPosition(pos).toString());
String extraMessage = EXTRA_MESSAGE;
Intent sharedPreferencesDisplayIntent = new Intent(SharedPreferencesSelectionActivity.this,SharedPreferencesDisplayActivity.class);
sharedPreferencesDisplayIntent.putExtra(extraMessage,value);
startActivity(sharedPreferencesDisplayIntent);
}
// don't execute the above code if its the first time through
// do to onItemSelected being called during view initialization.
}
Try this
spinner.postDelayed(new Runnable() {
#Override
public void run() {
addListeners();
}
}, 1000);.o
Code
spinner.setOnTouchListener(new View.OnTouchListener() {
#Override public boolean onTouch(View view, MotionEvent motionEvent) { isSpinnerTouch=true; return false; }});
holder.spinner_from.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int slot_position, long l) {
if(isSpinnerTouch)
{
Log.d("spinner_from", "spinner_from");
spinnerItemClickListener.onSpinnerItemClickListener(position, slot_position, AppConstant.FROM_SLOT_ONCLICK_CODE);
}
else {
}
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
You could achieve it by setOnTouchListener first then setOnItemSelectedListener in onTouch
#Override
public boolean onTouch(final View view, final MotionEvent event) {
view.setOnItemSelectedListener(this)
return false;
}
This worked for me:
spinner.setSelection(0, false);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
spinner.setOnItemSelectedListener(listener);
}, 500);
Based on Abhi's answer i made this simple listener
class SpinnerListener constructor(private val onItemSelected: (position: Int) -> Unit) : AdapterView.OnItemSelectedListener {
private var selectionCount = 0
override fun onNothingSelected(parent: AdapterView<*>?) {
//no op
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (selectionCount++ > 1) {
onItemSelected(position)
}
}
}
You can create custom OnItemSelectedListener like this. I've taken val check=0 and in onItemSelected() method i did check if its count is 0? If 0 means its called during initialization. So simply ignore it.
I've also called separate abstract method called onUserItemSelected() I'll call this method is check > 0. This works perfectly fine for me.
abstract class MySpinnerItemSelectionListener : AdapterView.OnItemSelectedListener {
abstract fun onUserItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long)
private var check = 0
override fun onItemSelected(
parent: AdapterView<*>?,
view: View,
position: Int,
id: Long
) {
if (++check > 1) {
onUserItemSelected(parent, view, position, id)
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
And then you can set listener like this.
mySpinner.onItemSelectedListener = object : MySpinnerItemSelectionListener() {
override fun onUserItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
//your user selection spinner code goes here
}
}
Had the same problem and this works for me:
I have 2 spinners and I update them during init and during interactions with other controls or after getting data from the server.
Here is my template:
public class MyClass extends <Activity/Fragment/Whatever> implements Spinner.OnItemSelectedListener {
private void removeSpinnersListeners() {
spn1.setOnItemSelectedListener(null);
spn2.setOnItemSelectedListener(null);
}
private void setSpinnersListeners() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
spn1.setOnItemSelectedListener(MyClass.this);
spn2.setOnItemSelectedListener(MyClass.this);
}
}, 1);
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// Your code here
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
}
When the class is initiating use setSpinnersListeners() instead of directly setting the listener.
The Runnable will prevent the spinner from firing onItemSelected right after the you set their values.
If you need to update the spinner (after a server call etc.) use removeSpinnersListeners() right before your update lines, and setSpinnersListeners() right after the update lines. This will prevent onItemSelected from firing after the update.
For me, Abhi's solution works great up to Api level 27.
But it seems that from Api level 28 and upwards, onItemSelected() is not called when listener is set, which means onItemSelected() is never called.
Therefore, I added a short if-statement to check Api level:
public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
if(Build.VERSION.SDK_INT >= 28){ //onItemSelected() doesn't seem to be called when listener is set on Api 28+
check = 1;
}
if(++check > 1) {
//Do your action here
}
}
I think that's quite weird and I'm not sure wether others also have this problem, but in my case it worked well.
I placed a TextView on top of the Spinner, same size and background as the Spinner, so that I would have more control over what it looked like before the user clicks on it. With the TextView there, I could also use the TextView to flag when the user has started interacting.
My Kotlin code looks something like this:
private var mySpinnerHasBeenTapped = false
private fun initializeMySpinner() {
my_hint_text_view.setOnClickListener {
mySpinnerHasBeenTapped = true //turn flag to true
my_spinner.performClick() //call spinner click
}
//Basic spinner setup stuff
val myList = listOf("Leonardo", "Michelangelo", "Rafael", "Donatello")
val dataAdapter: ArrayAdapter<String> = ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, myList)
my_spinner.adapter = dataAdapter
my_spinner.onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
if (mySpinnerHasBeenTapped) { //code below will only run after the user has clicked
my_hint_text_view.visibility = View.GONE //once an item has been selected, hide the textView
//Perform action here
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
//Do nothing
}
}
}
Layout file looks something like this, with the important part being that the Spinner and TextView share the same width, height, and margins:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Spinner
android:id="#+id/my_spinner"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:background="#drawable/bg_for_spinners"
android:paddingStart="8dp"
android:paddingEnd="30dp"
android:singleLine="true" />
<TextView
android:id="#+id/my_hint_text_view"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:background="#drawable/bg_for_spinners"
android:paddingStart="8dp"
android:paddingEnd="30dp"
android:singleLine="true"
android:gravity="center_vertical"
android:text="*Select A Turtle"
android:textColor="#color/green_ooze"
android:textSize="16sp" />
</FrameLayout>
I'm sure the other solutions work where you ignore the first onItemSelected call, but I really don't like the idea of assuming it will always be called.
I solved this problem like this:
In the activity lifecycle method whose name is onResume():
I added Spinner.setOnItemSelectedListener(this);
As a result, when our spinner call onclick method in the initialize, it does not work.
onResume method starts working when the finished Android page is displayed.

Understanding why onClick() is Called Even After onDispatchTouchEvent() Returns True

Lets say, in an Android app, we want to have the ability to temporarily and reliably ignore all user touches at any moment.
From the research I have done on stack-overflow as well as here, here, and here, the agreed-upon solution seems to be something like this:
(Code of MainActivity.java):
// returning true should mean touches are ignored/blocked
#Override
public boolean dispatchTouchEvent(MotionEvent pEvent) {
if (disableTouches) {
return true;
} else {
return super.dispatchTouchEvent(pEvent);
}
}
However, when we introduce the Android Monkey Exerciser Tool and send touch events to the app at a rapid rate, it becomes apparent that pigs begin to fly at the quantum level -- we can get calls to onClick() even after/during times where "blockTouches" has been set to true.
MY QUESTION IS: Why is that? -- Is this a normal Android behavior, or did I make a mistake in my code? :)
Note: I have already ruled out the possibility of onClick() being called by user input other than touches (and therefore being uncontrolled by the onDispatchTouchEvent() method)... by adding "—-pct-touch 100" to the monkey command.
Here is the code I am using for this test:
MainActivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
View rootView; // turns black when "touch rejection" is in progress
View allowedButton;
View notAllowedButton;
// Used to decide whether to process touch events.
// Set true temporarily when notAllowedButton is clicked.
boolean touchRejectionAnimationInProgress = false;
int errorCount = 0; // counting "unexpected/impossible" click calls
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootView = findViewById(R.id.rootView);
allowedButton = findViewById(R.id.allowedButton);
notAllowedButton = findViewById(R.id.notAllowedButton);
allowedButton.setOnClickListener(this);
notAllowedButton.setOnClickListener(this);
allowedButton.setBackgroundColor(Color.GREEN);
notAllowedButton.setBackgroundColor(Color.RED);
}
// returning true should mean touches are ignored/blocked
#Override
public boolean dispatchTouchEvent(MotionEvent pEvent) {
if (touchRejectionAnimationInProgress) {
Log.i("XXX", "touch rejected in dispatchTouchevent()");
return true;
} else {
return super.dispatchTouchEvent(pEvent);
}
}
#Override
public void onClick(View viewThatWasClicked){
Log.i("XXX", "onClick() called. View clicked: " + viewThatWasClicked.getTag());
//checking for unexpected/"impossible"(?) calls to this method
if (touchRejectionAnimationInProgress) {
Log.i("XXX!", "IMPOSSIBLE(?) call to onClick() detected.");
errorCount ++;
Log.i("XXX!", "Number of unexpected clicks: " + errorCount);
return;
} // else proceed...
if (viewThatWasClicked == allowedButton) {
// Irrelevant
} else if (viewThatWasClicked == notAllowedButton) {
// user did something that is not allowed.
touchRejectionAnimation();
}
}
// When the user clicks on something "illegal,"
// all user input is ignored temporarily for 200 ms.
// (arbitrary choice of duration, but smaller is better for testing)
private void touchRejectionAnimation() {
Log.i("XXX", "touchRejectionAnimation() called.");
touchRejectionAnimationInProgress = true;
rootView.setBackgroundColor(Color.BLACK);
// for logging/debugging purposes...
final String rejectionID = (new Random().nextInt() % 9999999) + "";
Log.i("XXX", "rejection : " + rejectionID + " started.");
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try { Thread.sleep(200); } catch (Exception e) {
Log.e("XXX", "exception in touchRejection() BG thread!");
}
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i("XXX", "rejection " + rejectionID + " ending");
rootView.setBackgroundColor(Color.WHITE);
touchRejectionAnimationInProgress = false;
}
});
}
});
thread.start();
}
}
layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="#+id/allowedButton"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="32dp"
android:layout_marginBottom="32dp"
android:tag="allowedButton"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/notAllowedButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="#+id/notAllowedButton"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="32dp"
android:layout_marginBottom="32dp"
android:tag="view2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/allowedButton"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
If you don't want your onClick() to be triggered on any view click.
Following are the steps which need to take care.
Create custom viewGroup eg: MyConstraintLayout and add all child
inside it.
Override onInterceptTouchEvent(MotionEvent ev) and return it has true.
public class MyConstraintLayout extends ConstraintLayout {
private boolean mIsViewsTouchable;
public ParentView(Context context) {
super(context);
}
public ParentView(Context context, AttributeSet attrs) {
super(context, attrs);
inflate(context, R.layout.custom_view, this);
}
public ParentView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setViewsTouchable(boolean isViewTouchable) {
mIsViewsTouchable = isViewTouchable;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mIsViewsTouchable;
}
}
Note: Use setViewsTouchable() method as per your requirement, If you pass the parameter as true all views are not clickable if false your views will be clickable.

Custom EditText Password Dots [duplicate]

How to use custom image instead of '*' in edittext password field?
see image:
Any answer or hint will be greatly appreciated.
The answer comes from this tutorial and it covers a behaviour when a user:
enters into the login screen, keyboard will open automatically.
tries to enter value in it then textbox background changes to textbox with star background.
tries to cancel/delete the input value by using back key on keyboard then textbox background will change to textbox without star background.
First of all you have to create two drawables:
Then, according to this approach, you have to implement addTextChangedListener method on your EditText. After that, as a parameter, you create a new instance of a TextWatcher class and you implement its methods:
etxtPin1.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
if(etxtPin1.getText().toString().trim().length()==1){
etxtPin1.clearFocus();
etxtPin2.requestFocus();
etxtPin1.setBackgroundResource(R.drawable.pin_txt_bg_star);
}
}
});
Then, you have to implement setOnKeyListener and its method onKey:
this.etxtPin1.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View paramView, int paramInt, KeyEvent paramKeyEvent) {
if ((paramKeyEvent.getAction() == KeyEvent.ACTION_DOWN)&&(paramInt == 67) && (LoginActivity.this.etxtPin2.getText().length() == 0)) {
etxtPin1.requestFocus();
etxtPin1.setBackgroundResource(R.drawable.pin_txt_bg);
etxtPin1.setText("");
}
return false;
}
});
Another approach: create you own class which extends PasswordTransformationMethod.
public class MyPasswordTransformationMethod extends PasswordTransformationMethod {
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return new PasswordCharSequence(source);
}
private class PasswordCharSequence implements CharSequence {
private CharSequence mSource;
public PasswordCharSequence(CharSequence source) {
mSource = source; // Store char sequence
}
public char charAt(int index) {
return '*'; // This is the important part
}
public int length() {
return mSource.length(); // Return default
}
public CharSequence subSequence(int start, int end) {
return mSource.subSequence(start, end); // Return default
}
}
};
Reference: In android how to show asterisk (*) in place of dots in EditText having inputtype as textPassword?
I think simple way is, you have to define 6-edittext in xml layout.
and set width and height with custom background resorces.
Like,
<?xml version="1.0" encoding="utf-8"?>
<solid android:color="#FFFFFF" />
<stroke
android:width="1dp"
android:color="#9999" />
<corners
android:bottomLeftRadius="1dp"
android:bottomRightRadius="1dp"
android:topLeftRadius="1dp"
android:topRightRadius="1dp" />
and read all edittext value.
I am not sure but it will help you.

on listview filtration is not working properly

I am facing problem while filtration of list .I am using onTextChange() method for it .It is working properly but problem is that as i type single character in edit Text the soft keyboard gets disappear and I have to tap edit text again to type complete string .I have also done code so that soft keyboard does not disappear after text change but to type still I have to tap on edit text.Here is my code :
search.addTextChangedListener(new TextWatcher() {
# Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
String startwith = s.toString();
ArrayList < Myshares > filterList = new ArrayList < Myshares > ();
for (int i = 0; i < fileList.size(); i++) {
if (fileList.get(i).getName().toLowerCase().startsWith(startwith) || fileList.get(i).getName().toUpperCase().startsWith(startwith)) {
filterList.add(fileList.get(i));
}
}
adapter = new MListAdapter(PlayListActivity.this, filterList);
playListView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
#
Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#
Override
public void afterTextChanged(Editable s) {
search.setSelection(search.getText().length());
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(search, InputMethodManager.SHOW_IMPLICIT);
}
});
here is my xml code for edit text:
<EditText
android:ems="10"
android:id="#+id/search_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:layout_weight="2"
android:background="#drawable/searchbox"
android:editable="true"
android:focusable="true"
android:inputType="textFilter"
android:hint="Search"
android:keepScreenOn="true"
android:paddingLeft="10dp"
android:textSize="15dp" />
i have also set android:focusable="false " property on list view so that it doesn't overlap soft keyboard. But the problem with edit text is still same .Here are screenshots
As I insert A Single character list gets filter and cursor is gone from edit text box.
//ArrayAdapter Definition. Make it activity class field.
ArrayAdapter<String> adapter;
//initialize the adapter by loading complete data and set Adapter to ListView.
//**Note: I have used ArrayList<String> below.**
ArrayList <String> filterList = new ArrayList <Myshares> ();
for (int i = 0; i < fileList.size(); i++) {
if (fileList.get(i).getName().toLowerCase() || fileList.get(i).getName().toUpperCase()) {
filterList.add(fileList.get(i));
}
}
adapter = new ArrayAdapter<String>(this, R.layout.searchresult_item, R.id.search_item_name,filterList);
//Here, searchresult_item is the layout file name. and search_item_name is the Textview inside it.
search.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int start, int before, int count) {
YourActivity.this.Youradapter.getFilter().filter(cs);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
I guess you have added the edit text in list view by addView() Method. It is indeed happening because all the views are redrawn, so the edit text representing whatever row used to be focused is now a completely different object. Set a variable in your adapter: int currentlyFocusedRow;
in getView for your adapter:
Add an onFocusChanged listener to edit text
and when that edit text gains focus, set
currentlyFocusedRow (int variable) = row that focused(contained) edit text happens to be in. Also set any edited text that is in the currentlyFocusedRow to be focused.
Reply me if you see some problems in this.

How to make toast when user tries to insert text longer than maximum limit?

How to make a Toast to pop up when the user tries to insert text longer than maximum limit?
<EditText
android:maxLength="28"
...
/>
I tried to use TextWatcher but it doesn't work properly:
public class MyActivity extends Activity implements TextWatcher
{
// ...
#Override
public void afterTextChanged(Editable arg0){}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3){}
#Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3)
{
if(etUsername.isFocused() && etUsername.getText().length() == 28)
Toast.makeText(MyActivity.this, "The username must be at most 28 characters!" , Toast.LENGTH_SHORT).show();
else if(etPassword.isFocused() && etPassword.getText().length() == 10)
Toast.makeText(MyActivity.this, "The password must be at most 10 characters!" , Toast.LENGTH_SHORT).show();
}
}
EDIT:
I also tried to put the code inside afterTextChanged and beforeTextChanged but it doesn't solve the problem.
EDIT2:
What I want is that the Toast only can be popped up when there are 28 characters in the EditText and the user is trying to add a 29th character. The Toast in my code above will pop up only if there are 27 characters in the TextEdit and the user inserts a 28th character.
The only way to achieve desired functionality is to change android:maxLength to desiredMaxLength + 1 and to check for the length in afterTextChanged(). You will have to manually delete the last character when length == desiredMaxLength + 1 (i.e. manipulate the s parameter in afterTextChanged(Editable s)). Please be careful not to put yourself in infinite loop because after changing s afterTextChanged() will be called again.
This solution work for my case:
#Override
public void afterTextChanged(Editable s) {
if(etUsername.isFocused() && etUsername.getText().toString().trim().length() > MAX_CHARS){
etUsername.setText(s.toString().substring(0, MAX_CHARS));
etUsername.setSelection(s.length()-1);
Toast.makeText(getActivity(), "Maximum number of characters reached.", Toast.LENGTH_SHORT).show();
}
}
Hope it helps.
Instead of:
if(etUsername.isFocused() && etUsername.getText().length() == 28)
Use this:
if(arg0.length() > 28)
{
String temp=arg0.subString(0,27)
etUsername.setText(temp);
//Toast come's here
}
Complete code is below. it is working perfectly.
String tempReview;
EditText et_userReview;
TextView tv_charactersRemaining;
Public void afterTextChanged(Editable s){
if(s.toString().length() <= 200) {
tv_charactersRemaining.setText(200 - s.toString().length()+ " characters remaining");
tempReview = s.toString();
} else {
et_userReview.setText(tempReview);
Editable etext = et_userReview.getText();
Selection.setSelection(etext, 200);
Toast.makeText(getApplicationContext(), "No more characters You have reached the limit.", Toast.LENGTH_SHORT).show();
}
}

Categories