I want to create the following concept:
First expand the view to its parent's width (it's not the same).
Then expand the view to its parent's height.
I know I can create the sequence with AnimatorSet(). What I cannot find is what property/-ies to animate to create the result.
My thoughts for the width animation:
I would probably need to animate either two points which define left and right or alternatively animate the translation and the width.
I'm a bit confused, which properties would do the trick?
You'll need to define custom properties to do this (I know, silly). Height example:
public static final Property<View, Integer> PROPERTY_HEIGHT =
new Property<View, Integer>(Integer.class, "viewLayoutHeight") {
#Override
public void set(View object, Integer value) {
object.getLayoutParams().height = value.intValue();
object.requestLayout();
}
#Override
public Integer get(View object) {
return object.getLayoutParams().height;
}
};
Then use as a normal property:
ObjectAnimator.ofInt(view, PROPERTY_HEIGHT, minHeight, maxHeight);
You'll need to get the parent dimensions at runtime (perhaps using a ViewTreeObserver.OnGlobalLayoutListener).
I think that there is no need to complicate things, you can use width property first then followed by height in AnimatorSet, Your view gravity property will define in which direction view will expand ...
Related
I have ScrollView layout (or other layout type it doesn't mater) which has child views. And this parent layout has paddingLeft and paddingRight. I want to have this padding set for each child, but sometimes I have exception where I want that child to reach edges of the display completely (for example TextView with background color). Is there any way how to allow this to happen? I don't wanna set padding for every single child separately.
You told us what you want: "sometimes a Child shouldn't have to respect its Parent padding", so the special behaviour should occurs on Children.
The solution is to EXTEND Child's main View in which it chooses its MARGIN by default and then create a "Child.setNoMargins()" method that removes them from itself when required.
public static class ExtendedTextView extends TextView {
private boolean mHasNoMargins = false;
public ExtendedTextView(Context context) {
super(context);
}
public void setNoMargins() {
mHasNoMargins = true;
if (!isInLayout() && isAttachedToWindow()) requestLayout();
}
#Override
public void setLayoutParams(ViewGroup.LayoutParams params) {
if (!mHasNoMargins && (params instanceof ViewGroup.MarginLayoutParams)) {
((ViewGroup.MarginLayoutParams)params).setMarginStart(20);
((ViewGroup.MarginLayoutParams)params).setMarginEnd(20);
}
super.setLayoutParams(params);
}
}
In this way all created "ExtendedTextView" have default Left/Right margins until you call "setNoMargins()".
Obliviously my code works only if parent ViewGroup supports LayoutParams having Margins (most of them).
I have some views that need some margins set programmatically (from an applyWindowInsets listener), but the views seem to be ignoring any margins I set with my code, even though I am not animating the margins.
I'm able to set padding just fine, but I cannot accomplish what I need using only padding.
The issue seems to be related to MotionLayout since it works fine if it is a ConstraintLayout.
I've been using this util method.
public static void addTopMargin(View v, int margin) {
((ViewGroup.MarginLayoutParams) v.getLayoutParams()).topMargin += margin;
}
The issue you're having is that MotionLayout derives its margins from the assigned ConstraintSets, so changing the base margin isn't actually doing anything. To get this to work, you need to target one or both of the ConstraintSets that define the MotionScene:
val motionLayout = findViewById(R.id.motionLayoutId)
motionLayout.getConstraintSet(R.id.startingConstraintSet)?
.setMargin(targetView.id, anchorId, value)
You could also do this for more than one view with a let:
val motionLayout = findViewById(R.id.motionLayoutId)
motionLayout.getConstraintSet(R.id.startingConstraintSet)?.let {
setMargin(firstView.id, anchorId, value)
setMargin(secondView.id, anchorId, value)
}
For the top margin, use ConstraintSet.TOP, etc.
Remember that if you're not wanting to animate that margin, you'll have to assign to both the start and end ConstraintSet.
Just adding a simple note to UnOrthodox solution that in case of you don't need to animate that margin you will need to keep the base margin with adding the ConstraintSets margin:
public static void addTopMargin(View v, int margin) {
((ViewGroup.MarginLayoutParams) v.getLayoutParams()).topMargin += margin;
MotionLayout root = findViewById(R.id.motion_layout);
root.getConstraintSet(R.id.start).setMargin(firstView.id, anchorId, margin);
root.getConstraintSet(R.id.end).setMargin(firstView.id, anchorId, margin);
}
onBindViewHolder
Is a nice method but there is one problem - The View has not necessarily been measured yet. So where can I adjust things like amount of content in TextView's etc if I cannot get the actual measurements on the View? I want to change dynamically change the length of Strings rendered in the Item view if the ItemView is a certain width in comparison to the string length. I have measured the CharSet length etc. No problem, but how do I know if it is too long if I cannot measure the width of the View? with the items played out. The String can also be between two items etc. So I need to at least know where I can access this kind of information. Thanks.
You can add a listener to listen for changes in the view tree and get the view's width and height after it has finished measurement.
final ViewTreeObserver obs = mTextView.getViewTreeObserver();
obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw () {
int height = mTextView.getHeight();
int width = mTextView.getWidth();
// Return true to proceed with the current drawing pass, or false to cancel.
return true;
}
});
I have a statically defined relative layout with height set to wrap_content.
I add several children to it dynamically. This appears just fine.
I now have to add a few more children and line them up above previously added children. This sort of works. (Note: I can only do this once those previously added children have actually been added, as my new children depend on their width.)
I can only see the changes if I change layout height to say 50dp vs wrap_content.
I tried calling invalidate(), postInvalidate() and requestLayout() on the holding layout, but that didn't work. What am I not doing?
public void layOutExtras(CustomView section) {
if (section.isUnderlined()) {
int labelOrientation = section.getLabelOrientation();
View line = createLine(labelOrientation, section.getMeasuredWidth(), section.getId());
addView(line);
}
}
private View createLine(int orientation, int width, int viewId) {
RelativeLayout.LayoutParams params = new LayoutParams(width, 4);
params.addRule(ABOVE, viewId);
params.addRule(ALIGN_RIGHT, viewId);
View line = new ImageView(context);
line.setBackgroundColor(Color.RED);
line.setLayoutParams(params);
return line;
}
Sounds like the issue I had a few days ago. The issue is probably due to the height not getting set right. Try setting the minimum height of both the new view and the container you're modifying.
I need to position a TextView the way its baseline is 20dp from the bottom of the container.
How can I achieve this?
The layout with bottom margin or padding produces the same result.
I would like to make the text 'sit' on the purple line.
When I write 'sit' I mean, the 'wert' should touch the line, not 'q...y'.
The padding / margin is equal to the purple square size:
If you still need it, I wrote custom method, to not create lots of custom views. It works for me with TextView:
public static void applyExistingBotMarginFromBaseline(View view) {
final int baseline = view.getBaseline();
final int height = view.getHeight();
final ViewGroup.MarginLayoutParams marginLayoutParams;
try {
marginLayoutParams = ((ViewGroup.MarginLayoutParams) view.getLayoutParams());
} catch (ClassCastException e) {
throw new IllegalArgumentException("Applying margins on a view with wrong layout params.");
}
final int baselineMarginValue = baseline + marginLayoutParams.bottomMargin;
marginLayoutParams.bottomMargin = baselineMarginValue - height;
view.setLayoutParams(marginLayoutParams);
}
You can apply it when view is measured already, so like this:
final TextView title = (TextView) findViewById(R.id.title);
title.post(new Runnable() {
#Override public void run() {
Utils.applyExistingBotMarginFromBaseline(title);
}
});
Also you can use databinding framework and write your own custom BindingAdapter with a bit customized method, to use it from xml.
Your problem is not the padding/margin referenced to the parent, I think is about your font, I recommend you to change the fontFamily:"yourStyle"
even worst you have to re-difine your own font style which is explained here Custom fonts and XML layouts (Android) or Set specific font in a styles.xml