Java tutorial
/* * Copyright (C) 2016 Borja Bravo 'Alvarez * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.abcs.hqbtravel.wedgt; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Build; import android.support.v4.content.ContextCompat; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.util.AttributeSet; import android.view.View; import android.view.ViewTreeObserver; import android.widget.TextView; import com.abcs.sociax.android.R; /** * Created by borja on 17/4/16. */ public class ReadMoreTextView extends TextView { private static final int TRIM_MODE_LINES = 0; private static final int TRIM_MODE_LENGTH = 1; private static final int DEFAULT_TRIM_LENGTH = 240; private static final int DEFAULT_TRIM_LINES = 2; private static final boolean DEFAULT_SHOW_TRIM_EXPANDED_TEXT = true; private static final String ELLIPSIZE = "... "; private CharSequence text; private BufferType bufferType; private boolean readMore = true; private int trimLength; private CharSequence trimCollapsedText; private CharSequence trimExpandedText; private ReadMoreClickableSpan viewMoreSpan; private int colorClickableText; private boolean showTrimExpandedText; private int trimMode; private int lineEndIndex; private int trimLines; public ReadMoreTextView(Context context) { this(context, null); } public ReadMoreTextView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ReadMoreTextView); this.trimLength = typedArray.getInt(R.styleable.ReadMoreTextView_trimLength, DEFAULT_TRIM_LENGTH); int resourceIdTrimCollapsedText = typedArray.getResourceId(R.styleable.ReadMoreTextView_trimCollapsedText, R.string.read_more); int resourceIdTrimExpandedText = typedArray.getResourceId(R.styleable.ReadMoreTextView_trimExpandedText, R.string.read_less); this.trimCollapsedText = getResources().getString(resourceIdTrimCollapsedText); this.trimExpandedText = getResources().getString(resourceIdTrimExpandedText); this.trimLines = typedArray.getInt(R.styleable.ReadMoreTextView_trimLines, DEFAULT_TRIM_LINES); this.colorClickableText = typedArray.getColor(R.styleable.ReadMoreTextView_colorClickableText, ContextCompat.getColor(context, R.color.tljr_statusbarcolor)); this.showTrimExpandedText = typedArray.getBoolean(R.styleable.ReadMoreTextView_showTrimExpandedText, DEFAULT_SHOW_TRIM_EXPANDED_TEXT); this.trimMode = typedArray.getInt(R.styleable.ReadMoreTextView_trimMode, TRIM_MODE_LINES); typedArray.recycle(); viewMoreSpan = new ReadMoreClickableSpan(); onGlobalLayoutLineEndIndex(); setText(); } private void setText() { super.setText(getDisplayableText(), bufferType); setMovementMethod(LinkMovementMethod.getInstance()); setHighlightColor(Color.TRANSPARENT); } private CharSequence getDisplayableText() { return getTrimmedText(text); } @Override public void setText(CharSequence text, BufferType type) { this.text = text; bufferType = type; setText(); } private CharSequence getTrimmedText(CharSequence text) { if (trimMode == TRIM_MODE_LENGTH) { if (text != null && text.length() > trimLength) { if (readMore) { return updateCollapsedText(); } else { return updateExpandedText(); } } } if (trimMode == TRIM_MODE_LINES) { if (text != null && lineEndIndex > 0) { if (readMore) { return updateCollapsedText(); } else { return updateExpandedText(); } } } return text; } private CharSequence updateCollapsedText() { int trimEndIndex = text.length(); switch (trimMode) { case TRIM_MODE_LINES: trimEndIndex = lineEndIndex - (ELLIPSIZE.length() + trimCollapsedText.length() + 1); if (trimEndIndex < 0) { trimEndIndex = trimLength + 1; } break; case TRIM_MODE_LENGTH: trimEndIndex = trimLength + 1; break; } SpannableStringBuilder s = new SpannableStringBuilder(text, 0, trimEndIndex).append(ELLIPSIZE) .append(trimCollapsedText); return addClickableSpan(s, trimCollapsedText); } private CharSequence updateExpandedText() { if (showTrimExpandedText) { SpannableStringBuilder s = new SpannableStringBuilder(text, 0, text.length()).append(trimExpandedText); return addClickableSpan(s, trimExpandedText); } return text; } private CharSequence addClickableSpan(SpannableStringBuilder s, CharSequence trimText) { s.setSpan(viewMoreSpan, s.length() - trimText.length(), s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); return s; } public void setTrimLength(int trimLength) { this.trimLength = trimLength; setText(); } public void setColorClickableText(int colorClickableText) { this.colorClickableText = colorClickableText; } public void setTrimCollapsedText(CharSequence trimCollapsedText) { this.trimCollapsedText = trimCollapsedText; } public void setTrimExpandedText(CharSequence trimExpandedText) { this.trimExpandedText = trimExpandedText; } public void setTrimMode(int trimMode) { this.trimMode = trimMode; } public void setTrimLines(int trimLines) { this.trimLines = trimLines; } private class ReadMoreClickableSpan extends ClickableSpan { @Override public void onClick(View widget) { readMore = !readMore; setText(); } @Override public void updateDrawState(TextPaint ds) { ds.setColor(colorClickableText); } } private void onGlobalLayoutLineEndIndex() { if (trimMode == TRIM_MODE_LINES) { getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { ViewTreeObserver obs = getViewTreeObserver(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { obs.removeOnGlobalLayoutListener(this); } else { obs.removeGlobalOnLayoutListener(this); } refreshLineEndIndex(); setText(); } }); } } private void refreshLineEndIndex() { try { if (trimLines == 0) { lineEndIndex = getLayout().getLineEnd(0); } else if (trimLines > 0 && getLineCount() >= trimLines) { lineEndIndex = getLayout().getLineEnd(trimLines - 1); } else { lineEndIndex = getLayout().getLineEnd(getLayout().getLineCount() - 1); } } catch (Exception e) { e.printStackTrace(); } } }