Layout Animation Android[facebook]
Solution 1:
Ok After spending 2 days reading about similair problems and how people solved them I finally was able to create the thing I wanted. I was not able to do it with 2 diffrent XML files, but I doubt it is not possible.
I did encountert some problems tho.
After the first animation ended, the button was not clickable. This is because the animation shows that everything is moved but it does not update the layout, so the button is still at the position where the animation started. So I had to calculate the new position of the layout.
I think I read somewhere that this is no longer an issue in 3.0, but correct me if I am wrong
Another was that when I had my animation finally working the way I wanted my underlaying view did disapear before the animation was finished because I invoked view.setVisabilty(View.GONE);
.
Now the problem was when I did not invoke that method, the animation just hang for a second and then shooter to the end position of the animation.
So I added a empty LinearLayout (can be anything) , Default property on GONE, when the animation starts set it on Visible. when you revert the animation, set it again to gone.
after doing this the animation was working the way I wanted.
And if you are using a Rel, Linear, or any other layout. then you cant stack views in the Z order so you have to use an SurfaceView.
so heres main.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/RelativeLayout1"android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical" ><SurfaceViewandroid:id="@+id/surfaceView1"android:layout_width="fill_parent"android:layout_height="fill_parent" /><RelativeLayoutandroid:id="@+id/layout"android:layout_width="220dp"android:layout_height="fill_parent"android:background="#ffee00"android:orientation="vertical" ><LinearLayoutandroid:id="@+id/fake_layouy"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"android:visibility="gone"></LinearLayout><ListViewandroid:id="@+id/listView1"android:layout_width="match_parent"android:layout_height="wrap_content" ></ListView></RelativeLayout><RelativeLayoutandroid:id="@+id/layoutTwo"android:layout_width="fill_parent"android:layout_height="fill_parent"android:background="#ff00ee"android:orientation="vertical"><LinearLayoutandroid:id="@+id/linearLayout1"android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_alignParentTop="true"android:background="#ff0000"android:layout_margin="2dp"><Buttonandroid:id="@+id/button"android:layout_width="50dp"android:layout_height="wrap_content"android:text="slide" /></LinearLayout></RelativeLayout></RelativeLayout>
heres the java code
publicclassMenuAnimationActivityextendsActivity {
private Button buttonSwitch;
private View subLayout;
private View topLayout;
private ListView subViewListView;
private String listViewDummyContent[]={"Android","iPhone","BlackBerry","AndroidPeople"};
private Display display;
private View fakeLayout;
private AnimationListener AL;
// Values for after the animationprivateint oldLeft;
privateint oldTop;
privateint newleft;
privateint newTop;
privateint screenWidth;
privateint animToPostion;
// TODO change the name of the animToPostion for a better explanation.privatebooleanmenuOpen=false;
/** Called when the activity is first created. */@OverridepublicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
buttonSwitch = (Button)findViewById(R.id.button);
subLayout = (View) findViewById(R.id.layout);
topLayout = (View) findViewById(R.id.layoutTwo);
subViewListView=(ListView)findViewById(R.id.listView1);
fakeLayout = (View)findViewById(R.id.fake_layouy);
subViewListView.setAdapter(newArrayAdapter<String>(this,android.R.layout.simple_list_item_1 , listViewDummyContent));
display = getWindowManager().getDefaultDisplay();
screenWidth = display.getWidth();
intcalcAnimationPosition= (screenWidth /3);
// Value where the onTop Layer has to animate// also the max width of the layout underneath // Set Layout params for subLayout according to calculation
animToPostion = screenWidth - calcAnimationPosition;
RelativeLayout.LayoutParamsparams=newRelativeLayout.LayoutParams(animToPostion, RelativeLayout.LayoutParams.FILL_PARENT);
subLayout.setLayoutParams(params);
topLayout.setOnTouchListener(newOnTouchListener() {
@OverridepublicbooleanonTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
if (menuOpen == true) {
animSlideLeft();
}
}
returnfalse;
}
});
buttonSwitch.setOnClickListener(newView.OnClickListener() {
@OverridepublicvoidonClick(View v) {
if(menuOpen == false){
animSlideRight();
} elseif (menuOpen == true) {
animSlideLeft();
}
}
});
AL = newAnimationListener() {
@OverridepublicvoidonAnimationStart(Animation animation) {
buttonSwitch.setClickable(false);
topLayout.setEnabled(false);
}
@OverridepublicvoidonAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
@OverridepublicvoidonAnimationEnd(Animation animation) {
if(menuOpen == true) {
Log.d("", "Open");
topLayout.layout(oldLeft, oldTop, oldLeft + topLayout.getMeasuredWidth(), oldTop + topLayout.getMeasuredHeight() );
menuOpen = false;
buttonSwitch.setClickable(true);
topLayout.setEnabled(true);
} elseif(menuOpen == false) {
Log.d("","FALSE");
topLayout.layout(newleft, newTop, newleft + topLayout.getMeasuredWidth(), newTop + topLayout.getMeasuredHeight() );
topLayout.setEnabled(true);
menuOpen = true;
buttonSwitch.setClickable(true);
}
}
};
}
publicvoidanimSlideRight(){
fakeLayout.setVisibility(View.VISIBLE);
newleft = topLayout.getLeft() + animToPostion;
newTop = topLayout.getTop();
TranslateAnimationslideRight=newTranslateAnimation(0,newleft,0,0);
slideRight.setDuration(500);
slideRight.setFillEnabled(true);
slideRight.setAnimationListener(AL);
topLayout.startAnimation(slideRight);
}
publicvoidanimSlideLeft() {
fakeLayout.setVisibility(View.GONE);
oldLeft = topLayout.getLeft() - animToPostion;
oldTop = topLayout.getTop();
TranslateAnimationslideLeft=newTranslateAnimation(newleft,oldLeft,0,0);
slideLeft.setDuration(500);
slideLeft.setFillEnabled(true);
slideLeft.setAnimationListener(AL);
topLayout.startAnimation(slideLeft);
}
}
I did some extra coding on touching views and stuff.
And the final result
before Animation
after First Animation
And after the second Animation back to the left it states returns as the first Image.
Al those posts that helped me really deserve some credit but I cant find any of them.
Edit
GIThttps://bitbucket.org/maikelbollemeijer/sidepanelswitcher
Update: https://github.com/jfeinstein10/SlidingMenu this lib is compatible with Actionbar Sherlock.
hope this helps
Solution 2:
I had the similar requirement that make layout animation like Facebook app. To do that, I made a customized ViewGroup (called AnimationLayout). Hope these code helping.
The AnimationLayout needs two child: Sidebar and Content. (by assigning @+id/animation_sidebar and @+id/animation_content to corresponding one)
This is the layout xml, the SideBar has a button and a listview. The Content has a textview and a button(it binds to a callback function).
<?xml version="1.0" encoding="utf-8"?><org.zeroxlab.widget.AnimationLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/animation_layout"android:layout_width="match_parent"android:layout_height="match_parent"
><LinearLayoutandroid:id="@+id/animation_sidebar"android:layout_width="200dip"android:layout_height="match_parent"android:background="#550000"android:orientation="vertical"
><Buttonandroid:id="@+id/button_test"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Sidebar Button"
/><ListViewandroid:id="@+id/sidebar_list"android:layout_width="match_parent"android:layout_height="match_parent"
/></LinearLayout><LinearLayoutandroid:id="@+id/animation_content"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#003300"android:clickable="true"
><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Content Button"android:onClick="onClickButton"
/><TextViewandroid:id="@+id/text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="The Answer to Life, the Universe, and Everything -- is 42"
/></LinearLayout></org.zeroxlab.widget.AnimationLayout>
This is the testing Activity. It initialize a ListView and assigns itself as a Listener to AnimationLayout.
package test.julian.hello;
import org.zeroxlab.widget.AnimationLayout;
import android.app.Activity;
import android.app.ActivityManager;
import android.os.Bundle;
import android.widget.*;
import android.util.Log;
import android.view.View;
publicclassHelloAndroidextendsActivityimplementsAnimationLayout.Listener {
ListView mList;
AnimationLayout mLayout;
String[] mStrings = {"a", "b", "c", "d", "e", "f", "g"};
@OverridepublicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.app_layout);
mLayout = (AnimationLayout) findViewById(R.id.animation_layout);
mLayout.setListener(this);
mList = (ListView) findViewById(R.id.sidebar_list);
mList.setAdapter(
newArrayAdapter<String>(
this, android.R.layout.simple_list_item_multiple_choice
, mStrings));
mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
publicvoidonClickButton(View v) {
mLayout.toggleSidebar();
}
@OverridepublicvoidonSidebarOpened() {
Log.d("Foo", "opened");
}
@OverridepublicvoidonSidebarClosed() {
Log.d("Foo", "opened");
}
@OverridepublicbooleanonContentTouchedWhenOpening() {
Log.d("Foo", "going to close sidebar");
mLayout.closeSidebar();
returntrue;
}
}
This is the AnimationLayout.
/*
* Copyright (C) 2012 0xlab - http://0xlab.org/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Authored by Julian Chu <walkingice AT 0xlab.org>
*/package org.zeroxlab.widget;
import test.julian.hello.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
publicclassAnimationLayoutextendsViewGroup {
publicfinalstaticintDURATION=500;
protectedboolean mOpened;
protected View mSidebar;
protected View mContent;
protectedintmSidebarWidth=150; // by defaultprotected Animation mAnimation;
protected OpenListener mOpenListener;
protected CloseListener mCloseListener;
protected Listener mListener;
protectedbooleanmPressed=false;
publicAnimationLayout(Context context) {
this(context, null);
}
publicAnimationLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@OverridepublicvoidonFinishInflate() {
super.onFinishInflate();
mSidebar = findViewById(R.id.animation_sidebar);
mContent = findViewById(R.id.animation_content);
if (mSidebar == null) {
thrownewNullPointerException("no view id = animation_sidebar");
}
if (mContent == null) {
thrownewNullPointerException("no view id = animation_content");
}
mOpenListener = newOpenListener(mSidebar, mContent);
mCloseListener = newCloseListener(mSidebar, mContent);
}
@OverridepublicvoidonLayout(boolean changed, int l, int t, int r, int b) {
/* the title bar assign top padding, drop it */
mSidebar.layout(l, 0, l + mSidebarWidth, 0 + mSidebar.getMeasuredHeight());
if (mOpened) {
mContent.layout(l + mSidebarWidth, 0, r + mSidebarWidth, b);
} else {
mContent.layout(l, 0, r, b);
}
}
@OverridepublicvoidonMeasure(int w, int h) {
super.onMeasure(w, h);
super.measureChildren(w, h);
mSidebarWidth = mSidebar.getMeasuredWidth();
}
@OverrideprotectedvoidmeasureChild(View child, int parentWSpec, int parentHSpec) {
/* the max width of Sidebar is 90% of Parent */if (child == mSidebar) {
intmode= MeasureSpec.getMode(parentWSpec);
intwidth= (int)(getMeasuredWidth() * 0.9);
super.measureChild(child, MeasureSpec.makeMeasureSpec(width, mode), parentHSpec);
} else {
super.measureChild(child, parentWSpec, parentHSpec);
}
}
@OverridepublicbooleanonInterceptTouchEvent(MotionEvent ev) {
if (!isOpening()) {
returnfalse;
}
intaction= ev.getAction();
if (action != MotionEvent.ACTION_UP
&& action != MotionEvent.ACTION_DOWN) {
returnfalse;
}
/* if user press and release both on Content while
* sidebar is opening, call listener. otherwise, pass
* the event to child. */intx= (int)ev.getX();
inty= (int)ev.getY();
if (mContent.getLeft() < x
&& mContent.getRight() > x
&& mContent.getTop() < y
&& mContent.getBottom() > y) {
if (action == MotionEvent.ACTION_DOWN) {
mPressed = true;
}
if (mPressed
&& action == MotionEvent.ACTION_UP
&& mListener != null) {
mPressed = false;
return mListener.onContentTouchedWhenOpening();
}
} else {
mPressed = false;
}
returnfalse;
}
publicvoidsetListener(Listener l) {
mListener = l;
}
/* to see if the Sidebar is visible */publicbooleanisOpening() {
return mOpened;
}
publicvoidtoggleSidebar() {
if (mContent.getAnimation() != null) {
return;
}
if (mOpened) {
/* opened, make close animation*/
mAnimation = newTranslateAnimation(0, -mSidebarWidth, 0, 0);
mAnimation.setAnimationListener(mCloseListener);
} else {
/* not opened, make open animation */
mAnimation = newTranslateAnimation(0, mSidebarWidth, 0, 0);
mAnimation.setAnimationListener(mOpenListener);
}
mAnimation.setDuration(DURATION);
mAnimation.setFillAfter(true);
mAnimation.setFillEnabled(true);
mContent.startAnimation(mAnimation);
}
publicvoidopenSidebar() {
if (!mOpened) {
toggleSidebar();
}
}
publicvoidcloseSidebar() {
if (mOpened) {
toggleSidebar();
}
}
classOpenListenerimplementsAnimation.AnimationListener {
View iSidebar;
View iContent;
OpenListener(View sidebar, View content) {
iSidebar = sidebar;
iContent = content;
}
publicvoidonAnimationRepeat(Animation animation) {
}
publicvoidonAnimationStart(Animation animation) {
iSidebar.setVisibility(View.VISIBLE);
}
publicvoidonAnimationEnd(Animation animation) {
iContent.clearAnimation();
mOpened = !mOpened;
requestLayout();
if (mListener != null) {
mListener.onSidebarOpened();
}
}
}
classCloseListenerimplementsAnimation.AnimationListener {
View iSidebar;
View iContent;
CloseListener(View sidebar, View content) {
iSidebar = sidebar;
iContent = content;
}
publicvoidonAnimationRepeat(Animation animation) {
}
publicvoidonAnimationStart(Animation animation) {
}
publicvoidonAnimationEnd(Animation animation) {
iContent.clearAnimation();
iSidebar.setVisibility(View.INVISIBLE);
mOpened = !mOpened;
requestLayout();
if (mListener != null) {
mListener.onSidebarClosed();
}
}
}
publicinterfaceListener {
publicvoidonSidebarOpened();
publicvoidonSidebarClosed();
publicbooleanonContentTouchedWhenOpening();
}
}
When the SideBar closed, it looks like this.
When the SideBar opened, it looks like this.
Solution 3:
I took the solution from walkingice (https://github.com/walkingice/gui-sliding-sidebar) and added to it, making a widget in which the "sidebar" can come in from the top or bottom as well as the left or right. You can also specify the sidebar width (or height) as a percent of the parent width (or height). The sidebar can be stationary behind the main content view or slide in.
The project is by SolutionStream, and it's available here: https://github.com/solutionstream/sidebarlayout
It's open source (Apache 2.0 license), so feel free to check out the code and use it (under the license), either as an example or directly.
DISCLOSURE: The above link is to a project that I created myself at SolutionStream.
Post a Comment for "Layout Animation Android[facebook]"