2011-12-21 130 views
9

我想動畫兩個不同的佈局。佈局動畫Android [Facebook]

the way I want

我已經有我想要的方式,我只想動畫不同的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倉庫。 看到我的答案的鏈接。

回答

16

好花2個,每天看有關similair問題,以及如何解決的人他們我終於能夠創造我想要的東西之後。 我不能用2個不同的XML文件來完成,但我懷疑這是不可能的。

我確實遇到過一些問題。

第一動畫結束後,該按鈕是不能點擊。 這是因爲動畫顯示所有內容都已移動,但不會更新佈局,因此該按鈕仍處於動畫開始的位置。 所以我不得不計算佈局的新位置。

我覺得我讀的地方,這不再是在3.0的問題,但糾正我,如果我錯了

另一個原因是,當我有我的動畫終於工作的方式,我希望我的墊層鑑於以前那樣disapear動畫完成了,因爲我調用了view.setVisabilty(View.GONE);。 現在的問題是,當我沒有調用該方法時,動畫只是掛了一秒,然後射擊到動畫的結束位置。 因此,當動畫開始將它設置爲可見時,我在GONE上添加了一個空的LinearLayout(可以是任何東西),默認屬性。當您恢復動畫時,請將其重新設置。這樣做後動畫工作我想要的方式。

如果您使用的是相對,直線,或任何其他佈局。 然後你不能在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_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical" > 

    <SurfaceView 
     android:id="@+id/surfaceView1" 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" /> 

    <RelativeLayout 
     android:id="@+id/layout" 
     android:layout_width="220dp" 
     android:layout_height="fill_parent" 
     android:background="#ffee00" 
     android:orientation="vertical" > 

     <LinearLayout 
      android:id="@+id/fake_layouy" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:orientation="vertical" android:visibility="gone"> 
     </LinearLayout> 

     <ListView 
      android:id="@+id/listView1" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" > 
     </ListView> 
    </RelativeLayout> 

    <RelativeLayout 
     android:id="@+id/layoutTwo" 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" 
     android:background="#ff00ee" 
     android:orientation="vertical"> 

     <LinearLayout 
      android: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"> 

      <Button 
       android:id="@+id/button" 
       android:layout_width="50dp" 
       android:layout_height="wrap_content" 
       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);     
     } 
} 

我做了一些額外的編碼上觸摸的意見和東西。

而最終的結果

之前動畫

enter image description here

後的第一個動畫

enter image description here

而第二個動畫回到離開它指出的回報作爲第一後圖片。

那些幫助我真的值得信任的帖子,但我無法找到他們。

編輯

GIThttps://bitbucket.org/maikelbollemeijer/sidepanelswitcher

更新: https://github.com/jfeinstein10/SlidingMenu 這個Lib是動作條福爾摩斯兼容。

希望這有助於

+0

不錯的工作......但如何選擇左側菜單可以請告訴我..所以我可以打開特定項目。 – 2012-11-20 19:32:40

+0

你的意思是如何選擇listView中的一個項目?就像我的情況Android,黑莓,iPhone? – 2012-11-20 20:16:32

+0

是這item..select任何特定項目顯示它的菜單屏幕上.. – 2012-11-21 05:21:29

8

我有類似的要求,使類似Facebook的應用程序佈局動畫。爲此,我做了一個定製的ViewGroup(稱爲AnimationLayout)。希望這些代碼的幫助。

AnimationLayout需要兩個孩子:側邊欄和內容。 (通過分配@ + id/animation_sidebar和@ + id/animation_content到相應的一個)

這是佈局xml,SideBar有一個按鈕和一個列表視圖。內容有一個文本視圖和一個按鈕(它綁定到一個回調函數)。

<?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_width="match_parent" 
    android:layout_height="match_parent" 
    > 
    <LinearLayout 
     android:id="@+id/animation_sidebar" 
     android:layout_width="200dip" 
     android:layout_height="match_parent" 
     android:background="#550000" 
     android:orientation="vertical" 
     > 
     <Button 
      android:id="@+id/button_test" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:text="Sidebar Button" 
      /> 
     <ListView 
      android:id="@+id/sidebar_list" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      /> 
    </LinearLayout> 
    <LinearLayout 
     android:id="@+id/animation_content" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:background="#003300" 
     android:clickable="true" 
     > 
     <Button android:id="@+id/button" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:text="Content Button" 
      android:onClick="onClickButton" 
      /> 
     <TextView android: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> 

這是測試活動。它初始化一個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; 
    } 
} 

這是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; 

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關閉時,它看起來像這樣。

http://i.stack.imgur.com/tynLw.png

在側欄打開時,它看起來是這樣的。

http://i.stack.imgur.com/QJC8q.png

+0

不錯的解決方案,已經修好了,但不錯。 Thanx在您的解決方案中貢獻了 – 2012-03-08 21:19:22

+0

,按鈕等視圖會隨着佈局移動到新的位置還是保持原來的位置? – 2cupsOfTech 2012-07-17 08:19:20

+0

它將移動到新位置,以便您可以單擊它。我在github上創建了一個演示項目(https://github.com/walkingice/gui-sliding-sidebar) – walkingice 2012-07-18 18:22:29

0

我從walkingice(https://github.com/walkingice/gui-sliding-sidebar)的水溶液,並添加到它,使一個小窗口,其中「側邊欄」,可以從頂部或底部進來,以及左或右。您還可以將邊欄寬度(或高度)指定爲父寬度(或高度)的百分比。側邊欄可以在主內容視圖或幻燈片後面靜止

該項目是SolutionStream,這是可以在這裏找到。https://github.com/solutionstream/sidebarlayout

它是開源的(Apache 2的。0許可證),因此請隨時查看代碼並使用它(根據許可證),作爲示例或直接使用。

披露:上面的鏈接是我創造了自己在SolutionStream項目。