From 2c681b6516352223f0e280e9ea001cde371a3bbb Mon Sep 17 00:00:00 2001 From: deskid Date: Fri, 3 Nov 2017 19:20:18 +0800 Subject: [PATCH 1/3] fix: fix issue #24, #22, #17 --- .../readmoretextviewsample/MainActivity.java | 49 +++++++++++ app/src/main/res/drawable/round_btn_bg.xml | 9 ++ app/src/main/res/layout/activity_main.xml | 6 ++ app/src/main/res/layout/list_item.xml | 16 ++++ .../readmoretextview/ReadMoreTextView.java | 82 ++++++++++++++++--- 5 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 app/src/main/res/drawable/round_btn_bg.xml create mode 100644 app/src/main/res/layout/list_item.xml diff --git a/app/src/main/java/com/borjabravo/readmoretextviewsample/MainActivity.java b/app/src/main/java/com/borjabravo/readmoretextviewsample/MainActivity.java index 8f50499..f4caa6b 100644 --- a/app/src/main/java/com/borjabravo/readmoretextviewsample/MainActivity.java +++ b/app/src/main/java/com/borjabravo/readmoretextviewsample/MainActivity.java @@ -1,8 +1,15 @@ 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; public class MainActivity extends AppCompatActivity { @@ -18,5 +25,47 @@ 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)); + listView.setAdapter(new ItemAdapter()); + } + + static class ItemAdapter extends RecyclerView.Adapter { + + @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(context.getString(R.string.lorem_ipsum)); + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(context, "you clicked " + position, Toast.LENGTH_SHORT).show(); + } + }); + + //todo ReadMoreTextView should have a pulbic method to reset textView's collapse status + // e.g. + //((ReadMoreTextView) holder.itemView).setCollapsed(position % 2 == 0) + } + + @Override + public int getItemCount() { + return 10; + } + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView text; + + public ViewHolder(View itemView) { + super(itemView); + text = (TextView) itemView.findViewById(R.id.text); + } } } diff --git a/app/src/main/res/drawable/round_btn_bg.xml b/app/src/main/res/drawable/round_btn_bg.xml new file mode 100644 index 0000000..61f675c --- /dev/null +++ b/app/src/main/res/drawable/round_btn_bg.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 01a64c6..8b17d4b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -37,4 +37,10 @@ android:layout_height="wrap_content" /> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml new file mode 100644 index 0000000..58ff126 --- /dev/null +++ b/app/src/main/res/layout/list_item.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java b/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java index f560151..b5ee144 100644 --- a/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java +++ b/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java @@ -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; @@ -76,13 +78,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); } @@ -125,7 +170,15 @@ private CharSequence updateCollapsedText() { 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; } @@ -193,28 +246,31 @@ 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; } From 68169280a7a6826235ade18ae29aa5b31cbb54ca Mon Sep 17 00:00:00 2001 From: deskid Date: Wed, 29 Nov 2017 14:37:28 +0800 Subject: [PATCH 2/3] update: add toggleCollapsed method && update the demo to show usage with RecyclerView --- .../readmoretextviewsample/MainActivity.java | 55 ++++++++++++++++--- .../readmoretextview/ReadMoreTextView.java | 25 ++++++++- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/borjabravo/readmoretextviewsample/MainActivity.java b/app/src/main/java/com/borjabravo/readmoretextviewsample/MainActivity.java index f4caa6b..b137353 100644 --- a/app/src/main/java/com/borjabravo/readmoretextviewsample/MainActivity.java +++ b/app/src/main/java/com/borjabravo/readmoretextviewsample/MainActivity.java @@ -11,8 +11,16 @@ 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 mItems = new ArrayList<>(); + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -27,11 +35,27 @@ protected void onCreate(Bundle savedInstanceState) { text4.setText(getString(R.string.one_line_text)); RecyclerView listView = (RecyclerView) findViewById(R.id.list); listView.setLayoutManager(new LinearLayoutManager(this)); - listView.setAdapter(new ItemAdapter()); + 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 { + private List mItems; + + public ItemAdapter(List 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); @@ -41,7 +65,7 @@ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { @Override public void onBindViewHolder(ViewHolder holder, final int position) { final Context context = holder.itemView.getContext(); - holder.text.setText(context.getString(R.string.lorem_ipsum)); + holder.text.setText(mItems.get(position).text); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -49,23 +73,38 @@ public void onClick(View v) { } }); - //todo ReadMoreTextView should have a pulbic method to reset textView's collapse status - // e.g. - //((ReadMoreTextView) holder.itemView).setCollapsed(position % 2 == 0) + 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 10; + return mItems.size(); } } static class ViewHolder extends RecyclerView.ViewHolder { - TextView text; + ReadMoreTextView text; public ViewHolder(View itemView) { super(itemView); - text = (TextView) itemView.findViewById(R.id.text); + text = (ReadMoreTextView) itemView.findViewById(R.id.text); } } + + static class Item { + public String text; + public boolean readMore; + } } diff --git a/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java b/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java index b5ee144..aba66f2 100644 --- a/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java +++ b/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java @@ -56,6 +56,8 @@ public class ReadMoreTextView extends TextView { private int lineEndIndex; private int trimLines; + private Watcher watcher; + public ReadMoreTextView(Context context) { this(context, null); } @@ -167,6 +169,9 @@ private CharSequence getTrimmedText(CharSequence text) { } private CharSequence updateCollapsedText() { + if (watcher != null) { + watcher.onCollapsed(); + } int trimEndIndex = text.length(); switch (trimMode) { case TRIM_MODE_LINES: @@ -194,6 +199,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); @@ -206,6 +214,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(); @@ -278,4 +295,10 @@ private void refreshLineEndIndex() { e.printStackTrace(); } } -} + + public interface Watcher { + public void onExpanded(); + + public void onCollapsed(); + } +} \ No newline at end of file From 1afc3b619b067b31ff60e376a4fe514b5650267a Mon Sep 17 00:00:00 2001 From: deskid Date: Wed, 29 Nov 2017 15:22:05 +0800 Subject: [PATCH 3/3] fix: remove unnecessary lineCount check --- .../com/borjabravo/readmoretextview/ReadMoreTextView.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java b/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java index aba66f2..f5bdb3a 100644 --- a/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java +++ b/readmoretextview/src/main/java/com/borjabravo/readmoretextview/ReadMoreTextView.java @@ -157,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(); }