自定义可展开、伸缩的textView

效果

代码

ExpandTextView:

public class ExpandTextView extends LinearLayout {
    private static final int COLLAPSED_LINES = 2;
    private static final int DEFAULT_ANIM_DURATION = 100;
    private static final float DEFAULT_ANIM_ALPHA_START = 0.7f;

    private int maxCollapsedLines;
    private final int animationDuration;
    private final float animAlphaStart;
    private int textHeightWithMaxLines;
    private int marginBetweenTxtAndBottom;
    private int collapsedHeight;
    private boolean mRelayout = true;
    private boolean mCollapsed = true;
    private boolean animating;
    private boolean expandable;

    private TextView expandContent;

    public ExpandTextView(Context context) {
        this(context, null);
    }

    public ExpandTextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public ExpandTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        LayoutInflater.from(context).inflate(R.layout.expand_text_view, this);
        expandContent = (TextView) findViewById(R.id.expand_content);
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ExpandTextView);
        maxCollapsedLines = typedArray.getInt(R.styleable.ExpandTextView_collapseLine, COLLAPSED_LINES);
        animationDuration = typedArray.getInt(R.styleable.ExpandTextView_animationDuration, DEFAULT_ANIM_DURATION);
        animAlphaStart = typedArray.getFloat(R.styleable.ExpandTextView_animationAlphaStart, DEFAULT_ANIM_ALPHA_START);
        typedArray.recycle();
        setOrientation(LinearLayout.VERTICAL);
}

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!mRelayout || getVisibility() == View.GONE) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        mRelayout = false;

        // Setup with optimistic case, Everything fits. No button needed
        expandContent.setMaxLines(Integer.MAX_VALUE);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // If the text fits in collapsed mode, we are done.
        if (expandContent.getLineCount() <= maxCollapsedLines) {
            expandable = false;
           collapsedHeight = getMeasuredHeight();
        } else {
            expandable = true;
        }

        // Saves the text height with max lines
        textHeightWithMaxLines = getRealTextViewHeight(expandContent);
        // Doesn't fit in collapsed mode. Collapse text view as needed. Show button.
        if (mCollapsed) {
            expandContent.setMaxLines(maxCollapsedLines);
        }
        // Re-measure with new setup
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mCollapsed) {
            // Gets the margin between the TextView's bottom and the ViewGroup's bottom
            expandContent.post(new Runnable() {
                @Override
                public void run() {
                    marginBetweenTxtAndBottom = getHeight() - expandContent.getHeight();
                }
            });
            // Saves the collapsed height of this ViewGroup
            collapsedHeight = getMeasuredHeight();
        }
    }

    public boolean Expandable() {
        return expandable;
    }

    public void setMaxLines(int maxLines) {
        maxCollapsedLines = maxLines;
    }

    private static int getRealTextViewHeight(@NonNull TextView textView) {
        Layout layout = textView.getLayout();
        int textHeight = 0;
        int padding = 0;
        if(layout != null) {
            int lineCount = textView.getLineCount();
            textHeight = textView.getLayout().getLineTop(lineCount);
           padding = textView.getCompoundPaddingTop() + textView.getCompoundPaddingBottom();
        }
        return textHeight + padding;
    }

    public void expand() {
        mCollapsed = !mCollapsed;
        int startHeight = getHeight();
        int endHeight = getHeight() + textHeightWithMaxLines - expandContent.getHeight();
        applyAnimation(this, startHeight, endHeight, animationDuration);
    }

    public void collapse() {
        mCollapsed = !mCollapsed;
        int startHeight = getHeight();
        int endHeight = collapsedHeight;
        applyAnimation(this, startHeight, endHeight, animationDuration);
    }

    public void setText(@Nullable CharSequence text) {
        expandContent.setText(text);
    }

    //setText and then update mRelayout value.
    public void updatetText() {
        mRelayout = true;
        requestLayout();
        invalidate();
    }

    /*public void setEllipsize(TextUtils.TruncateAt ellipse) {
        expandContent.setEllipsize(ellipse);
    }*/

    public CharSequence getText() {
        if (expandContent == null) {
            return "";
        }
        return expandContent.getText();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // while an animation is in progress, intercept all the touch events to children to
        // prevent extra clicks during the animation
        return animating;
    }

    @Override
    public void setOrientation(int orientation) {
        if (LinearLayout.HORIZONTAL == orientation) {
            throw new IllegalArgumentException("ExpandableTextView only supports Vertical Orientation.");
       }
        super.setOrientation(orientation);
    }

    private void applyAnimation(View view, int startHeight, int endHeight, int duration) {
        ValueAnimator scaleAnimator = ValueAnimator.ofInt(startHeight, endHeight);
        scaleAnimator.setInterpolator(new AccelerateInterpolator());
        scaleAnimator.setDuration(duration);
        scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int XPoint = (int) animation.getAnimatedValue();
                expandContent.setMaxHeight(XPoint - marginBetweenTxtAndBottom);
                view.getLayoutParams().height = (int) XPoint;
                view.requestLayout();
            }
        });
        scaleAnimator.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                animating = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                // clear the animation flag
                animating = false;
                // clear animation here to avoid repeated applyTransformation() calls
//                    clearAnimation();
                scaleAnimator.cancel();
//                setEllipsize(TextUtils.TruncateAt.MARQUEE);
            }
        });
        ValueAnimator alphaAnimation = ValueAnimator.ofFloat(1f, animAlphaStart, 1f);
        alphaAnimation.setDuration(duration);
        AnimatorSet set = new AnimatorSet();
        set.play(scaleAnimator).with(alphaAnimation);
        set.start();
    }
}

ExpandArrowView:

public class ExpandArrowView extends FrameLayout {
    private TextView expandArrowTextView;
    private String expandStr;
    private String shrinkStr;

    private Context context;

    public ExpandArrowView(Context context) {
        this(context, null);
    }  

    public ExpandArrowView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ExpandArrowView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        LayoutInflater.from(context).inflate(R.layout.expand_arrow_view, this);
        expandArrowTextView = (TextView) findViewById(R.id.expand_arrow_text);

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandArrowView);
        expandStr = ta.getString(R.styleable.ExpandArrowView_expand_arrow_text);
        shrinkStr = ta.getString(R.styleable.ExpandArrowView_shrink_arrow_text);
        ta.recycle();
    }

    public void collapse() {
        if (expandStr != null) {
            expandArrowTextView.setText(expandStr);
            expandArrowTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_downarrow, 0);
            expandArrowTextView.setTextColor(context.getResources().getColor(R.color.online_detail_header_expand_arrow_text_color));
            expandArrowTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimensionPixelOffset(R.dimen.online_detail_header_expand_arrow_text_size));
        }
    }

    public void expand() {
        if (shrinkStr != null) {
            expandArrowTextView.setText(shrinkStr);
            expandArrowTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_uparrow, 0);
            expandArrowTextView.setTextColor(context.getResources().getColor(R.color.online_detail_header_expand_arrow_text_color));
            expandArrowTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimensionPixelOffset(R.dimen.online_detail_header_expand_arrow_text_size));
        }
    }
}

expand_arrow_view:




    

ExpandView:

public class ExpandView extends FrameLayout {
    private Context context;
    private ExpandTextView expandTextView;
    private FrameLayout expandContentBlow;
    private ExpandArrowView expandArrow;
    private View spaceView;
    private View rootView;
    private OnlineResource trackResource;

    private static int DEFAULT_LINES = 2;
    private boolean expanded;
    private boolean noViewShowSpace;

    public ExpandView(Context context) {
        this(context, null);
    }

    public ExpandView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ExpandView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;

        rootView = LayoutInflater.from(context).inflate(R.layout.expand_view, this);
        expandTextView = (ExpandTextView) findViewById(R.id.expand_text_view);
        expandContentBlow = (FrameLayout) findViewById(R.id.expand_content_blow);
        expandArrow = (ExpandArrowView) findViewById(R.id.expand_arrow);
        spaceView = findViewById(R.id.space);

        rootView.setOnClickListener(v -> swapState());

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandView);
        DEFAULT_LINES = ta.getInteger(R.styleable.ExpandView_collapse_text_line, DEFAULT_LINES);
        expanded = ta.getBoolean(R.styleable.ExpandView_default_expand, false);
        noViewShowSpace = ta.getBoolean(R.styleable.ExpandView_no_view_show_space, true);
        ta.recycle();
        init();
    }

    public void setTrackResource(OnlineResource onlineResource) {
        this.trackResource = onlineResource;
    }

    public void init() {
        expandTextView.setMaxLines(DEFAULT_LINES);
        this.post(() -> {
            if (expanded) {
                expand();
           } else {
                collapse();
            }
        });
    }

    public void addView(View child) {
        if (child != null) {
            expandContentBlow.addView(child);
        }
    }

    public void setText(CharSequence text) {
        expandTextView.setText(text);
    }

    public void setText(CharSequence text, View child) {
        setText(text);
        addView(child);
    }

    public static void showTextView(ExpandView textView, String str) {
        if (Assertions.checkNotNull(textView)) {
            if (!TextUtils.isEmpty(str)) {
                textView.setText(str);
            } else {
                textView.setVisibility(View.GONE);
            }
        }
    }

    public static void showTextView(TextView textView, String str) {
        if (Assertions.checkNotNull(textView)) {
            if (!TextUtils.isEmpty(str)) {
                textView.setText(str);
            } else {
               textView.setVisibility(View.GONE);
            }
        }
    }

    public static void showTextView(TextView textView, String foreStr, String str)  {  
        if (Assertions.checkNotNull(textView)) {
            if (!TextUtils.isEmpty(str)) {
                String text = foreStr + " " + str;
                textView.setText(text);
            } else {
                textView.setVisibility(View.GONE);
           }
        }
    }

    /*public static  void showTextView(TextView textView, List resources) {
        if (!ListUtils.isEmpty(resources)) {
            StringBuffer sb = new StringBuffer();
            for (T resource : resources) {
                if (resource instanceof MusicArtist){
                    sb.append(((MusicArtist)resource).getName()).append(",").append(" ");
                } else if (resource instanceof Album) {
                    sb.append(((Album)resource).getName()).append(",").append(" ");
                }
            }
            int index = sb.toString().length() - 2;
            if (index >= 0) {
                sb.deleteCharAt(index);
            }
            textView.setText(sb);
        } else {
            textView.setVisibility(View.GONE);
        }
    }*/

    private void swapState() {
        if (expanded) {
            OnlineTrackingUtil.trackDetailSeeMoreClicked(trackResource);
            expand();
        } else {
            collapse();
        }
    }

    public void collapse() {
        expanded = !expanded;
        String text = expandTextView.getText() != null ? expandTextView.getText().toString() : null;
        boolean expandContentBlowVisible = childViewVisible(expandContentBlow);

        if ("".equals(text) && expandContentBlowVisible) {
            viewGone(expandTextView);
            viewVisible(expandContentBlow);
            expandArrow.collapse();
           viewGone(expandArrow);
        } else if ("".equals(text) && !expandContentBlowVisible) {
            viewGone(expandTextView);
            viewGone(expandContentBlow);
            expandArrow.collapse();
            viewGone(expandArrow);
            noViewShowSpace();
        } else if (!"".equals(text) && !expandContentBlowVisible) {
            expandTextView.collapse();
            viewVisible(expandTextView);
            viewGone(expandContentBlow);
            expandArrow.collapse();
            if (expandTextView.Expandable()) {
                viewVisible(expandArrow);
            } else {
                viewGone(expandArrow);
                noViewShowSpace();
            }
        } else {
            expandTextView.collapse();
            viewVisible(expandTextView);
            viewGone(expandContentBlow);
            expandArrow.collapse();
            viewVisible(expandArrow);
        }
    }

    public void expand() {
        expanded = !expanded;
        String text = expandTextView.getText() != null ? expandTextView.getText().toString() : null;
        boolean expandContentBlowVisible = childViewVisible(expandContentBlow);

        if ("".equals(text) && expandContentBlowVisible) {
            viewGone(expandTextView);
            viewVisible(expandContentBlow);
            expandArrow.expand();
            viewGone(expandArrow);
        } else if ("".equals(text) && !expandContentBlowVisible) {
            viewGone(expandTextView);
            viewGone(expandContentBlow);
            expandArrow.expand();
            viewGone(expandArrow);
            noViewShowSpace();
        } else if (!"".equals(text) && !expandContentBlowVisible) {
            expandTextView.expand();
            viewVisible(expandTextView);
            viewGone(expandContentBlow);
           expandArrow.expand();
            if (expandTextView.Expandable()) {
                viewVisible(expandArrow);
            } else {
                viewGone(expandArrow);
                noViewShowSpace();
            }
        } else {
            expandTextView.expand();
            viewVisible(expandTextView);
            viewVisible(expandContentBlow);
            expandArrow.expand();
            viewVisible(expandArrow);
        }
    }

    private void noViewShowSpace() {
        if (noViewShowSpace) {
            spaceView.setVisibility(VISIBLE);
        } else {
            spaceView.setVisibility(GONE);
        }
    }

    private boolean childViewVisible(ViewGroup parent) {
        int childCount = parent.getChildCount();
        if (childCount == 0) {
            return false;
        }
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            if (child instanceof ViewGroup) {
                if (child.getVisibility() != GONE) {
                    return childViewVisible((ViewGroup) child);
                }
            } else {
                if (child.getVisibility() != GONE) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean ellipsis(TextView textView) {
        Layout layout = textView.getLayout();
        if (layout != null) {
            int lines = layout.getLineCount();
            if (lines > 0) {
                int ellipsisCount = layout.getEllipsisCount(lines - 1);
                if (ellipsisCount > 0) {
                    return true;
                }
            }
        }
        return false;
    }

    private void viewGone(View view) {
        if (view.getVisibility() != GONE) {
            view.setVisibility(GONE);
        }
    }

    private void viewVisible(View view) {
        if (view.getVisibility() != VISIBLE) {
            view.setVisibility(VISIBLE);
        }
    }
}

expand_view.xml:




    

    

    

    


你可能感兴趣的:(自定义可展开、伸缩的textView)