实现的效果图如下 ;
接下来 我们来看下布局
xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.wjkj.expendletextviewtest.MainActivity">
android:layout_width="match_parent"
android:id="@+id/expand_text_view"
app:maxCollapsedLines="2"
app:animDuration="200"
android:layout_height="wrap_content">
android:id="@+id/expandable_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="17dp"
android:ellipsize="end"
android:maxLines="4"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="@color/colorAccent"
android:textSize="14dp"
tools:text="这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文。" />
android:id="@+id/expand_collapse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:layout_marginTop="9dp"
android:background="@android:color/transparent"
android:paddingRight="10dp"
android:text="查看详情>>"
android:textColor="@color/colorPrimary"
android:textSize="12sp" />
布局文件相对比较简单
java代码相对比较简单 下面上代码
public class MainActivity extends AppCompatActivity { ExpandableTextView expand_text_view; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); expand_text_view = findViewById(R.id.expand_text_view); expand_text_view.setText("adasd asd " + " asd asd asd asd asd asdasdasddasdasdasdasdasdddddsadsdasd s dasd as das dasd sad asd asd asd asd " + " ads asd asd as das ads asd d asd as dasd asd这里是内容信息正文," + "这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文,这里是内容信息正文。 "); } }
自定义view
class ExpandableTextView extends LinearLayout implements View.OnClickListener { private static final String TAG = ExpandableTextView.class.getSimpleName(); /* The default number of lines */ private static final int MAX_COLLAPSED_LINES = 8; /* The default animation duration */ private static final int DEFAULT_ANIM_DURATION = 300; /* The default alpha value when the animation starts */ private static final float DEFAULT_ANIM_ALPHA_START = 0.7f; protected TextView mTv; protected TextView mTextView; // Button to expand/collapse private boolean mRelayout; private boolean mCollapsed = true; // Show short version as default. private int mCollapsedHeight; private int mTextHeightWithMaxLines; private int mMaxCollapsedLines; private int mMarginBetweenTxtAndBottom; private int mAnimationDuration; private float mAnimAlphaStart; private boolean mAnimating; /* Listener for callback */ private OnExpandStateChangeListener mListener; /* For saving collapsed status when used in ListView */ private SparseBooleanArray mCollapsedStatus; private int mPosition; public ExpandableTextView(Context context) { this(context, null); } public ExpandableTextView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public ExpandableTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs); } @Override public void setOrientation(int orientation) { if (LinearLayout.HORIZONTAL == orientation) { throw new IllegalArgumentException("ExpandableTextView only supports Vertical Orientation."); } super.setOrientation(orientation); } @Override public void onClick(View view) { if (mTextView.getVisibility() != View.VISIBLE) { return; } mCollapsed = !mCollapsed; if (mCollapsedStatus != null) { mCollapsedStatus.put(mPosition, mCollapsed); } // mark that the animation is in progress mAnimating = true; Animation animation; if (mCollapsed) { animation = new ExpandCollapseAnimation(this, getHeight(), mCollapsedHeight); } else { animation = new ExpandCollapseAnimation(this, getHeight(), getHeight() + mTextHeightWithMaxLines - mTv.getHeight()); } animation.setFillAfter(true); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { applyAlphaAnimation(mTv, mAnimAlphaStart); } @Override public void onAnimationEnd(Animation animation) { // clear animation here to avoid repeated applyTransformation() calls clearAnimation(); // clear the animation flag mAnimating = false; // notify the listener if (mListener != null) { mListener.onExpandStateChanged(mTv, !mCollapsed); } } @Override public void onAnimationRepeat(Animation animation) { } }); clearAnimation(); startAnimation(animation); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // while an animation is in progress, intercept all the touch events to children to // prevent extra clicks during the animation return mAnimating; } @Override protected void onFinishInflate() { super.onFinishInflate(); findViews(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // If no change, measure and return if (!mRelayout || getVisibility() == View.GONE) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } mRelayout = false; // Setup with optimistic case // i.e. Everything fits. No button needed mTextView.setVisibility(View.GONE); mTv.setMaxLines(Integer.MAX_VALUE); // Measure super.onMeasure(widthMeasureSpec, heightMeasureSpec); // If the text fits in collapsed mode, we are done. if (mTv.getLineCount() <= mMaxCollapsedLines) { return; } // Saves the text height w/ max lines mTextHeightWithMaxLines = getRealTextViewHeight(mTv); // Doesn't fit in collapsed mode. Collapse text view as needed. Show // button. if (mCollapsed) { mTv.setMaxLines(mMaxCollapsedLines); } mTextView.setVisibility(View.VISIBLE); // Re-measure with new setup super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mCollapsed) { // Gets the margin between the TextView's bottom and the ViewGroup's bottom mTv.post(new Runnable() { @Override public void run() { mMarginBetweenTxtAndBottom = getHeight() - mTv.getHeight(); } }); // Saves the collapsed height of this ViewGroup mCollapsedHeight = getMeasuredHeight(); } } public void setOnExpandStateChangeListener(@Nullable OnExpandStateChangeListener listener) { mListener = listener; } public void setText(@Nullable CharSequence text) { mRelayout = true; mTv.setText(text); setVisibility(TextUtils.isEmpty(text) ? View.GONE : View.VISIBLE); } public void setText(@Nullable CharSequence text, @NonNull SparseBooleanArray collapsedStatus, int position) { mCollapsedStatus = collapsedStatus; mPosition = position; boolean isCollapsed = collapsedStatus.get(position, true); clearAnimation(); mCollapsed = isCollapsed; setText(text); getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; requestLayout(); } @Nullable public CharSequence getText() { if (mTv == null) { return ""; } return mTv.getText(); } private void init(AttributeSet attrs) { TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableTextView); mMaxCollapsedLines = typedArray.getInt(R.styleable.ExpandableTextView_maxCollapsedLines, MAX_COLLAPSED_LINES); mAnimationDuration = typedArray.getInt(R.styleable.ExpandableTextView_animDuration, DEFAULT_ANIM_DURATION); mAnimAlphaStart = typedArray.getFloat(R.styleable.ExpandableTextView_animAlphaStart, DEFAULT_ANIM_ALPHA_START); typedArray.recycle(); // enforces vertical orientation setOrientation(LinearLayout.VERTICAL); // default visibility is gone setVisibility(GONE); } private void findViews() { mTv = (TextView) findViewById(R.id.expandable_text); mTv.setOnClickListener(this); mTextView = (TextView) findViewById(R.id.expand_collapse); mTextView.setOnClickListener(this); } private static boolean isPostHoneycomb() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; } private static boolean isPostLolipop() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private static void applyAlphaAnimation(View view, float alpha) { if (isPostHoneycomb()) { view.setAlpha(alpha); } else { AlphaAnimation alphaAnimation = new AlphaAnimation(alpha, alpha); // make it instant alphaAnimation.setDuration(0); alphaAnimation.setFillAfter(true); view.startAnimation(alphaAnimation); } } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private static Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) { Resources resources = context.getResources(); if (isPostLolipop()) { return resources.getDrawable(resId, context.getTheme()); } else { return 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; } class ExpandCollapseAnimation extends Animation { private final View mTargetView; private final int mStartHeight; private final int mEndHeight; public ExpandCollapseAnimation(View view, int startHeight, int endHeight) { mTargetView = view; mStartHeight = startHeight; mEndHeight = endHeight; setDuration(mAnimationDuration); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { final int newHeight = (int) ((mEndHeight - mStartHeight) * interpolatedTime + mStartHeight); mTv.setMaxHeight(newHeight - mMarginBetweenTxtAndBottom); if (Float.compare(mAnimAlphaStart, 1.0f) != 0) { applyAlphaAnimation(mTv, mAnimAlphaStart + interpolatedTime * (1.0f - mAnimAlphaStart)); } mTargetView.getLayoutParams().height = newHeight; 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 interface OnExpandStateChangeListener { /** * Called when the expand/collapse animation has been finished * * @param textView - TextView being expanded/collapsed * @param isExpanded - true if the TextView has been expanded */ void onExpandStateChanged(TextView textView, boolean isExpanded); }
这样就能简单的实现我们想要的效果 至于其他类似布局 仅仅在此基础上做稍微改动即可