前言
Android应用中通常有一些很长的文案,比如有些用户评论或者文章内容特别的长,如果整体都展示出来可能会占据很大的空间,其他的控件都无法完全展现在用户面前。通常程序会对长文案做收缩操作,只展示开头的部分,用户如果真的对内容感兴趣可以点击展开按钮或文字,这种功能在应用中很常见。现在就来简单实现两种常见的展开缩放功能。
实现效果
自定义控件实现
自定义控件实现要求展开/缩放按钮和长文案TextView是分开的空间,通常可以用LinearLayout作为整体的布局,TextView放在最前方,按钮可以放在最右边或者最下方,用户点击按钮会修改TextView的android:maxLines属性,打开时androi:maxLines为Integer.MAX_VALUE,文案缩起来时设置为最小展示行数。
public class ExpandableTextView extends LinearLayout {
private TextView mText;
private ImageView mArrow;
private boolean mIsExpanded = true;
public ExpandableTextView(Context context) {
this(context, null);
}
public ExpandableTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ExpandableTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setOrientation(LinearLayout.VERTICAL);
mText = new TextView(getContext());
LinearLayout.LayoutParams params = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mText.setText(R.string.long_text);
mText.setMaxLines(3);
mText.setEllipsize(TextUtils.TruncateAt.END);
addView(mText, params);
mArrow = new ImageView(getContext());
mArrow.setImageResource(R.drawable.ic_arrow_downward);
LinearLayout.LayoutParams arrowParams = new LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
arrowParams.gravity = Gravity.CENTER_HORIZONTAL;
addView(mArrow, arrowParams);
mArrow.setOnClickListener(view -> {
// 如果当前文案处于展开状态,设置最大行数为3
// 收缩文案到3行状态
if (mIsExpanded) {
mText.setMaxLines(3);
mText.setEllipsize(TextUtils.TruncateAt.END);
mArrow.setImageResource(R.drawable.ic_arrow_downward);
} else {
// 如果当前文案处于收缩状态,将文案展开
mText.setMaxLines(Integer.MAX_VALUE);
mArrow.setImageResource(R.drawable.ic_arrow_upward);
}
mIsExpanded = !mIsExpanded;
});
}
}
ClickSpan实现
Android中有SpannableString可以为文本添加各种效果,如果需要在文案的最后添加展开/收缩文字,就需要使用ClickSpan功能。Android的TextUtils工具类提供了一个根据长度来获取压缩字符串的方法,压缩字符串是将”查看更多“添加到压缩字符串后面,并且为它添加点击事件。展开文案后在后面添加“收起查看”文案并且也为它添加收起的ClicKSpan事件。
public class TextViewActivity extends AppCompatActivity {
// 展示文案textView
private TextView textView;
// 压缩是展示的文案
private SpannableString collapsedString;
// 展开式展示的文案
private SpannableString expandedString;
// 是否处于展开状态
private boolean expanded = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_text_view);
textView = (TextView) findViewById(R.id.text);
// 初始化压缩和展示文案
textView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
if (expandedString == null) {
// 为原始全部文案添加“收起查看<<”并为这6个字符设置点击事件
String text = textView.getText().toString();
String alltext = text + "收起查看<<";
expandedString = new SpannableString(alltext);
expandedString.setSpan(new TextClickSpan(textView), alltext.length() - 6, alltext.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
// 计算当展示2.5行宽度的文案时压缩文案
String ellipse = TextUtils.ellipsize(text, textView.getPaint(), 2.5f * textView.getWidth(), TextUtils.TruncateAt.END).toString();
// 为压缩文案添加“查看更多>>”并且设置点击事件
String result = ellipse + "查看更多>>";
collapsedString = new SpannableString(result);
collapsedString.setSpan(new TextClickSpan(textView), result.length() - 6, result.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.parseColor("#36969696"));
textView.setText(collapsedString);
}
});
}
private class TextClickSpan extends ClickableSpan {
private TextView textView;
public TextClickSpan(TextView textView) {
this.textView = textView;
}
@Override
public void onClick(View widget) {
// 如果在文案展开情况下点击就展示压缩文案,否则展示全部文案
if (expanded) {
textView.setText(collapsedString);
} else {
textView.setText(expandedString);
}
expanded = !expanded;
}
}
}
长文案展开和收缩实现起来还是比较简单的,不过作为一个常用的功能还是值得记录一下。