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
相关产品推荐
相关产品推荐

