假设我有一个垂直线性布局:
[v1]
[v2]
默认情况下,v1已经可见= GONE。我想用一个展开动画显示v1,同时下推v2。
我是这样做的:
Animation a = new Animation()
{
int initialHeight;
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final int newHeight = (int)(initialHeight * interpolatedTime);
v.getLayoutParams().height = newHeight;
v.requestLayout();
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
initialHeight = height;
}
@Override
public boolean willChangeBounds() {
return true;
}
};
但是用这个解决方案,当动画开始时我有一个眨眼。我认为这是由于v1在应用动画之前显示全尺寸。
用javascript,这是一行jQuery!android有什么简单的方法吗?
你可以使用Transition或Animator来改变要展开/折叠的部分的可见性,或者使用不同布局的ConstraintSet。
最简单的一个是使用motionLayout与2个不同的布局和constraintSets改变从一个布局到另一个按钮点击。可以在布局之间进行更改
val constraintSet = ConstraintSet()
constraintSet.clone(this, R.layout.layout_collapsed)
val transition = ChangeBounds()
transition.interpolator = AccelerateInterpolator(1.0f)
transition.setDuration(300)
TransitionManager.beginDelayedTransition(YOUR_VIEW, transition)
constraintSet.applyTo(YOUR_VIEW)
使用过渡api
RotateX.kt
我在gif中使用改变rotationX的过渡api创建了一个。
class RotateX : Transition {
@Keep
constructor() : super()
@Keep
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun getTransitionProperties(): Array<String> {
return TRANSITION_PROPERTIES
}
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun createAnimator(
sceneRoot: ViewGroup,
startValues: TransitionValues?,
endValues: TransitionValues?
): Animator? {
if (startValues == null || endValues == null) return null
val startRotation = startValues.values[PROP_ROTATION] as Float
val endRotation = endValues.values[PROP_ROTATION] as Float
if (startRotation == endRotation) return null
val view = endValues.view
// ensure the pivot is set
view.pivotX = view.width / 2f
view.pivotY = view.height / 2f
return ObjectAnimator.ofFloat(view, View.ROTATION_X, startRotation, endRotation)
}
private fun captureValues(transitionValues: TransitionValues) {
val view = transitionValues.view
if (view == null || view.width <= 0 || view.height <= 0) return
transitionValues.values[PROP_ROTATION] = view.rotationX
}
companion object {
private const val PROP_ROTATION = "iosched:rotate:rotation"
private val TRANSITION_PROPERTIES = arrayOf(PROP_ROTATION)
}
}
创建目标扩展按钮的XML文件
<?xml version="1.0" encoding="utf-8"?>
<transitionSet
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/fast_out_slow_in">
<transition class="com.smarttoolfactory.tutorial3_1transitions.transition.RotateX">
<targets>
<target android:targetId="@id/ivExpand" />
</targets>
</transition>
<autoTransition android:duration="200" />
</transitionSet>
My layout to be expanded or collapsed
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<com.google.android.material.card.MaterialCardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="2dp"
android:clickable="true"
android:focusable="true"
android:transitionName="@string/transition_card_view"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:cardPreventCornerOverlap="false">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivAvatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/avatar_1_raster" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivExpand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:padding="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_expand_more_24" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="6dp"
android:text="Some Title"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/ivAvatar"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="@+id/tvTitle"
app:layout_constraintTop_toBottomOf="@id/tvTitle"
tools:text="Tuesday 7pm" />
<TextView
android:id="@+id/tvBody"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:lines="1"
android:text="@string/bacon_ipsum_short"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/ivAvatar"
app:layout_constraintTop_toBottomOf="@id/tvDate" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal"
android:overScrollMode="never"
android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvBody"
tools:listitem="@layout/item_image_destination" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
并设置项目的可见性,以折叠或展开
private fun setUpExpandedStatus() {
if (isExpanded) {
binding.recyclerView.visibility = View.VISIBLE
binding.ivExpand.rotationX = 180f
} else {
binding.recyclerView.visibility = View.GONE
binding.ivExpand.rotationX = 0f
}
}
然后开始过渡
val transition = TransitionInflater.from(itemView.context)
.inflateTransition(R.transition.icon_expand_toggle)
TransitionManager.beginDelayedTransition(parent, transition)
isExpanded = !isExpanded
setUpExpandedStatus()
我创建了动画和过渡样本,包括gif上的那个,你可以在那里检查它们。
根据@Tom Esterez和@Seth Nelson的解决方案,我对它们进行了简化。与原始解决方案一样,它不依赖于开发人员选项(动画设置)。
private void resizeWithAnimation(final View view, int duration, final int targetHeight) {
final int initialHeight = view.getMeasuredHeight();
final int distance = targetHeight - initialHeight;
view.setVisibility(View.VISIBLE);
Animation a = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime == 1 && targetHeight == 0) {
view.setVisibility(View.GONE);
}
view.getLayoutParams().height = (int) (initialHeight + distance * interpolatedTime);
view.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(duration);
view.startAnimation(a);
}
这是我的解决方案,我的ImageView从100%增长到200%,并返回到原来的大小,使用res/anim/文件夹中的两个动画文件
anim_grow.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:fromXScale="1.0"
android:toXScale="2.0"
android:fromYScale="1.0"
android:toYScale="2.0"
android:duration="3000"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="2000" />
</set>
anim_shrink.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:fromXScale="2.0"
android:toXScale="1.0"
android:fromYScale="2.0"
android:toYScale="1.0"
android:duration="3000"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="2000" />
</set>
发送一个ImageView到setAnimationGrowShrink()方法
ImageView img1 = (ImageView)findViewById(R.id.image1);
setAnimationGrowShrink(img1);
setAnimationGrowShrink()方法:
private void setAnimationGrowShrink(final ImageView imgV){
final Animation animationEnlarge = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_grow);
final Animation animationShrink = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_shrink);
imgV.startAnimation(animationEnlarge);
animationEnlarge.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
imgV.startAnimation(animationShrink);
}
});
animationShrink.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
imgV.startAnimation(animationEnlarge);
}
});
}
如果你不想扩展或折叠所有的方式-这里是一个简单的HeightAnimation -
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class HeightAnimation extends Animation {
protected final int originalHeight;
protected final View view;
protected float perValue;
public HeightAnimation(View view, int fromHeight, int toHeight) {
this.view = view;
this.originalHeight = fromHeight;
this.perValue = (toHeight - fromHeight);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().height = (int) (originalHeight + perValue * interpolatedTime);
view.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
}
用法:
HeightAnimation heightAnim = new HeightAnimation(view, view.getHeight(), viewPager.getHeight() - otherView.getHeight());
heightAnim.setDuration(1000);
view.startAnimation(heightAnim);
/**
* Animation that either expands or collapses a view by sliding it down to make
* it visible. Or by sliding it up so it will hide. It will look like it slides
* behind the view above.
*
*/
public class FinalExpandCollapseAnimation extends Animation
{
private View mAnimatedView;
private int mEndHeight;
private int mType;
public final static int COLLAPSE = 1;
public final static int EXPAND = 0;
private LinearLayout.LayoutParams mLayoutParams;
private RelativeLayout.LayoutParams mLayoutParamsRel;
private String layout;
private Context context;
/**
* Initializes expand collapse animation, has two types, collapse (1) and
* expand (0).
*
* @param view
* The view to animate
* @param type
* The type of animation: 0 will expand from gone and 0 size to
* visible and layout size defined in xml. 1 will collapse view
* and set to gone
*/
public FinalExpandCollapseAnimation(View view, int type, int height, String layout, Context context)
{
this.layout = layout;
this.context = context;
mAnimatedView = view;
mEndHeight = mAnimatedView.getMeasuredHeight();
if (layout.equalsIgnoreCase("linear"))
mLayoutParams = ((LinearLayout.LayoutParams) view.getLayoutParams());
else
mLayoutParamsRel = ((RelativeLayout.LayoutParams) view.getLayoutParams());
mType = type;
if (mType == EXPAND)
{
AppConstant.ANIMATED_VIEW_HEIGHT = height;
}
else
{
if (layout.equalsIgnoreCase("linear"))
mLayoutParams.topMargin = 0;
else
mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
}
setDuration(600);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f)
{
if (mType == EXPAND)
{
if (layout.equalsIgnoreCase("linear"))
{
mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT
+ (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
}
else
{
mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT
+ (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
}
mAnimatedView.setVisibility(View.VISIBLE);
}
else
{
if (layout.equalsIgnoreCase("linear"))
mLayoutParams.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
else
mLayoutParamsRel.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
}
mAnimatedView.requestLayout();
}
else
{
if (mType == EXPAND)
{
if (layout.equalsIgnoreCase("linear"))
{
mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT;
mLayoutParams.topMargin = 0;
}
else
{
mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT;
mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
}
mAnimatedView.setVisibility(View.VISIBLE);
mAnimatedView.requestLayout();
}
else
{
if (layout.equalsIgnoreCase("linear"))
mLayoutParams.height = 0;
else
mLayoutParamsRel.height = 0;
mAnimatedView.setVisibility(View.GONE);
mAnimatedView.requestLayout();
}
}
}
private int convertPixelsIntoDensityPixels(int pixels)
{
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int) metrics.density * pixels;
}
}
该类可以通过以下方式调用
if (findViewById(R.id.ll_specailoffer_show_hide).getVisibility() == View.VISIBLE) {
((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown_up);
FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
findViewById(R.id.ll_specailoffer_show_hide),
FinalExpandCollapseAnimation.COLLAPSE,
SpecialOfferHeight, "linear", this);
findViewById(R.id.ll_specailoffer_show_hide)
.startAnimation(finalExpandCollapseAnimation);
((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
} else {
((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown);
FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
findViewById(R.id.ll_specailoffer_show_hide),
FinalExpandCollapseAnimation.EXPAND,
SpecialOfferHeight, "linear", this);
findViewById(R.id.ll_specailoffer_show_hide)
.startAnimation(finalExpandCollapseAnimation);
((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
}
这是一个片段,我用来调整一个视图(线性布局)的宽度与动画。
代码应该根据目标大小进行扩展或收缩。如果你想要一个fill_parent宽度,你必须在将标志设置为true时将父类. getmeasuredwidth作为目标宽度。
希望它能帮助到你们中的一些人。
public class WidthResizeAnimation extends Animation {
int targetWidth;
int originaltWidth;
View view;
boolean expand;
int newWidth = 0;
boolean fillParent;
public WidthResizeAnimation(View view, int targetWidth, boolean fillParent) {
this.view = view;
this.originaltWidth = this.view.getMeasuredWidth();
this.targetWidth = targetWidth;
newWidth = originaltWidth;
if (originaltWidth > targetWidth) {
expand = false;
} else {
expand = true;
}
this.fillParent = fillParent;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (expand && newWidth < targetWidth) {
newWidth = (int) (newWidth + (targetWidth - newWidth) * interpolatedTime);
}
if (!expand && newWidth > targetWidth) {
newWidth = (int) (newWidth - (newWidth - targetWidth) * interpolatedTime);
}
if (fillParent && interpolatedTime == 1.0) {
view.getLayoutParams().width = -1;
} else {
view.getLayoutParams().width = newWidth;
}
view.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;
}
}