How to make a group of views editable - android

I have a layout in which there are spinners, editText and checkboxes. There two modes:
1- edit all views (edit mode)
2- view (non edit mode)
But I don't want to do it for each view . Is there any way to set editable true or false?

This method will enable/disable all widgets of your parent layout.
public void enableAllView(ViewGroup rootView, boolean state) {
for (int i = 0; i < rootView.getChildCount(); i++) {
View childAt = rootView.getChildAt(i);
if (childAt instanceof ViewGroup ) {
enableAllView((ViewGroup) childAt, state);
} else {
if (childAt instanceof EditText) {
EditText child = (EditText) childAt;
child.setEnabled(state);
child.setFocusable(state);
} else if (childAt instanceof Spinner) {
Spinner child = (Spinner) childAt;
child.setEnabled(state);
child.setFocusable(state);
} else if (childAt instanceof CheckBox) {
CheckBox child = (CheckBox) childAt;
child.setEnabled(state);
child.setFocusable(state);
}
}
}
}
call this method like this--
enableAllView(rootView, true); // in case of edit(enable)
enableAllView(rootView, false); // in case of view(disable)
//rootView is a view in which your spinners/editText/checkbox are availabe.

in edit mode use this in every view
yourView.setEnabled(true);
in read mode use this
yourView.setEnabled(false);

The simple way is to create a set of such views manually:
val editableViews: Set<View> = setOf(v1, v2, v3)
and use it:
editableViews.forEach { it.enabled = isEditMode }
If you have a complex layout you may add dynamic initialization:
private fun getAllViews(
view: View,
set: MutableSet<View>,
filter: (view: View) -> Boolean = {true}
){
val viewGroup = view as? ViewGroup
if (viewGroup != null) {
for (i: Int in 0 until viewGroup.childCount) {
val child = viewGroup.getChildAt(i)
getAllViews(child, set)
}
} else {
if (filter()) {
set.add(view)
}
}
}
initialize it in onViewCreated or in onCreate
val views = mutableSetOf<View>()
getAllViews(root, views) {
it is Spinner || it is EditText || it is Checkbox
}
editableViews = views
It collects all required views, so you may make them enabled or disabled. But you should note, that this variant is not so flexible and you should prefer just the first one. (In case any exception you have to exclude some)

Related

ListView adds duplicates of itself when changing the mode to light/dark

I have a problem with my android app for making notes. When I change mode (in app) from default to dark or light, the list duplicate itself. I have a custom adapter for ListView:
public View getView(int position, View listView, ViewGroup parent) {
View v = listView;
TextView tvTitle;
TextView tvContent;
Note n = noteList.get(position);
Set<String> folders = n.getFolders();
Set<String> foldersForChips = new HashSet<>(folders);
foldersForChips.remove("Notes");
foldersForChips.remove("All Notes");
NoteHolder holder = new NoteHolder();
if (listView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.row_layout, null);
// view of the labels of the note ------------------------------------------------------
holder.foldersChipGroup = (ChipGroup) v.findViewById(R.id.labels_of_note);
holder.foldersChipGroup.setChipSpacingHorizontal(2);
holder.foldersChipGroup.setClickable(false);
holder.foldersChipGroup.setFocusable(false);
for (String folder : foldersForChips) {
Chip folderChip = new Chip(context);
folderChip.setText(folder);
folderChip.setTextSize(8);
folderChip.setEnsureMinTouchTargetSize(false);
folderChip.setHeight(40);
folderChip.setChipMinHeight(10);
folderChip.setBackgroundColor(Color.TRANSPARENT);
folderChip.setChipBackgroundColor(null);
ChipDrawable chipFolderDrawable = ChipDrawable.createFromAttributes(context, null,0, R.style.Widget_App_Chip);
folderChip.setChipDrawable(chipFolderDrawable);
holder.foldersChipGroup.addView(folderChip);
}
tvTitle = (TextView) v.findViewById(R.id.title);
tvContent = (TextView) v.findViewById(R.id.content);
holder.titleView = tvTitle;
holder.contentView = tvContent;
v.setTag(holder);
} else {
holder = (NoteHolder) v.getTag();
}
// view of the title and content of the note -----------------------------------------------
if (n.getTitle() == "" || n.getTitle().isEmpty() || n.getTitle() == null ) {
holder.contentView.setVisibility(View.VISIBLE);
holder.contentView.setText(n.getContent());
holder.contentView.setPadding(20,0,20, 0);
} else if (n.getContent() == "" || n.getContent().isEmpty() || n.getContent() == null){
holder.titleView.setVisibility(View.VISIBLE);
holder.titleView.setText(n.getTitle());
holder.titleView.setPadding(20,0,20, 0);
} else {
holder.titleView.setVisibility(View.VISIBLE);
holder.titleView.setText(n.getTitle());
holder.titleView.setPadding(20,0,20, 0);
holder.contentView.setVisibility(View.VISIBLE);
holder.contentView.setText(n.getContent());
holder.contentView.setPadding(20,0,20, 0);
}
// background color ------------------------------------------------------------------------
holder.cardView = v.findViewById(R.id.cardView);
if (getItem(position).getBackgroundColor() != null) {
holder.cardView.setCardBackgroundColor(Color.parseColor(getItem(position).getBackgroundColor()));
}
return v;
}
the button which change the mode in MainActivity.java:
FloatingActionButton fabLight = findViewById(R.id.light);
fabLight.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
themeChange.setVisibility(View.VISIBLE);
SharedPreferences settingsPreferences = getApplicationContext().getSharedPreferences("com.example.settings", Context.MODE_PRIVATE);
settingsPreferences.edit().putInt("Mode", AppCompatDelegate.MODE_NIGHT_NO).apply();
listView.refreshDrawableState();
noteAdapter.notifyDataSetChanged();
//listView.setAdapter(noteAdapter);
}
});
I found similar problem: ListView Added Duplicate item in list when screen orientation changes, but unfortunaltely the answers does not help me - I do not know how to implement them. I am new with android and I will be grateful for some guidance.
I tried refreshDrawableState() and setAdapter(noteAdapter) on my listView and notifyDataSetChanged() on adapter but it does not help. It is probable that I implement getView or my list in it wrong but I do not know how to improve that.
Thank you in advance for all help.
In case - all files are in my GitHub repository:
https://github.com/wmaterkowska/MyNotes_app.git
(My polish answer has been deleted so changed it to English)
Method onCreate() is evoked when activity is launched first time, but also after screen configuration changes (and in some other situations) - for instance orientation or as in your case theme. There is no guarantee that this method will be called only single time, so if you create a list in here you should clear it to avoid duplication of the content every time method is called.
notesToShow.clear(); //Add a line here
for (Note note : allNotes) {
if (note.getFolders().contains(folder) && !note.getFolders().contains("Recycle Bin")) {
notesToShow.add(note);
}
}
Hope it helps!

Visible password with TextInputLayouts passwordToggleEnabled

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.

How to identify if a view is Button or Textview

I have a error function that takes View as an input, and shows error by changing its background to red while there is any problem with view's value as follows -
public static void error(View v) {
v.setBackgroundResource(android.R.color.holo_red_light);
}
I am using this function in another function where I am using it this way -
public static void nonEmptyNonZero(View v) {
String check = "";
check = ((TextView) v).getText().toString();
if ((check.equals(null)) || check.equals("0") || check.equals("")) {
error(v);
}
}
my issue is, that v could be anything (TextView, EditText). So how to identify what is the type of the view. So that I could use proper casting while getting its value using -
check = ((TextView) v).getText().toString();
PS - I have used instanceOf already, I am not sure how casting of a View works.
Like your Views in Object List
List<Object> objectViews=new ArrayList();
views
EditText txtEditText=new EditText();
TextView txtTextView=new TextView();
objectViews.add(txtEditText);
objectViews.add(txtTextView);
traverse to find object (view)
Object view=new Object();
for(int i=0;i<objectViews.size;i++)
{
view.get(i);
if(view instanceof EditText)
{
Log.e("EditText","value="((EditText) view).getText());
}
else if(view instanceof TextView)
{
Log.e("TextView","value="((TextView) view).getText());
}
}

Method to get all EditTexts in a View

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;
}

How to iterate through a view's elements

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.

Categories

Resources