package com.florianmski.tracktoid.ui.widgets; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.Property; import android.view.LayoutInflater; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; import com.florianmski.tracktoid.R; public class ReadMoreView extends LinearLayout implements ValueAnimator.AnimatorUpdateListener { private final static int MAX_LINES = 4; // TODO should remember its state protected View vContent; protected ScrollView sv; protected TextView tvReadMore; protected TextView tvText; private int contentId; private int textId; private int contentMaxHeight; private int contentMinHeight; private int readMoreHeight; private boolean isExpanded = true; private Interpolator interpolator = new AccelerateDecelerateInterpolator(); private static final Property<View, Integer> VIEW_LAYOUT_HEIGHT = new Property<View, Integer>(Integer.class, "viewLayoutHeight") { @Override public void set(View object, Integer value) { object.getLayoutParams().height = value; object.requestLayout(); } @Override public Integer get(View object) { return object.getLayoutParams().height; } }; public ReadMoreView(Context context) { super(context); } public ReadMoreView(Context context, AttributeSet attrs) { super(context, attrs); setOrientation(LinearLayout.VERTICAL); TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.ReadMoreView); // TODO IllegalArgumentException contentId = attributes.getResourceId(R.styleable.ReadMoreView_content, R.id.viewContent); textId = attributes.getResourceId(R.styleable.ReadMoreView_text, R.id.viewContent); attributes.recycle(); tvReadMore = (TextView) LayoutInflater.from(context).inflate(R.layout.view_read_more, this, false); // getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() // { // @Override // public void onGlobalLayout() // { // getViewTreeObserver().removeGlobalOnLayoutListener(this); // // // by default, collapse // setExpanded(false, false); // } // }); } @Override protected void onFinishInflate() { super.onFinishInflate(); vContent = findViewById(contentId); tvText = (TextView) findViewById(textId); addView(tvReadMore); setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { toggle(true); } }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { vContent.measure(widthMeasureSpec, heightMeasureSpec); tvText.measure(widthMeasureSpec, heightMeasureSpec); tvReadMore.measure(widthMeasureSpec, heightMeasureSpec); contentMaxHeight = vContent.getMeasuredHeight(); contentMinHeight = Math.min((tvText.getLineHeight() * MAX_LINES), tvText.getMeasuredHeight()) + tvText.getTotalPaddingTop() + tvText.getTotalPaddingBottom() + vContent.getPaddingTop() + vContent.getPaddingBottom(); readMoreHeight = tvReadMore.getMeasuredHeight(); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } public void setExpanded(boolean expanded, boolean animate) { if(isExpanded != expanded) toggle(animate); } public void toggle(boolean animate) { ObjectAnimator expandCollapseContentAnimator = ObjectAnimator.ofInt(vContent, VIEW_LAYOUT_HEIGHT, vContent.getHeight(), isExpanded ? contentMinHeight : contentMaxHeight); ObjectAnimator expandCollapseReadMoreAnimator = ObjectAnimator.ofInt(tvReadMore, VIEW_LAYOUT_HEIGHT, tvReadMore.getHeight(), isExpanded ? readMoreHeight : 0); AnimatorSet as = new AnimatorSet(); as.play(expandCollapseContentAnimator).with(expandCollapseReadMoreAnimator); as.setInterpolator(interpolator); expandCollapseContentAnimator.addUpdateListener(this); as.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { isExpanded = !isExpanded; } }); as.setDuration(animate ? 300 : 0); as.start(); } public boolean isExpanded() { return isExpanded; } public void makeItScroll(ScrollView scrollView) { this.sv = scrollView; } @Override public void onAnimationUpdate(ValueAnimator animation) { // we want to make it scroll only in case we are collapsed, the content is not higher than the view // and the contentMaxHeight is not enough to from to the bottom of the scrollview // if(sv != null && !isExpanded && contentMaxHeight < sv.getHeight() && (getBottom() + sv.getPaddingTop()) >= sv.getBottom()) // { // // TODO when scrolling to bottom, it's off by some pixels, weird // int pixels = Math.round(sv.getBottom() * animation.getAnimatedFraction()); // sv.scrollTo(0, pixels); // } int bottom = vContent.getBottom() + vContent.getPaddingBottom(); if(sv != null && !isExpanded && bottom > sv.getScrollY()) { sv.scrollTo(0, bottom); } } }