You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android滚动动画中TextView长文本被截断问题求助

解决Android TextView缩放位移后文本被LinearLayout截断的问题

问题根源

你当前使用的是View动画(scale/translate),android:fillAfter="true"仅改变了TextView的视觉显示,并未修改它的实际布局尺寸。同时手动将LinearLayout高度硬编码为原高度的一半,当文本内容较长时,LinearLayout的实际高度不足以容纳TextView的真实内容(即便视觉上已缩小),最终导致文本被截断。

具体解决方案

1. 替换View动画为属性动画,修改真实布局属性

放弃XML定义的View动画,改用ObjectAnimator直接修改TextView的textSize、translationX、translationY等真实属性,让布局系统能感知到尺寸变化,避免视觉表现与布局逻辑脱节。

示例代码:

// 文本缩小+位移的属性动画
private void startZoomOutAnimation(TextView tv) {
    // 缩小字体尺寸(从25sp改为12.5sp)
    ObjectAnimator textSizeAnim = ObjectAnimator.ofFloat(tv, "textSize", 25f, 12.5f);
    // X轴位移(基于屏幕宽度的28%)
    float translateX = -getResources().getDisplayMetrics().widthPixels * 0.28f;
    ObjectAnimator translateXAnim = ObjectAnimator.ofFloat(tv, "translationX", 0, translateX);
    // Y轴位移(基于自身高度的一半)
    float translateY = -tv.getHeight() * 0.5f;
    ObjectAnimator translateYAnim = ObjectAnimator.ofFloat(tv, "translationY", 0, translateY);

    AnimatorSet set = new AnimatorSet();
    set.setDuration(1000);
    set.setInterpolator(new AccelerateDecelerateInterpolator());
    set.playTogether(textSizeAnim, translateXAnim, translateYAnim);
    set.start();
}

// 文本恢复缩放与位置的动画
private void startZoomInAnimation(TextView tv) {
    ObjectAnimator textSizeAnim = ObjectAnimator.ofFloat(tv, "textSize", 12.5f, 25f);
    ObjectAnimator translateXAnim = ObjectAnimator.ofFloat(tv, "translationX", tv.getTranslationX(), 0);
    ObjectAnimator translateYAnim = ObjectAnimator.ofFloat(tv, "translationY", tv.getTranslationY(), 0);

    AnimatorSet set = new AnimatorSet();
    set.setDuration(1000);
    set.setInterpolator(new AccelerateDecelerateInterpolator());
    set.playTogether(textSizeAnim, translateXAnim, translateYAnim);
    set.start();
}

2. 动态计算LinearLayout目标高度,避免硬编码

不要直接用layout2.getHeight() / 2,而是计算TextView缩放后的实际高度,再加上LinearLayout的padding、margin等,确保高度足够容纳内容。

修改滚动监听与slideView方法:

// 滚动监听逻辑优化
scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
    boolean isStart = false;

    @Override
    public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        if (scrollY > 0 && !isStart) {
            startZoomOutAnimation(recipeTitle);
            // 计算目标高度:缩小后的TextView高度 + 上下margin
            int targetHeight = (int) (recipeTitle.getHeight() * 0.5f) + 
                    getResources().getDimensionPixelOffset(R.dimen.text_margin_top) * 2;
            slideView(layout2, layout2.getHeight(), targetHeight);
            isStart = true;
        } else if (scrollY <= 0) {
            startZoomInAnimation(recipeTitle);
            // 恢复为wrap_content对应的真实高度
            int targetHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
            slideView(layout2, layout2.getHeight(), targetHeight);
            isStart = false;
        }
    }
});

// 修改slideView方法,支持WRAP_CONTENT目标高度
public static void slideView(View view, int currentHeight, int newHeight) {
    int finalHeight = newHeight;
    // 如果目标高度是WRAP_CONTENT,先测量View的真实高度
    if (newHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
        view.measure(
                View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
        );
        finalHeight = view.getMeasuredHeight();
    }

    ValueAnimator slideAnimator = ValueAnimator.ofInt(currentHeight, finalHeight).setDuration(1000);
    slideAnimator.addUpdateListener(animation1 -> {
        Integer value = (Integer) animation1.getAnimatedValue();
        view.getLayoutParams().height = value;
        view.requestLayout();
    });

    AnimatorSet animationSet = new AnimatorSet();
    animationSet.setInterpolator(new AccelerateDecelerateInterpolator());
    animationSet.play(slideAnimator);
    animationSet.start();
}

3. 禁止LinearLayout裁剪子View

在LinearLayout的布局代码中添加android:clipChildren="false",确保TextView位移后不会被父布局裁剪:

<LinearLayout
    android:id="@+id/layout2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:clipChildren="false"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@id/cons1">

    <TextView
        android:id="@+id/t1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:fontFamily="@font/poppins"
        android:text="How to make french toast"
        android:textColor="@color/black"
        android:textSize="25sp"
        android:textStyle="bold" />

</LinearLayout>

注:TextView原有的app:layout_constraint属性可删除,因为它的父布局是LinearLayout,这些约束属性无效

4. 优化滚动触发逻辑,避免重复动画

增加滚动阈值,只有当滚动距离超过设定值时才触发动画,避免快速滚动时重复执行:

private static final int SCROLL_THRESHOLD = 50;
scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
    boolean isStart = false;
    int lastScrollY = 0;

    @Override
    public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        if (scrollY - lastScrollY > SCROLL_THRESHOLD && !isStart) {
            startZoomOutAnimation(recipeTitle);
            int targetHeight = (int) (recipeTitle.getHeight() * 0.5f) + 
                    getResources().getDimensionPixelOffset(R.dimen.text_margin_top) * 2;
            slideView(layout2, layout2.getHeight(), targetHeight);
            isStart = true;
        } else if (lastScrollY - scrollY > SCROLL_THRESHOLD && isStart) {
            startZoomInAnimation(recipeTitle);
            int targetHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
            slideView(layout2, layout2.getHeight(), targetHeight);
            isStart = false;
        }
        lastScrollY = scrollY;
    }
});

内容的提问来源于stack exchange,提问作者mikegrep

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 23:05:33