package com.gh.common.view; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; import android.text.Layout; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.style.ClickableSpan; import android.util.AttributeSet; import android.view.View; import com.gh.common.util.DisplayUtils; import com.gh.gamecenter.R; import androidx.appcompat.widget.AppCompatTextView; import androidx.core.content.ContextCompat; public class ExpandTextView extends AppCompatTextView { private CharSequence mSnapshotText; private String mEndText = "..."; private String mExpandText = mEndText + "全文"; private boolean mUseGradientAlphaEndText = false; private int mMaxLines = 3; // 由于sdk版本限制(getMaxLines) 这里设置默认值 private static int DEFAULT_ADDITIONAL_END_TEXT_COUNT = 2; private boolean mInitLayout = false; private boolean mIsExpanded = false; // 位于 recyclerView 时需要自行在外层管理是否已展开 private ExpandCallback mExpandCallback; public ExpandTextView(Context context) { super(context); } public ExpandTextView(Context context, AttributeSet attrs) { super(context, attrs); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mMaxLines = getMaxLines(); } TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandTextView); mUseGradientAlphaEndText = ta.getBoolean(R.styleable.ExpandTextView_useGradientAlphaEndText, false); mEndText = ta.getString(R.styleable.ExpandTextView_endText) == null ? mEndText : ta.getString(R.styleable.ExpandTextView_endText); mExpandText = ta.getString(R.styleable.ExpandTextView_expandText) == null ? mExpandText : ta.getString(R.styleable.ExpandTextView_expandText); ta.recycle(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mInitLayout && !mIsExpanded && getLineCount() > mMaxLines) { mSnapshotText = getText(); mInitLayout = false; showExpandButton(); } } public void setExpendText(String text) { this.mExpandText = text; } public void setExpandCallback(ExpandCallback callback) { this.mExpandCallback = callback; } @Override public void setText(CharSequence text, BufferType type) { mInitLayout = true; super.setText(text, type); } private void showExpandButton() { String finalEndText = ""; Layout layout = getLayout(); int start = layout.getLineStart(0); int lastLineEnd = layout.getLineEnd(mMaxLines - 1); int lastLineStart = layout.getLineStart(mMaxLines - 1); float lastLineRight = layout.getLineRight(mMaxLines - 1); int viewWidth = getWidth() - getPaddingRight() - getPaddingLeft(); int additionalEndTextCount = 0; TextPaint paint = getPaint(); float expandTextWidth; if (mUseGradientAlphaEndText) { additionalEndTextCount = DEFAULT_ADDITIONAL_END_TEXT_COUNT; // 如果不加多个括号的话有可能算不对,惊了,明明是同样的 paint 同样的文字,长度却会略有不同 expandTextWidth = paint.measureText(mEndText + mExpandText + " "); } else { expandTextWidth = paint.measureText(mExpandText); } CharSequence content = mSnapshotText.subSequence(start, lastLineEnd); if (viewWidth - lastLineRight > expandTextWidth) { if (mUseGradientAlphaEndText) { finalEndText = content.toString().substring(content.length() - additionalEndTextCount, content.length()) + mEndText; finalEndText = finalEndText.replace("\n", ""); content = content.subSequence(0, content.length() - additionalEndTextCount) + finalEndText + mExpandText; } else { content = content.toString().trim() + mExpandText; } } else { CharSequence lastText = mSnapshotText.subSequence(lastLineStart, lastLineEnd); for (int i = lastText.length() - 1; i > 0; i--) { CharSequence sequence = lastText.subSequence(0, i); float w = paint.measureText(sequence.toString()); if (viewWidth - w - DisplayUtils.dip2px(5) > expandTextWidth) { if (mUseGradientAlphaEndText) { finalEndText = lastText.subSequence(i - additionalEndTextCount, i) + mEndText; finalEndText = finalEndText.replace("\n", ""); content = mSnapshotText.subSequence(start, lastLineStart + i - additionalEndTextCount) + finalEndText + mExpandText; } else { content = mSnapshotText.subSequence(start, lastLineStart + i) + mExpandText; } break; } } } SpannableStringBuilder msp = new SpannableStringBuilder(mSnapshotText); int length = msp.length(); int startPosition = 0; startPosition = content.length() - finalEndText.length() - mExpandText.length(); startPosition = Math.max(startPosition, 0); // 避免越界 if (startPosition >= length) return; msp.replace(startPosition, length, finalEndText + mExpandText); msp.setSpan(new ClickableSpan() { @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setColor(ContextCompat.getColor(getContext(), R.color.theme_font)); ds.setUnderlineText(false); } @Override public void onClick(View widget) { mIsExpanded = true; setMaxLines(Integer.MAX_VALUE); setText(mSnapshotText); if (mExpandCallback != null) { mExpandCallback.onExpand(); } } }, startPosition + mEndText.length(), msp.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); msp.setSpan(new GradientAlphaTextSpan(), startPosition, startPosition + finalEndText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); setText(msp); setMovementMethod(CustomLinkMovementMethod.getInstance()); } /** * 此方法仅更改标记,不做实际展开的操作 */ public void setIsExpanded(boolean isExpanded) { mIsExpanded = isExpanded; } public void setExpandMaxLines(int maxLines) { mMaxLines = maxLines; setMaxLines(maxLines); } public interface ExpandCallback { void onExpand(); } }