2010-03-31 2 views
54

Я пытаюсь анимировать простой ImageView в своем приложении, и я хочу, чтобы он скользил в нижней части экрана и приходил в положение покоя, где верхние 50 пикселей представления находятся в верхней части экрана (например, конечная позиция ImageView должна быть -50px в X). Я попытался использовать AbsoluteLayout для этого, но это фактически отсекает верхние 50 пикселей ImageView таким образом, что верхний 50px никогда не отображается. Мне нужно, чтобы верхняя часть 50 пикселей изображения была видна/визуализирована во время анимации, а затем просто немного успокоилась. Надеюсь, я объяснил это достаточно хорошо.Android - Как позиционировать View off-screen?

Вот что я в настоящее время используют в качестве макета и слайд-анимации (это в настоящее время не оказывает верхнюю 50px в ImageView):

Layout:

<?xml version="1.0" encoding="utf-8"?> 
    <AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android" 
     android:layout_height="fill_parent" 
     android:layout_width="fill_parent" 
     android:id="@+id/QuickPlayClipLayout"> 
     <ImageView android:id="@+id/Clip" 
     android:background="@drawable/clip" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:layout_y="-50dp"> 
     </ImageView> 
    </AbsoluteLayout> 

Animation :

<?xml version="1.0" encoding="utf-8"?> 
<set xmlns:android="http://schemas.android.com/apk/res/android"> 
    <translate android:fromYDelta="100%p" 
     android:toYDelta="0" 
     android:duration="1000"/> 
    <alpha android:fromAlpha="0.0" 
     android:toAlpha="1.0" 
     android:duration="1000" /> 
</set> 

Заранее благодарен.

+1

также см ' view.setTranslationX() 'или' view.offsetLeftAndRight() 'или' mainViewPanel.animate(). x (500); ' –

ответ

32

Я придумал решение, которое должно быть легко реализовано. Это включает в себя модификацию макета и активность, раздувающую макет ... см. Ниже:

Деятельность (QuickPlay.Java):

public class QuickPlay extends Activity implements AnimationListener 
{ 
    private ImageView myImageView; 
    private LinearLayout LL; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     this.setContentView(R.layout.quick_play_screen); 

     myImageView = (ImageView) this.findViewById(R.id.Clip); 
     LL = (LinearLayout) this.findViewById(R.id.QuickPlayClipLayout); 

     //finally 
     Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_in_quickplay); 
     anim.setAnimationListener(this); 
     LL.startAnimation(anim); 
    } 
    @Override 
    public void onAnimationEnd(Animation animation){} 

    @Override 
    public void onAnimationRepeat(Animation animation){} 

    @Override 
    public void onAnimationStart(Animation animation) 
    { 
     // This is the key... 
     //set the coordinates for the bounds (left, top, right, bottom) based on the offset value (50px) in a resource XML 
     LL.layout(0, -(int)this.getResources().getDimension(R.dimen.quickplay_offset), 
       LL.getWidth(), LL.getHeight() + (int)this.getResources().getDimension(R.dimen.quickplay_offset)); 
    } 
} 

Новый LinearLayout (CustomLinearLayout.java):

public class CustomLinearLayout extends LinearLayout 
{ 
    private Context myContext; 

    public CustomLinearLayout(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     myContext = context; 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec+((int)myContext.getResources().getDimension(R.dimen.quickplay_offset))); 
    } 
} 

Layout (/res/layout/quick_play_screen.xml):

<?xml version="1.0" encoding="utf-8"?> 
    <com.games.mygame.CustomLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
     android:layout_height="fill_parent" 
     android:layout_width="fill_parent" 
     android:id="@+id/QuickPlayClipLayout"> 
     <ImageView android:id="@+id/Clip" 
     android:background="@drawable/clip" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content"> 
     </ImageView> 
    </com.games.mygame.CustomLinearLayout> 

ресурсов (/ RES/значения /constants.xml):

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <dimen name="quickplay_offset">50dp</dimen> 
</resources> 

An Imation (/res/anim/slide_in_quickplay.xml):

<?xml version="1.0" encoding="utf-8"?> 
<set xmlns:android="http://schemas.android.com/apk/res/android"> 
    <translate android:fromYDelta="100%p" 
     android:toYDelta="0" 
     android:duration="1000"/> 
    <alpha android:fromAlpha="0.0" 
     android:toAlpha="1.0" 
     android:duration="1000" /> 
</set> 

Теперь программа делает именно то, что мне нужно это делать. Весь макет начинается с экрана внизу, скользит через 1 секунду и приходит в себя, где верхняя часть макета фактически находится на 50 пикселей сверху экрана (т.е. LL.getTop() = -50), а нижняя часть макета располагается внизу экрана (т.е. LL.getBottom() = 530 = 480 + 50).

+1

Невозможно подстраховать вас достаточно. Вы являетесь спасателем жизни. – Krishnabhadra

9

Это легко сделать, если вы сделаете прыжок, используя Canvas; те поддержки, которые снимают экран без каких-либо проблем. Однако это будет сложнее реализовать. Вам нужно будет реализовать пользовательский View и написать собственную анимацию в коде. По сути, это сводится к простой обработке 2D-графики, а не к представлениям с использованием встроенных XML-анимаций. Возможно, есть способ сделать это с помощью XML, но я больше знаком с холстами. Очень хорошее место, чтобы увидеть, как это обрабатывается в коде, - это пример игры Lunar Lander, который поставляется с SDK.

Грубо шагов вы должны следовать, являются:

  1. Поместите настраиваемое представление в файле XML, используя нечто вроде <your.package.AnimatingView>, установив его размер для заполнения родителя.

  2. Затем определите класс AnimatingView, который extends SurfaceView and implements SurfaceHolder.Callback. (Это дает вам доступ к чертежу Canvas, а не с помощью метода invalidate(). Это важно, потому что invalidate() обновляется только тогда, когда поток неактивен, например, в конце цикла. Для реализации вашей анимации вам необходимо иметь он рисует немедленно.)

  3. Затем вы можете реализовать цикл, который рисует ваше движущееся изображение по экрану. Цикл должен начинаться с рисования всего фона (потому что холст автоматически не стирается), а затем рисует изображение в новой позиции в зависимости от пройденного времени. Например, если вы хотите, чтобы ваша анимация заняла 1 секунду, то вы знаете, что если прошло 200 мс, представление должно было перемещать только 200/1000 или 1/5 пути от начальной позиции до конечной позиции ,

Вы можете увидеть некоторые примеры того, что я имею в виду в других моих ответов на вопросы анимации: основной ответ о usefulness of using SurfaceView и и example of the loop to use. Примечание: второй вопрос касался поворота, и, следовательно, некоторые из трудностей, о которых я говорил, не будут иметь к вам отношения. Удачи!

+0

Прежде всего, спасибо за ответ. То, что вы описали выше, было тем, что, как я думал, мне тоже нужно было делать, но надеялся, что есть еще один/более простой способ получить те же результаты. Как оказалось, я выяснил более простой способ (см. Мое опубликованное решение). Это может быть не самое лучшее или самое элегантное, но оно работает. – RyanM

30

Чтобы разместить свое мнение закадровый я использовал следующий код:

View myView = /* view you want to position offscreen */ 
int amountOffscreen = (int)(myView.getWidth() * 0.8); /* or whatever */ 
boolean offscreen = /* true or false */ 


int xOffset = (offscreen) ? amountOffscreen : 0; 
RelativeLayout.LayoutParams rlParams = 
    (RelativeLayout.LayoutParams)myView.getLayoutParams(); 
rlParams.setMargins(-1*xOffset, 0, xOffset, 0); 
myView.setLayoutParams(rlParams); 

Этот код будет позиционировать MyView закадровый по amountOffscreen, который в данном случае ставит 80% зрения закадровый оставляя только 20% на экране.

Не используйте метод layout() напрямую - Android будет выполнять последующие вызовы, чтобы аннулировать ваше представление по случайным причинам, и только layoutParams сохраняются по недействительным вызовам. Если вам интересно, посмотрите строки 904 to 912 of this file, чтобы узнать, почему вам нужно изменить layoutParams.

+0

Это отличная техника, но она вызывает мерцание в myView, когда на нее вызывается setLayoutParams (потому что setLayoutParams в свою очередь вызывает requestLayout). Есть ли хороший способ избежать мерцания? – PacificSky

+0

@PacificSky Я удаляю и добавляю представление в макет, а затем invalidate(). Это работает для меня, и я не вижу никаких мерцаний. Код: layout.removeView (view); layout.addView (view, layoutParams);недействительными(); – Niels

+1

Это замечательно –

2

У меня была группа для просмотра с детьми и перетаскивала взгляд на экран снизу - вроде как ящик. Я тогда отпустил бы, и если верхняя граница верхней группы представлений была в верхней половине экрана, я бы анимировал ее сверху после того, как пользователь выпустил прикосновение.

Когда это произошло, изображения в дочерних группах будут обрезаны во время анимации, но затем будут показаны после анимации.

Проблема: высота группы просмотра была wrap_content. Я решил это, установив высоту на значение, которое растянулось на экране перед началом анимации.

21

Вместо этого мы можем просто дать отрицательные значения layout_margin (Top/влево/вправо/снизу) например: Если вы хотите, чтобы ваш взгляд быть выключен из верхней части экрана, вы можете указать

android:layout_marginTop="-40dp" 
+0

сэкономил мне массу времени от опробования принятого большого ответа. – Sriram

+0

вы прекрасные –

+0

Это должен быть принятый ответ. – Matt

Смежные вопросы