Макет анимации Android[Facebook]
Я хочу анимировать два разных макета.
пример
У меня уже есть анимация, как я хочу, я просто хочу анимировать другой макет 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. Смотрите мои ответы по ссылке.
3 ответа
Хорошо, потратив 2 дня на чтение о проблемах similair и о том, как люди их решают, я наконец смог создать то, что хотел. Я не смог сделать это с 2-мя различными XML-файлами, но сомневаюсь, что это невозможно.
Я столкнулся с некоторыми проблемами, хотя.
После окончания первой анимации кнопка не была кликабельной. Это связано с тем, что анимация показывает, что все перемещено, но не обновляет макет, поэтому кнопка все еще находится в том месте, где началась анимация. Поэтому мне пришлось рассчитать новую позицию макета.
Я думаю, что где-то читал, что это больше не проблема в 3.0, но исправьте меня, если я ошибаюсь
Во-вторых, когда моя анимация, наконец, работала так, как я хотел, мой нижний вид исчезал до завершения анимации, потому что я вызывал view.setVisabilty(View.GONE);
, Теперь проблема заключалась в том, что когда я не вызывал этот метод, анимация просто зависала на секунду, а затем переместилась в конечную позицию анимации. Поэтому я добавил пустое свойство LinearLayout (может быть любым), свойство Default в GONE, когда анимация запускается, установите его в Visible. когда вы вернете анимацию, снова установите ее на. после этого анимация работала так, как я хотел.
И если вы используете Rel, Linear или любой другой макет. затем вы не можете составлять стеки в 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);
}
}
Я сделал несколько дополнительных кодов для трогательных взглядов и прочего.
И конечный результат
перед анимацией
после первой анимации
И после второй анимации слева она заявляет, что возвращается как первое изображение.
Все те посты, которые мне помогли, действительно заслуживают похвалы, но я не могу найти ни одного из них.
редактировать
GIT https://bitbucket.org/maikelbollemeijer/sidepanelswitcher
Обновление: https://github.com/jfeinstein10/SlidingMenu эта библиотека совместима с Actionbar Sherlock.
надеюсь это поможет
У меня было такое же требование, чтобы сделать анимацию макета, как приложение Facebook Для этого я создал настроенную ViewGroup (называемую AnimationLayout). Надеюсь, что этот код помогает.
Для AnimationLayout требуется два дочерних элемента: боковая панель и контент. (присваивая @+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_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();
}
}
Когда боковая панель закрыта, это выглядит так.
Когда открывается боковая панель, это выглядит так.
Я взял решение от walkice ( https://github.com/walkingice/gui-sliding-sidebar) и добавил к нему, создав виджет, в котором "боковая панель" может входить как сверху, так и снизу, а также слева. или право. Вы также можете указать ширину боковой панели (или высоту) в процентах от родительской ширины (или высоты). Боковая панель может быть неподвижной за основным представлением контента или вставляться внутрь.
Проект от SolutionStream, и он доступен здесь: https://github.com/solutionstream/sidebarlayout
Это открытый исходный код (лицензия Apache 2.0), поэтому не стесняйтесь проверять код и использовать его (по лицензии), либо в качестве примера, либо напрямую.
РАСКРЫТИЕ: Приведенная выше ссылка относится к проекту, который я сам создал в SolutionStream.