Approaches for implementing Android animation that re-apportions view - android

I have a LinearLayout that contains a couple of views. It doesn't have to be LinearLayout-based - this is negotiable - but it provides this simple example.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#f00"
android:orientation="horizontal"/>
<LinearLayout
android:id="#+id/right"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#ff0"
android:orientation="horizontal"/>
</LinearLayout>
This is rendered as follows:
What I want to do is produce an animation sequence that slides out the yellow view, off the right of the screen, whilst simultaneously enlarging the red view to fill the space left behind.
It strikes me that there are a few ways to do this:
Weight-Based Animation
I can change the layout weight - specifically, iteratively reduce the yellow view's weight to zero. This will do the job as far as the red view is concerned. However if the yellow view has content in it, it will be squashed as it resizes down. It also doesn't run very smoothly.
This is almost acceptable in some contexts, because I can fade out the yellow view and its content to an alpha of zero, and then clear the empty space. Still not that smooth, but no squashing. However when I need the content to stay visible while moving, it ceases to be appropriate anyway.
Translation Animation
I can slide out the yellow view, a bit like this. However the vacated space is left behind, as you might expect. It does run perfectly smoothly.
A combination of the above?
I haven't tried this yet. I guess I could slide out the view whilst simultaneously reducing the weight of the empty space. I don't know if this is actually feasible, i.e. whether the weight change would affect the animated slide, or whether the two can be coherently run at once, or whether it will operate smoothly anyway.
Or, of course, something entirely different.
Any suggestions please?

Related

Android GLSurfaceView in ScrollView produces black borders

I have a GLSurfaceView that I want to put inside a ScrollView. I accomplished this by adding a FrameLayout inside a LinearLayout which was added to the ScrollView.
Everyhing works nice, except that I am getting a black border on top of the GLSurfaceView when scrolling. When the screen is still, everyhing looks nice. Anybody have any ideas.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="900dp"
android:orientation="horizontal"
android:layout_weight="1"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="35dp"
android:visibility="visible"
android:weightSum="1"
android:overScrollMode="never">
<FrameLayout
android:id="#+id/ecg_graph"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Generally speaking, you don't want to move SurfaceView (or its subclasses) around the screen. Bear in mind that SurfaceView has two parts, the Surface and the View. The View part -- usually just a transparent "hole" used during layout -- is handled by the app. The Surface part is a separate graphics layer composited by the system, and its size and position are managed by the Window Manager.
When you move a SurfaceView, the View part moves immediately, but the Surface lags behind, because moving it requires communicating with other processes. You should expect to see a black bar in the direction that the View is moving, because you've temporarily exposed an area outside what the Surface covers.
The preferred way to handle this is to use a TextureView, which behaves just like other Views. You would need to do your own EGL setup and thread management, since GLSurfaceView won't be handling those for you. You can find some examples in Grafika.

View without Activity - Placement and Layout

I am using the Code from JawsWare - Overlay View to create a View that lays above everything.
This works fine, but I failed to adapt the given layout to my needs.
What I want: Two Phases.
First Phase: A button in the upper left corner that is always there above all other apps.
When this button is pressed, we enter the Second Phase:
The button is replaced by two images which are to be horizontally aligned at a certain distance from the top, again floating above everything else.
I have to admit that I dont really understand Android Layouting, but I've tried fiddling with a RelativeLayout and an ImageView but it seems buggy.
Additionally, I encountered a very strange behaviour: Even if the Image is only about 200x200 px in the upper left corner, nearly the whole screen is "blocked" by the View (it receives all TouchEvents, even though there shouldt be anything). It seems that if I position the Layout using Margins to float in the middle of the screen, everything to the left and top of the visible button is also receiving touch events and not letting them pass through to the underlying app (even though there is no visible content).
Any ideas to why this happens and how to circumvent that?
Secondly: How can I achieve the two layouts from earlier?
Edit 1: My Layout. (please keep in mind that I just copied this and then did what I thought was right)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:onClick="overlayTextClicked"
android:padding="0dp"
android:id="#+id/overlay_layout_id"
>
<ImageView
android:id="#+id/imageview_info"
android:layout_width="200px"
android:layout_height="200px"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:scaleType="fitXY"
android:src="#drawable/picture_010512233437384578"
android:contentDescription=""/>
<TextView
android:id="#+id/textview_info"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:text="info"
android:textColor="#FFF"
android:orientation="vertical"
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
Afterwards I am trying to set the RelativeLayouts Gravity to TOP | CENTER_VERTICAL with a Top-Margin of 200px.
I believe what's going on here is your TextView is filling out the entire screen.
wrap_content bounds the size of a View with its content. match_parent fills a view to as big as it can get (i.e., whatever size the container is bound to).
So in this case, your RelativeLayout is does not have a max size it's bound to. Your TextView is going to try to get as big as it can get, so it's going to fill the screen. Likewise, the RelativeLayout is going to blow up to that size to wrap around the TextView.
Also, RelativeLayout doesn't really respond to Gravity well. That is used in LinearLayout and FrameLayout containers a lot, but RelativeLayout relational rules like "CENTER_IN_PARENT" are going to override whatever Gravity you set (if you set Gravity.RIGHT and "CENTER_IN_PARENT", then one has to win out I guess).

Making Android not clip the view while animating (simple flip-clock-like widget)

I'm implementing a simple flip-clock / counter / ticker widget which will consist of several instances of a following "digit" widget:
It's a digit placed on top of a background image. The digit is supposed to animate every second by sliding up and revealing next digit. During the animation both digits should stay "within" the background's boundary.
I'm trying to achieve this behavior by having a TextView with 2 lines - one digit per line - and animating this TextView's position upwards, until the next digit is fully visible. And then I will reset TextViews position and replace both digits at the same time, so that it's impossible to notice. Then I will repeat the process and make it look like the animation never ends.
Here you can see an intermediate state of the animation, when part of zero and part of nine is visible. I "mocked" it in the Graphical Layout editor of Eclipse, by setting the layout_marginTop property to a negative value.
Here's the layout file (the mentioned attribute is normally not there).
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="20dp"
android:layout_height="38dp"
android:background="#drawable/background_countdown_normal_grey"
android:clipChildren="false" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:paddingBottom="5dp"
android:paddingTop="5dp" >
<TextView
android:id="#+id/textview_countdown_digit"
style="#style/TextView.CountdownDigit"
android:layout_marginTop="-12dp"
android:text="0\n9" />
</LinearLayout>
</LinearLayout>
I tried two solutions. By using ViewPropertyAnimator on either translateY or y, I get a smooth animation, but the original clipping of the TextView does not change during animation, so in effect the second digit is never visible. As you can see, I tried clipChildren property, but it doesn't seem to change anything.
My second approach was to use ValueAnimator with a custom Evaluator, which modifies the topMargin of LayoutParams on the TextView. It works, but the animation is very choppy even on high-end devices.
So my question is, how to avoid view clipping during animation and make so in an efficient way? Is there a better approach?
I found an alternative solution in which I use a ScrollView instead. My layout looks like this:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scrollview_countdown_digit"
android:layout_width="20dp"
android:layout_height="38dp"
android:background="#drawable/background_countdown_normal_grey"
android:paddingBottom="5dp"
android:paddingTop="5dp"
android:scrollbars="none" >
<TextView
android:id="#+id/textview_countdown_digit1"
style="#style/TextView.CountdownDigit"
android:text="0\n9" />
</ScrollView>
I animate using ObjectAnimator by scrollY property. Works well so far.
I also tried having 2 TextViews intsead of one (in my initial layout), but it did not change the fact of clipping.

fill_parent in LinearLayout isn't being honored

After much research on both SO and google, I haven't seen any one with exactly the same problem I am experiencing, so here it is:
I recently redid the entire UI on an android app. For the most part, I made only cosmetic changes to each of the screens. They appear in the UI editor of eclipse perfectly as expected. However, directly after doing this, two of the screens stopped being laid out correctly on both all tested devices and the emulator.
Now, the big problem one these two screens was that the root level LinearLayout didn't appear to be actually honoring the fill_parent for either layout_height or layout_width. it looks like it's being measured as if it were set to wrap_content instead. It only takes up about 70% of the screen - which is just enough to wrap the individual elements inside the root LinearLayout. I would post an image, but as a new user, I am not allowed to.
The layout isn't stretching to fill the screen. Here's the code for layout, except that there are a few more in the LinearLayouts containing a TextView and an EditText.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF000000"
android:orientation="vertical" >
<TextView
style="#style/sans.white.16.bold"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:text="#string/edit_account" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<com.teamunify.ondeck.widget.TUTextView
style="#style/sans.white.14"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="#string/first_name" />
<com.teamunify.ondeck.widget.TUEditText
android:id="#+id/acc_editor_first"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:selectAllOnFocus="true"
android:singleLine="true" />
</LinearLayout>
I think the root LinearLayout should be filling both height and width. I've used this same layout many MANY times in our app without problems. (A quick count revealed that I used 76 LinearLayouts in our app, and all but two of them are working.)
At first, I suspected that perhaps our custom classes measure was wrecking things, so I changed the layout to use all plain EditTexts, but there was no change. I double checked the activity, but it isn't doing anything except to load this xml. So, in desperation, I redid the layout like so:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF000000"
android:orientation="vertical" >
<TextView
style="#style/sans.white.16.bold"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center|top"
android:paddingTop="4dp"
android:text="#string/edit_account" />
</LinearLayout>
After that, The words Edit Account appear with a black background mashed into the upper left corner. Clearly, the LinearLayout isn't filling the parent.
Long story short, I am asking how to fix this so that the LinearLayout fills the screen as expected.
I am at a complete loss as to why this is happening, and I am certainly hoping that someone on SO has an idea. This has got me pulling my hair out!
Try setting fixed width and height for the root layout.
Then only you will be able to debug who is driving length and width. It is very much possible that parent activity or background activity is setting dimensions. Once you identify the root cause you can go back to original settings.
As of now from your code snippet given here, nothing wrong here
If you use a custom activity as a container to other activities for some reason (In our case, we were recreating the look of our iOS app, and needed a custom menu to show up along the button side of the screen) the window manager seems to get a bit confused with what the actual height and width of the nested activities should be. A call to fillparent or matchparent ends up wrapping the content instead.
We ended up changing the behavior of several methods in our container activity class to make this work.

redundant onDraw() calls in FrameLayout

I have the following layout structure in my xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<MyEntireView
android:id="#+id/map"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</MyEntireView>
<FrameLayout
android:id="#+id/focusedlayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom">
<MyDetailedView
android:id="#+id/focus"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</MyDetailedView>
</FrameLayout>
</FrameLayout>
MyEntireView is a map (a Drawable) and MyDetailedView pops up on top of it upon onTouch() and contains only a certain piece of the whole map (also Drawable) that displays in greater detail the area which the user touched on the entire map in MyEntireView.
When MyDetailedView is shown - i.e. its setVisibility(View.VISIBLE) - there appear some transparent shapes (also some Bitmaps) on top and are redrawn regularly (e.g., 1 sec interval). The shapes are added dynamically as children of the #+id/focusedlayout as there is always a different number of them and their position is relevant to the detailed map picture.
And the problem is that each single update is processed multiple times. Each time a shape updates, then MyEntireView's onDraw() is called, MyDetailedView's onDraw() is called, and then these two are called again. And only after this the shape itself is invalidated.
As a result, this behaviour makes the shape flicker, since MyDetailedView redraws and hides the shape for 10ms or so.
I must probably be overlooking some simple logic, but I really can't get if there is a way to force only one View of a ViewGroup invalidate().
Or maybe there is an alternate structure/layout that I could use to fit my purpose?
Thanks a lot!
P.S.
The entire code is quite big to put here but I'm willing to insert any pieces upon request

Categories

Resources