I am using a TextInputLayout with the new function from the Support Library: passwordToggleEnabled. This gives a nice "eye"-icon that lets the user toggle password visibility on and off.
My question is if there is a way to use this functionality but start with password visible?
My xml:
<android.support.design.widget.TextInputLayout
android:id="#+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true">
<EditText
android:id="#+id/password_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/prompt_password"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
The toggle looks similar to this:
I have not found a way to do this in xml, and not a way to manually toggle the visibility after the view is rendered. If I set the input type of the EditText to textVisiblePassword, the toggle is not shown. If I do it in code using for instance mPasswordEditText.setTransformationMethod(null); the password is shown but the toggle is gone and the user can't hide the password again. I know I can do it all manually but just wondering if I can make it work with the new magic toggle
Easiest way is below Another solution is at last of this answer
private void setupPasswordToggleView() {
final TextInputLayout textInputLayout = mRootView.findViewById(R.id.password);
// You can skip post-call and write directly the code which is inside run method.
// But to be safe (as toggle-view is child of TextInputLayout, post call
// has been added.
textInputLayout.post(new Runnable() {
#Override
public void run() {
CheckableImageButton passwordToggleView = textInputLayout.findViewById(R.id.text_input_password_toggle);
// passwordToggleView.toggle(); // Can not use as restricted to use same library group
// passwordToggleView.setChecked(true); // Can not use as restricted to use same library group
passwordToggleView.performClick();
}
});
}
Now let me explain the answer
While looking into code of TextInputLayout.java I found that, there is a layout design_text_input_password_icon.xml which is being added to TextInputLayout.java. Below is that code
private void updatePasswordToggleView() {
if (mEditText == null) {
// If there is no EditText, there is nothing to update
return;
}
if (shouldShowPasswordIcon()) {
if (mPasswordToggleView == null) {
mPasswordToggleView = (CheckableImageButton) LayoutInflater.from(getContext())
.inflate(R.layout.design_text_input_password_icon, mInputFrame, false);
mPasswordToggleView.setImageDrawable(mPasswordToggleDrawable);
mPasswordToggleView.setContentDescription(mPasswordToggleContentDesc);
mInputFrame.addView(mPasswordToggleView); // << HERE IS THAT
.........
}
Now next target was to find design_text_input_password_icon.xml and lookup id of the toggle view. So found the layout design_text_input_password_icon.xml here and it has written as
18<android.support.design.widget.CheckableImageButton
19 xmlns:android="http://schemas.android.com/apk/res/android"
20 android:id="#+id/text_input_password_toggle"
21 android:layout_width="wrap_content"
22 android:layout_height="wrap_content"
23 android:layout_gravity="center_vertical|end|right"
24 android:background="?attr/selectableItemBackgroundBorderless"
25 android:minHeight="48dp"
26 android:minWidth="48dp"/>
I found the id text_input_password_toggle of that view and now everything was easy to just find that view in it's viewgroup and perform action on that.
Another solution would be to iterate childs of TextInputLayout and check if it is CheckableImageButton and then perform click on it. By this way there would not be dependancy on id of that view and if Android changes the id of view, our solution will still work. (Although they do not change id of a view in normal cases).
private void setupPasswordToggleViewMethod2() {
final TextInputLayout textInputLayout = mRootView.findViewById(R.id.password);
textInputLayout.post(new Runnable() {
#Override
public void run() {
View toggleView = findViewByClassReference(textInputLayout, CheckableImageButton.class);
if (toggleView != null) {
toggleView.performClick();
}
}
});
}
Where findViewByClassReference(View rootView, Class<T> clazz) original utility class is defined as below
public static <T extends View> T findViewByClassReference(View rootView, Class<T> clazz) {
if(clazz.isInstance(rootView)) {
return clazz.cast(rootView);
}
if(rootView instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) rootView;
for(int i = 0; i < viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
T match = findViewByClassReference(child, clazz);
if(match != null) {
return match;
}
}
}
return null;
}
With the Material Components Library (1.1.0 , 1.2.0-beta01, 1.3.0-alpha01) to start with a visible password just use:
<com.google.android.material.textfield.TextInputLayout
app:endIconMode="password_toggle"
/>
and in your code:
textInputLayout.getEditText().setTransformationMethod(null);
If you want to return to the default behavior:
textInputLayout.getEditText()
.setTransformationMethod(PasswordTransformationMethod.getInstance());
Just removing android:inputType="textPassword" worked for me
One of the ways is, we can search CheckableImageButton from TextInputLayout, and then programmatically perform onClick on it, based on the password visibility status of EditText.
Here's the code snippet.
private CheckableImageButton findCheckableImageButton(View view) {
if (view instanceof CheckableImageButton) {
return (CheckableImageButton)view;
}
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0, ei = viewGroup.getChildCount(); i < ei; i++) {
CheckableImageButton checkableImageButton = findCheckableImageButton(viewGroup.getChildAt(i));
if (checkableImageButton != null) {
return checkableImageButton;
}
}
}
return null;
}
//...
if (passwordEditText.getTransformationMethod() != null) {
CheckableImageButton checkableImageButton = findCheckableImageButton(passwordTextInputLayout);
if (checkableImageButton != null) {
// Make password visible.
checkableImageButton.performClick();
}
}
I was able to get it to start in clear-text mode with the following bit of code. Basically, I had to find the right View using the content description.
If they provided a setter method for mPasswordToggledVisibility that would make things a lot easier...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextInputLayout til = findViewById(R.id.password);
CharSequence cs = til.getPasswordVisibilityToggleContentDescription();
ArrayList<View> ov = new ArrayList<>();
til.findViewsWithText(ov, cs,View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
if( ov.size() == 1 ) {
Checkable c = (Checkable)ov.get(0);
// As far as I can tell the check for "isChecked" here isn't needed,
// since it always starts unchecked by default. However, if you
// wanted to check for state, you could do it this way.
if( c != null && !c.isChecked()) {
ov.get(0).performClick();
}
}
}
try this
if (inputEditText.getTransformationMethod() == null) {
inputEditText.setTransformationMethod(new PasswordTransformationMethod());
} else {
inputEditText.setTransformationMethod(null);
}
inputEditText.setSelection(inputEditText.getText().length());
You can use the bellow code:
TextInputLayout yourTextInputLayoutId = findViewById(R.id.yourTextInputLayoutId);
FrameLayout frameLayout = (FrameLayout) (yourTextInputLayoutId).getChildAt(0);
CheckableImageButton checkableImageButton = (CheckableImageButton) frameLayout.getChildAt(1);
checkableImageButton.performClick();
Here yourTextInputLayoutId is your TextInputLayout id from xml.
To start with Password visible,
Do not include
android:inputType="textPassword"
In
<com.google.android.material.textfield.TextInputEditText>
....
</com.google.android.material.textfield.TextInputEditText>
You can add in your xml file in TextInputLayout
passwordToggleEnabled="true"
passwordToggleDrawable=""#drawable/show_password_selector"
and make your show_password_selector.xml
this will look the same as the picture you sent
You can use:
yourEditText.setTransformationMethod(new PasswordTransformationMethod());
To re-show the readable password, just pass null as transformation method:
yourEditText.setTransformationMethod(null);
so user can hide it again.
Related
I'm trying to better understand the following situation that arose while refactoring some "selection highlighting" code (to take advantage of tinting).
There's a list that's populated with an adapter, CodebookAdapter, where each item's defined as:
CodebookAdapter List Item Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#FFFFFFFF">
<ImageView
android:id="#+id/item_icon_iv"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="center_vertical" />
<TextView
android:id="#+id/item_header_tv"
android:layout_width="match_parent"
android:layout_height="20dp"
android:textColor="#FF000000"
android:textSize="14dp"/>
<!--android:background="#FFFFFFFF"-->
</LinearLayout>
The method below, HiliteCodeItem(), sets the TextView, item_header_tv, to selected.
I've set the background-tint first on the list-item itself, and then just on the enclosed TextView (to avoid undesired highlighting of the entire layout):
// option 1 - item_header_tv's background can be omitted/null, highlights ok
/////////////////////////////////////////////////////////////
v.Background.SetTintList(_csl);
// option 2 - item_header_tv's background cannot be omitted/null
/////////////////////////////////////////////////////////////
tv.Background.SetTintList(_csl);
Why if in option 2 the background must be explicitly set (or else tv.Background.SetTintList(_csl); throws null ex), but in option 1 item_header_tv's background get's highlighted?
Is the enclosing list item's LinearLayout doing a null check on the background of TextView and instantiating one?
public class Codebook : LinearLayout
{
protected virtual void HiliteCodeItem(TextView codeDesc, Code code)
{
_codebookAdapter.SelectedCode = code;
//codeDesc.SetBackgroundColor(SelectedCodeListItemBgColor);
codeDesc.Selected = true;
_codebookAdapter.NotifyDataSetChanged();
}
protected class CodebookAdapter : ArrayAdapter<Code>
{
private Codebook _; // explicit outer object ref
private int _listItemRes;
private List<Code> _items;
private Android.Content.Res.ColorStateList _csl;
public Code SelectedCode { get; set; }
public CodebookAdapter(Context context, int listItemRes, List<Code> items, Codebook outer)
: base(context, listItemRes.Layout, items)
{
_ = outer;
_listItemRes = listItemRes;
_items = items;
_csl = _._context.Resources.GetColorStateList(Resource.Color.codebook_code_list_item_color_state_list);
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
TextView tv;
if (v == null)
{
v = _._inflater.Inflate(_listItemRes, parent, false);
tv = v.FindViewById<TextView>(Resource.Id.item_header_tv);
// option 1 - item_header_tv's background can be omitted/null, highlights ok
/////////////////////////////////////////////////////////////
v.Background.SetTintList(_csl);
// option 2 - item_header_tv's background cannot be omitted/null
/////////////////////////////////////////////////////////////
tv.Background.SetTintList(_csl);
}
else
tv = v.FindViewById<TextView>(Resource.Id.item_header_tv);
if (_items == null || _items.Count == 0)
{
return v;
}
Code code = _items[position];
if (code != null)
{
if (code == SelectedCode)
{
//tvCodeHeader.SetBackgroundColor(_.SelectedCodeListItemBgColor);
tvCodeHeader.Selected = true;
}
else
{
//tvCodeHeader.SetBackgroundColor(_.UnselectedCodeListItemBgColor);
tvCodeHeader.Selected = false;
}
}
}
}
}
Why if in option 2 the background must be explicitly set (or else tv.Background.SetTintList(_csl); throws null ex), but in option 1 item_header_tv's background get's highlighted?
The first works because you've set android:background="#FFFFFFFF" to the LinearLayout, the code v = _._inflater.Inflate(_listItemRes, parent, false); points to the this LinearLayout. So it's background is not omitted/null.
The Background cannot be null if you want to SetTintList, the second line doesn't work because Background of your TextView v is null.
By the way, controls like Button has Background set by default, you don't need to specify the Background property for them to use SetTintList.
I am working on android application where I am using ActionBar so there one is navigation drawer icon to open it and title of ActionBar in ActionBar. I want to set a click listener on title of ActionBar such that it start a new Activity and set click listener different on navigation drawer icon to open navigation drawer menu.
I achieved a click on navigation drawer icon but when I click on title of ActionBar title also then it open the navigation drawer menu. Is there any way to set different click listener on title of ActionBar.
Thanks in advance.
Try adding this code under the onCreate() function. This will grab the resource the action bar title is under, and assign it an id you can use to add an OnClickListener to. Let me know how it goes!
final int abTitleId = getResources().getIdentifier("action_bar_title", "id", "android");
findViewById(abTitleId).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Do something
}
});
You could use a custom layout for the title and assign a listener to it:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getActionBar();
if (actionBar != null) {
// Disable the default and enable the custom
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
View customView = getLayoutInflater().inflate(R.layout.actionbar_title, null);
// Get the textview of the title
TextView customTitle = (TextView) customView.findViewById(R.id.actionbarTitle);
// Change the font family (optional)
customTitle.setTypeface(Typeface.MONOSPACE);
// Set the on click listener for the title
customTitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.w("MainActivity", "ActionBar's title clicked.");
}
});
// Apply the custom view
actionBar.setCustomView(customView);
}
}
actionbar_title.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<TextView
android:id="#+id/actionbarTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:text="#string/app_name"/>
</LinearLayout>
I think Simas's answer is the best one, but here's a hacky version in case you prefer that.
ViewTools.findActionBarTitle(getWindow().getDecorView()).setOnClickListener(...);
This one should be universal in that it works with:
stock Android ActionBar
Theme.AppCompat support ActionBar
v21-style setActionBar
use <Toolbar android:id="#+id/action_bar"
or pass in the inflated Toolbar as root
v21-style setSupportActionBar
use <android.support.v7.widget.Toolbar android:id="#id/action_bar"
or pass in the inflated Toolbar as root
custom Toolbar implementations may need a little adjustment,
but then you could encapsulate this in that custom class.
Though I only tested with support:v22.
/** #param root usually Activity.getWindow().getDecorView() or your custom Toolbar */
public static #Nullable View findActionBarTitle(#NonNull View root) {
return findActionBarItem(root, "action_bar_title", "mTitleTextView");
}
/** #param root usually Activity.getWindow().getDecorView() or your custom Toolbar */
public static #Nullable View findActionBarSubTitle(#NonNull View root) {
return findActionBarItem(root, "action_bar_subtitle", "mSubtitleTextView");
}
private static #Nullable View findActionBarItem(#NonNull View root,
#NonNull String resourceName, #NonNull String toolbarFieldName) {
View result = findViewSupportOrAndroid(root, resourceName);
if (result == null) {
View actionBar = findViewSupportOrAndroid(root, "action_bar");
if (actionBar != null) {
result = reflectiveRead(actionBar, toolbarFieldName);
}
}
if (result == null && root.getClass().getName().endsWith("widget.Toolbar")) {
result = reflectiveRead(root, toolbarFieldName);
}
return result;
}
#SuppressWarnings("ConstantConditions")
private static #Nullable View findViewSupportOrAndroid(#NonNull View root, #NonNull String resourceName) {
Context context = root.getContext();
View result = null;
if (result == null) {
int supportID = context.getResources().getIdentifier(resourceName, "id", context.getPackageName());
result = root.findViewById(supportID);
}
if (result == null) {
int androidID = context.getResources().getIdentifier(resourceName, "id", "android");
result = root.findViewById(androidID);
}
return result;
}
#SuppressWarnings("unchecked")
public static <T> #Nullable T reflectiveRead(#NonNull Object object, #NonNull String fieldName) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (T)field.get(object);
} catch (Exception ex) {
Log.w("HACK", "Cannot read " + fieldName + " in " + object, ex);
}
return null;
}
If you are using Toolbar with support v7:21.
Check out the following code:
Field titleField = Toolbar.class.getDeclaredField("mTitleTextView");
titleField.setAccessible(true);
TextView barTitleView = (TextView) titleField.get(mToolbar);
barTitleView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
You can do this easily using Toolbar. Define toolbar in layout xml file as given below:
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="?colorPrimary"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<TextView
android:id="#+id/toolbarTitle"
style="#style/TextAppearance.Widget.AppCompat.Toolbar.Title"
android:background="?attr/selectableItemBackground"
android:layout_width="wrap_content"
android:gravity="center_vertical"
android:layout_height="match_parent" />
</android.support.v7.widget.Toolbar>
Then you can set the listener in Activity using this code:
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
TextView toolbarTitle= (TextView) findViewById(R.id.toolbarTitle);
toolbarTitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// DO SOMETHING HERE
}
});
If you want to use the currently existing ActionBar and not the Toolbar, use the following:
ActionBar actBar = getSupportActionBar();
if(actBar != null) {
actBar.setTitle(R.string.your_ab_title);
}
//Set actions to take when the AB is clicked
Toolbar ab = findViewById(R.id.action_bar);
if(ab != null){
for (int i= 0; i < ab.getChildCount(); i++){
View child = ab.getChildAt(i);
if(child instanceof TextView || child instanceof ImageView) {
child.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String url = "http://www.HoverDroids.com";
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
}
});
}
}
}
If you know the actual text that is in your Title, and you are reasonably sure that no other TextView on the screen shares that title, you can use a recursive View tree search to find it.
This is a great solution because it doesn't require reflection of internal knowledge of how to Toolbar is constructed, and gives you direct access to the TextView.
#Nullable
public static TextView findTextViewWithText(#Nullable View toCheck, String toFind) {
if (toCheck instanceof TextView) {
String foundText = ((TextView) toCheck).getText().toString();
if (foundText.equals(toFind)) {
return (TextView) toCheck;
}
} else if (toCheck instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) toCheck).getChildCount(); i++) {
TextView found = findTextViewWithText(((ViewGroup) toCheck).getChildAt(i), toFind);
if (found != null) {
return found;
}
}
}
return null;
}
The most reliable view to call this on is the decor view but feel free to experiment what works best for your purposes, your mileage may vary.
View found = findTextViewWithText(
getActivity().getWindow().getDecorView(), "My Title");
if (found != null) {
// Do something, like set a click listener
}
I know its too late, but for or those who use SupportActionBar like this and still have not found a clean solution:
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
For the default configuration without logo and custom views, 1st item (index 0) will be the Home/Back ImageView, 2nd item will be our Title TextView and 3rd item will be the OptionMenu Imageview.
Getting child at index 1 would return title. Adding an OnClickListener to the child will make it work like a chram:
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.getChildAt(1).setOnClickListener(v -> {
// title is clicked, call ur function here
// can also verify that the view is title itself by converting it to textview
try {
String title = ((TextView)v).getText().toString();
// title will be your activity title
} catch (Exception e) {
e.printStackTrace();
// if you got an exception, the view is not title.
// Check changing the index, in case you have custom views in the toolbar.
}
});
You can do this easily using Toolbar. Define toolbar in layout xml file as given below:
<android.support.v7.widget.Toolbar
android:id="#+id/MainActivityToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="#string/app_name"
android:textSize="30sp"
tools:ignore="RelativeOverlap"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
/>
<Button
android:id="#+id/LogOutButton"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:text="#string/logout" />
</RelativeLayout>
</android.support.v7.widget.Toolbar>
Then you can set the listener in Activity using this code:
setSupportActionBar((Toolbar) findViewById(R.id.MainActivityToolbar));
logOutButton = findViewById(R.id.LogOutButton);
logOutButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//define your function for logout or something else
LogOut();
}
});
I know it's very late to comment here but I came across this question when I searched for how to add OnClick for Action bar title.
Below is what I found and worked for me, hope it will help someone like me.
I wrote it for a fragment in my app.
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
actionBar.setTitle("");
((AppCompatActivity) getActivity()).setSupportActionBar((Toolbar) getActivity().findViewById(R.id.toolbar));
TextView toolbarTitle = (TextView) getActivity().findViewById(R.id.toolbarTitle);
toolbarTitle.setText("New title");
toolbarTitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Action bar title clicked
}
});
actionBar.show();
Can anyone see why this is not working..
My SearchView is in the ActionBar and is always shown. I want to know when a user PRESSES the searchview... not when it expands or gains focus.
This code sits within onCreateOptionsMenu
SearchView = _searchView;
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
_searchView = (SearchView) menu.findItem(R.id.menu_finder_text_search).getActionView();
_searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
_searchView.setIconifiedByDefault(false); // Do not iconify the widget, we want to keep it open!
_searchView.setFocusable(false);
_searchView.setClickable(true);
_searchView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//DO SOMETHING!
}
});
Anyone?
SearchView is inherited from LinearLayout, so we can setOnClickListener for each child, like this:
public static void setSearchViewOnClickListener(View v, OnClickListener listener) {
if (v instanceof ViewGroup) {
ViewGroup group = (ViewGroup)v;
int count = group.getChildCount();
for (int i = 0; i < count; i++) {
View child = group.getChildAt(i);
if (child instanceof LinearLayout || child instanceof RelativeLayout) {
setSearchViewOnClickListener(child, listener);
}
if (child instanceof TextView) {
TextView text = (TextView)child;
text.setFocusable(false);
}
child.setOnClickListener(listener);
}
}
}
from: http://www.trinea.cn/android/searchview-setonclicklistener-not-working/
Ok, it does not answer the problem it only avoids it.
I have used this link to create a listener for when the keyboard is shown. This gives me an event at the right time for me.
https://stackoverflow.com/a/7423586/1312937
Try this:
1) Bind the view
#BindView(R.id.search) SearchView search;
2) In your onCreate(), write the following code.
search.setIconifiedByDefault(false);
search.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
search.setIconified(false);
}
});
3) And your SearchView should have this following attributes.
<SearchView
android:id="#+id/search"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:background="#drawable/rounded_corners_box"
android:drawableLeft="#android:drawable/ic_menu_search"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1"
android:queryHint="Search your item.."
android:textColor="#android:color/black"
android:textColorHint="#color/colorPrimary"
app:defaultQueryHint="Select locality"/>
NOTE:
android:background="#drawable/rounded_corners_box" -- your custom border xml file.
android:drawableLeft="#android:drawable/ic_menu_search" -- search icon from drawable file.
Bind the Searchviews button to a custom ImageView and add the onClickListener there
ImageView searchButton = this.searchView.findViewById(android.support.v7.appcompat.R.id.search_button);
searchButton.setOnClickListener(v -> {
// Your code here
//This is needed since you are overwriting the default click behaviour
searchView.setIconified(false);
});
Recently stuck with this problem and found a simple solution.
searchView.setOnQueryTextFocusChangeListener(object : View.OnFocusChangeListener{
override fun onFocusChange(p0: View?, p1: Boolean) {
// Enter your code here
}
})
This method will be called when you will tap on search field and soft keyboard will appear.
Use the interface OnTouchListener: http://developer.android.com/reference/android/view/View.OnTouchListener.html
This requires a tiny bit more implementation code, but gives superior control over the UI. This solution assumes the user will be using a touch screen to interact with the View.
int search_button_id = context.getResources().getIdentifier("android:id/search_button", null, null);
ImageView search_button_view = (ImageView) mSearchView.findViewById(search_button_id);
search_button_view.setOnTouchListener((view, motionEvent) -> {
mSearchView.setIconified(false);
return true;
});
can anyone help me with coding a method to get all EditTexts in a view? I would like to implement the solution htafoya posted here:
How to hide soft keyboard on android after clicking outside EditText?
Unfortunately the getFields() method is missing and htafoya did not answer our request to share his getFields() method.
EDIT
MByD pointed me to an error, thus making my answer almost identical to that of blackbelt. I have edited mine to the correct approach.
You could do a for-each loop and then check if each view is of the type EditText:
ArrayList<EditText> myEditTextList = new ArrayList<EditText>();
for( int i = 0; i < myLayout.getChildCount(); i++ )
if( myLayout.getChildAt( i ) instanceof EditText )
myEditTextList.add( (EditText) myLayout.getChildAt( i ) );
You could also, instead of having a list of EditTexts, have a list of ID's and then just add the id of the child to the list: myIdList.add( child.getId() );
To access your layout you need to get a reference for it. This means you need to provide an ID for your layout in your XML:
<LinearLayout android:id="#+id/myLinearLayout" >
//Here is where your EditTexts would be declared
</LinearLayout>
Then when you inflate the layout in your activity you just make sure to save a reference to it:
LinearLayout myLinearLayout;
public void onCreate( Bundle savedInstanceState ) {
super( savedInstanceState );
setContentView( R.layout.myLayoutWithEditTexts );
...
myLinearLayout = (LinearLayout) findViewById( R.id.myLinearLayout );
}
You then have a reference to your the holder of your EditTexts within the activity.
Here's a method I wrote to recursively check all EditText children of a ViewGroup, handy for a long sign-up form I had to do and probably more maintainable.
private EditText traverseEditTexts(ViewGroup v)
{
EditText invalid = null;
for (int i = 0; i < v.getChildCount(); i++)
{
Object child = v.getChildAt(i);
if (child instanceof EditText)
{
EditText e = (EditText)child;
if(e.getText().length() == 0) // Whatever logic here to determine if valid.
{
return e; // Stops at first invalid one. But you could add this to a list.
}
}
else if(child instanceof ViewGroup)
{
invalid = traverseEditTexts((ViewGroup)child); // Recursive call.
if(invalid != null)
{
break;
}
}
}
return invalid;
}
private boolean validateFields()
{
EditText emptyText = traverseEditTexts(mainLayout);
if(emptyText != null)
{
Toast.makeText(this, "This field cannot be empty.", Toast.LENGTH_SHORT).show();
emptyText.requestFocus(); // Scrolls view to this field.
}
return emptyText == null;
}
You can do it by calling View#getFocusables, which will return an arraylist of all focusable views in a View.
Then you can either check if they are EditTexts, with (instanceof) or act on all of them.
This Methods walks recursively through all ViewGroups and collects their TextViews. I use this to assign a new Color to all TextViews (even those embedded in predefined Widgets like Switch etc that make use of TextViews)
private HashSet<TextView> getTextViews(ViewGroup root){
HashSet<TextView> views=new HashSet<>();
for(int i=0;i<root.getChildCount();i++){
View v=root.getChildAt(i);
if(v instanceof TextView){
views.add((TextView)v);
}else if(v instanceof ViewGroup){
views.addAll(getTextViews((ViewGroup)v));
}
}
return views;
}
Get all Edit Text in any type of layout.
public List<EditText> getAllEditTexts(ViewGroup layout){
List<EditText> views = new ArrayList<>();
for(int i =0; i< layout.getChildCount(); i++){
View v =layout.getChildAt(i);
if(v instanceof EditText){
views.add((EditText)v);
}
}
return views;
}
I have a view with radios, inputs and a button and when I click it, I want to check that all inputs contain information. How can I iterate through the view's elements in the activity and check if every textview meets the aforementioned requirement ? Thanks.
I've done something similar in some code I don't have with me at the moment, but from memory it should be something like this (assuming a parent view LinearLayout with an id of "layout"):
LinearLayout layout = (LinearLayout)findViewById(R.id.layout);
boolean success = formIsValid(layout);
public boolean formIsValid(LinearLayout layout) {
for (int i = 0; i < layout.getChildCount(); i++) {
View v = layout.getChildAt(i);
if (v instanceof EditText) {
//validate your EditText here
} else if (v instanceof RadioButton) {
//validate RadioButton
} //etc. If it fails anywhere, just return false.
}
return true;
}
To apply the method by kcoppock recursively, you can change it to this:
private void loopViews(ViewGroup view) {
for (int i = 0; i < view.getChildCount(); i++) {
View v = view.getChildAt(i);
if (v instanceof EditText) {
// Do something
} else if (v instanceof ViewGroup) {
this.loopViews((ViewGroup) v);
}
}
}
If you are writing in Kotlin, Android Jetpack's Kotlin extensions (KTX) provide extension functions for iterating over a ViewGroup's children.
myViewGroup.forEach { ... }
myViewGroup.forEachIndexed { index, view -> ... }
Just add the dependency to your app. Check the link above to get the most up-to-date version.
implementation "androidx.core:core-ktx:1.2.0"
These extensions contains hoards of useful functions otherwise chalked up as boilerplate. Worth checking out now to save time in the future!
Your onClickListener supplies the View v object; use View rV = v.getRootView() to position yourself on the form. Then use rV.findViewWithTag( ... ) or rV.findViewByID(R.id. ... ) to locate your form elements.