Jor*_*Gil 49 android textview android-animation
我有一个TextView首先显示长文本的一小部分.
用户可以按"查看更多"按钮展开TextView并查看该文本的其余部分.
进行测试时,我可以通过简单地将TextView.setMaxLines4之间的值换成折叠和Integer.MAX_VALUE进行扩展来实现.
现在,我希望这种行为伴随着一个动画.我知道在这个问题中,一个解决方案几乎已经完成,但我试图实现它并且我没有成功.
有人可以帮我这个吗?
提前致谢.
rok*_*oid 73
您可以在ExpandableTexTView上查看我的博客文章:
这个想法是,最初TextView将显示一小段长文本,当它被点击时,它将显示文本的其余部分.
所以这是我如何解决它的代码.
package com.rokonoid.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.SpannableStringBuilder;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
/**
* User: Bazlur Rahman Rokon
* Date: 9/7/13 - 3:33 AM
*/
public class ExpandableTextView extends TextView {
private static final int DEFAULT_TRIM_LENGTH = 200;
private static final String ELLIPSIS = ".....";
private CharSequence originalText;
private CharSequence trimmedText;
private BufferType bufferType;
private boolean trim = true;
private int trimLength;
public ExpandableTextView(Context context) {
this(context, null);
}
public ExpandableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
this.trimLength = typedArray.getInt(R.styleable.ExpandableTextView_trimLength, DEFAULT_TRIM_LENGTH);
typedArray.recycle();
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
trim = !trim;
setText();
requestFocusFromTouch();
}
});
}
private void setText() {
super.setText(getDisplayableText(), bufferType);
}
private CharSequence getDisplayableText() {
return trim ? trimmedText : originalText;
}
@Override
public void setText(CharSequence text, BufferType type) {
originalText = text;
trimmedText = getTrimmedText(text);
bufferType = type;
setText();
}
private CharSequence getTrimmedText(CharSequence text) {
if (originalText != null && originalText.length() > trimLength) {
return new SpannableStringBuilder(originalText, 0, trimLength + 1).append(ELLIPSIS);
} else {
return originalText;
}
}
public CharSequence getOriginalText() {
return originalText;
}
public void setTrimLength(int trimLength) {
this.trimLength = trimLength;
trimmedText = getTrimmedText(originalText);
setText();
}
public int getTrimLength() {
return trimLength;
}
}
Run Code Online (Sandbox Code Playgroud)
并在attr.xml中添加以下行
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ExpandableTextView">
<attr name="trimLength" format="integer"/>
</declare-styleable>
</resources>
Run Code Online (Sandbox Code Playgroud)
将以下内容放在main.xml中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.rokonoid.widget.ExpandableTextView
android:id="@+id/lorem_ipsum"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)
并测试你的活动
package com.rokonoid.widget;
import android.app.Activity;
import android.os.Bundle;
public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String yourText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Ut volutpat interdum interdum. Nulla laoreet lacus diam, vitae " +
"sodales sapien commodo faucibus. Vestibulum et feugiat enim. Donec " +
"semper mi et euismod tempor. Sed sodales eleifend mi id varius. Nam " +
"et ornare enim, sit amet gravida sapien. Quisque gravida et enim vel " +
"volutpat. Vivamus egestas ut felis a blandit. Vivamus fringilla " +
"dignissim mollis. Maecenas imperdiet interdum hendrerit. Aliquam" +
" dictum hendrerit ultrices. Ut vitae vestibulum dolor. Donec auctor ante" +
" eget libero molestie porta. Nam tempor fringilla ultricies. Nam sem " +
"lectus, feugiat eget ullamcorper vitae, ornare et sem. Fusce dapibus ipsum" +
" sed laoreet suscipit. ";
ExpandableTextView expandableTextView = (ExpandableTextView) findViewById(R.id.lorem_ipsum);
expandableTextView.setText(yourText);
}
}
Run Code Online (Sandbox Code Playgroud)
Ama*_*i82 64
ObjectAnimator animation = ObjectAnimator.ofInt(yourTextView, "maxLines", tv.getLineCount());
animation.setDuration(200).start();
Run Code Online (Sandbox Code Playgroud)
这将完全扩展您的TextView超过200毫秒.您可以替换tv.getLineCount()为希望将其折叠回来的多行文本.
---- ----更新
以下是一些便捷方法:
private void expandTextView(TextView tv){
ObjectAnimator animation = ObjectAnimator.ofInt(tv, "maxLines", tv.getLineCount());
animation.setDuration(200).start();
}
private void collapseTextView(TextView tv, int numLines){
ObjectAnimator animation = ObjectAnimator.ofInt(tv, "maxLines", numLines);
animation.setDuration(200).start();
}
Run Code Online (Sandbox Code Playgroud)
如果您使用的是API 16+,则可以使用textView.getMaxLines()轻松确定textView是否已扩展.
private void cycleTextViewExpansion(TextView tv){
int collapsedMaxLines = 3;
ObjectAnimator animation = ObjectAnimator.ofInt(tv, "maxLines",
tv.getMaxLines() == collapsedMaxLines? tv.getLineCount() : collapsedMaxLines);
animation.setDuration(200).start();
}
Run Code Online (Sandbox Code Playgroud)
笔记:
如果尚未设置maxLines,或者您设置了 textView 的高度(以像素为单位),则可以获得ArrayIndexOutOfBounds异常.
以上示例总是需要200毫秒,无论它们是扩展3行还是400行.如果您想要一致的扩展速率,您可以执行以下操作:
int duration = (textView.getLineCount() - collapsedMaxLines) * 10;
Run Code Online (Sandbox Code Playgroud)
Cli*_*fus 14
我为此创建了一个开源库,因为我对在互联网上找到的其他解决方案不满意.我把它放在GitHub上,任何人都可以免费使用.
public class ExpandableTextView extends TextView
{
// copy off TextView.LINES
private static final int MAXMODE_LINES = 1;
private OnExpandListener onExpandListener;
private TimeInterpolator expandInterpolator;
private TimeInterpolator collapseInterpolator;
private final int maxLines;
private long animationDuration;
private boolean animating;
private boolean expanded;
private int originalHeight;
public ExpandableTextView(final Context context)
{
this(context, null);
}
public ExpandableTextView(final Context context, final AttributeSet attrs)
{
this(context, attrs, 0);
}
public ExpandableTextView(final Context context, final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
// read attributes
final TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView, defStyle, 0);
this.animationDuration = attributes.getInt(R.styleable.ExpandableTextView_animation_duration, BuildConfig.DEFAULT_ANIMATION_DURATION);
attributes.recycle();
// keep the original value of maxLines
this.maxLines = this.getMaxLines();
// create default interpolators
this.expandInterpolator = new AccelerateDecelerateInterpolator();
this.collapseInterpolator = new AccelerateDecelerateInterpolator();
}
@Override
public int getMaxLines()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
return super.getMaxLines();
}
try
{
final Field mMaxMode = TextView.class.getField("mMaxMode");
mMaxMode.setAccessible(true);
final Field mMaximum = TextView.class.getField("mMaximum");
mMaximum.setAccessible(true);
final int mMaxModeValue = (int) mMaxMode.get(this);
final int mMaximumValue = (int) mMaximum.get(this);
return mMaxModeValue == MAXMODE_LINES ? mMaximumValue : -1;
}
catch (final Exception e)
{
return -1;
}
}
/**
* Toggle the expanded state of this {@link ExpandableTextView}.
* @return true if toggled, false otherwise.
*/
public boolean toggle()
{
if (this.expanded)
{
return this.collapse();
}
return this.expand();
}
/**
* Expand this {@link ExpandableTextView}.
* @return true if expanded, false otherwise.
*/
public boolean expand()
{
if (!this.expanded && !this.animating && this.maxLines >= 0)
{
this.animating = true;
// notify listener
if (this.onExpandListener != null)
{
this.onExpandListener.onExpand(this);
}
// get original height
this.measure
(
MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
);
this.originalHeight = this.getMeasuredHeight();
// set maxLines to MAX Integer
this.setMaxLines(Integer.MAX_VALUE);
// get new height
this.measure
(
MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
);
final int fullHeight = this.getMeasuredHeight();
final ValueAnimator valueAnimator = ValueAnimator.ofInt(this.originalHeight, fullHeight);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(final ValueAnimator animation)
{
final ViewGroup.LayoutParams layoutParams = ExpandableTextView.this.getLayoutParams();
layoutParams.height = (int) animation.getAnimatedValue();
ExpandableTextView.this.setLayoutParams(layoutParams);
}
});
valueAnimator.addListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationEnd(final Animator animation)
{
ExpandableTextView.this.expanded = true;
ExpandableTextView.this.animating = false;
}
});
// set interpolator
valueAnimator.setInterpolator(this.expandInterpolator);
// start the animation
valueAnimator
.setDuration(this.animationDuration)
.start();
return true;
}
return false;
}
/**
* Collapse this {@link TextView}.
* @return true if collapsed, false otherwise.
*/
public boolean collapse()
{
if (this.expanded && !this.animating && this.maxLines >= 0)
{
this.animating = true;
// notify listener
if (this.onExpandListener != null)
{
this.onExpandListener.onCollapse(this);
}
// get new height
final int fullHeight = this.getMeasuredHeight();
final ValueAnimator valueAnimator = ValueAnimator.ofInt(fullHeight, this.originalHeight);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(final ValueAnimator animation)
{
final ViewGroup.LayoutParams layoutParams = ExpandableTextView.this.getLayoutParams();
layoutParams.height = (int) animation.getAnimatedValue();
ExpandableTextView.this.setLayoutParams(layoutParams);
}
});
valueAnimator.addListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationEnd(final Animator animation)
{
// set maxLines to original value
ExpandableTextView.this.setMaxLines(ExpandableTextView.this.maxLines);
ExpandableTextView.this.expanded = false;
ExpandableTextView.this.animating = false;
}
});
// set interpolator
valueAnimator.setInterpolator(this.collapseInterpolator);
// start the animation
valueAnimator
.setDuration(this.animationDuration)
.start();
return true;
}
return false;
}
/**
* Sets the duration of the expand / collapse animation.
* @param animationDuration duration in milliseconds.
*/
public void setAnimationDuration(final long animationDuration)
{
this.animationDuration = animationDuration;
}
/**
* Sets a listener which receives updates about this {@link ExpandableTextView}.
* @param onExpandListener the listener.
*/
public void setOnExpandListener(final OnExpandListener onExpandListener)
{
this.onExpandListener = onExpandListener;
}
/**
* Returns the {@link OnExpandListener}.
* @return the listener.
*/
public OnExpandListener getOnExpandListener()
{
return onExpandListener;
}
/**
* Sets a {@link TimeInterpolator} for expanding and collapsing.
* @param interpolator the interpolator
*/
public void setInterpolator(final TimeInterpolator interpolator)
{
this.expandInterpolator = interpolator;
this.collapseInterpolator = interpolator;
}
/**
* Sets a {@link TimeInterpolator} for expanding.
* @param expandInterpolator the interpolator
*/
public void setExpandInterpolator(final TimeInterpolator expandInterpolator)
{
this.expandInterpolator = expandInterpolator;
}
/**
* Returns the current {@link TimeInterpolator} for expanding.
* @return the current interpolator, null by default.
*/
public TimeInterpolator getExpandInterpolator()
{
return this.expandInterpolator;
}
/**
* Sets a {@link TimeInterpolator} for collpasing.
* @param collapseInterpolator the interpolator
*/
public void setCollapseInterpolator(final TimeInterpolator collapseInterpolator)
{
this.collapseInterpolator = collapseInterpolator;
}
/**
* Returns the current {@link TimeInterpolator} for collapsing.
* @return the current interpolator, null by default.
*/
public TimeInterpolator getCollapseInterpolator()
{
return this.collapseInterpolator;
}
/**
* Is this {@link ExpandableTextView} expanded or not?
* @return true if expanded, false if collapsed.
*/
public boolean isExpanded()
{
return this.expanded;
}
public interface OnExpandListener
{
void onExpand(ExpandableTextView view);
void onCollapse(ExpandableTextView view);
}
}
Run Code Online (Sandbox Code Playgroud)
使用ExpandableTextView非常简单,它只是一个常规的TextView,并添加了一些额外的功能.通过定义android:maxLines属性,您可以设置TextView折叠状态的默认行数.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<at.blogc.android.views.ExpandableTextView
android:id="@+id/expandableTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lorem_ipsum"
android:maxLines="5"
android:ellipsize="end"
app:animation_duration="1000"/>
<!-- Optional parameter animation_duration: sets the duration of the expand animation -->
<Button
android:id="@+id/button_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/expand"/>
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)
在您的活动或片段中:
final ExpandableTextView expandableTextView = (ExpandableTextView) this.findViewById(R.id.expandableTextView);
final Button buttonToggle = (Button) this.findViewById(R.id.button_toggle);
// set animation duration via code, but preferable in your layout files by using the animation_duration attribute
expandableTextView.setAnimationDuration(1000L);
// set interpolators for both expanding and collapsing animations
expandableTextView.setInterpolator(new OvershootInterpolator());
// or set them separately
expandableTextView.setExpandInterpolator(new OvershootInterpolator());
expandableTextView.setCollapseInterpolator(new OvershootInterpolator());
// toggle the ExpandableTextView
buttonToggle.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(final View v)
{
expandableTextView.toggle();
buttonToggle.setText(expandableTextView.isExpanded() ? R.string.collapse : R.string.expand);
}
});
// but, you can also do the checks yourself
buttonToggle.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(final View v)
{
if (expandableTextView.isExpanded())
{
expandableTextView.collapse();
buttonToggle.setText(R.string.expand);
}
else
{
expandableTextView.expand();
buttonToggle.setText(R.string.collapse);
}
}
});
// listen for expand / collapse events
expandableTextView.setOnExpandListener(new ExpandableTextView.OnExpandListener()
{
@Override
public void onExpand(final ExpandableTextView view)
{
Log.d(TAG, "ExpandableTextView expanded");
}
@Override
public void onCollapse(final ExpandableTextView view)
{
Log.d(TAG, "ExpandableTextView collapsed");
}
});
Run Code Online (Sandbox Code Playgroud)
您可以轻松地将此库作为gradle依赖项添加到Android项目中.看一下Github上的项目,获取进一步的说明:
https://github.com/Blogcat/Android-ExpandableTextView
小智 6
平滑扩展(使用heigh和ObjectAnimator)
FYI:需要API 11
public static void expandCollapsedByMaxLines(@NonNull final TextView text) {
final int height = text.getMeasuredHeight();
text.setHeight(height);
text.setMaxLines(Integer.MAX_VALUE); //expand fully
text.measure(View.MeasureSpec.makeMeasureSpec(text.getMeasuredWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED));
final int newHeight = text.getMeasuredHeight();
ObjectAnimator animation = ObjectAnimator.ofInt(text, "height", height, newHeight);
animation.setDuration(250).start();
}
Run Code Online (Sandbox Code Playgroud)
PS我假设TextView受maxLines限制.
PSS感谢Amagi82的ObjectAnimator示例
如果要基于行数执行此操作,则可以采用以下方法:
(完整代码要点)
/**
* Ellipsize the text when the lines of text exceeds the value provided by {@link #makeExpandable} methods.
* Appends {@link #MORE} or {@link #LESS} as needed.
* TODO: add animation
* Created by vedant on 3/10/15.
*/
public class ExpandableTextView extends TextView {
private static final String TAG = "ExpandableTextView";
private static final String ELLIPSIZE = "... ";
private static final String MORE = "more";
private static final String LESS = "less";
private String mFullText;
private int mMaxLines;
//...constructors...
public void makeExpandable(String fullText, int maxLines) {
mFullText =fullText;
mMaxLines = maxLines;
ViewTreeObserver vto = getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewTreeObserver obs = getViewTreeObserver();
obs.removeOnGlobalLayoutListener(this);
if (getLineCount() <= maxLines) {
setText(mFullText);
} else {
setMovementMethod(LinkMovementMethod.getInstance());
showLess();
}
}
});
}
/**
* truncate text and append a clickable {@link #MORE}
*/
private void showLess() {
int lineEndIndex = getLayout().getLineEnd(mMaxLines - 1);
String newText = mFullText.substring(0, lineEndIndex - (ELLIPSIZE.length() + MORE.length() + 1))
+ ELLIPSIZE + MORE;
SpannableStringBuilder builder = new SpannableStringBuilder(newText);
builder.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
showMore();
}
}, newText.length() - MORE.length(), newText.length(), 0);
setText(builder, BufferType.SPANNABLE);
}
/**
* show full text and append a clickable {@link #LESS}
*/
private void showMore() {
// create a text like subText + ELLIPSIZE + MORE
SpannableStringBuilder builder = new SpannableStringBuilder(mFullText + LESS);
builder.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
showLess();
}
}, builder.length() - LESS.length(), builder.length(), 0);
setText(builder, BufferType.SPANNABLE);
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
您可以使用新的 TransitionManager 进行动画并调用 maxLines 属性来设置数量
fun toggleReadMoreTextView(linesWhenCollapsed: Float) {
if (viewDataBinding.textView.maxLines != Integer.MAX_VALUE) {
// exapand
viewDataBinding.textView.maxLines = Integer.MAX_VALUE
} else {
// collapse
viewDataBinding.textView.maxLines = linesWhenCollapsed
}
// start animation
TransitionManager.beginDelayedTransition(viewDataBinding.constraintLayout)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
67972 次 |
| 最近记录: |