Android rotation animation with ScrollView - android

I am trying to animate a custom scroll view which I have resized its using LayoutParams (height & width).
Something like that:
ObjectAnimator rotation = ObjectAnimator.ofFloat(view, "rotation", rotationValue);
rotation.start();
Animation runs fine but all of the ScrollView's content is drawn within the rotation pivot bounds. Images attached.
Anyone?
Thanks.
before: http://i.stack.imgur.com/TIxZL.png
after: http://i.stack.imgur.com/EWtag.png

I suspect your problem is that you need to set the android:clipChildren attribute on the parent ViewGroup.
Quoting the ViewGroup documentation for android:clipChildren:
Defines whether a child is limited to draw inside of its bounds or not. This is useful with animations that scale the size of the children to more than 100% for instance. In such a case, this property should be set to false to allow the children to draw outside of their bounds. The default value of this property is true.
So either add android:clipChildren="false" to the XML layout, or call the ViewGroup's setClipChildren method if you are building the interface programatically.

Related

Custom View with LAYER_TYPE_SOFTWARE clips view

I've created a custom view which extends TextView. I want to use a BlurMaskFilter on an oval, however, unless I use LAYER_TYPE_SOFTWARE, the oval isn't blurred as expected. Using LAYER_TYPE_SOFTWARE will blur the view correctly, however now it is clipped to its bounds which I want to avoid as I am trying to create a colored shadow effect.
Bizarrely I only have this issue on API's 21-27 (min API is 21). On API's 28+ BlurMaskFilter works correctly with hardware acceleration and also has no issues drawing outside the view bounds.
LAYER_TYPE_HARDWARE
LAYER_TYPE_SOFTWARE
LAYER_TYPE_HARDWARE API 28+ (Desired effect)
I've tried setting clipChildren=false on all the views in the hierarchy, with no luck.
I also tried increasing the canvas.clipRect using negative values as per this post: https://stackoverflow.com/a/15506277/5752426 however this only seemed to work when using positive values to decrease the drawing area, and had no effect when using negative values to draw outside the view bounds.
I can somewhat get around the issue by increasing the view bounds in onMeasure() and then only painting inside those view bounds, however, this isn't ideal as I now have to account for this extra space when positioning my views in the layouts. Furthermore, the problem becomes inconsistent to account for as different views might have different elevations and shadow/blur sizes.
init block for the custom view:
init {
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
setWillNotDraw(false)
...
onDraw for the custom view:
override fun onDraw(canvas: Canvas?) {
...
if (elevation > 0f) {
circleShadowBackgroundPaint.maskFilter = circleBlurMaskFilter
canvas?.drawOval(circleBlurBackground, circleShadowBackgroundPaint)
}
...
Any help would be appreciated!
If you set this View's LayerType to LAYER_TYPE_SOFTWARE,the clipCHildren and clipToPadding will no longer has anly effect on this View.You should setLayerType on its Parent(or parent's parent ,if the parent is as large as the size of this View).
You can try (View (getParent())).setLayerType(View.LAYER_TYPE_SOFTWARE, null)

How To Scale A Layout While Keep Its Children Original Scale

Can I Scale A Layout(e.g LinearLayout) from 0.5 to 1.0 while Keeping the size And position of the TextView inside the layout to 1.0 During the whole transformation (the overflow parts should hidden)?
I see That The Scale Animation in IOS Keep the scale of the Children And How Can I Achieve this in Android ?
Plus,On Starting Animation, The Parent Layout of the animated block has already make space for it,But in IOS ,the parent makes room gradually during the animation, does it have an option for me to do the same thing in Android ?
This can be done pretty easily with the Property Animation API.
havent tried it on LinearLayout but if the view children are ordered correctly with their "layout_width" & "layout_height" you can use the ValueAnimator class to scale the parent view via LayoutParams.width & height.
Only setback is API 11 and above.

Android Custom View sizing

I am attempting to experiment in creating my own Views by subclassing "View." I want to simulate a new Button type class that will be drawn with the onDraw where I simply draw a rectangle from the canvas being passed in. I then add that custom View into the layout XML file, but I cannot control its size. For example, I set the width to "fill_parent" and the height to "wrap_content" but I don't know what the content of my View is. (BTW, the LinearLayout that my view is in is oriented to "vertical")
Can anyone give me some pointers in how to control the size of my View? For example, how does my custom view know what parameters were set in the XML file?
Android cannot detect the contents of your Canvas, so it assumes the contents are 0x0.
To do what you're looking for, you have to override onLayout or onMeasure (onLayout is specified by Android developers as a way to lay out children instead). You will also need a way to calculate the size of your contents.
First, start with a member variable called Point mSize = new Point(0, 0);. In a method other than onDraw, but called just as often, you will want to replace this with the size of your contents. If you draw a circle that has a radius of 25, do: mSize.x = 50; mSize.y = 50;.
Next, I'll introduce you to this answer (for onMeasure). It shows how to set the size of an item based on its parent's dimensions (divided by 2, in this case). Then, you can pull the size from mSize.x and mSize.y and apply those dimensions.
Finally, calling invalidate() after you measure your new mSize should force the View to lay itself out again, and adjust to your new parameters. This is the one part I'm unsure of in this regard.
NB: Also, you could call setLayoutParams using a fixed width and height once you calculate the size of your Canvas contents. Not sure of the performance implications for this, though.

How can I dynamically set the position of view in Android?

How can I change the position of view through code? Like changing its X, Y position. Is it possible?
For anything below Honeycomb (API Level 11) you'll have to use setLayoutParams(...).
If you can limit your support to Honeycomb and up you can use the setX(...), setY(...), setLeft(...), setTop(...), etc.
Yes, you can dynamically set the position of the view in Android. Likewise, you have an ImageView in LinearLayout of your XML file. So you can set its position through LayoutParams.But make sure to take LayoutParams according to the layout taken in your XML file. There are different LayoutParams according to the layout taken.
Here is the code to set:
LayoutParams layoutParams=new LayoutParams(int width, int height);
layoutParams.setMargins(int left, int top, int right, int bottom);
imageView.setLayoutParams(layoutParams);
There are different valid answers already, but none seems to properly suggest which method(s) to use in which case, except for the corresponding API level restrictions:
If you can wait for a layout cycle and the parent view group supports MarginLayoutParams (or a subclass), set marginLeft / marginTop accordingly.
If you need to change the position immediately and persistently (e.g. for a PopupMenu anchor), additionally call layout(l, t, r, b) with the same coordinates. This preempts what the layout system will confirm later.
For immediate (temporary) changes (such as animations), use setX() / setY() instead. In cases where the parent size doesn't depend on WRAP_CHILDREN, it might be fine to use setX() / setY() exclusively.
Never use setLeft() / setRight() / setBottom() / setTop(), see below.
Background:
The mLeft / mTop / mBottom / mRight fields get filled from the corresponding LayoutParams in layout(). Layout is called implicitly and asynchronously by the Android view layout system. Thus, setting the MarginLayoutParams seems to be the safest and cleanest way to set the position permanently. However, the asynchronous layout lag might be a problem in some cases, e.g. when using a View to render a cursor, and it's supposed to be re-positioned and serve as a PopupMenu anchor at the same time. In this case, calling layout() worked fine for me.
The problems with setLeft() and setTop() are:
Calling them alone is not sufficient -- you also need to call setRight() and setBottom() to avoid stretching or shrinking the view.
The implementation of these methods looks relatively complex (= doing some work to account for the view size changes caused by each of them)
They seem to cause strange issues with input fields: EditText soft numeric keyboard sometimes does not allow digits
setX() and setY() work outside of the layout system, and the corresponding values are treated as an additional offset to the left / top / bottom / right values determined by the layout system, shifting the view accordingly. They seem to have been added for animations (where an immediate effect without going through a layout cycle is required).
There is a library called NineOldAndroids, which allows you to use the Honeycomb animation library all the way down to version one.
This means you can define left, right, translationX/Y with a slightly different interface.
Here is how it works:
ViewHelper.setTranslationX(view, 50f);
You just use the static methods from the ViewHelper class, pass the view and which ever value you want to set it to.
I would recommend using setTranslationX and setTranslationY. I'm only just getting started on this myself, but these seem to be the safest and preferred way of moving a view. I guess it depends a lot on what exactly you're trying to do, but this is working well for me for 2D animation.
You can try to use the following methods, if you're using HoneyComb Sdk(API Level 11).
view.setX(float x);
Parameter x is the visual x position of this view.
view.setY(float y);
Parameter y is the visual y position of this view.
I hope it will be helpful to you. :)
For support to all API levels you can use it like this:
ViewPropertyAnimator.animate(view).translationYBy(-yourY).translationXBy(-yourX).setDuration(0);
Set the left position of this view relative to its parent:
view.setLeft(int leftPosition);
Set the right position of this view relative to its parent:
view.setRight(int rightPosition);
Set the top position of this view relative to its parent:
view.setTop(int topPosition);
Set the bottom position of this view relative to its parent:
view.setBottom(int bottomPositon);
The above methods are used to set the position the view related to its parent.
Use LayoutParams.
If you are using a LinearLayout you have to import android.widget.LinearLayout.LayoutParams, else import the proper version of LayoutParams for the layout you're using, or it will cause a ClassCastException, then:
LayoutParams layoutParams = new LayoutParams(int width, int height);
layoutParams.setMargins(int left, int top, int right, int bottom);
imageView.setLayoutParams(layoutParams);
NB: Note that you can use also imageView.setLeft(int dim), BUT THIS WON'T set the position of the component, it will set only the position of the left border of the component, the rest will remain at the same position.
Use RelativeLayout, place your view in it, get RelativeLayout.LayoutParams object from your view and set margins as you need. Then call requestLayout() on your view. This is the only way I know.
In Kotlin you can do it as below;
view
.animate()
.x(50f)
.y(100f)
.duration = 500L
I found that #Stefan Haustein comes very close to my experience, but not sure 100%. My suggestion is:
setLeft() / setRight() / setBottom() / setTop() won't work sometimes.
If you want to set a position temporarily (e.g for doing animation, not affected a hierachy) when the view was added and shown, just use setX()/ setY() instead. (You might want search more in difference setLeft() and setX())
And note that X, Y seem to be absolute, and it was supported by AbsoluteLayout which now is deprecated. Thus, you feel X, Y is likely not supported any more. And yes, it is, but only partly. It means if your view is added, setX(), setY() will work perfectly; otherwise, when you try to add a view into view group layout (e.g FrameLayout, LinearLayout, RelativeLayout), you must set its LayoutParams with marginLeft, marginTop instead (setX(), setY() in this case won't work sometimes).
Set position of the view by marginLeft and marginTop is an unsynchronized process. So it needs a bit time to update hierarchy. If you use the view straight away after set margin for it, you might get a wrong value.
One thing to keep in mind with positioning is that each view has an index relative to its parent view. So if you have a linear layout with three subviews, the subviews will each have an index: 0, 1, 2 in the above case.
This allows you to add a view to the last position (or the end) in a parent view by doing something like this:
int childCount = parent.getChildCount();
parentView.addView(newView, childCount);
Alternatively you could replace a view using something like the following:
int childIndex = parentView.indexOfChild(childView);
childView.setVisibility(View.GONE);
parentView.addView(newView, childIndex);

Drawing custom view using Android

I am drawing a custom view in my application which basically takes arguments(XML) as the text to display and then keeps on rotating it infinitely.
While I was making this control I had a few doubts which I want to ask:
I have made 2 of my stylable attributes which I have declared in the attrs.xml file. These attributes are to set the width and the width of the circle used in my control. These values I would use in the ondraw method and the onmeasure method. I ran my program by declaring just these but there was an error which asked me to put android:width and android:height attributes. My question is why would I need those if I am already using my custom attributes to define the height and the width of my view
I am coding on a mac. Now when I was making my attrs.xml file, to get the autocomplete list which we usually get by ctrl+space was not showing up. Why is that. For e.g., i wanted to know what values can I give the format attribute of my custom attribute that like I am demostrating in the following:
<attr name ="somename" format="value I wanted to find out through autocomplete"> . Why is the autocomplete not popping up? It does pop up when I am making a.java file.
In my ondraw() method I wanted to draw a circle. Now for this I would require the co-ordinates of the center of the circle. So what I do is put 0, 0. But this is not correct. I want the circle to be at the center of the parent view it is in. So if my view is in a hierarchy I want it to respect that. I want to give the co-ordinates relative to the parent view. What is happeining right now is that it is being drawn at the top left corner of my screen. This is what I don't want. What do I do to achieve this?
why would I need those if I am already using my custom attributes to define the height and the width of my view?
This is because Android needs to know how and where to put your view in the layout. You can implement your view to make use of your custom height/width requirements by overriding View.onMeasure() to return your own size parameters. You can then just set android:layout_width="wrap_content" android:layout_height="wrap_content" in your layout file.
Why is the autocomplete not popping up? It does pop up when I am making a.java file.
Autocomplete and autodoc implementation for Android xml files is shaky at best, so it's not really surprising that it doesn't work. I'm not aware of a fix and AFAIK it's an IDE bug.
In my ondraw() method I wanted to draw
a circle. Now for this I would require
the co-ordinates of the center of the
circle. So what I do is put 0, 0. But
this is not correct. I want the circle
to be at the center of the parent view
it is in. So if my view is in a
hierarchy I want it to respect that. I
want to give the co-ordinates relative
to the parent view. What is happeining
right now is that it is being drawn at
the top left corner of my screen. This
is what I don't want. What do I do to
achieve this?
If you implemented onMeasure() correctly, the coordinates relative to parent should be taken care of already. To get the center of your view use the following code:
void onDraw(Canvas canvas)
{
int centerX = this.getWidth() / 2;
int centerY = this.getHeight()) / 2;
//..draw code here
}
Edit
Here's an example with source code that should help: http://mindtherobot.com/blog/272/android-custom-ui-making-a-vintage-thermometer/

Categories

Resources