布局动画 Android[Facebook]
Posted
技术标签:
【中文标题】布局动画 Android[Facebook]【英文标题】:Layout Animation Android[Facebook] 【发布时间】:2012-01-25 02:17:07 【问题描述】:我想为两种不同的布局制作动画。
例子
我已经按照我想要的方式制作了动画,我只想为不同的 XML 布局制作动画。 有一个类LayoutAnimationController,但是我真的不知道怎么用。 有人能给我指出正确的方向吗,举个例子或很好的解释。
这是我用来制作动画的代码。
TranslateAnimation slide = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 300f, 0,0 );
slide.setAnimationListener(AL);
slide.setFillAfter(true);
slide.setDuration(1000);
parentlayout.startAnimation(slide);
更新 由于获得了很多赞成票,我决定将一个示例项目放入 Git 存储库中。 请参阅我的答案以获取链接。
【问题讨论】:
【参考方案1】:好的,在花了 2 天时间阅读了类似的问题以及人们如何解决这些问题后,我终于能够创造出我想要的东西。 我无法使用 2 个不同的 XML 文件来做到这一点,但我怀疑这是不可能的。
我确实遇到了一些问题。
第一个动画结束后,按钮不可点击。 这是因为动画显示所有东西都被移动了但并没有更新布局,所以按钮仍然在动画开始的位置。 所以我不得不计算布局的新位置。
我想我在某处读到这在 3.0 中不再是问题,但如果我错了,请纠正我
另一个问题是,当我的动画最终按照我想要的方式工作时,我的底层视图确实在动画完成之前消失了,因为我调用了view.setVisabilty(View.GONE);
。
现在的问题是当我没有调用该方法时,动画只是挂了一秒钟,然后射击到动画的结束位置。
所以我添加了一个空的LinearLayout(可以是任何东西),默认属性为GONE,动画开始时将其设置为Visible。当您还原动画时,再次将其设置为消失。
完成此操作后,动画按我想要的方式工作。
如果您使用的是相对、线性或任何其他布局。 那么您不能按 Z 顺序堆叠视图,因此您必须使用 SurfaceView。
这里是 main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout1"
android:layout_
android:layout_
android:orientation="vertical" >
<SurfaceView
android:id="@+id/surfaceView1"
android:layout_
android:layout_ />
<RelativeLayout
android:id="@+id/layout"
android:layout_
android:layout_
android:background="#ffee00"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/fake_layouy"
android:layout_
android:layout_
android:orientation="vertical" android:visibility="gone">
</LinearLayout>
<ListView
android:id="@+id/listView1"
android:layout_
android:layout_ >
</ListView>
</RelativeLayout>
<RelativeLayout
android:id="@+id/layoutTwo"
android:layout_
android:layout_
android:background="#ff00ee"
android:orientation="vertical">
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_
android:layout_
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" android:background="#ff0000" android:layout_margin="2dp">
<Button
android:id="@+id/button"
android:layout_
android:layout_
android:text="slide" />
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
这是java代码
public class MenuAnimationActivity extends Activity
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 animation
private int oldLeft;
private int oldTop;
private int newleft;
private int newTop;
private int screenWidth;
private int animToPostion;
// TODO change the name of the animToPostion for a better explanation.
private boolean menuOpen = false;
/** Called when the activity is first created. */
@Override
public void onCreate(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(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1 , listViewDummyContent));
display = getWindowManager().getDefaultDisplay();
screenWidth = display.getWidth();
int calcAnimationPosition = (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.LayoutParams params = new RelativeLayout.LayoutParams(animToPostion, RelativeLayout.LayoutParams.FILL_PARENT);
subLayout.setLayoutParams(params);
topLayout.setOnTouchListener(new OnTouchListener()
@Override
public boolean onTouch(View v, MotionEvent event)
if(event.getAction() == MotionEvent.ACTION_DOWN)
if (menuOpen == true)
animSlideLeft();
return false;
);
buttonSwitch.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
if(menuOpen == false)
animSlideRight();
else if (menuOpen == true)
animSlideLeft();
);
AL = new AnimationListener()
@Override
public void onAnimationStart(Animation animation)
buttonSwitch.setClickable(false);
topLayout.setEnabled(false);
@Override
public void onAnimationRepeat(Animation animation)
// TODO Auto-generated method stub
@Override
public void onAnimationEnd(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);
else if(menuOpen == false)
Log.d("","FALSE");
topLayout.layout(newleft, newTop, newleft + topLayout.getMeasuredWidth(), newTop + topLayout.getMeasuredHeight() );
topLayout.setEnabled(true);
menuOpen = true;
buttonSwitch.setClickable(true);
;
public void animSlideRight()
fakeLayout.setVisibility(View.VISIBLE);
newleft = topLayout.getLeft() + animToPostion;
newTop = topLayout.getTop();
TranslateAnimation slideRight = new TranslateAnimation(0,newleft,0,0);
slideRight.setDuration(500);
slideRight.setFillEnabled(true);
slideRight.setAnimationListener(AL);
topLayout.startAnimation(slideRight);
public void animSlideLeft()
fakeLayout.setVisibility(View.GONE);
oldLeft = topLayout.getLeft() - animToPostion;
oldTop = topLayout.getTop();
TranslateAnimation slideLeft = new TranslateAnimation(newleft,oldLeft,0,0);
slideLeft.setDuration(500);
slideLeft.setFillEnabled(true);
slideLeft.setAnimationListener(AL);
topLayout.startAnimation(slideLeft);
我在触摸视图和东西上做了一些额外的编码。
以及最终结果
动画之前
第一个动画之后
在第二个动画返回左侧后,它表示返回为第一个图像。
所有那些帮助我的帖子都值得称赞,但我找不到任何一个。
编辑
GIT https://bitbucket.org/maikelbollemeijer/sidepanelswitcher
更新: https://github.com/jfeinstein10/SlidingMenu 此库与 Actionbar Sherlock 兼容。
希望对你有帮助
【讨论】:
干得好...但是如何选择左侧菜单可以请告诉我..这样我可以打开特定项目。 你的意思是如何在 listView 中选择一个项目?比如我的安卓、黑莓、iPhone? 这很简单,示例:listView.setOnItemClickListener(new OnItemClickListener() public void onItemClick(AdapterView> parent, View view, int rowID, long arg3) // TODO 自动生成的方法存根 ); 感谢您的帮助...它正在工作,但我对此犯了一些错误。所以这就是它不工作的原因。但现在它工作正常.. 这里好,很高兴我能提供帮助【参考方案2】:我有类似的要求,使布局动画像 Facebook 应用程序一样。为此,我制作了一个自定义的 ViewGroup(称为 AnimationLayout)。希望这些代码有所帮助。
AnimationLayout 需要两个子项:Sidebar 和 Content。 (通过将@+id/animation_sidebar 和@+id/animation_content 分配给对应的一个)
这是布局xml,侧边栏有一个按钮和一个列表视图。内容有一个文本视图和一个按钮(它绑定到一个回调函数)。
<?xml version="1.0" encoding="utf-8"?>
<org.zeroxlab.widget.AnimationLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/animation_layout"
android:layout_
android:layout_
>
<LinearLayout
android:id="@+id/animation_sidebar"
android:layout_
android:layout_
android:background="#550000"
android:orientation="vertical"
>
<Button
android:id="@+id/button_test"
android:layout_
android:layout_
android:text="Sidebar Button"
/>
<ListView
android:id="@+id/sidebar_list"
android:layout_
android:layout_
/>
</LinearLayout>
<LinearLayout
android:id="@+id/animation_content"
android:layout_
android:layout_
android:background="#003300"
android:clickable="true"
>
<Button android:id="@+id/button"
android:layout_
android:layout_
android:text="Content Button"
android:onClick="onClickButton"
/>
<TextView android:id="@+id/text"
android:layout_
android:layout_
android:text="The Answer to Life, the Universe, and Everything -- is 42"
/>
</LinearLayout>
</org.zeroxlab.widget.AnimationLayout>
这是测试活动。它初始化一个 ListView 并将自己指定为 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;
public class HelloAndroid extends Activity implements AnimationLayout.Listener
ListView mList;
AnimationLayout mLayout;
String[] mStrings = "a", "b", "c", "d", "e", "f", "g";
@Override
public void onCreate(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(
new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_multiple_choice
, mStrings));
mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
public void onClickButton(View v)
mLayout.toggleSidebar();
@Override
public void onSidebarOpened()
Log.d("Foo", "opened");
@Override
public void onSidebarClosed()
Log.d("Foo", "opened");
@Override
public boolean onContentTouchedWhenOpening()
Log.d("Foo", "going to close sidebar");
mLayout.closeSidebar();
return true;
这是动画布局。
/*
* 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;
public class AnimationLayout extends ViewGroup
public final static int DURATION = 500;
protected boolean mOpened;
protected View mSidebar;
protected View mContent;
protected int mSidebarWidth = 150; // by default
protected Animation mAnimation;
protected OpenListener mOpenListener;
protected CloseListener mCloseListener;
protected Listener mListener;
protected boolean mPressed = false;
public AnimationLayout(Context context)
this(context, null);
public AnimationLayout(Context context, AttributeSet attrs)
super(context, attrs);
@Override
public void onFinishInflate()
super.onFinishInflate();
mSidebar = findViewById(R.id.animation_sidebar);
mContent = findViewById(R.id.animation_content);
if (mSidebar == null)
throw new NullPointerException("no view id = animation_sidebar");
if (mContent == null)
throw new NullPointerException("no view id = animation_content");
mOpenListener = new OpenListener(mSidebar, mContent);
mCloseListener = new CloseListener(mSidebar, mContent);
@Override
public void onLayout(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);
@Override
public void onMeasure(int w, int h)
super.onMeasure(w, h);
super.measureChildren(w, h);
mSidebarWidth = mSidebar.getMeasuredWidth();
@Override
protected void measureChild(View child, int parentWSpec, int parentHSpec)
/* the max width of Sidebar is 90% of Parent */
if (child == mSidebar)
int mode = MeasureSpec.getMode(parentWSpec);
int width = (int)(getMeasuredWidth() * 0.9);
super.measureChild(child, MeasureSpec.makeMeasureSpec(width, mode), parentHSpec);
else
super.measureChild(child, parentWSpec, parentHSpec);
@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
if (!isOpening())
return false;
int action = ev.getAction();
if (action != MotionEvent.ACTION_UP
&& action != MotionEvent.ACTION_DOWN)
return false;
/* if user press and release both on Content while
* sidebar is opening, call listener. otherwise, pass
* the event to child. */
int x = (int)ev.getX();
int y = (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;
return false;
public void setListener(Listener l)
mListener = l;
/* to see if the Sidebar is visible */
public boolean isOpening()
return mOpened;
public void toggleSidebar()
if (mContent.getAnimation() != null)
return;
if (mOpened)
/* opened, make close animation*/
mAnimation = new TranslateAnimation(0, -mSidebarWidth, 0, 0);
mAnimation.setAnimationListener(mCloseListener);
else
/* not opened, make open animation */
mAnimation = new TranslateAnimation(0, mSidebarWidth, 0, 0);
mAnimation.setAnimationListener(mOpenListener);
mAnimation.setDuration(DURATION);
mAnimation.setFillAfter(true);
mAnimation.setFillEnabled(true);
mContent.startAnimation(mAnimation);
public void openSidebar()
if (!mOpened)
toggleSidebar();
public void closeSidebar()
if (mOpened)
toggleSidebar();
class OpenListener implements Animation.AnimationListener
View iSidebar;
View iContent;
OpenListener(View sidebar, View content)
iSidebar = sidebar;
iContent = content;
public void onAnimationRepeat(Animation animation)
public void onAnimationStart(Animation animation)
iSidebar.setVisibility(View.VISIBLE);
public void onAnimationEnd(Animation animation)
iContent.clearAnimation();
mOpened = !mOpened;
requestLayout();
if (mListener != null)
mListener.onSidebarOpened();
class CloseListener implements Animation.AnimationListener
View iSidebar;
View iContent;
CloseListener(View sidebar, View content)
iSidebar = sidebar;
iContent = content;
public void onAnimationRepeat(Animation animation)
public void onAnimationStart(Animation animation)
public void onAnimationEnd(Animation animation)
iContent.clearAnimation();
iSidebar.setVisibility(View.INVISIBLE);
mOpened = !mOpened;
requestLayout();
if (mListener != null)
mListener.onSidebarClosed();
public interface Listener
public void onSidebarOpened();
public void onSidebarClosed();
public boolean onContentTouchedWhenOpening();
SideBar 关闭时的样子。
SideBar 打开时是这样的。
【讨论】:
不错的解决方案,已经修复但很好。感谢您的贡献 在您的解决方案中,按钮等视图是否随布局移动到新位置或保持在其原始位置? 它移动到新位置,以便您可以单击它。我在 github 上为此创建了一个演示项目。(github.com/walkingice/gui-sliding-sidebar)【参考方案3】:我从walkingice (https://github.com/walkingice/gui-sliding-sidebar) 中获取了解决方案并将其添加到其中,制作了一个小部件,其中“侧边栏”可以从顶部或底部以及左侧或右侧进入。您还可以将侧边栏宽度(或高度)指定为父宽度(或高度)的百分比。侧边栏可以固定在主要内容视图后面或滑入。
该项目由 SolutionStream 提供,可在此处获得:https://github.com/solutionstream/sidebarlayout
它是开源的(Apache 2.0 许可),因此请随意查看代码并使用它(根据许可),作为示例或直接使用。
披露:以上链接指向我自己在 SolutionStream 创建的一个项目。
【讨论】:
以上是关于布局动画 Android[Facebook]的主要内容,如果未能解决你的问题,请参考以下文章
让动画不再僵硬:Facebook Rebound Android动画库介绍
在 facebook 应用程序中创建登录布局,如 android