2015-12-08 14 views
10

używam poniżej dwóch metod (inspired/copied from here) do expand i collapse niektóre TextViews w ScrollView klikając na „nagłówku” - TextView.Expand/Collapse Animacja: Małe opóźnienie || MeasureSpec zwraca błędną wartość

Pseudo struktura układ:

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

Przegroda jest prosty View, height zestaw do 1dp. Na treści TextViews styl obejmuje:

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

i pewien margines & wyściółkę.

Metody tutaj:

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; 
} 

Problem tutaj: Wszystko działa poprawnie - aż expand animation osiągnie ostatnią linię tekstu - jeśli istnieje zbyt mało characters na nim, to pozostaje, skoki, wybucha? - jakkolwiek chcesz to nazwać - aż do pełnego rozwinięcia.

Collapsing wydaje się działać dobrze.

Próbowałem innych wartości Interpolator, innego mnożnika w metodzie computeDurationFromHeight.

Niektóre badania:

    • 4 linie, na czwartej linii wszystkiego więcej niż 17 znaków działa dobrze, mniej niż 18 znaków i to tyle.

    • 3 linie i nieistotna ilość znaków na ostatniej linii działającej poprawnie.

    • czasami animation działa na pierwszym expand, ale nie na drugim.

    • Wygląda na to, że TextView zostanie obliczony nieprawidłowo. Z wysokim multiplier Widziałem niektóre text plopping się na < 0,5s ciągu następnego nagłówka TextView
    • usuwając smoothScrollTo w expand niczego nie zmienia (oprócz przewijania oczywiście ..)
    • inne interpolatorami również „czkawki” , ale krótszy

    ważne:

  • niektóre Logowanie applyTransformation (patrz poniżej) mnie do t Wskazuje, że widzę, że final height jest drukowany dwa razy z dokładnością do 50 punktów (piksele? dp?) różnica. //smoothly increasing height and then: final height = 202 height = 252 final height = 252 Podczas gdy otrzymam targetHeight = 203 - więc height zostanie najpierw błędnie obliczony, ale potem pojawia się jakaś magia?

@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()); 
    } 
} 

Czy ktoś może wskazać, co mi brakuje?

+0

Czy istnieje powód, dla którego sprawdzasz, czy interpolowany czas = 1, a następnie ustawiasz wysokość na WRAP_CONTENT? Czemu to robić? Dlaczego po prostu nie powiedzieć, że wysokość jest zawsze równa interpolatedTime * targetHeight? Kiedy interpolowany czas = 1, wtedy wysokość będzie celować w miarę pożądanego poziomu. Myślę, że ustawienie wysokości WRAP_CONTENT na końcu jest przyczyną jąkania. –

+0

Wziąłem tę linię z powyższej odpowiedzi - jeśli używam tylko "interpolatedTime * celHeight", ostatnia linia nie jest drukowana w ogóle. Już odkryłem, że problem jest spowodowany błędnym obliczeniem 'height' ..:/ – yennsarah

+0

' v.requestLayout(); 'sprawia, że ​​widok rodzica wywołania metody() i wszystkie dzieci są ponownie układ. Kolejne połączenia mogą prowadzić do opóźnień. Próba animowania wartości Rect zamiast parametrów układu. –

Odpowiedz

0

może to spowodować ponieważ wydatkowaniu ostatni element może wywołać przewijanie ponieważ wysokość układ coraz większe, więc „Push” wyświetlacz dół

próby dodania FrameLayout na dole z wysokości 20dp i spróbować obserwator jeśli czyni żadnej różnicy

+0

Niestety, to nic nie zmienia. – yennsarah

0

jestem 99% pewien, że trzeba zmienić <item name="android:layout_height">0dp</item> (z TextView animowany), aby być wrap_content i ustaw jego stan początkowy do GONE (jak to ty stan końcowy po animacji zwinięcia).

+0

To nie działa. : / – yennsarah

Powiązane problemy