I want to move and resize a TextView from the center of the screen to the upper left corner with the default 16dp margin. I used the code:
val pos = convertDpToPixel(16.0f, context)
testNameTextView
.animate()
.scaleX(0.5f)
.scaleY(0.5f)
.x(pos)
.y(pos)
It does not work because the final margin depends on the length of the text.
If I do not resize the view everything works fine.
I have also tried to do this:
testNameTextView
.animate()
.scaleX(0.5f)
.scaleY(0.5f)
.withEndAction {
testNameTextView
.animate()
.x(pos)
.y(pos)
}
but it did not make any difference. How can I resize and move the text?
I used:
testNameTextView
.animate()
.scaleX(SCALE)
.scaleY(SCALE)
.x(posTestName - (testNameTextView.width - testNameTextView.width * SCALE) / 2 )
.y(posTestName - (testNameTextView.height - testNameTextView.height * SCALE) / 2 )
So what happens is that the text is repositioned and then scaled keeping the center of the text fixed, so if you just do what I had done the left margin is incorrect because the text was shrunk.
You need to subtract the difference between half the width of the text before the scale and half the width of the text after the scaling.
PS
During the animation the translation and the resizing are done at the same time but the effect is the same as if the text was first translated and then scaled keeping the center of the text fixed
Related
I have an image 3264x1836 and I display it in a custom AppCompatImageView having dimensions 1280x720. I want to preserve the image size so I use ScaleType.CENTER, but I also want that its top-left corner is positioned at the coordinates 0,0 of my custom view, so I set the padding left and top accordingly.
Now, to achieve that I had to use 3264-1280 (the difference between the widths) as left padding and 1836-720 (the difference between the heights) as top padding while, in my opinion, these values should be both divided by 2.
Can somebody explain why?
It is likely that you are breaking the AppCompatImageView code by asking it to do something that wasn't anticipated or tested.
While the above could still be true, the padding that you are applying is correct. Here are the calculations:
For simplicity, let's take a look at the left padding needed to shift the graphic. The same sort of calculations will work for the top padding.
The width of the graphic is greater than the width of the ImageView.
d = wgraphic - wimageview
If the graphic is centered in the ImageView, then the overhang on the left and right sides will be 1/2 the difference in widths.
s = d / 2
The graphic is centered within the padding of the ImageView. The amount of padding that has to be specified must be just enough to shift the center of the graphic by the amount s.
The initial center of the ImageView without padding is
ci = wimageview / 2
The shifted center with left padding ( p ) is
cs = p + (wimageview - p) / 2
So,
s = cs - ci = p + (wimageview - p) / 2 - (wimageview / 2)
Solving for the padding needed for a shift of s we get p = 2s = d. In other words, the padding we need is twice the shift required which is what you are seeing.
A fix that doesn't involve padding would be to specify
android:scaleType="matrix"
You should remove the padding. The new scale type will apply the identity matrix to the image and place it in the top/left corner without resizing.
I have a view that I want to animate- scale it to a bigger size and at the same time move to the very top of the screen, just below the notification bar. The problem is however, at least I think, that the translationY complies with initial view scale and moves it to the top of the screen, but as the view grows, it gets cut off at top.
This is the code I'm using
image.animate()
.alpha(1f)
.translationY(0)
.scaleX(6f)
.scaleY(6f)
.start()
Any simple solution to this? A way I could calculate the real value I should set translationY to (based on scale) in order to be positioned correctly? Any help appreciated!
Android Studio 2.0 beta 6
I am trying to use ViewPropertyAnimator to move a ImageView (ivSettings) inside a toolbar so that it is 20dp from the right and 20dp from the top, from is current location. And move the ImageView (ivSearch) 20dp from the left and top.
The imageViews are contained in a Toolbar.
This is the initial state and I want to move the icons into the upper corners inside the toolbar.
The code I am using is this to get the width and then subtract a value to get the ivSettings to be 20dp from the right.
final DisplayMetrics displayMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
final float widthPx = displayMetrics.widthPixels;
ivSearch.animate()
.setInterpolator(new AccelerateInterpolator())
.x(20)
.y(20)
.setDuration(250)
.start();
ivSettings.animate()
.setInterpolator(new AccelerateInterpolator())
.x(widthPx - 160)
.y(20)
.setDuration(250)
.start();
However, having tried this on different screen size I can't get the exact width calculation. Is there a better way of doing this?
Many thanks for any suggestions
You should be able to use the translationX and translationY properties to achieve the effect you want. These properties act as offsets from the original X and Y coordinates of the View.
Essentially translationX will displace the a View towards the right for a positive value and left for a negative value from it's X coordinate. Similarly translationY displaces the View towards the bottom for positive and top for negative values from it's Y coordinate.
Coming to your question, I am assuming that the final position you want to reach is the top left corner for the search icon, and the top right corner for the settings icon with zero padding for each view.
For the search icon I suggest you start by placing it in the top left corner of the toolbar. Then set both the translationX and translationY to 20p. That should place the search icon in the same place as your image. To move your search icon to the top left all you have to do is animate translationX & Y from 20dp to 0 dp.
Repeat the same for the settings icon, but set translationX to -20dp and translationY to 20dp. This way it'll be placed at the same position as your image. Animate both values to 0 to achieve your desired animation.
Here's the animation code for reference.
ivSearch.animate()
.setInterpolator(new AccelerateInterpolator())
.translationX(0) // Takes value in pixels. From 20dp to 0dp
.translationY(0) // Takes value in pixels. From 20dp to 0dp
.setDuration(250)
.start();
ivSettings.animate()
.setInterpolator(new AccelerateInterpolator())
.translationX(0) // Takes value in pixels. From -20dp to 0dp
.translationY(0) // Takes value in pixels. From 20dp to 0dp
.setDuration(250)
.start();
// To get pixels from dp
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
The great thing about this approach is that you no longer need to know the dimensions of the parent. All you care about is the offsets that you have specified via translationX and translationY.
I believe the problem is that the Animator methods are expecting the values to be in raw pixel values. It looks like the code would only work as you intend on a medium density screen. E.g. A hdpi screen would require 30px instead of 20px to be located at the same location. On higher density screens the code as it exists would push the icons closer to the edges...
That means we need to translate the expected dp value into raw pixel values and use those for animation. There is a handy method for converting dp to px so that it's correct on every device. I'm assuming that you want the left side of the gear to be 160dp from the right(Note I'm specifically saying the left side of the gear because the 160dp value should also include the width of the gear image as the x property is from the left of the image). So 160dp = gear image width + desired right margin distance.
Say you have a resource called ic_gear:
Drawable drawable = getResources().getDrawable(R.drawable.ic_gear);
float bitmapWidth = getIntrinsicWidth();
float rawPX20DPValue = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics());
float rawPXLeftOffset = rawPX20DPValue + bitmapWidth;
ivSearch.animate()
.setInterpolator(new AccelerateInterpolator())
.x(rawPX20DPValue)
.y(rawPX20DPValue)
.setDuration(250)
.start();
ivSettings.animate()
.setInterpolator(new AccelerateInterpolator())
.x(widthPx - rawPXLeftOffset)
.y(rawPX20DPValue)
.setDuration(250)
.start();
Also check on the offsets(padding) of the gear and search icon before the move as that will likely affect the end result as well.
In my app, I have a feature where you can put a frame around an image.
I use setBackground(Drawable) to apply the image of the frame to an ImageView. When the ImageView is clicked, I want to blow the content area (the image contained in the frame) up to the size of the screen. For this, I need to know the difference between the entire view and the content area, i.e. the thickness of the border.
Is there a way to get the size of the content area? (Accessing View.getWidth() for the ImageView gives content + border. I'm trying to get the border / padding.)
You can use:
Rect padding = new Rect();
view.getBackground().getPadding(padding);
int contentWidth = view.getWidth() - padding.left - padding.right;
int contentHeight = view.getHeight() - padding.top - padding.bottom;
If your background is a nine patch, the getPadding method will return the insets of the drawable
Here's an alternative approach:
The horizontal and vertical borders of the 9patch frame should not be stretched vertically and horizontally, respectively. Therefore you have constant width of the vertical border and height of the horizontal border. You can open the 9patch with any image editor that supports measuring and measure those lengths.
Use those values as margin or padding of the content view. Providing a single 9patch for all DPIs should work as the system would stretch it for higher DPIs but your padding/margin would be in DPs, so it should be the same size as the frame.
Let's say the following coordinates are relative to (0, 0) being the top left corner of the phone's screen with increasing positive x values going to the right and increasing positive y values going down as diagrammed here: http://t.cyol.com/cache/temp/img/2011/02/1000/119/img/img_1297675862_0.jpg
I'd like to do a "pan out" animation where there is a little box whose top left corner is at (x, y), and it has width w and height h where x, y, w, and h are greater than 0. Everywhere inside that box is some content. Everywhere outside that box is black.
Over 500 milliseconds, the box's top left corner should move to (0, 0) and its width and height will grow to fill the entire screen. That is, over half a second, the box pans out to fullscreen.
The content inside the box is a WebView.
How do I achieve this animation? I tried scaling, but that's not what I want to achieve, because the content inside the box shouldn't get squished. Translating only works if the box starts out in a corner.
In order to achieve this effect, the whole view must be relayouted at each frame of the animation. You'll need to change the layout params of the view and call requestLayout(). Check this link:
Android Animate Scale Image to fit screen width and height