ConstrainLayout ConstraintSet - not working properly with Start/End constrains - android

Looks like ConstraintSet is finding hard to cope up with Start/End constrains.
This example is taken from Google samples.
Github: android-ConstraintLayoutExamples
When you replace Left & Right constrains with Start & End, ConstraintSet - not working properly, It's working with Left/Right constrains only.
For example replace
layout_constraintStart_toStartOf with layout_constraintLeft_toLeftOf & replace
layout_constraintEnd_toEndOf with layout_constraintRight_toRightOf
in following files:
constraintset_example_main.xml
constraintset_example_big.xml
Behaviour:
onClick of image:
private ConstraintSet mConstraintSetNormal = new ConstraintSet();
private ConstraintSet mConstraintSetBig = new ConstraintSet();
public void toggleMode(View v) {
TransitionManager.beginDelayedTransition(mRootLayout);
mShowBigImage = !mShowBigImage;
applyConfig();
}
private void applyConfig() {
if (mShowBigImage) {
mConstraintSetBig.applyTo(mRootLayout);
} else {
mConstraintSetNormal.applyTo(mRootLayout);
}
}
By default Android studio uses start/ end constrains hence it's I want to know root cause and possible fix.
Or Is this a bug with ConstrainSet itself?

This does look like a problem with ConstraintSet, but let's see. The following analysis is based upon the sample project with the link that you supplied.
In the sample project, I have updated ConstraintLayout to the most recent version:
compile 'com.android.support.constraint:constraint-layout:1.1.0-beta5'
I did this in case we are trying to track down an issue that has already been addressed. I also updated the layout constraintset_example_big and replaced all left/right constraints with start/end constraints as follows:
constraintset_example_big.xml
<android.support.constraint.ConstraintLayout
android:id="#+id/activity_constraintset_example"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/imageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="24dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:onClick="toggleMode"
android:scaleType="centerCrop"
android:src="#drawable/lake"
app:layout_constraintDimensionRatio="h,16:9"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="#string/lake_tahoe_image" />
<TextView
android:id="#+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/lake_tahoe_title"
android:textSize="30sp"
app:layout_constraintStart_toStartOf="#+id/imageView"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/imageView" />
<TextView
android:id="#+id/textView11"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="#string/lake_discription"
app:layout_constraintStart_toStartOf="#+id/textView9"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/textView9"
app:layout_constraintEnd_toEndOf="#+id/imageView"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="16dp"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintVertical_bias="0.0" />
</android.support.constraint.ConstraintLayout>
With these changes in place, this is what we see.
This is clearly not right. It is supposed to look like this after the transition.
After some debugging, I tracked the issue down to this line in ConstraintSetExampleActivity.java:
mConstraintSetBig.load(this, R.layout.constraintset_example_big);
ConstraintSet#load() seems to be straightforward, but if we replace the code above with an explicit inflation of the layout followed by a clone of the ConstraintSet on the inflated layout as follows:
// mConstraintSetBig.load(this, R.layout.constraintset_example_big);
ConstraintLayout cl = (ConstraintLayout) getLayoutInflater().inflate(R.layout.constraintset_example_big,null);
mConstraintSetBig.clone(cl);
We see this behavior in the app which is much better.
So my takeaway is that ConstraintSet#load() has a problem with start/end constraints. The workaround is to inflate the ConstraintLayout then do a clone.
ConstraintSetExampleActivity#onCreate()
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.constraintset_example_main);
mRootLayout = (ConstraintLayout) findViewById(R.id.activity_constraintset_example);
// Note that this can also be achieved by calling
// `mConstraintSetNormal.load(this, R.layout.constraintset_example_main);`
// Since we already have an inflated ConstraintLayout in `mRootLayout`, clone() is
// faster and considered the best practice.
mConstraintSetNormal.clone(mRootLayout);
// Load the constraints from the layout where ImageView is enlarged.
// Toggle the comment status on the following three lines to fix/break.
// mConstraintSetBig.load(this, R.layout.constraintset_example_big);
ConstraintLayout cl = (ConstraintLayout) getLayoutInflater().inflate(R.layout.constraintset_example_big,null);
mConstraintSetBig.clone(cl);
if (savedInstanceState != null) {
boolean previous = savedInstanceState.getBoolean(SHOW_BIG_IMAGE);
if (previous != mShowBigImage) {
mShowBigImage = previous;
applyConfig();
}
}
}

This issue is known and will be fixed in the 1.1 beta 6 release
https://issuetracker.google.com/issues/74253269

For those who faces issues like constraint set clone not working properly, my layout was not updating to new constraints when i called clone and applyTo methods after an api call, turns out it was due to a loading dialog i showed before the change that caused the error.

Related

Android - Detecting when a view's constraints have been fully resolved/loaded

I have quite a complex problem to do with Android views. I am creating a paint application, and I have two views: a transparent background view and the pixel art board.
For both views, I want the height and width to be calculated off of the distance between view A and B:
Instead of calculating the distance between these two views, I simply 'constraint' a view in the middle like so, and then extract its height by using its measuredHeight property (and yes, you could also calculate the distance between view A and B in the code, but my problem still remains when I try that):
Now, here's the XML code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/fragment_background_color_daynight"
tools:context=".activities.canvas.CanvasActivity">
<View
android:id="#+id/activityCanvas_topView"
android:layout_width="match_parent"
android:layout_height="90dp"
android:background="#color/fragment_background_color_daynight"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.therealbluepandabear.pixapencil.customviews.colorswitcherview.ColorSwitcherView
android:id="#+id/activityCanvas_colorSwitcherView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:isPrimarySelected="false"
app:layout_constraintBottom_toBottomOf="#+id/activityCanvas_colorPickerRecyclerView"
app:layout_constraintEnd_toEndOf="#+id/activityCanvas_topView"
app:layout_constraintTop_toTopOf="#+id/activityCanvas_colorPickerRecyclerView"
app:primaryColor="#android:color/holo_green_dark"
app:secondaryColor="#color/black" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/activityCanvas_colorPickerRecyclerView"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="#+id/activityCanvas_topView"
app:layout_constraintEnd_toStartOf="#+id/activityCanvas_colorSwitcherView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/activityCanvas_primaryFragmentHost"
tools:listitem="#layout/color_picker_layout" />
<FrameLayout
android:id="#+id/activityCanvas_distanceContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/activityCanvas_tabLayout"
app:layout_constraintEnd_toEndOf="#+id/activityCanvas_primaryFragmentHost"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/activityCanvas_topView" />
<com.google.android.material.card.MaterialCardView
android:id="#+id/fragmentOuterCanvas_canvasFragmentHostCardViewParent"
style="#style/activityCanvas_canvasFragmentHostCardViewParent_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="1dp"
app:layout_constraintBottom_toTopOf="#+id/activityCanvas_tabLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/activityCanvas_topView">
<!-- At runtime, the width and height will be calculated -->
<com.therealbluepandabear.pixapencil.customviews.transparentbackgroundview.TransparentBackgroundView
android:id="#+id/activityCanvas_transparentBackgroundView"
android:layout_width="0dp"
android:layout_height="0dp" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.tabs.TabLayout
android:id="#+id/activityCanvas_tabLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:tabStripEnabled="false"
app:layout_constraintBottom_toTopOf="#+id/activityCanvas_viewPager2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_tools_str" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_filters_str" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_color_palettes_str" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_brushes_str" />
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/activityCanvas_viewPager2"
android:layout_width="0dp"
android:layout_height="110dp"
app:layout_constraintBottom_toBottomOf="#+id/activityCanvas_primaryFragmentHost"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/activityCanvas_coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<FrameLayout
android:id="#+id/activityCanvas_primaryFragmentHost"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Of course, when it comes to calculating, I thought it would be best practise to utilize AndroidX's OneShotPreDrawListener, like so:
OneShotPreDrawListener.add(binding.root) {
binding.activityCanvasTransparentBackgroundView!!.setViewWidth(binding.activityCanvasDistanceContainer!!.measuredHeight)
binding.activityCanvasTransparentBackgroundView!!.setViewHeight(binding.activityCanvasDistanceContainer!!.measuredHeight)
}
Now, for some reason, the result looks like so:
Why is this the case!
I did some debugging, and when I log the height of view C, I get the following:
This is wrong. So, as an experiment, I added a GlobalLayoutListener to detect when exactly the view's constraints get resolved:
binding.activityCanvasDistanceContainer?.viewTreeObserver?.addOnGlobalLayoutListener( object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
Log.d("M_LOG", binding.activityCanvasDistanceContainer?.measuredHeight.toString())
}
})
Result:
So, after the first couple of times it's 438, until after the 4th/5th time it shoots up to 1000.
I am really confused why this is happening, as I want to run the event when the constraints have been fully resolved and calculated, but using OneShotPreDrawListener (or any other alternative) is just running the event when the view has been drawn, but not yet when it has been positioned properly.
I am confused what to do. How can I run an event when the view's constraints have been fully calculated?
Edit for Chetichamp:
I have debugged this and I think I can reproduce this error and tell you in what scenario it occurs and in which scenario it does not.
Basically in my app, prior to activity creation, I have a fragment called NewProjectFragment, which looks like so:
Here's the code for when the 'Done' button is pressed:
binding.fragmentNewCanvasDoneButton.setOnClickListener {
checkForTitleError()
checkForWidthError()
checkForHeightError()
if (!invalidTitle && !invalidWidth && !invalidHeight) {
try {
val title =
binding.fragmentNewCanvasProjectTitleTextInputEditText.text.toString()
val widthValue: Int =
binding.fragmentNewCanvasWidthTextInputEditText.text.toString().toInt()
val heightValue: Int =
binding.fragmentNewCanvasHeightTextInputEditText.text.toString().toInt()
if (widthValue + heightValue >= 2000 && (requireActivity() as MainActivity).showLargeCanvasSizeWarning) {
val frameLayout: FrameLayout =
this#setOnClickListeners.activity?.layoutInflater?.inflate(
R.layout.dont_show_large_canvas_warning_again_checkbox,
requireView().findViewById(android.R.id.content),
false
)
as FrameLayout
val checkBox = frameLayout.getChildAt(0) as MaterialCheckBox
requireActivity().showDialog(
getString(R.string.generic_warning_in_code_str),
getString(R.string.dialog_large_canvas_warning_text_in_code_str),
getString(R.string.dialog_large_canvas_warning_positive_button_text_in_code_str),
{ _, _ ->
if (checkBox.isChecked) {
(requireActivity() as MainActivity).showLargeCanvasSizeWarning =
false
with((requireActivity() as MainActivity).sharedPreferenceObject.edit()) {
putBoolean(
StringConstants.Identifiers.SHARED_PREFERENCE_SHOW_LARGE_CANVAS_SIZE_WARNING_IDENTIFIER,
(requireActivity() as MainActivity).showLargeCanvasSizeWarning
)
apply()
}
}
caller.onDoneButtonPressed(
title,
widthValue,
heightValue,
paramSpotLightInProgress
)
},
getString(R.string.dialog_unsaved_changes_negative_button_text_in_code_str),
{ _, _ ->
},
frameLayout
)
} else {
caller.onDoneButtonPressed(
title,
widthValue,
heightValue,
paramSpotLightInProgress
)
}
} catch (exception: Exception) {
HapticFeedbackWrapper.performHapticFeedback(binding.fragmentNewCanvasDoneButton)
}
} else {
HapticFeedbackWrapper.performHapticFeedback(binding.fragmentNewCanvasDoneButton)
}
}
As you can see, it has a listener, so the code for the listener, which is in MainActivity, is like so (maybe this is causing the issue? and I just don't need a listener like this? I don't know if you think this is why I get the issue):
fun MainActivity.extendedOnDoneButtonPressed(projectTitle: String, width: Int, height: Int, spotLightInProgress: Boolean) {
startActivity(
Intent(this, CanvasActivity::class.java)
.putExtra(StringConstants.Extras.PROJECT_TITLE_EXTRA, projectTitle)
.putExtra(StringConstants.Extras.WIDTH_EXTRA, width)
.putExtra(StringConstants.Extras.HEIGHT_EXTRA, height)
.putExtra(StringConstants.Extras.SPOTLIGHT_IN_PROGRESS_EXTRA, spotLightInProgress)
)
}
Now, what I come to the conclusion is that all of this extra work is causing a delay, because when you simply tap on a pre-existing project, we can see that the intent is much simpler:
fun MainActivity.extendedOnCreationTapped(param: PixelArt) {
startActivity(
Intent(this, CanvasActivity::class.java)
.putExtra(StringConstants.Extras.INDEX_EXTRA, pixelArtData.indexOf(param))
.putExtra(StringConstants.Extras.PROJECT_TITLE_EXTRA, param.title))
}
With a simple intent like so, the problem doesn't get reproduced, and it sizes properly.
What I relaized is that the work done in NewProject fragment is causing a delay, and when I simply scrap out the work and perfom a simple intent, the problem is 'fixed'. I don't know how to fix this but hopefully it can help with finding a solution.
Debugging even further
When I debug the issue even further, I notice something strange. The measuredHeight of the root layout jumps up by one thousand:
This is not observed when the creation is tapped with the simple intent. I am lost for words as to how strange this bug is, I've never seen this in my life.
There is something going on with your code that is touching your views that is causing the multiple layouts. Without that code being posted, it is impossible to say what.
To help you with debugging, set the one shot to the following:
OneShotPreDrawListener.add(binding.root) {
Log.d("Applog", "OneShot")
binding.activityCanvasTransparentBackgroundView.layoutParams.width =
binding.activityCanvasDistanceContainer.measuredWidth
binding.activityCanvasTransparentBackgroundView.layoutParams.height =
binding.activityCanvasDistanceContainer.measuredHeight
binding.activityCanvasTransparentBackgroundView.requestLayout()
}
In my test, when placing this in the onCreateView() function of a fragment, "OneShot" is logged just once and the size of the view is adjusted as you say it should be. (btw, requestLayout() was required in this test.)
I would take a look at any other code that touches your views to see if something else is triggering the multiple calls: maybe setWidth() or setHeight().
I will add that I have noticed in the past that ConstraintLayout will call the global layout listener multiple times. Usually, I would say, the listener is removed on the first call, so this behavior would not be noticed.
Instead of the global layout listener or the predraw listener, you could try a layout listener:
binding.root.doOnLayout {
// Your code here
}
or
binding.root.doOnNextLayout {
// Your code here
}
doOnNextLayout is guaranteed to be called only once, while doOnLayout may be called multiple times.
Wow guys.
I finally found a solution, after 12 hours of nonstop debugging, almost going clinically insane, and logging and destroying my codebase.
I realized that the keyboard from the NewProjectFragment was cutting the view, causing the bug. Holy moly... I am ecstatic that I finally found why this is happening but at the same time shocked how I didn't discover this before!
The solution was to add the following to the activity:
<activity
android:name="com.therealbluepandabear.pixapencil.activities.canvas.CanvasActivity"
android:windowSoftInputMode="stateHidden" // this
android:hardwareAccelerated="true"
android:exported="false" />
Prior to this, the windowSoftInputMode was adjustResize -- causing the bug.

ConstraintLayout intermittent layout failure

ConstraintLayout intermittently fails to layout correctly when a view is set from GONE to VISIBLE shortly after an activity is resumed:
<android.support.constraint.ConstraintLayout
android:id="#+id/constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<TextView
android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="#id/text1"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
override fun onResume() {
super.onResume()
text1.text = ""
text1.visibility = View.GONE
text2.text = ""
text2.visibility = View.GONE
text1.postDelayed({
text1.text = "Hello"
text1.visibility = View.VISIBLE
text2.text = "World"
text2.visibility = View.VISIBLE
}, 100
)
}
Full source code here
Instrumenting the TextView class reveals that the TextView instances are measured correctly but their width is set to 0 when they are laid out.
I wonder if the ConstraintLayout LinearSystem is non-deterministic. Are there maps that are iterated over where the iteration order is undefined? (I've seen this with Cassowary)
I'm looking to your statement in github page:
ConstraintLayout intermittently fails to layout correctly when a view is set from GONE to VISIBLE shortly after an activity is resumed
I've checked out your project and changed 100ms to 1000ms.
Here's the output:
It seems to me, that you expect that the moment you perform textview.setVisibility(View.GONE) you expect the view to be not visible. That's not the way android works. You are merely posting an event to the MessageQueue that would be handled later by Looper, and this 100ms is not enough for human eye to see those changes happening.
This was a bug in the ConstraintLayout fixed in constraint-layout:1.1.0-beta2 https://issuetracker.google.com/issues/65613481

How to make google.android.gms.common.SignInButton accessible via TalkBack

I've been working on making our app more accessible for user's using it with TalkBack. One issue I've ran into is the Google SignInButton announces itself as "Button" via TalkBack (not even "unlabeled").
I've tried setting its contentDescription both in xml and code like other views in the app, but it hasn't made a difference to this behavior.
Any ideas on how to get this view to announce itself as "Google Button" for instance would be much appreciated!
If it helps, this is what the view looks like in the xml:
<com.google.android.gms.common.SignInButton
android:id="#+id/google_sign_in_button"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="0dp"
android:layout_weight="1" />
Thanks!
Did you try adding a content description?
<com.google.android.gms.common.SignInButton
android:id="#+id/google_sign_in_button"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="0dp"
android:contentDescription="#string/some_localized_string"
android:layout_weight="1" />
During debugging I found that SignInButton has a single child element. Setting the contentDescription on this child element got the correct behavior from TalkBack. Here is the code:
SignInButton signInButton = (SignInButton) view.findViewById(R.id.google_sign_in_button);
signInButton.setSize(SignInButton.SIZE_ICON_ONLY);
signInButton.setScopes(mGso.getScopeArray());
signInButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
googleSignIn();
}
});
if (signInButton.getChildCount() > 0) {
View signInButtonInnerContent = signInButton.getChildAt(0);
signInButtonInnerContent.setContentDescription(getString(R.string.google_button_description));
}

TextInputLayout disable animation

I am using the TextInputLayout to implement the floating label pattern. However, when I set the text programmatically on the EditText, I still get the animation of the label moving from the control to the label - the same as if the user had clicked it.
I don't want this animation though if I set it programmatically, is this possible? Here is my code:
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/root">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/editText1" />
</android.support.design.widget.TextInputLayout>
And in the onResume I do:
TextInputLayout root = (TextInputLayout)findViewById(R.id.root);
EditText et = (EditText)root.findViewById(R.id.editText1);
et.setText("Actual text");
root.setHint("Hint");
As of v23 of the support libs, a setHintAnimationEnabled method has been added. Here are the docs. So you can set this new attribute to false in the XML and then programmatically set it to true after you've finished populating the editText. OR simply handle it all programmatically, as needed.
So in your example, that would become something like:
TextInputLayout root = (TextInputLayout)findViewById(R.id.root);
root.setHintAnimationEnabled(false);
root.setHint("Hint");
EditText et = (EditText)root.findViewById(R.id.editText1);
et.setText("Actual text");
// later...
root.setHintAnimationEnabled(true);
Of course, be sure to open your Android SDK Manager and update your Android Support Library to rev. 23 and Android Support Repository to rev. 17 first and then add that to build.gradle via:
compile 'com.android.support:design:23.0.0'
Note that as the Design library depends on the Support v4 and AppCompat Support Libraries, those will be included automatically when you add the Design library dependency.
Put this attribute app:hintEnabled="false" here:
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintEnabled="false">
It works for me
I have found an (unwieldy) way to do that. Looking into the source code of the TextInputLayout, I've discovered that the only occasion when the class doesn't update it's hint with animation is when the EditText is added to it. The only obstacle is that you can only add it once to the layout, once it is there, it will permanently be tied to it, and there's no way to remove it.
So here's the solution:
Create a TextInputLayout without the EditText. Programmatically or via XML inflation - doesn't matter, but it has to be empty.
Create the EditText and set its text to whatever you need.
Add the EditText to the TextInputLayout.
Here's an example:
TextInputLayout hintView = (TextInputLayout) findViewById(R.id.hint_view);
hintView.setHint(R.string.hint);
EditText fieldView = new EditText(hintView.getContext());
fieldView.setText(value);
hintView.addView(fieldView);
Unfortunately, if you want to set the text to something else without the animation, you will have to repeat all these steps, except for the creation of a new EditText (the last one can be reused). I hope Google can fix this, because it is really inconvenient, but for now that's what we have.
Update: this is, thankfully, fixed in the design library 23.0.0, so just update to that version, and you won't have to do all this crazy stuff.
You can disable it by using
app:hintAnimationEnabled="false"
I will give you simple code and you don't have to make all programmatically just add xlm attribute and that's all.
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintAnimationEnabled="true"
app:passwordToggleEnabled="true">
<EditText
android:id="#+id/password_field_activity_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:hint="#string/password"
android:inputType="textPassword"
android:padding="10dp"
android:textColor="#color/color_holo_light_gray"
android:textColorHint="#color/color_holo_light_gray"
android:textCursorDrawable="#drawable/cursor_drawable" />
</android.support.design.widget.TextInputLayout>
Hope it will help
I wrote a small method to run after loading the view hierarchy that disables animation on initial display but enables it after wards. Add this to your Base Activity/Fragment/View and it will solve it issue.
private void setTextInputLayoutAnimation(View view) {
if (view instanceof TextInputLayout) {
TextInputLayout til = (TextInputLayout) view;
til.setHintAnimationEnabled(false);
til.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override public boolean onPreDraw() {
til.getViewTreeObserver().removeOnPreDrawListener(this);
til.setHintAnimationEnabled(true);
return false;
}
});
return;
}
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
for (int i = 0; i < group.getChildCount(); i++) {
View child = group.getChildAt(i);
setTextInputLayoutAnimation(child);
}
}
}
I check out ideas about support library v23 - it's does not work yet :( Programmatically inserting of EditText is unusable if you wish to use databinding labrary
In result of research I found solution without full disabling of animation:
layout.xml
<android.support.design.widget.TextInputLayout
android:id="#+id/text_til"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/product_title_placeholder">
<android.support.design.widget.TextInputEditText
android:id="#+id/text_tiet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#={bindingText}"/>
</android.support.design.widget.TextInputLayout>
Activity.java
final TextInputLayout textLayout = (TextInputLayout) findViewById(R.id.text_til);
if(binding.getText() != null) {
// disable animation
textLayout.setHintAnimationEnabled(false);
final TextInputEditText editText = (TextInputEditText) titleLayout.findViewById(R.id.text_tiet);
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean b) {
// enable animation after layout inflated
textLayout.setHintAnimationEnabled(true);
editText.setOnFocusChangeListener(null);
}
});
}
// manage focus for new binding object
((View)textLayout .getParent()).setFocusableInTouchMode(binding.getText() == null);
It's looks not prettily, but it works :)

The layout rules align_parent_end property does not show as true

At a point in my app I'm cycling through the objects on screen and checking if they're the last object in that line. To do that, I'm checking the Align Parent End property (which is checked for the last widget in each line). Here's part of my activity xml:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/relativeLayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Average Temperature:"
android:id="#+id/AverageTemperatureText"
android:layout_below="#+id/TemperaturesText"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="10dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:id="#+id/AvgTempNum"
android:layout_alignBottom="#+id/AverageTemperatureText"
android:layout_toRightOf="#+id/AverageTemperatureText"
android:layout_below="#+id/TempMiddle"
android:gravity="center|bottom"
android:editable="false"
android:text="0.0"
android:maxLength="7"
android:nextFocusForward="#+id/TotalObservedVolumer"
android:layout_alignRight="#+id/FreeWaterVolume"
android:layout_alignEnd="#+id/FreeWaterVolume"
android:layout_alignParentEnd="true" />
^^^^ Right here. It's true.
You get the idea. Then, in my code, I'm cycling through the relative layout children, then for each view on it, checking it for that particular property. If it's true, I'm supposed to do something. But with the code I have, it's always false. So what am I doing wrong? Here it is:
RelativeLayout relLayout = (RelativeLayout) findViewById(R.id.relativeLayout);
for(int i = 0; i < relLayout.getChildCount(); i++) {
View view = relLayout.getChildAt(i);
// Assess if it's the last field in that line
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
int[] rules = params.getRules();
if (rules[RelativeLayout.ALIGN_PARENT_END] == RelativeLayout.TRUE) {
// my code
}
}
Thanks!!
Fixed, but in a curious way.
In my case, all the last fields on screen had Align Parent End flagged. So I found online this would be the way to do it:
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
int[] rules = params.getRules();
if ((rules[RelativeLayout.ALIGN_PARENT_END] == RelativeLayout.TRUE)) {
// Do something about it
}
Where RelativeLayout.ALIGN_PARENT_END stands for 21, according to http://developer.android.com/reference/android/widget/RelativeLayout.html
However, my code was returning false for that check. So I started displaying the value for the property, and it really was false. I tried this for a while, then I saw one of my edittexts returned true for it. But the activity xml showed this
android:layout_alignParentRight="true"
in addition to alignParentEnd. Which didn't make sense, since alignParentRight is supposed to be rule 11, according to the same link above.
So I devised this code to expose which rules where true, and put it in my code:
for(int x = 0; x < 22; x++) {
if (rules[x] == RelativeLayout.TRUE) Log.i("Property true:", String.valueOf(x));
}
Which gave me 11 for all fields, and 11 and 21 for the field that responded TRUE before. Remember, all fields had android:layout_alignParentEnd="true" in the activity xml.
So to fix my code, I'm now checking rules[RelativeLayout.ALIGN_PARENT_RIGHT] and it works, but I strongly believe those two properties are switched somehow. Can anybody confirm? Or please englighten me on where I screwed up, which is more likely :)

Categories

Resources