Android开发中自定义可扩展的textView
2023-12-19 21:03:48
我们在开发中,不免要用到可扩展的textView,今天我们就自定义一个:
第一步:初始化控件:
TypedArray typedArray = this.getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
this.mMaxCollapsedLines = typedArray.getInt(R.styleable.ExpandableTextView_maxCollapsedLines, 8);
this.mAnimationDuration = typedArray.getInt(R.styleable.ExpandableTextView_animDuration, 300);
this.mAnimAlphaStart = typedArray.getFloat(R.styleable.ExpandableTextView_animAlphaStart, 0.7F);
this.mExpandDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_expandDrawable);
this.mCollapseDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_collapseDrawable);
if (this.mExpandDrawable == null) {
this.mExpandDrawable = getDrawable(this.getContext(), R.drawable.icon_live_home_arrow);
}
if (this.mCollapseDrawable == null) {
this.mCollapseDrawable = getDrawable(this.getContext(), R.drawable.icon_live_home_below);
}
typedArray.recycle();
this.setVisibility(GONE);
第二步:找对应的控件
this.mTv = (TextView) this.findViewById(R.id.expandable_text); this.mTv.setOnClickListener(this); this.mButton = (ImageButton) this.findViewById(R.id.expand_collapse); this.mButton.setImageDrawable(this.mCollapsed ? this.mExpandDrawable : this.mCollapseDrawable); this.mButton.setOnClickListener(this);
第三步:调用onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (this.mRelayout && this.getVisibility() != GONE) {
this.mRelayout = false;
this.mButton.setVisibility(GONE);
this.mTv.setMaxLines(Integer.MAX_VALUE);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (this.mTv.getLineCount() > this.mMaxCollapsedLines) {
this.mTextHeightWithMaxLines = getRealTextViewHeight(this.mTv);
if (this.mCollapsed) {
this.mTv.setMaxLines(this.mMaxCollapsedLines);
}
this.mButton.setVisibility(VISIBLE);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (this.mCollapsed) {
this.mTv.post(new Runnable() {
@Override
public void run() {
ExpandableTextView.this.mMarginBetweenTxtAndBottom = ExpandableTextView.this.getHeight() - ExpandableTextView.this.mTv.getHeight();
}
});
this.mCollapsedHeight = this.getMeasuredHeight();
}
}
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
第四步:添加动画:
class ExpandCollapseAnimation extends Animation {
private final View mTargetView;
private final int mStartHeight;
private final int mEndHeight;
public ExpandCollapseAnimation(View view, int startHeight, int endHeight) {
this.mTargetView = view;
this.mStartHeight = startHeight;
this.mEndHeight = endHeight;
this.setDuration((long) ExpandableTextView.this.mAnimationDuration);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newHeight = (int) ((float) (this.mEndHeight - this.mStartHeight) * interpolatedTime + (float) this.mStartHeight);
ExpandableTextView.this.mTv.setMaxHeight(newHeight - ExpandableTextView.this.mMarginBetweenTxtAndBottom);
if (Float.compare(ExpandableTextView.this.mAnimAlphaStart, 1.0F) != 0) {
ExpandableTextView.applyAlphaAnimation(ExpandableTextView.this.mTv, ExpandableTextView.this.mAnimAlphaStart + interpolatedTime * (1.0F - ExpandableTextView.this.mAnimAlphaStart));
}
this.mTargetView.getLayoutParams().height = newHeight;
this.mTargetView.requestLayout();
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
@Override
public boolean willChangeBounds() {
return true;
}
}
下面附上全部的代码以供参考:
public class ExpandableTextView extends RelativeLayout implements View.OnClickListener {
private static final String TAG = ExpandableTextView.class.getSimpleName();
private static final int MAX_COLLAPSED_LINES = 8;
private static final int DEFAULT_ANIM_DURATION = 300;
private static final float DEFAULT_ANIM_ALPHA_START = 0.7F;
protected TextView mTv;
protected ImageButton mButton;
private boolean mRelayout;
private boolean mCollapsed;
private int mCollapsedHeight;
private int mTextHeightWithMaxLines;
private int mMaxCollapsedLines;
private int mMarginBetweenTxtAndBottom;
private Drawable mExpandDrawable;
private Drawable mCollapseDrawable;
private int mAnimationDuration;
private float mAnimAlphaStart;
private boolean mAnimating;
private ExpandableTextView.OnExpandStateChangeListener mListener;
private SparseBooleanArray mCollapsedStatus;
private int mPosition;
public ExpandableTextView(Context context) {
this(context, (AttributeSet) null);
}
public ExpandableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mCollapsed = true;
this.init(attrs);
}
@TargetApi(11)
public ExpandableTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mCollapsed = true;
this.init(attrs);
}
public void setOrientation(int orientation) {
if (0 == orientation) {
throw new IllegalArgumentException("ExpandableTextView only supports Vertical Orientation.");
} else {
// super.setOrientation(orientation);
}
}
@Override
public void onClick(View view) {
if (this.mButton.getVisibility() == VISIBLE) {
this.mCollapsed = !this.mCollapsed;
this.mButton.setImageDrawable(this.mCollapsed ? this.mExpandDrawable : this.mCollapseDrawable);
if (this.mCollapsedStatus != null) {
this.mCollapsedStatus.put(this.mPosition, this.mCollapsed);
}
this.mAnimating = true;
ExpandableTextView.ExpandCollapseAnimation animation;
if (this.mCollapsed) {
animation = new ExpandableTextView.ExpandCollapseAnimation(this, this.getHeight(), this.mCollapsedHeight);
} else {
animation = new ExpandableTextView.ExpandCollapseAnimation(this, this.getHeight(), this.getHeight() + this.mTextHeightWithMaxLines - this.mTv.getHeight());
}
animation.setFillAfter(true);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
ExpandableTextView.applyAlphaAnimation(ExpandableTextView.this.mTv, ExpandableTextView.this.mAnimAlphaStart);
}
@Override
public void onAnimationEnd(Animation animation) {
ExpandableTextView.this.clearAnimation();
ExpandableTextView.this.mAnimating = false;
if (ExpandableTextView.this.mListener != null) {
ExpandableTextView.this.mListener.onExpandStateChanged(ExpandableTextView.this.mTv, !ExpandableTextView.this.mCollapsed);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
this.clearAnimation();
this.startAnimation(animation);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return this.mAnimating;
}
@SuppressLint("MissingSuperCall")
@Override
protected void onFinishInflate() {
this.findViews();
}
@Override
@SuppressLint("WrongConstant")
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (this.mRelayout && this.getVisibility() != GONE) {
this.mRelayout = false;
this.mButton.setVisibility(GONE);
this.mTv.setMaxLines(Integer.MAX_VALUE);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (this.mTv.getLineCount() > this.mMaxCollapsedLines) {
this.mTextHeightWithMaxLines = getRealTextViewHeight(this.mTv);
if (this.mCollapsed) {
this.mTv.setMaxLines(this.mMaxCollapsedLines);
}
this.mButton.setVisibility(VISIBLE);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (this.mCollapsed) {
this.mTv.post(new Runnable() {
@Override
public void run() {
ExpandableTextView.this.mMarginBetweenTxtAndBottom = ExpandableTextView.this.getHeight() - ExpandableTextView.this.mTv.getHeight();
}
});
this.mCollapsedHeight = this.getMeasuredHeight();
}
}
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public void setOnExpandStateChangeListener(@Nullable ExpandableTextView.OnExpandStateChangeListener listener) {
this.mListener = listener;
}
@SuppressLint("WrongConstant")
public void setText(@Nullable CharSequence text) {
this.mRelayout = true;
this.mTv.setText(text);
this.setVisibility(TextUtils.isEmpty(text) ? GONE : VISIBLE);
}
public void setText(@Nullable CharSequence text, @NonNull SparseBooleanArray collapsedStatus, int position) {
this.mCollapsedStatus = collapsedStatus;
this.mPosition = position;
boolean isCollapsed = collapsedStatus.get(position, true);
this.clearAnimation();
this.mCollapsed = isCollapsed;
this.mButton.setImageDrawable(this.mCollapsed ? this.mExpandDrawable : this.mCollapseDrawable);
this.setText(text);
this.getLayoutParams().height = -2;
this.requestLayout();
}
@Nullable
public CharSequence getText() {
return (CharSequence) (this.mTv == null ? "" : this.mTv.getText());
}
@SuppressLint("WrongConstant")
private void init(AttributeSet attrs) {
TypedArray typedArray = this.getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
this.mMaxCollapsedLines = typedArray.getInt(R.styleable.ExpandableTextView_maxCollapsedLines, 8);
this.mAnimationDuration = typedArray.getInt(R.styleable.ExpandableTextView_animDuration, 300);
this.mAnimAlphaStart = typedArray.getFloat(R.styleable.ExpandableTextView_animAlphaStart, 0.7F);
this.mExpandDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_expandDrawable);
this.mCollapseDrawable = typedArray.getDrawable(R.styleable.ExpandableTextView_collapseDrawable);
if (this.mExpandDrawable == null) {
this.mExpandDrawable = getDrawable(this.getContext(), R.drawable.icon_live_home_arrow);
}
if (this.mCollapseDrawable == null) {
this.mCollapseDrawable = getDrawable(this.getContext(), R.drawable.icon_live_home_below);
}
typedArray.recycle();
this.setVisibility(GONE);
}
private void findViews() {
this.mTv = (TextView) this.findViewById(R.id.expandable_text);
this.mTv.setOnClickListener(this);
this.mButton = (ImageButton) this.findViewById(R.id.expand_collapse);
this.mButton.setImageDrawable(this.mCollapsed ? this.mExpandDrawable : this.mCollapseDrawable);
this.mButton.setOnClickListener(this);
}
private static boolean isPostHoneycomb() {
return Build.VERSION.SDK_INT >= 11;
}
private static boolean isPostLolipop() {
return Build.VERSION.SDK_INT >= 21;
}
@TargetApi(11)
private static void applyAlphaAnimation(View view, float alpha) {
if (isPostHoneycomb()) {
view.setAlpha(alpha);
} else {
AlphaAnimation alphaAnimation = new AlphaAnimation(alpha, alpha);
alphaAnimation.setDuration(0L);
alphaAnimation.setFillAfter(true);
view.startAnimation(alphaAnimation);
}
}
@TargetApi(21)
private static Drawable getDrawable(@NonNull Context context, int resId) {
Resources resources = context.getResources();
return isPostLolipop() ? resources.getDrawable(resId, context.getTheme()) : resources.getDrawable(resId);
}
private static int getRealTextViewHeight(@NonNull TextView textView) {
int textHeight = textView.getLayout().getLineTop(textView.getLineCount());
int padding = textView.getCompoundPaddingTop() + textView.getCompoundPaddingBottom();
return textHeight + padding;
}
public interface OnExpandStateChangeListener {
void onExpandStateChanged(TextView var1, boolean var2);
}
class ExpandCollapseAnimation extends Animation {
private final View mTargetView;
private final int mStartHeight;
private final int mEndHeight;
public ExpandCollapseAnimation(View view, int startHeight, int endHeight) {
this.mTargetView = view;
this.mStartHeight = startHeight;
this.mEndHeight = endHeight;
this.setDuration((long) ExpandableTextView.this.mAnimationDuration);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newHeight = (int) ((float) (this.mEndHeight - this.mStartHeight) * interpolatedTime + (float) this.mStartHeight);
ExpandableTextView.this.mTv.setMaxHeight(newHeight - ExpandableTextView.this.mMarginBetweenTxtAndBottom);
if (Float.compare(ExpandableTextView.this.mAnimAlphaStart, 1.0F) != 0) {
ExpandableTextView.applyAlphaAnimation(ExpandableTextView.this.mTv, ExpandableTextView.this.mAnimAlphaStart + interpolatedTime * (1.0F - ExpandableTextView.this.mAnimAlphaStart));
}
this.mTargetView.getLayoutParams().height = newHeight;
this.mTargetView.requestLayout();
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
@Override
public boolean willChangeBounds() {
return true;
}
}
}
注意:其中用到的资源如下:
<declare-styleable name="ExpandableTextView">
<attr name="maxCollapsedLines" format="integer"/>
<attr name="animDuration" format="integer"/>
<attr name="contentTextSize" format="dimension"/>
<attr name="contentTextColor" format="color"/>
<attr name="contentLineSpacingMultiplier" format="float"/>
<attr name="expandDrawable" format="reference"/>
<attr name="expandText" format="string"/>
<attr name="collapseDrawable" format="reference"/>
<attr name="collapseText" format="string"/>
<attr name="expandCollapseTextColor" format="color"/>
<attr name="animAlphaStart" format="float"/>
<attr name="DrawableAndTextGravity">
<enum name="left" value="0"/>
<enum name="center" value="1"/>
<enum name="right" value="2"/>
</attr>
</declare-styleable>
文章来源:https://blog.csdn.net/qq_36451275/article/details/135088233
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!