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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
.DS_Store
/build
/captures
/.idea/
1 change: 0 additions & 1 deletion .idea/.name

This file was deleted.

8 changes: 1 addition & 7 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 71 additions & 3 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public class ExpandableTextView extends TextView{
// is specifically for inner toggle
private ExpandableClickListener mExpandableClickListener;
private OnExpandListener mOnExpandListener;
// this field must control in outside when touch in recyclerView such as write in POJO
private boolean isSpanHandle;

public ExpandableTextView(Context context) {
super(context);
Expand Down Expand Up @@ -244,61 +246,59 @@ private CharSequence getNewTextByConfig(){
mTextLineCount = -1;
switch (mCurrState){
case STATE_SHRINK: {
// update by kHRYSTAL fix support '\n'
mLayout = new DynamicLayout(mOrigText, mTextPaint, mLayoutWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
//计算共有多少行
mTextLineCount = mLayout.getLineCount();

//如果文字行数小于限制行数 那么直接返回 不需要折叠
if (mTextLineCount <= mMaxLinesOnShrink) {
return mOrigText;
}
int indexEnd = getValidLayout().getLineEnd(mMaxLinesOnShrink - 1);
int indexStart = getValidLayout().getLineStart(mMaxLinesOnShrink - 1);
int indexEndTrimmed = indexEnd
- getLengthOfString(mEllipsisHint)
- (mShowToExpandHint ? getLengthOfString(mToExpandHint) + getLengthOfString(mGapToExpandHint) : 0);
if (indexEndTrimmed <= 0) {
return mOrigText.subSequence(0, indexEnd);

if (mOrigText == null || mOrigText.length() == 0) {
return mOrigText;
}

int remainWidth = getValidLayout().getWidth() -
(int) (mTextPaint.measureText(mOrigText.subSequence(indexStart, indexEndTrimmed).toString()) + 0.5);
// 计算限制最大行的第一个字index
int start = getValidLayout().getLineStart(mMaxLinesOnShrink - 1);
// 计算限制最大行的最后一个字index 有可能是换行符 有可能是"" 需要递减
int end = getValidLayout().getLineEnd(mMaxLinesOnShrink - 1);
// 获取最后一行文字内容
CharSequence content = mOrigText.subSequence(start, end);

float moreWidth = getPaint().measureText(mToExpandHint, 0, mToExpandHint.length());
float gapWidth = getPaint().measureText(mGapToExpandHint, 0, mGapToExpandHint.length());
float maxWidth = getValidLayout().getWidth() - moreWidth - gapWidth;
// 获取一行显示的最大长度
getPaint().setSubpixelText(true);
// TODO 注意改方法如果设置宽度为wrap_content len会返回0 导致错误异常!!
int len = getPaint().breakText(content, 0, content.length(), true, maxWidth, null);
len = Math.min(len, end);

// 判断这行最后一个字是不是换行符 如果是 左移最大index
if (content.charAt(len - 1) == '\n') {
len -= 1;
}
// 计算最后一行的剩余空间
int endSpaceWidth = getValidLayout().getWidth() -
(int) (mTextPaint.measureText(mOrigText.subSequence(start, start + len).toString()) + 0.5);
// 计算 "... 全文"所占空间
float widthTailReplaced = mTextPaint.measureText(getContentOfString(mEllipsisHint)
+ (mShowToExpandHint ? (getContentOfString(mToExpandHint) + getContentOfString(mGapToExpandHint)) : ""));

int indexEndTrimmedRevised = indexEndTrimmed;
if (remainWidth > widthTailReplaced) {
int extraOffset = 0;
int extraWidth = 0;
while (remainWidth > widthTailReplaced + extraWidth) {
extraOffset++;
if (indexEndTrimmed + extraOffset <= mOrigText.length()) {
extraWidth = (int) (mTextPaint.measureText(
mOrigText.subSequence(indexEndTrimmed, indexEndTrimmed + extraOffset).toString()) + 0.5);
} else {
break;
}
}
indexEndTrimmedRevised += extraOffset - 1;
} else {
int extraOffset = 0;
int extraWidth = 0;
while (remainWidth + extraWidth < widthTailReplaced) {
extraOffset--;
if (indexEndTrimmed + extraOffset > indexStart) {
extraWidth = (int) (mTextPaint.measureText(mOrigText.subSequence(indexEndTrimmed + extraOffset, indexEndTrimmed).toString()) + 0.5);
} else {
break;
+ (mShowToExpandHint ? (getContentOfString(mToExpandHint) + getContentOfString(mGapToExpandHint)) + 0.5 : ""));
// 如果最后一行剩余空间小于 ... 全文所占空间 需要递减文字长度
if (endSpaceWidth <= widthTailReplaced) {
while (endSpaceWidth <= widthTailReplaced) {
len = len - 1;
if (content.charAt(len - 1) == '\n') {
len = len - 1;
}
endSpaceWidth = getValidLayout().getWidth() -
(int) (mTextPaint.measureText(mOrigText.subSequence(start, start + len).toString()));
}
indexEndTrimmedRevised += extraOffset;
}
return createShrinkText(mOrigText.subSequence(0, start + len));
// update end

SpannableStringBuilder ssbShrink = new SpannableStringBuilder(mOrigText, 0, indexEndTrimmedRevised)
.append(mEllipsisHint);
if (mShowToExpandHint) {
ssbShrink.append(getContentOfString(mGapToExpandHint) + getContentOfString(mToExpandHint));
ssbShrink.setSpan(mTouchableSpan, ssbShrink.length() - getLengthOfString(mToExpandHint), ssbShrink.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return ssbShrink;
}
case STATE_EXPAND: {
if (!mShowToShrinkHint) {
Expand All @@ -320,15 +320,34 @@ private CharSequence getNewTextByConfig(){
return mOrigText;
}

private Spanned createShrinkText(CharSequence limitContent) {
SpannableStringBuilder ssShrink = new SpannableStringBuilder(limitContent)
.append(mEllipsisHint);
if (mShowToExpandHint) {
ssShrink.append(mGapToExpandHint).append(mToExpandHint);
ssShrink.setSpan(mTouchableSpan, ssShrink.length() - getLengthOfString(mToExpandHint), ssShrink.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return ssShrink;
}

public void setExpandListener(OnExpandListener listener){
mOnExpandListener = listener;
}

public boolean isSpanHandle() {
return isSpanHandle;
}

public void setIsSpanHandle(boolean isSpanHandle) {
this.isSpanHandle = isSpanHandle;
}

private Layout getValidLayout(){
return mLayout != null ? mLayout : getLayout();
}

private void toggle(){
isSpanHandle = false;
switch (mCurrState){
case STATE_SHRINK:
mCurrState = STATE_EXPAND;
Expand Down Expand Up @@ -377,7 +396,8 @@ public interface OnExpandListener{
private class ExpandableClickListener implements View.OnClickListener{
@Override
public void onClick(View view) {
toggle();
if (!isSpanHandle)
toggle();
}
}

Expand Down Expand Up @@ -442,11 +462,7 @@ public void setPressed(boolean isSelected) {

@Override
public void onClick(View widget) {
if(hasOnClickListeners()
&& (getOnClickListener(ExpandableTextView.this) instanceof ExpandableClickListener)) {
}else{
toggle();
}
toggle();
}

@Override
Expand Down Expand Up @@ -481,21 +497,30 @@ public boolean onTouchEvent(TextView textView, Spannable spannable, MotionEvent
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mPressedSpan = getPressedSpan(textView, spannable, event);
if (mPressedSpan != null) {
isSpanHandle = true;
mPressedSpan.setPressed(true);
Selection.setSelection(spannable, spannable.getSpanStart(mPressedSpan),
spannable.getSpanEnd(mPressedSpan));
} else {
isSpanHandle = false;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
TouchableSpan touchedSpan = getPressedSpan(textView, spannable, event);
if (mPressedSpan != null && touchedSpan != mPressedSpan) {
mPressedSpan.setPressed(false);
mPressedSpan = null;
Selection.removeSelection(spannable);
isSpanHandle = true;
} else {
isSpanHandle = false;
}
} else {
if (mPressedSpan != null) {
mPressedSpan.setPressed(false);
super.onTouchEvent(textView, spannable, event);
isSpanHandle = true;
} else {
isSpanHandle = false;
}
mPressedSpan = null;
Selection.removeSelection(spannable);
Expand Down
1 change: 1 addition & 0 deletions sample/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/build
/.idea/
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
Expand Down Expand Up @@ -79,7 +80,9 @@ private void inflateListViews(){
mFlag = !mFlag;
}

class TheBaseAdapter extends BaseAdapter implements ExpandableTextView.OnExpandListener{
class TheBaseAdapter extends BaseAdapter
implements ExpandableTextView.OnExpandListener
{

private SparseArray<Integer> mPositionsAndStates = new SparseArray<>();
private List<String> mList;
Expand Down Expand Up @@ -132,8 +135,7 @@ public void run() {
viewHolder.etv.setTag(position);
viewHolder.etv.setExpandListener(this);
Integer state = mPositionsAndStates.get(position);

viewHolder.etv.updateForRecyclerView(content.toString(), etvWidth, state== null ? 0 : state);//第一次getview时肯定为etvWidth为0
viewHolder.etv.updateForRecyclerView(content.toString(), etvWidth, state == null ? 0 : state);//第一次getview时肯定为etvWidth为0

return convertView;
}
Expand All @@ -142,15 +144,16 @@ public void run() {
public void onExpand(ExpandableTextView view) {
Object obj = view.getTag();
if(obj != null && obj instanceof Integer){
mPositionsAndStates.put((Integer)obj, view.getExpandState());
mPositionsAndStates.put((Integer)obj, 1);
}
}

//
@Override
public void onShrink(ExpandableTextView view) {
Object obj = view.getTag();
if(obj != null && obj instanceof Integer){
mPositionsAndStates.put((Integer)obj, view.getExpandState());
mPositionsAndStates.put((Integer)obj, 0);
mListView.setSelection((Integer) obj);
}
}
}
Expand Down
Loading