TextInputLayout.setError() leaves empty space after clearing the error - android

I recently used TextInputLayout and it's setError() method. The problem I'm getting is, when I clear the error by calling setError(null) it leaves so much of empty space at the bottom.
Normal:
With error:
After clearing error:
After looking at the source, I found that they are making the view INVISIBLE instead of GONE
.setListener(new ViewPropertyAnimatorListenerAdapter() {
#Override
public void onAnimationEnd(View view) {
view.setVisibility(INVISIBLE); // here it is
updateLabelVisibility(true);
} }).start();
I'm wondering why is it so? How to resolve this to avoid the empty space?

Check out the docs for
public void setErrorEnabled (boolean enabled)
It says
Whether the error functionality is enabled or not in this layout.
Enabling this functionality before setting an error message via
setError(CharSequence), will mean that this layout will not change
size when an error is displayed.
Well based on this, try setting setErrorEnabled(true) before setError(), and, set setErrorEnabled(false) after setError(null).

Method setErrorEnabled(false) will clear the extra space, so call it after setError(null).

Dont use setErrorEnabled(boolean), it just doesnt show up the error from the second time.
public class MyTextInputLayout extends android.support.design.widget.TextInputLayout {
public MyTextInputLayout(Context context) {
super(context);
}
public MyTextInputLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setError(#Nullable CharSequence error) {
super.setError(error);
View layout = getChildAt(1);
if (layout != null) {
if (error != null && !"".equals(error.toString().trim())) {
layout.setVisibility(VISIBLE);
} else {
layout.setVisibility(GONE);
}
}
}
}
Then just setError(errorMessage); or setError(null);

See this page. Google will release the fix in future support library version. It says,
If you want to fix it now you can extends the TextInputLayout and
override the setErrorEnabled() method, but I cant guarantee the
backward compatibility. Because its some danger to change state in
TextInputLayout.
public class TextInputLayout extends android.support.design.widget.TextInputLayout{
public TextInputLayout(Context context) {
super(context);
}
public TextInputLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setErrorEnabled(boolean enabled) {
super.setErrorEnabled(enabled);
if (enabled) {
return;
}
if (getChildCount() > 1) {
View view = getChildAt(1);
if (view != null) {
view.setVisibility(View.GONE);
}
}
}
}

I create a custom view for avoiding repeated code and override setError method.
public class UserInputView extends TextInputLayout {
public UserInputView(Context context) {
this(context, null);
}
public UserInputView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public UserInputView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setError(#Nullable CharSequence error) {
boolean isErrorEnabled = error != null;
setErrorEnabled(isErrorEnabled);
super.setError(error);
}
}

The source code of TextInputLayout show the following:
If you need to clear the error, just use
til.setErrorEnabled(false);
This will hide the error text and stretch the bottom space to its standard size.
In case you need to set the error again, just use
til.setError("Your text");
which automatically calls til.setErrorEnabled(true) as it assumes you need the error functionality.

This is extension in kotlin solving problem:
fun TextInputLayout.clearError() {
error = null
isErrorEnabled = false
}

The following code works fine
textInputLatout.getEditText().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 (s.length() < 1) {
textInputLayout.setErrorEnabled(true);
textInputLayout.setError("Please enter a value");
}
if (s.length() > 0) {
textInputLayout.setError(null);
textInputLayout.setErrorEnabled(false);
}
}
#Override
public void afterTextChanged(Editable s) {
}
});

By using mTextInputLayout.setErrorEnabled(false); i have solved this problem

Then you should override it like so:
#Override
public void onAnimationEnd(View view)
{
view.setVisibility(GONE); // <-- this is where you make it GONE
updateLabelVisibility(true);
}
Or try this i.e. on a button or whatever you are using:
final Button btn = (Button) findViewById(R.id.btn);
btn.setVisibility(View.GONE); //<--- makes the button gone

Related

android app run onPreDraw infinitely

Step 1. Create a new app with Navigation Drawer template.
Step 2. Add a customized button and override the onMeasure method.
#CoordinatorLayout.DefaultBehavior(MyButton.Behavior.class)
public class MyButton extends android.support.v7.widget.AppCompatButton {
private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mPreDrawListener == null) {
mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
offsetTopAndBottom(50);
return true;
}
};
ViewParent p = getParent();
if (p instanceof View) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
((View)p).getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
}
}
}
}
public static class Behavior extends CoordinatorLayout.Behavior<MyButton> {
#Override
public boolean onLayoutChild(CoordinatorLayout parent, MyButton child, int layoutDirection) {
final List<View> dependencies = parent.getDependencies(child);
return super.onLayoutChild(parent, child, layoutDirection);
}
#Override
public void onAttachedToLayoutParams(#NonNull CoordinatorLayout.LayoutParams lp) {
if (lp.dodgeInsetEdges == Gravity.NO_GRAVITY) {
// If the developer hasn't set dodgeInsetEdges, lets set it to BOTTOM so that
// we dodge any Snackbars
lp.dodgeInsetEdges = Gravity.BOTTOM;
}
}
}
}
Step 3. Use MyButton in app_bar_main layout.
Step 4. Set a breakpoint in onPreDraw and then I could see it will be executed infinitely. If I comment the offsetTopAndBottom(50), everything goes fine.
I also trace the source code and find app receive vsync signal again and again which cause the onVsync function in Choreographer.java run infinitely. Why this happens?
Update
If I set a breakpoint as below and comment onPreDraw, this breakpoint finally will not be reached, otherwise, it can be reached always.
The callback onPreDraw() is called before each frame is drawn. Since you normally keep on drawing frames (~60fps), it is normal that it is called "infinitely".
To avoid this behavior, the usual pattern is to remove the listener as first statement in onPreDraw():
view.getViewTreeObserver().removeOnPreDrawListener(this);
where view is the downcasted parent in your case.
You can see example code in this video. The engineers are part of the Android Framework team.

Get caps lock state in Android

How can I get the caps lock state in Android using a hardware keyboard?
In pure Java it can be detected with
boolean isOn = Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK);
But this does not work with Android...
Try This (didn't test it):
public class CustomEditText extends EditText{
public CustomEditText(Context context) {
super(context);
}
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(event.isCapsLockOn()){
//Do what Ever
}
return super.onKeyDown(keyCode, event);
}
}
isCapsLockOn
You can use android:inputType="textCapSentences" in your editText, if you want to make the first letter, start with caps. There are other options also, if you want some different behaviour.
I don't think there is any Android API which can detect state of capslock. But i do have its alternative.
If you want to detect that whether CAPSLOCK is on or off, Its better to use TextWatcher for your EditText.
mEditText.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) {
String character = s.toString();
/*
Matche entered character with Rajex wheter its capital or small
*/
if (Pattern.matches("[a-z]",character)) {
Log.e("","CapsLock is OFF");
}
else if (Pattern.matches("[A-Z]",character)){
Log.e("", "CapsLock is ON");
}
}
#Override
public void afterTextChanged(Editable s) {
}
});

VisibilityAwareImageButton Usage

I was recently working with ImageButtons and I came across this new type of ImageButton 'VisibilityAwareImageButton'. It would be really helpful if someone could tell me the usage of this ImageButton and how is it different from the regular ImageButton? Thanks in advance :)
Here's the full source for VisibilityAwareImageButton.
class VisibilityAwareImageButton extends ImageButton {
private int mUserSetVisibility;
public VisibilityAwareImageButton(Context context) {
this(context, null);
}
public VisibilityAwareImageButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VisibilityAwareImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mUserSetVisibility = getVisibility();
}
#Override
public void setVisibility(int visibility) {
internalSetVisibility(visibility, true);
}
final void internalSetVisibility(int visibility, boolean fromUser) {
super.setVisibility(visibility);
if (fromUser) {
mUserSetVisibility = visibility;
}
}
final int getUserSetVisibility() {
return mUserSetVisibility;
}
}
It appears to be almost exactly the same as a regular ImageButton, only it keeps track of the last visibility actually set by the user. The only usage I can find is in the FloatingActionButton source. It is used to keep track of what the user wants the visibility of the view to be while it does it's own internal changes and animations. i.e.
if (child.getUserSetVisibility() != VISIBLE) {
// The view isn't set to be visible so skip changing it's visibility
return false;
}
It's in the design support library and has package visibility, so it seems like Google intends on using it internally (and seemingly only for the FAB implementation at this time).

Why View dispatchKeyEvent method not called?

My activity would show a ShowcaseView which add to decor view((ViewGroup) activity.getWindow().getDecorView()).addView(mShowcaseView);
,and i wanna detect the key event to handle something,so i override the dispatchKeyEvent() to do what i desired. but it seem like dispatchKeyEvent() method never get called,whats worse, PhoneWindow.DecorView#dispatchKeyEvent() neither get called,i don't know why,please help me.
Thanks.:)
here is my ShowcaseView briefly source code.
public class ShowcaseView extends FrameLayout {
public ShowcaseView(Context context) {
super(context);
init(context);
}
public ShowcaseView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ShowcaseView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
setWillNotDraw(false);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setFocusable(true);
setEnabled(true);
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.d("Showcase", "dispatchKeyEvent: called");
super.dispatchKeyEvent(event);
return executeKeyEvent(event);
}
private boolean executeKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP
&& event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
hide();
}
return true;
}
}
According https://developer.android.com/training/keyboard-input/commands.html:
Note: When handling keyboard events with the KeyEvent class and related APIs, you should expect that such keyboard events come only from a hardware keyboard. You should never rely on receiving key events for any key on a soft input method (an on-screen keyboard).
To dispatch key events from on-screen keyboard you can add textWatcher to editText.
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
}
});
But i cannot see input fields in your code snippet.

There is a way to start contentScrim animation sooner on CollapsingToolbarLayout?

In this example the contentScrim attribute is set with a color, but I can't figure out how to control when it starts. I woud like to start the color transition sooner.
Can you give me a hint? Thanks in advance.
You'd have to create a class that extends CollapsingToolbarLayout. Something like this (you might need to adjust that so it exactly fits your needs):
public class CustomCollapsingToolbarLayout extends CollapsingToolbarLayout {
public static interface Listener {
public void onContentScrimAnimationStarted(boolean showing);
}
private Listener mListener;
public CustomCollapsingToolbarLayout(Context context) {
super(context);
}
public CustomCollapsingToolbarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomCollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setScrimsShown(boolean shown, boolean animate) {
super.setScrimsShown(shown, animate);
if (animate && mListener != null) {
mListener.onContentScrimAnimationStarted(shown);
}
}
public void setListener(Listener listener) {
mListener = listener;
}
}
And just call setListener on your CustomCollapsingToolbarLayout instance.
CustomCollapsingToolbarLayout mToolbarLayout =
(CustomCollapsingToolbarLayout) findViewById(R.id.toolbar_layout);
mToolbarLayout.setListener(new Listener() {
#Override
public void onContentScrimAnimationStarted(boolean showing) {
//do what you want
}
});
EDIT (actually answering the question):
Modify the scrimVisibleHeightTrigger value (with the setScrimVisibleHeightTrigger method of the CollapsingToolbarLayout) to change the starting point of the animation.

Categories

Resources