Как можно настроить свойства Animate Layout для ViewGroups? - PullRequest
8 голосов
/ 22 сентября 2011

У меня есть следующий макет:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_height="match_parent"
              android:layout_width="match_parent">

    <FrameLayout android:id="@+id/viewgroup_left"
                 android:layout_height="match_parent"
                 android:layout_weight="2"
                 android:layout_width="0dp">

        ... children ... 

    </FrameLayout>

    <LinearLayout android:id="@+id/viewgroup_right"
                  android:layout_height="match_parent"
                  android:layout_weight="1"
                  android:layout_width="0dp"
                  android:orientation="vertical">

        ... children ...

    </LinearLayout>

</LinearLayout>

Я получаю что-то вроде этого:

  +------------------------+------------+
  |                        |            |
  |                        |            |
  |         Left           |   Right    |
  |                        |            |
  |                        |            |
  +------------------------+------------+

Когда переключается определенный переключатель, я хочу анимировать Left такШирина увеличивается, чтобы заполнить весь экран.В то же время я хотел бы анимировать ширину Right, чтобы она уменьшалась до нуля.Позже, когда тумблер снова переключается, мне нужно вернуть вещи в вышеуказанное состояние.

Я пытался написать свою собственную анимацию, которая вызывает View.getWidth(), но когда я оживляю это значение (установив1010 *) это шире, чем когда это началось.Я подозреваю, что просто делаю это неправильно.Я также прочитал всю документацию по анимации Honeycomb, но я не хочу переводить или масштабировать ... Я хочу анимировать свойство layout layout.Я не могу найти пример этого.

Как правильно это сделать?

Ответы [ 3 ]

12 голосов
/ 23 сентября 2011

Поскольку никто еще не помог вам, и мой первый ответ был таким беспорядком, я постараюсь дать вам правильный ответ на этот раз; -)

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

Но в любом случае, вот код, который отлично работает (вы даже можете переключаться во время его анимации).

Краткое объяснение :
Вы вызываете toggle с логическим значением для вашего направления, и это запустит цикл вызова обработчика анимации. Это увеличит или уменьшит веса обоих видов в зависимости от направления и прошедшего времени (для плавного расчета и анимации). Цикл вызова анимации будет вызывать себя до тех пор, пока он не достиг начальной или конечной позиции.

Макет:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:weightSum="10"
    android:id="@+id/slide_layout">
    <TextView
        android:layout_weight="7"
        android:padding="10dip"
        android:id="@+id/left"
        android:layout_width="0dip"
        android:layout_height="fill_parent"></TextView>
    <TextView
        android:layout_weight="3"
        android:padding="10dip"
        android:id="@+id/right"
        android:layout_width="0dip"
        android:layout_height="fill_parent"></TextView>
</LinearLayout>

Активность:

public class TestActivity extends Activity {

    private static final int ANIMATION_DURATION = 1000;

    private View mSlidingLayout;
    private View mLeftView;
    private View mRightView;

    private boolean mAnimating = false;
    private boolean mLeftExpand = true;
    private float mLeftStartWeight;
    private float mLayoutWeightSum;
    private Handler mAnimationHandler = new Handler();
    private long mAnimationTime;

    private Runnable mAnimationStep = new Runnable() {
        @Override
        public void run() {
            long currentTime = System.currentTimeMillis();
            float animationStep = (currentTime - mAnimationTime) * 1f / ANIMATION_DURATION;
            float weightOffset = animationStep * (mLayoutWeightSum - mLeftStartWeight);

            LinearLayout.LayoutParams leftParams = (LinearLayout.LayoutParams)
                    mLeftView.getLayoutParams();
            LinearLayout.LayoutParams rightParams = (LinearLayout.LayoutParams)
                    mRightView.getLayoutParams();

            leftParams.weight += mLeftExpand ? weightOffset : -weightOffset;
            rightParams.weight += mLeftExpand ? -weightOffset : weightOffset;

            if (leftParams.weight >= mLayoutWeightSum) {
                mAnimating = false;
                leftParams.weight = mLayoutWeightSum;
                rightParams.weight = 0;
            } else if (leftParams.weight <= mLeftStartWeight) {
                mAnimating = false;
                leftParams.weight = mLeftStartWeight;
                rightParams.weight = mLayoutWeightSum - mLeftStartWeight;
            }

            mSlidingLayout.requestLayout();

            mAnimationTime = currentTime;

            if (mAnimating) {
                mAnimationHandler.postDelayed(mAnimationStep, 30);
            }
        }
    };

    private void toggleExpand(boolean expand) {
        mLeftExpand = expand;

        if (!mAnimating) {
            mAnimating = true;
            mAnimationTime = System.currentTimeMillis();
            mAnimationHandler.postDelayed(mAnimationStep, 30);
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.slide_test);

        mLeftView = findViewById(R.id.left);
        mRightView = findViewById(R.id.right);
        mSlidingLayout = findViewById(R.id.slide_layout);

        mLeftStartWeight = ((LinearLayout.LayoutParams)
                mLeftView.getLayoutParams()).weight;
        mLayoutWeightSum = ((LinearLayout) mSlidingLayout).getWeightSum();
    }
}
2 голосов
/ 02 июня 2014

Просто добавив мои 2 цента здесь, к отличному ответу Knickedi - на тот случай, если кому-то это понадобится:

Если вы анимируете с использованием весов, у вас возникнут проблемы с отсечением / отсечением для содержащихся представлений и групп просмотра.Это особенно верно, если вы используете группы просмотра с весом в качестве контейнеров фрагментов.Чтобы преодолеть это, вам также может понадобиться анимировать поля проблемных дочерних представлений и контейнеров групп просмотра / фрагментов.

И, чтобы делать все это вместе, всегда лучше использовать ObjectAnimator и AnimatorSet (если вы можете их использовать) вместе с некоторыми служебными классами, такими как MarginProxy

1 голос
/ 23 ноября 2015

Другим способом решения, опубликованного @knickedi, является использование ObjectAnimator вместо Runnable. Идея состоит в том, чтобы использовать ObjectAnimator для регулировки веса левого и правого представлений. Однако представления должны быть настроены так, чтобы вес можно было выставить как свойство для анимации ObjectAnimator.

Итак, сначала определите настроенное представление (на примере LinearLayout):

public class CustomLinearLayout extends LinearLayout {
    public CustomLinearLayout(Context context) {
        super(context);
    }

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

    public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setMyWeight(float value) {
        LinearLayout.LayoutParams p = (LinearLayout.LayoutParams)getLayoutParams();
        p.weight = value;
        requestLayout();
    }
}

Затем обновите XML макета, чтобы использовать этот пользовательский линейный макет.

Затем, когда вам нужно переключить анимацию, используйте ObjectAnimator:

ObjectAnimator rightView = ObjectAnimator.ofFloat(viewgroup_right, "MyWeight", 0.5f, 1.0f);
ObjectAnimator leftView = ObjectAnimator.ofFloat(viewgroup_left, "MyWeight", 0.5f, 0.0f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(1000); // 1 second of animation
animatorSet.playTogether(rightView, leftView);
animatorSet.start();

В приведенном выше коде предполагается, что оба вида имеют линейную компоновку и имеют половину веса для начала. Анимация расширит правый вид до полного веса (поэтому левый будет скрыт). Обратите внимание, что ObjectAnimator анимируется с помощью свойства «MyWeight» настроенного линейного макета. AnimatorSet используется для связывания левого и правого ObjectAnimators вместе, поэтому анимация выглядит плавно.

Такой подход уменьшает необходимость в написании исполняемого кода и вычислении веса внутри него, но для него требуется определенный класс.

...