Подтвердить что ты не робот

Развернуть/свернуть Анимация: Малый лаг || MeasureSpec возвращает неверное значение

Я использую следующие два метода (вдохновленные/скопированные здесь) в expand и collapse некоторые TextViews в ScrollView, нажав на "header" - TextView.

Структура псевдоструктуры:

<ScrollView>
    <LinearLayout>
        <LinearLayout>
            <!-- some other stuff here -->
        </LinearLayout>
        <TextView "header1"/>
        <View "fancydivider"/>
        <TextView "content1">
        <TextView "header2"/>
        <View "fancydivider"/>
        <TextView "content2">
    </LinearLayout>
</ScrollView>

Divider - это простой View, height для 1dp. Стиль content-TextViews включает в себя:

    <item name="android:layout_height">0dp</item>
    <item name="android:layout_width">match_parent</item>

и некоторый запас и отступ.

Способы здесь:

public static void expand(final View v) {

    //v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

    int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
    int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);

    final int targetHeight = v.getMeasuredHeight();

    // Older versions of android (pre API 21) cancel animations for views with a height of 0.
    v.getLayoutParams().height = 1;
    v.setVisibility(View.VISIBLE);
    Animation a = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            v.getLayoutParams().height = interpolatedTime == 1
                    ? ViewGroup.LayoutParams.WRAP_CONTENT
                    : (int) (targetHeight * interpolatedTime);
            scrollView.smoothScrollTo(0, (int) (targetHeight * interpolatedTime));
            v.requestLayout();
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };

    a.setInterpolator(easeInOutQuart);
    a.setDuration(computeDurationFromHeight(v));
    v.startAnimation(a);

}

public static void collapse(final View v) {
    final int initialHeight = v.getMeasuredHeight();

    Animation a = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if (interpolatedTime == 1) {
                v.setVisibility(View.GONE);
            } else {
                v.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime);
                v.requestLayout();
            }
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };

    a.setInterpolator(easeInOutQuart);
    a.setDuration(computeDurationFromHeight(v));
    v.startAnimation(a);
}

private static int computeDurationFromHeight(View view) {
    // 1dp/ms * multiplier
    return (int) (view.getMeasuredHeight() / view.getContext().getResources().getDisplayMetrics().density) * 4;
}

Проблема здесь: Все работает нормально - до тех пор, пока expand animation не дойдет до последней строки текста - если на нем слишком мало characters, то она лагает, прыгает, взрывается? - однако вы хотите назвать его - до полного расширения.

Collapsing, похоже, работает нормально.

Я пробовал другие значения Interpolator, другой множитель в методе computeDurationFromHeight.

Некоторые тесты:

    • 4 строки, на четвертой строке все более 17 символов работает нормально, меньше 18 символов и отстает.

    • 3 строки и нерелевантное количество символов в последней строке работают нормально.

    • иногда animation работает на первом expand, но не на втором.

    • Кажется, что TextView вычисляется неправильно. С высоким multiplier я видел, как некоторые text всплывали для < 0.5s над следующим заголовком TextView
    • удаление smoothScrollTo в expand ничего не меняет (кроме прокрутки, конечно..)
    • Другие интерполяторы также имеют "икоты", но более короткие

важно:

Некоторые записи в applyTransformation (см. ниже) привели меня к сути, что я вижу, что final height печатается дважды - с разницей в 50 точек (пикселей? dp?). //smoothly increasing height and then: final height = 202 height = 252 final height = 252 Пока я получаю targetHeight = 203 - так что height сначала вычисляется неправильно, но потом происходит какая-то магия?
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    v.getLayoutParams().height = interpolatedTime == 1
                    ? ViewGroup.LayoutParams.WRAP_CONTENT
                    : (int) (targetHeight * interpolatedTime);
    v.requestLayout();

    scrollView.smoothScrollTo(0, interpolatedTime == 1
                    ? v.getHeight() : (int) (targetHeight * interpolatedTime));

    Log.d("Anim", "height = " + v.getHeight());
    if (interpolatedTime == 1){
        Log.d("Anim", "final height = " + v.getHeight());
    }
}

Может ли кто-нибудь указать, что мне не хватает?

4b9b3361

Ответ 1

это может быть вызвано тем, что использование последнего элемента может вызвать прокрутку, потому что высота макета становится больше, поэтому "нажмите" дисплей вниз

попробуйте добавить FrameLayout внизу с высотой 20dp и попытаться обозревать, если это имеет значение

Ответ 2

Я на 99% уверен, что вам нужно изменить <item name="android:layout_height">0dp</item> (анимированного TextView) на wrap_content и установить его начальное состояние на GONE (так как это окончательное состояние после краха анимация).