Intro:
I have a LinearLayout, which contains two sub LinearLayouts, like so:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/dual_pane"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:weightSum="1.0">
<!-- Screen 1 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#ff0000"
android:layout_weight="1">
</LinearLayout>
<!-- Screen 2 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#ff6600"
android:layout_weight="1">
</LinearLayout>
</LinearLayout>
Initially, I want "Screen 1" to take all screen width available. Therefore, my R.id.dual_pane has it's weightSum attribute to 1.0. This works fine! if weightSum=1.0, Screen 1 occupies the whole screen!
After loading some resources, I change my R.id.dual_pane weighSum to 2.0, which results in both Screen 1 and Screen 2 taking 50% off the width of the screen. This also works perfect. When weightSum=2.0, both screens take 50% of the width.
Problem:
I would like to animate the weightSum property, so my Screen2 will slide in.
I am targeting HoneyComb, so minSDK version is 11, and I figured, using the new ObjectAnimator framework, I could easily animate this property, to get a nice smooth effect. I verified that LinearLayout indeed has getWeightSum() and setWeightSum() methods (which is required to use the ObjectAnimator, I think).
Own effort:
Here's my code to showing and hiding Screen2 using the ObjectAnimator :
private void showScreen2()
{
//Not-animated will work...
//mDualPane.setWeightSum(2.0f);
// Now try to animate the weightSum
float ws = mDualPane.getWeightSum();
ObjectAnimator anim = ObjectAnimator.ofFloat(mDualPane, "weightSum", ws, 2.0f);
anim.setDuration(5000);
anim.start();
}
private void hideScreen2()
{
//Not-animated will work...
//mDualPane.setWeightSum(1.0f);
// Now try to animate the weightSum
float ws = mDualPane.getWeightSum();
ObjectAnimator anim = ObjectAnimator.ofFloat(mDualPane, "weightSum", ws, 1.0f);
anim.setDuration(5000);
anim.start();
}
Here, my mDualPane is my root LinearLayout...
Question:
When I call these functions, nothing happens. The screen stays exactly like it was before.
Do I need to call requestLayout() on my mDualPane somewhere? Am I missing some knowledge of the ObjectAnimator? Or is it impossible to animate the weightSum property?
ALSO:
1) I don't want to mess with hard-coded widths, and animate those. For now I want 50-50 for both screens, but I might change it later. Anyway, i need to be able to set a specific ratio between the two widths.
2) I have looked at LayoutTransition combined with toggling visibility, but to no avail
I was right in the sense that I need to update the layout myself:
float ws = mDualPane.getWeightSum();
ObjectAnimator anim = ObjectAnimator.ofFloat(mDualPane, "weightSum", ws, 2.0f);
anim.setDuration(5000);
anim.addUpdateListener(this);
anim.start();
Now, I added an UpdateListener to the ObjectAnimator, which is implemented by my Activity and updates the layout:
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mDualPane.requestLayout();
}
It seems strange to me, that ObjectAnimator doesn't call this itself, but anyway, this is how to get it working.
The solution is especially nice, in my opinion, since you can very nicely animate layouts sliding in, independent of screensize...
Related
I have a Xamarin project. I develop for IOS,Android and UWP. In my application, I have my manual layout logic for UI Elements. In IOS, I can use the frame property in order to set where the view is going to be rendered. I can do the same in UWP by using Canvas as the container and Canvas.Left,Canvas.Top properties to set x,y locations and my code has the logic to do the layout. I am confused about how to achieve this in Android. AbsoluteLayout seemed to be a perfect match, but it's deprecated. Can I achieve this with some other Layout or should I create my custom ViewGroup class?
You can use a FrameLayout and position items in it using the top and left margin. In XML it would look something like:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="100dp"
android:layout_marginTop="100dp"
/>
</FrameLayout>
If you want to set it from code then you can use the LayoutParams:
FrameLayout.LayoutParams param = (FrameLayout.LayoutParams) view.getLayoutParams();
param.leftMargin = 100;
param.topMargin = 100;
param.height = 50;
param.width = 50;
view.setLayoutParams(param);
Note that the values in code are pixels not dp so you would have to convert. I'm not sure how this would convert to Xaramin but it gives you the idea.
Either way you'll have to consider what will happen when a user with an unusual device size uses your app. The reason Android doesn't have much use for absolute layouts is that there are so many different device sizes/densities that they are usually impractical.
You can use Relative Layout/ Frame Layout / Custom Layout. instead of Absolute Layout, As Absolute layout is harder to maintain is the reason its depreciated.
I have a vertical linearlayout with three clickable imageviews within the linearlayout. When i rotate the linearlayout by 90 degrees using simple animation the problem arises. The imageviews are rotated correctly but the onclick events for the imageviews are not rotated along with the linearlayout and remain in the original position as before the animation.
Below is my main java code
westplayer = (LinearLayout) findViewById(R.id.linearLayout_west);
// Create an animation instance
Animation an = new RotateAnimation(0.0f, 180.0f, 32, 180);
// Set the animation's parameters
an.setDuration(0); // duration in ms
an.setRepeatCount(0); // -1 = infinite repeated
an.setRepeatMode(Animation.REVERSE); // reverses each repeat
an.setFillAfter(true); // keep rotation after animation
// Apply animation to linearlayout
westplayer.setAnimation(an);
The code above handles the animation part. The code below follows the animation and is supoosed to update the layout positions but is not working for me.
// Update Layout
int top=westplayer.getTop();
int bottom=westplayer.getBottom();
int left=westplayer.getLeft();
int right=westplayer.getRight();
westplayer.layout(left, top , right, bottom );
The xml is as follows:
<LinearLayout
android:id="#+id/linearLayout_west"
android:layout_width="42dp"
android:layout_height="250dp"
android:layout_alignParentLeft="true"
android:layout_below="#+id/linearLayout_north"
android:duplicateParentState="false"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="#+id/imageViewW1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/spades_14"
/>
<ImageView
android:id="#+id/imageViewW2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/spades_14"
android:layout_marginTop="-15dp"
/>
<ImageView
android:id="#+id/imageViewW3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/spades_14"
android:layout_marginTop="-15dp"
/>
</LinearLayout>
I have got the update layout code from this and also found another solution that i have also tried this and still no positive results. I need this to work for API 10. Any help is much appreciated
Yes, you got the expected result.
Since Animation in android only try to apply a transformation matrix on the bitmap of view, it doesn't change the position and size of view.
So after your animation, although you setFillAfter(true) to let the view looks rotated, but in fact the view are still in the same place and doesn't move a little bit.
The problem you face, is because you use a Tween Animation. The View will rotate and only the visible part will move. But the positions of the ImageView will remain the same like defined in the layout. That's why the clickable area stays the same.
To redefine the position, according to the animation you will have to use a Property Animation, like a ObjectAnimator or a ValueAnimator. With this the propertys defined in xml will be updated. Now the clickable areas move along with the views.
I'm trying to animate an ImageView so it's slowly shown from left to right.
When I asked this before I was having trouble to explain what I wanted, so this time I created the desired effect using HTML / JS:
http://jsfiddle.net/E2uDE/
What would be the best way to get this effect in Android?
I tried changing the scaleType and then applying a ScaleAnimation directly to that ImageView:
Layout:
<ImageView
android:id="#+id/graphImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:scaleType="centerCrop"
android:background="#android:color/transparent"
android:contentDescription="#string/stroom_grafiek"
/>
Java:
scale = new ScaleAnimation((float)0,
(float)1, (float)1, (float)1,
Animation.RELATIVE_TO_SELF, (float)0,
Animation.RELATIVE_TO_SELF, (float)1);
scale.setDuration(1000);
graphImage.startAnimation(scale);
But this stil scales the image.
I also tried wrapping the ImageView in a FrameLayout, hoping I could just animate the FrameLayout:
<FrameLayout
android:layout_width="70dp"
android:layout_height="fill_parent"
android:clipChildren="true"">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left|clip_horizontal" />
</FrameLayout>
This will still try to scale my ImageView to fit inside the FrameLayout.
There are several ways to do this - one way is to simply change the clip on the canvas (i.e. the area where the view is allowed to draw) of the ImageView drawing your image. Slowly increasing the right-hand edge of the draw bounds will result in same effect as in your example link.
I have written some example code which illustrates this technique. If you download/build/run the project, clicking on the image will run the reveal animation.
Have a look at the onDraw method of the CoveredImageView class, which has the following basic structure:
#Override
protected void onDraw(Canvas canvas) {
...
canvas.getClipBounds(mRect);
mRect.right = ...;
canvas.clipRect(mRect);
...
super.onDraw(canvas);
...
invalidate();
}
The clip is adjusted and then invalidate() is called, causing onDraw() to be called again.
I have also written some code to interpolate the right clip value from the elapsed time, which does not depend on any particular version of Android. However, as a note, from Android 3.0 there is a new animation framework and a ValueAnimator class which could help perform such tasks.
You could overlap your imageview with another one, for example one with a plain white drawable. Then you can animate the overlapping imageview without worrying about scale until it has a width of 0 and remove it. For that to work you have to place your imageView in a RelativeLayout ofc.
I have simple task: fade screen (+ other actions, but it doesnt matter now) when something happens (say, button pressed).
My minSDKVersion is 11. I do it in this way: activity layout contains a view:
<View
android:id="#+id/bgFadingView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0"
android:background="#android:color/black" />
and method where I fade screen is as follows:
protected void fadeBGView(boolean fadeIn)
{
ObjectAnimator animator = ObjectAnimator.ofFloat(bgFadingView, "alpha", fadeIn ? 0.75f : 0f); //bgFadingView is initialized via findViewById(R.id.bgFadingView);
animator.setDuration(1000);
animator.start();
}
in other words, I have black view, which occupies the whole screen and when I need to fadeIn, I change view's alpha from 0 to 0.75.
The problem is that it works with bad framerate... I see fading animation frame by frame - it works at about 15fps. What am I doing wrong? Android system fading works much more smoothly - I want to archieve the same result or close. How to?
All I had to do was:
ObjectAnimator.setFrameDelay(10);
default frame delay was too long, so my animations didnt work smoothly. The problem was not due to device performance limitations.
Is it possible to create a view that is bigger than the screen?
I need a view that has a bigger width then the screen of the device. I use this view in a rotation animation. During the rotation the parts that were not on the screen before animating the view will become visible.
Is there a way to achieve this effect with the android framework?
Update
I tried to set my parent layout much bigger then the screen and it is working. This will make somethings a little bit uncomfortable but it could work. The next problem now is that my layout still starts at the left side of the screen. I can't think of a method to make the layout to expand itself to the left and the right of the screen.
Ok I got an answer. It is not very nice because it uses a deprecated View class but it works at least on my current testing screen resolution other resolutions are tested tomorrow.
I wrapped the view that I wanted to expand beyond the screen in an absolute layout like this:
<AbsoluteLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true">
<ImageView
android:id="#+id/content"
android:layout_width="600dip"
android:layout_height="420dip"
android:scaleType="centerCrop"
android:layout_x="-200dip"
android:layout_y="60dip"
android:src="#color/testcolor" />
</AbsoluteLayout>
The -200 x coordinate makes the view stick 200dip out of the left side of the screen. If I'm animating the view those parts that are outside the screen will gradually become visible.
E.g. setting negative bottom margin together with setting extra large layout_height (large enough for you) solved the similar issue as for me.
Works fine at least using API 11+ animations/rotations.
Could look like:
android:layout_marginBottom="-1000dp"
android:layout_height="1000dp"
In case anyone still comes up on this page. The key is your root layout, it will only work with a FrameLayout (or the deprecated absolutelayout). Then you have two options to make your child view bigger.
through xml, this is quick and easy but you don't know the actual screen width & height in advance so your off with setting a ridiculously high value for layout_width & layout_height to cover all screens.
Calculate the screen size programatically and make the view's width/height proportional bigger to this..
Also be aware that your bigger view still starts in the top left corner of the screen so to account this you will have to give a negative top & left margin that's half of what you are adding to the view's width/height
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) viewToMakeBigger.getLayoutParams();
int marginLeft = (int) (viewToMakeBigger.getWidth()*0.1);
int marginTop = (int) (viewToMakeBigger.getHeight()*0.1);
params.width = (int) (viewToMakeBigger.getWidth()*1.2);
params.height = (int) (viewToMakeBigger.getHeight()*1.2);
params.leftMargin = -marginLeft;
params.topMargin = -marginTop;
viewToMakeBigger.setLayoutParams(params);
HorizontalScrollView:
http://developer.android.com/reference/android/widget/HorizontalScrollView.html
Layout container for a view hierarchy that can be scrolled by the user, allowing it to be larger than the physical display.
The simple axml below creates an ImageView that is 400dp wider than the screen (even though the layout_width is set to equal the parent's width) using a negative left and right margin of 200dp.
The ImageView is situated 250dp above the top of the screen using a negative top margin, with 450dp of 700dp vertical pixels visible on the screen.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:background="#FFFF0000"
android:layout_height="700dp"
android:layout_marginLeft="-200dp"
android:layout_marginRight="-200dp"
android:layout_marginTop="-250dp"
android:layout_width="match_parent" />
</RelativeLayout>
You can override the views in the onMeasure method. This will set your View dimensions to 1000x1000 px.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(1000, 1000);
}
Is it possible to create a view that is bigger than the screen?
Why not, you can define the layout_width and layout_height in px(or dip) as you want:
android:layout_width="10000px"
android:layout_height="20000px"
You need to change the size of the window, by getWindow().setLayout. This will increase the size for your window. Since the root layout can be as big as its parent you can then increase the size of the view you want to be bigger than the screen size. It works for me let me know
You can use ViewSwitcher to handle that. Used with Animation and a OnGestureListener looks pretty good.
You can do it programmatically:
FrameLayout.LayoutParams rootViewParams = (FrameLayout.LayoutParams) rootView.getLayoutParams();
rootViewParams.height=displayMetrics.heightPixels+(int)dpToPixels(60);
rootViewParams.width=displayMetrics.widthPixels+(int)dpToPixels(60);
rootView.setLayoutParams(rootViewParams);
rootView.setX(rootView.getX() - dpToPixels(30));
rootView.setY(rootView.getY() - dpToPixels(30));
MUST BE ONLY IN
"public void onWindowFocusChanged(boolean hasFocus)" method.
and
rootView = (RelativeLayout) findViewById(R.id.rootLayout);
Inside "protected void onCreate(Bundle savedInstanceState)" method.
Where yout .xml file is like this:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rootLayout"
tools:context="com.example.Activity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_centerHorizontal="true"
android:layout_margin="30dp"
android:layout_centerVertical="true"
android:layout_height="match_parent">
// Bla bla bla
</RelativeLayout>
and:
public float dpToPixels(float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}