英文:
Animate border of an imageview
问题
我有一个按钮,应该可以滑动,所以我想要动画来显示它的滑动能力。动画应该看起来类似于三星手机在接电话时的本机按钮上的动画,可以在此视频中看到:https://www.youtube.com/watch?v=_FRvMm-QXcY
我尝试添加一个带有背景可绘制的图像视图(白色圆圈),然后使用动画缩放x和y,但显然这会缩放整个图像,而不仅仅是背景可绘制部分。我一直在想如何动画化图像周围的圆圈,而不是图像本身。将圆圈添加为背景可能已经是错误的方法,所以我不一定需要坚持这种方法。
这是迄今为止我的代码:
<de.utils.customViews.SwipableImageView
android:id="@+id/accept_call_button"
android:layout_width="@dimen/accept_hangup_call_button"
android:layout_height="@dimen/accept_hangup_call_button"
android:layout_margin="@dimen/activity_vertical_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:contentDescription="@string/accept_call"
android:src="@drawable/ic_accept_call"
android:background="@drawable/swipe_circle"
/>
public class SwipableImageView extends androidx.appcompat.widget.AppCompatImageView {
public SwipableImageView(@NonNull Context context) {
super(context);
init();
}
public SwipableImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public SwipableImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init() {
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.swipable);
startAnimation(animation);
}
}
以及(迄今为止)的 swipable 动画(我知道我还需要处理 alpha,但我首先想弄清如何只动画圆圈):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="false"
>
<scale
android:duration="1000"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="2.0"
android:toYScale="2.0"
android:repeatCount="infinite"
android:interpolator="@android:anim/linear_interpolator" />
</set>
英文:
I have a button, that should be swipable, so I want to animate it to communicate it's capability to swipe. The animation should look similarly to the one in native Samsung phones when receiving a call (the one on the buttons), as can be seen in this video: https://www.youtube.com/watch?v=_FRvMm-QXcY
I have tried to add an image view with a background drawable (white circle) and then scale x and y with an animation, but obviously this scales the whole image, not just the background drawable. I've been wondering how to animate the circle around the image instead of the image itself. Adding the circle as a background may already be a wrong approach, so I don't necessarily need to stick to that.
Here's my code so far:
<de.utils.customViews.SwipableImageView
android:id="@+id/accept_call_button"
android:layout_width="@dimen/accept_hangup_call_button"
android:layout_height="@dimen/accept_hangup_call_button"
android:layout_margin="@dimen/activity_vertical_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:contentDescription="@string/accept_call"
android:src="@drawable/ic_accept_call"
android:background="@drawable/swipe_circle"
/>
public class SwipableImageView extends androidx.appcompat.widget.AppCompatImageView {
public SwipableImageView(@NonNull Context context) {
super(context);
init();
}
public SwipableImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public SwipableImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init() {
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.swipable);
startAnimation(animation);
}
}
and the (so far) animation swipable (I know I also have to work with alpha, but I wanted to figure out how to only animate the circle first):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="false"
>
<scale
android:duration="1000"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="2.0"
android:toYScale="2.0"
android:repeatCount="infinite"
android:interpolator="@android:anim/linear_interpolator" />
</set>
答案1
得分: 1
我认为以'hamburger'样式实现这个会更简单 - 所以你会有一个静态的SwipableImageView
和一个单独的Animation
视图,你只需将它放在主ImageView下面。
这样做会导致一些GPU过度绘制,因为视图嵌套。但这不应该是关键问题,因为它只会占用屏幕的一小部分。
此外,如果需要的话,你还可以将这个'hamburger'实现为一个CompoundView,这样在应用中可以更容易地重复使用。
更新
使用FrameLayout
的示例实现:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<View
android:id="@+id/animationBorder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<View
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
英文:
I think it would be much simpler to implement this in a 'hamburger' style - so you will have static SwipableImageView
and a separate Animation
view, which you simply put below your main ImageView.
By doing so we will receive little bit more of GPU overdraws, because of views nesting. But that shouldn't be critical at all, since it will take only small area of screen.
Also you can implement this 'hamburger' as a CompoundView, so it will be easier to reuse across the app if needed.
UPDATE
Sample implementation using FrameLayout
:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<View
android:id="@+id/animationBorder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<View
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
答案2
得分: 1
为了更详细地讨论Ruslan的想法,对于后台中的动画可绘制,您可以使用两个可绘制对象 - 一个用于白色中心圆圈,一个用于扩展环。
center_circle.xml
<inset
android:insetLeft="@dimen/swipeable_padding"
android:insetTop="@dimen/swipeable_padding"
android:insetRight="@dimen/swipeable_padding"
android:insetBottom="@dimen/swipeable_padding">
<shape
android:shape="oval">
<solid android:color="@android:color/white" />
</shape>
</inset>
您可以将此作为背景设置给一个 ImageView。插图留出了圆圈周围的空间。对于扩展环:
expanding_ring.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:thickness="10dp"
android:innerRadius="34dp"
android:useLevel="true">
<solid android:color="#55ffffff" />
</shape>
这将是动画 ImageView 的src
。
您需要以编程方式将环可绘制对象的级别设置为10,000,才能显示任何内容。
现在,在代码中,您可以使用 ValueAnimator 来更改环的厚度,从零到一个将填充 ImageView 的值。不幸的是,要以编程方式更改厚度,您的应用程序需要具有minSdk 29或更高的版本。
另一个对于任何API都适用的想法是编写一个可绘制类,该类将直接在画布上绘制扩展环。此类的签名将是
class SwipeableBackground extends Drawable implements Animatable
并将其设置为单个 ImageView 的背景,该 ImageView 也将包含电话图标作为src
。在此类中,您还可以使用 ValueAnimator 来帮助增加环的大小。
在这两种情况下,您将创建另一个 ValueAnimator 来在扩展结束时淡化环。这两个动画将放入 AnimatorSet 中,该集合将无限重复或在停止之前执行。
如果您愿意,也可以使用一些库来轻松实现这种动画效果。
英文:
To address Ruslan's idea in a little more detail, for the animation drawable in back, you could use two drawables - one for the white center circle and one for the expanding ring.
center_circle.xml
<inset
android:insetLeft="@dimen/swipeable_padding"
android:insetTop="@dimen/swipeable_padding"
android:insetRight="@dimen/swipeable_padding"
android:insetBottom="@dimen/swipeable_padding">
<shape
android:shape="oval">
<solid android:color="@android:color/white" />
</shape>
</inset>
You would use this as the background in an ImageView. The insets make room for the ring around the circle. For the expanding ring:
expanding_ring.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:thickness="10dp"
android:innerRadius="34dp"
android:useLevel="true">
<solid android:color="#55ffffff" />
</shape>
This would be the src
for the animation ImageView.
You will need to set the level for the ring drawable to 10,000 programatically for anything to show.
Now, in code, you would use a ValueAnimator to change the ring's thickness to go from zero to a value that will fill the ImageView. Unfortunately, to change the thickness programmatically, your app will need a minSdk of 29 or greater.
Another idea which would work for any API is to write a drawable class that will simply draw the expanding ring directly onto the canvas. The signature for this class would be
class SwipeableBackground extends Drawable implements Animatable
and it would be set as the background to a single ImageView that will also hold the phone icon as the src
. In this class you can also use the ValueAnimator to assist in growing the ring.
In both cases, you would create another ValueAnimator to fade the ring as the end of expansion. The two animators would be placed into an AmimatorSet which would repeat indefinitely or until stopped.
There may be some libraries that can easily do this animation if you want to go that route.
答案3
得分: 1
来自@Ruslan和@Cheticamp的答案中,我得出了以下解决方案:
创建用于动画按钮的FrameLayout:
<FrameLayout
android:id="@+id/accept_call_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_vertical_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<de.example.utils.customViews.SwipeAnimationView
android:layout_width="@dimen/accept_hangup_call_button"
android:layout_height="@dimen/accept_hangup_call_button"
android:layout_gravity="center"
android:src="@drawable/swipe_circle" />
<de.example.utils.customViews.SwipableImageView
android:layout_width="@dimen/accept_hangup_call_button"
android:layout_height="@dimen/accept_hangup_call_button"
android:layout_gravity="center"
android:contentDescription="@string/accept_call"
android:layout_margin="@dimen/anim_margin"
android:src="@drawable/ic_accept_call"
/>
</FrameLayout>
通话按钮(SwipableImageView)具有动画边距,这使得FrameLayout的大小与当前图标大小以及动画将占用的空间大小一致。
然后,我有一个带有init()函数的SwipeAnimationView,它只调用动画:
public void init() {
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.swipable);
startAnimation(animation);
}
此函数在每个构造函数中都会被调用。动画如下所示(现在还带有alpha更改):
<?xml version="1.0" encoding="utf-8">
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="false"
>
<scale
android:duration="1500"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="1.5"
android:toYScale="1.5"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:interpolator="@android:anim/linear_interpolator" />
<alpha
android:duration="500"
android:startOffset="1000"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:repeatCount="infinite"
android:interpolator="@android:anim/linear_interpolator"
/>
</set>
swipe_circle只是一个椭圆,因为Android Studio无法显示环形。然而,由于其他图标覆盖它,只需调整大小即可正常工作。但是,为了保持持续的居中,必须在动画中考虑pivotX和pivotY。
英文:
Out of the answers of @Ruslan and @Cheticamp I came to the following solution:
Create Framelayout for animated button:
<FrameLayout
android:id="@+id/accept_call_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_vertical_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<de.example.utils.customViews.SwipeAnimationView
android:layout_width="@dimen/accept_hangup_call_button"
android:layout_height="@dimen/accept_hangup_call_button"
android:layout_gravity="center"
android:src="@drawable/swipe_circle" />
<de.example.utils.customViews.SwipableImageView
android:layout_width="@dimen/accept_hangup_call_button"
android:layout_height="@dimen/accept_hangup_call_button"
android:layout_gravity="center"
android:contentDescription="@string/accept_call"
android:layout_margin="@dimen/anim_margin"
android:src="@drawable/ic_accept_call"
/>
</FrameLayout>
The call button (SwipableImageView) has an animation margin, which makes the framelayout align in size to the current icons size + the space the animation will take up.
Then I have the SwipeAnimationView with an init()-function that just calls the animation:
public void init() {
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.swipable);
startAnimation(animation);
}
This function gets called in every constructor.
The animation then is in the following xml (by now also with alpha changes):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="false"
>
<scale
android:duration="1500"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="1.5"
android:toYScale="1.5"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:interpolator="@android:anim/linear_interpolator" />
<alpha
android:duration="500"
android:startOffset="1000"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:repeatCount="infinite"
android:interpolator="@android:anim/linear_interpolator"
/>
</set>
The swipe_circle is just an oval, as Android Studio wouldn't display a ring shape. However, as the other icon overlays it, it works perfectly fine to just adapt the size. For continuos centering however, the pivotX and pivotY need to be considered in the animation.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论