Relative translation in property animation in xml - android

Currently I have an ImageView that extends the length of the device and is scaled by 3. Obviously, the sides are cropped outside the screen. I want the animation to start with the left-side of the image on the left-side of the device, then shift it over until the right-side of the image is on the right side of the device.
I can achieve them by setting up the image to it's initial base then basically doing this:
<objectAnimator
android:propertyName="translationX"
android:duration="6000"
android:valueTo="-1280"
android:valueType="floatType"
/>
However, this only works because I know the exact size of the image and the exact size of my Motorola Xoom. Naturally, I want this to work on any device, so I need something less hard-coded. With Tween animations, it works alright since you can translate something based off a percentage of it's size. It wasn't perfect, but it worked well enough for this effect. Property aniimations don't seem to have this. The translationX and the X properties must have units.
Is there a simple way to translate a view with property animations based on the relative location? Am I going to have to make a separate animation file for each dimension? Is there any other way I can achieve this effect? I'd prefer not to make my own animation.

Could you create code for separate device size 'buckets' that you want to support (e.g. tablet, phone landscape etc.) which has known dp widths. Then you could use this could below to scale the image (where 384 is 384dp for a certain device bucket width) and do something similar for the the valueTo int you need.
//Get the device screen pixel density so that you can scale
//the image in density independent pixels rather than pixels
final float scale = getActivity().getResources().
getDisplayMetrics().density;
//set the number of dp for the height and width by changing
//the number you multiply the (scale + 0.5f) by e.g. 384
int pixelsh = (int) (384 * scale + 0.5f);
int pixelsw = (int) (384 * scale + 0.5f);
This is the way to convert absolute pixels to dp pixels. I do not know how to use these for what you are doing but at least it is a start.

You should create the animation at run time by passing the screen size of the current device screen. First get the screen size (NOTE: the metrics (width, height) change depending on the rotation of the device):
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenSize = metrics.widthPixels;
and then create your ObjectAnimator:
ObjectAnimator oa = ObjectAnimator.ofFloat(*yourImageView*, "translationX", 0, (Float) screenSize);
oa.setDuration(6000);
oa.start();

Related

Android screen size inconsistency

As seen in this image:
I have a rounded rectangle that is the size of 1082 x 1796, and I used getWindowManager().getDefaultDisplay() to get and print the pixel values of the screen as seen in the circle bottom left. However, when I draw this bitmap, this happens:
Is the screen size different from the getDefaultDisplay() method? How do I fix this?
I think your shape is seen by the system as a 9patch. It use the first and last lines/columns in order to know how to resize the image in order to display it consistently.
That's why there is a 2px difference in width and height. I think you can correct it by modifying the png and removing the first and last lines/columns, or just use another way to draw the image. Maybe using another format (jpg ? bmp ?) for the image could do the job. I'm not sure.
you can do that with calculation to fit all screen devices based on width and height of the device screen:
DisplayMetrics dm = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = width * imageHeight / imageWidth;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, height);

Why dpToPixel should not be used to set layout dimensions?

Why in javadocs for dpToPixel (declared here) is stated that it shouldn't be used to set layout dimensions?
We don't use pixels to define layout dimensions. Using density independent pixels ensures proper layout sizes across a variety of devices. Meaning said layout will have roughly the same visual size on a 4" phone and a 7" tablet. (Which is completely unrelated to the problem at hand :) )
That being said the actual layout sizes (e.g. LayoutParams class) are in fact using whole pixels to define the resulting size. It is viable to use the dpToPixels method in this way:
float px = dpToPixels(16, getResources()); // 16 dp to pixels precise
int pxOffset = (int) px; // 16dp rounded down to whole pixels
int pxSize = (int) (0.5f + px); // 16dp rounded up to whole pixels
Now you can use these values in layouts, pxOffset for padding, margin etc. (can be zero) and pxSize for width and height (ensures at least 1px size unless zero). The same calculation is done when using methods int Resources.getDimensionPixelOffset(int) and int Resources.getDimensionPixelSize(int) which are suitable for use with layouts and float Resources.getDimension(int) which is suitable for drawing precisely.
EDIT
Elevation uses float values so using the precise dimension is completely fine.
TranslationX, translationY or translationZ are defined using float values. These and many more view properties are used for animation so it makes sense to use smooth values. If set by hand use whole integers for predictable behavior.

Layout stretching

Is there a layout, that would allow me to make it in absolute values, but when it would be on larger / smaller screen, it would stretch and adjusted those values, to fit onto the screen but preserve the same look?
Relative layout still keens on exact values.
For example, I have a button and I want it to have it the width of 1/3 of the screen of every device.
Setting manualy in code the width of an element to (for example) screenWidth/3 works. Yet I don't think it's clean. But this technique works.
Find device dimensions at runtime and set width of button at runtime.
Display mDisplay = getWindowManager().getDefaultDisplay();
int deviceWidth = mDisplay.getWidth();
int deviceHeight = mDisplay.getHeight();
button.getLayoutParams().width = deviceWidth / 3;
Give the values in dpi of your layout and view and they will adjust themselves on any screen

Spinning wheel half way off screen android

I have a requirement for a spinning wheel graphic that is half-off the bottom of the screen and then animates (spins) when users take action.
I can place the wheel graphic off the screen by calling:
Animation animation = new TranslateAnimation(Animation.ABSOLUTE,0,Animation.ABSOLUTE, 0,Animation.ABSOLUTE,wheelPos, Animation.ABSOLUTE, wheelPos);
animation.setDuration(1);
animation.setFillAfter(true);
wheel.startAnimation(animation);
That is fine, I can set the width to whatever I want via LayoutParams.
But how can I make the wheel the same size for different screen sizes? For example, I need to place the wheel Y pixels off the screen (wheelPos variable) above, but on a Nexus S versus Nexus 7 how can I calculate an appropriate Y value?
The same goes for width, how can I calculate a width so that it appears exactly the same size?
I note the Catch android app https://catch.com/ - if I expand their wheel menu, it's size is identical on the Nexus S versus Nexus 7 - how might they have achieved that?
Developer of Catch here. Thanks for the email pointing us to your question here.
For our wheel menu, its width and positioning are specified within XML layout files, using dp/dip (density-independent pixels). For a given dimension, say, 300dp, it will appear the same size on any Android device; the system will scale that value depending on whether the device's screen is low, medium, high, xhigh, etc. density.
So, set your width and Y offset using dp and you should be good to go. You can do it completely programmatically if you wish:
final int wheelWidth = 300;
final int wheelYoffset = 64;
DisplayMetrics dm = getResources().getDisplayMetrics();
final float scaledWheelWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, wheelWidth, dm);
final float scaledWheelYoffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, wheelYoffset, dm);
I personally find it much easier to maintain all of my UI dimensions as resources. In res/values/dimens.xml I'd add:
<dimen name="wheel_width">300dp</dimen>
<dimen name="wheel_y_offset">64dp</dimen>
and then if I needed to use those dimensions in code, I can grab them with a one-liner:
final float scaledWheelWidth = getResources().getDimensionPixelSize(R.dimen.wheel_width);
final float scaledWheelYoffset = getResources().getDimensionPixelOffset(R.dimen.wheel_y_offset);
Using dp for layout is one of the keystone principles of making an Android UI that scales nicely to different devices, and is especially critical for anything you expect your user to touch/interact with, since you can size your hit targets during development and rest assured that they will be physically similarly-sized no matter what device your app is running on.

What units to pass UI methods?

Reading the Supporting Multiple Screens of the Android developer guide it says it is always best to use density independent pixels (dp) instead of pixels and it also says Android handles most of the application rendering on different density devices.
Now my question is, if we look at the padding function of the View class
setPadding (int left, int top, int
right, int bottom)
left the left padding in pixels
top the top padding in pixels
right the right padding in pixels
bottom the bottom padding in pixels
When I use this function is it alright to pass direct values or do I have to pass converted values, from dp to px, to best show the UI in all devices?
Check out this padding reference and search for android:padding
It looks like you will need to convert from dip to pixels to set the padding. You should convert the values from dip to pixels so that it works correctly on different devices.
Convert your dip value to pixels and pad with that.
final float scale = getContext().getResources().getDisplayMetrics().density;
int valuePixels = (int)(valueDip * scale);

Categories

Resources