Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
package com.borjabravo.readmoretextviewsample;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.borjabravo.readmoretextview.ReadMoreTextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

private static final int COUNT = 30;
private List<Item> mItems = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -18,5 +33,78 @@ protected void onCreate(Bundle savedInstanceState) {
text3.setText(getString(R.string.lorem_ipsum3));
TextView text4 = findViewById(R.id.text4);
text4.setText(getString(R.string.one_line_text));
RecyclerView listView = (RecyclerView) findViewById(R.id.list);
listView.setLayoutManager(new LinearLayoutManager(this));
initMockData();
listView.setAdapter(new ItemAdapter(mItems));
}

private void initMockData() {
for (int i = 0; i < COUNT; i++) {
Item item = new Item();
item.text = i + " : " + getString(R.string.lorem_ipsum);
item.readMore = true;
mItems.add(item);
}
}

static class ItemAdapter extends RecyclerView.Adapter<ViewHolder> {

private List<Item> mItems;

public ItemAdapter(List<Item> items) {
mItems = new ArrayList<>(items);
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
final Context context = holder.itemView.getContext();
holder.text.setText(mItems.get(position).text);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "you clicked " + position, Toast.LENGTH_SHORT).show();
}
});

holder.text.setToggleWatcher(new ReadMoreTextView.Watcher() {
@Override
public void onExpanded() {
mItems.get(position).readMore = false;
}

@Override
public void onCollapsed() {
mItems.get(position).readMore = true;
}
});

holder.text.toggleCollapsed(mItems.get(position).readMore);
}

@Override
public int getItemCount() {
return mItems.size();
}
}

static class ViewHolder extends RecyclerView.ViewHolder {
ReadMoreTextView text;

public ViewHolder(View itemView) {
super(itemView);
text = (ReadMoreTextView) itemView.findViewById(R.id.text);
}
}

static class Item {
public String text;
public boolean readMore;
}
}
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/round_btn_bg.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<corners android:radius="6px" />
<stroke
android:width="2px"
android:color="#66666666" />
</shape>
6 changes: 6 additions & 0 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@
android:layout_height="wrap_content"
/>

<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />


</LinearLayout>
16 changes: 16 additions & 0 deletions app/src/main/res/layout/list_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:background="@drawable/round_btn_bg"
android:orientation="vertical">

<com.borjabravo.readmoretextview.ReadMoreTextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:trimCollapsedText="@string/show_all_content"
app:trimLines="5" />
</LinearLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@
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.Layout;
import android.text.Selection;
import android.text.Spannable;
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.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.TextView;
Expand Down Expand Up @@ -54,6 +56,8 @@ public class ReadMoreTextView extends TextView {
private int lineEndIndex;
private int trimLines;

private Watcher watcher;

public ReadMoreTextView(Context context) {
this(context, null);
}
Expand All @@ -76,13 +80,56 @@ public ReadMoreTextView(Context context, AttributeSet attrs) {
this.trimMode = typedArray.getInt(R.styleable.ReadMoreTextView_trimMode, TRIM_MODE_LINES);
typedArray.recycle();
viewMoreSpan = new ReadMoreClickableSpan();
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
TextView widget = (TextView) v;
Spannable buffer = Spannable.Factory.getInstance().newSpannable(widget.getText());
int action = event.getAction();

// https://stackoverflow.com/questions/8558732/listview-textview-with-linkmovementmethod-makes-list-item-unclickable

// to fix a bug when call setMovementMethod will make list item unclickable
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();

x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();

x += widget.getScrollX();
y += widget.getScrollY();

Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);

ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}

return true;
} else {
Selection.removeSelection(buffer);
}
}
return false;
}
});
onGlobalLayoutLineEndIndex();
setText();
}

private void setText() {
super.setText(getDisplayableText(), bufferType);
setMovementMethod(LinkMovementMethod.getInstance());
setHighlightColor(Color.TRANSPARENT);
}

Expand Down Expand Up @@ -110,9 +157,7 @@ private CharSequence getTrimmedText(CharSequence text) {
if (trimMode == TRIM_MODE_LINES) {
if (text != null && lineEndIndex > 0) {
if (readMore) {
if (getLayout().getLineCount() > trimLines) {
return updateCollapsedText();
}
return updateCollapsedText();
} else {
return updateExpandedText();
}
Expand All @@ -122,10 +167,21 @@ private CharSequence getTrimmedText(CharSequence text) {
}

private CharSequence updateCollapsedText() {
if (watcher != null) {
watcher.onCollapsed();
}
int trimEndIndex = text.length();
switch (trimMode) {
case TRIM_MODE_LINES:
trimEndIndex = lineEndIndex - (ELLIPSIZE.length() + trimCollapsedText.length() + 1);
trimEndIndex = lineEndIndex;
//find enough space to layout ELLIPSIZE
float ellipsizeWidth = getPaint().measureText(ELLIPSIZE + trimCollapsedText);
float collapsedWidth = getPaint().measureText(text.subSequence(trimEndIndex, trimEndIndex + 1).toString());
while (collapsedWidth < ellipsizeWidth) {
--trimEndIndex;
collapsedWidth += getPaint().measureText(text.subSequence(trimEndIndex, trimEndIndex + 1).toString());
}

if (trimEndIndex < 0) {
trimEndIndex = trimLength + 1;
}
Expand All @@ -141,6 +197,9 @@ private CharSequence updateCollapsedText() {
}

private CharSequence updateExpandedText() {
if (watcher != null) {
watcher.onExpanded();
}
if (showTrimExpandedText) {
SpannableStringBuilder s = new SpannableStringBuilder(text, 0, text.length()).append(trimExpandedText);
return addClickableSpan(s, trimExpandedText);
Expand All @@ -153,6 +212,15 @@ private CharSequence addClickableSpan(SpannableStringBuilder s, CharSequence tri
return s;
}

public void toggleCollapsed(boolean readMore) {
this.readMore = readMore;
setText();
}

public void setToggleWatcher(Watcher watcher) {
this.watcher = watcher;
}

public void setTrimLength(int trimLength) {
this.trimLength = trimLength;
setText();
Expand Down Expand Up @@ -193,33 +261,42 @@ public void updateDrawState(TextPaint ds) {

private void onGlobalLayoutLineEndIndex() {
if (trimMode == TRIM_MODE_LINES) {
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public void onGlobalLayout() {
public boolean onPreDraw() {
ViewTreeObserver obs = getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
obs.removeOnGlobalLayoutListener(this);
} else {
obs.removeGlobalOnLayoutListener(this);
}
obs.removeOnPreDrawListener(this);
refreshLineEndIndex();
setText();
return true;
}
});
}
}

@Override
public boolean performLongClick() {
//a side affect that every slide move will trigger a long click event
return false;
}

private void refreshLineEndIndex() {
try {
if (trimLines == 0) {
lineEndIndex = getLayout().getLineEnd(0);
lineEndIndex = getLayout().getLineVisibleEnd(0);
} else if (trimLines > 0 && getLineCount() >= trimLines) {
lineEndIndex = getLayout().getLineEnd(trimLines - 1);
lineEndIndex = getLayout().getLineVisibleEnd(trimLines - 1);
} else {
lineEndIndex = INVALID_END_INDEX;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

public interface Watcher {
public void onExpanded();

public void onCollapsed();
}
}