Android-浅谈原生动画

Android-浅谈原生动画,第1张

在日常开发当中,我们会使用 Android 的原生自带的动画效果来实现更友好的交互。在 自定义View 的时候,有可以用原生的动画来实现一些酷炫的效果。这篇博客主要是简单聊聊 Android 原生动画相关的知识,希望对看文章的小伙伴有所启发。

Android 的原生动画分类:

其中需要注意的是, 帧动画 也属于 View动画 的一种。

View动画 通过对场景的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生效果,它是一种渐进式动画。 View动画 还支持我们自定义开发。

帧动画 通过播放一系列有序的图像来实现动画效果,我们可以理解成的切换动画。需要注意的是,如果帧动画的过大或者过多可能会导致OOM的产生。

属性动画 是通过改变对象的属性来实现我们想要的动画效果。属性动画是 API 11 才是有的,目前一般开发App都是会从21开始开发的,这个知识点要记一下,面试可能会遇到它。

后续我会根据这三种动画来写详细的文章,感兴趣的小伙伴可以关注一下。

Android开发之ImageView播放GIF动画实例

Android的原生控件并不支持播放GIF格式的,如果想在Android中显示一张GIF动态,可以利用 ImageView控件来完成,但是放进去之后,你会发现,ImageView它只会显示这张的第一帧,不会产生任何的动画效果。我们必须通过自定义控件的方式来实现ImageView播放GIF 的功能。

首先我们来编写一个PowerImageView控件,让它既能支持ImageView控件原生的所有功能,同时还可以播放GIF动态。

先新建一个项目PowerImageViewTest,这里使用Android 40+Eclipse。

由于是要自定义控件,会需要一些自定义的控件属性,因此我们需要在values目录下新建一个attrsxml的文件,在这个文件中添加项目需要的自定义属性。

这里我们目前暂时只需要一个自动播放auto_play属性,XML文件代码如下:

<xml version="10" encoding="utf-8">

这个文件完成之后,下面我们来开始编写主类PowerImageView类,由于PowerImageView类需要支持ImageView的所有功能,我们必须要让PowerImageView继承自ImageView,代码如下:

public class PowerImageView extends ImageView implements OnClickListener {

/

播放GIF动画的关键类

/

private Movie mMovie;

/

开始播放按钮

/

private Bitmap mStartButton;

/

记录动画开始的时间

/

private long mMovieStart;

/

GIF的宽度

/

private int mImageWidth;

/

GIF的高度

/

private int mImageHeight;

/

是否正在播放

/

private boolean isPlaying;

/

是否允许自动播放

/

private boolean isAutoPlay;

/

PowerImageView构造函数。

@param context

/

public PowerImageView(Context context) {

super(context);

}

/

PowerImageView构造函数。

@param context

/

public PowerImageView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

/

PowerImageView构造函数,在这里完成所有必要的初始化 *** 作。

@param context

/

public PowerImageView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

TypedArray a = contextobtainStyledAttributes(attrs, RstyleablePowerImageView);

int resourceId = getResourceId(a, context, attrs);

if (resourceId != 0) {

// 当资源id不等于0时,就去获取该资源的流

InputStream is = getResources()openRawResource(resourceId);

// 使用Movie类对流进行解码

mMovie = MoviedecodeStream(is);

if (mMovie != null) {

// 如果返回值不等于null,就说明这是一个GIF,下面获取是否自动播放的属性

isAutoPlay = agetBoolean(RstyleablePowerImageView_auto_play, false);

Bitmap bitmap = BitmapFactorydecodeStream(is);

mImageWidth = bitmapgetWidth();

mImageHeight = bitmapgetHeight();

bitmaprecycle();

if (!isAutoPlay) {

// 当不允许自动播放的时候,得到开始播放按钮的,并注册点击事件

mStartButton = BitmapFactorydecodeResource(getResources(),Rdrawablestart_play);

setOnClickListener(this);

}

}

}

}

@Override

public void onClick(View v) {

if (vgetId() == getId()) {

// 当用户点击时,开始播放GIF动画

isPlaying = true;

invalidate();

}

}

@Override

protected void onDraw(Canvas canvas) {

if (mMovie == null) {

// mMovie等于null,说明是张普通的,则直接调用父类的onDraw()方法

superonDraw(canvas);

} else {

// mMovie不等于null,说明是张GIF

if (isAutoPlay) {

// 如果允许自动播放,就调用playMovie()方法播放GIF动画

playMovie(canvas);

invalidate();

} else {

// 不允许自动播放时,判断当前是否正在播放

if (isPlaying) {

// 正在播放就继续调用playMovie()方法,一直到动画播放结束为止

if (playMovie(canvas)) {

isPlaying = false;

}

invalidate();

} else {

// 还没开始播放就只绘制GIF的第一帧,并绘制一个开始按钮

mMoviesetTime(0);

mMoviedraw(canvas, 0, 0);

int offsetW = (mImageWidth - mStartButtongetWidth()) / 2;

int offsetH = (mImageHeight - mStartButtongetHeight()) / 2;

canvasdrawBitmap(mStartButton, offsetW, offsetH, null);

}

}

}

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

superonMeasure(widthMeasureSpec, heightMeasureSpec);

if (mMovie != null) {

// 如果是GIF则重写设定PowerImageView的大小

setMeasuredDimension(mImageWidth, mImageHeight);

}

}

/

开始播放GIF动画,播放完成返回true,未完成返回false。

@param canvas

@return 播放完成返回true,未完成返回false。

/

private boolean playMovie(Canvas canvas) {

long now = SystemClockuptimeMillis();

if (mMovieStart == 0) {

mMovieStart = now;

}

int duration = mMovieduration();

if (duration == 0) {

duration = 1000;

}

int relTime = (int) ((now - mMovieStart) % duration);

mMoviesetTime(relTime);

mMoviedraw(canvas, 0, 0);

if ((now - mMovieStart) >= duration) {

mMovieStart = 0;

return true;

}

return false;

}

/

通过Java反射,获取到src指定资源所对应的id。

@param a

@param context

@param attrs

@return 返回布局文件中指定资源所对应的id,没有指定任何资源就返回0。

/

private int getResourceId(TypedArray a, Context context, AttributeSet attrs) {

try {

Field field = TypedArrayclassgetDeclaredField("mValue");

fieldsetAccessible(true);

TypedValue typedValueObject = (TypedValue) fieldget(a);

return typedValueObjectresourceId;

} catch (Exception e) {

eprintStackTrace();

} finally {

if (a != null) {

arecycle();

}

}

return 0;

}

}

这个类的代码注释已经非常详细了,我再来简单地解释一下。可以看到,我们重写了ImageView中所有的构建函数,使得 PowerImageView的用法可以和ImageView完全相同。在构造函数中,则是对所有必要的数据进行了初始化 *** 作。首先,我们调用了 getResourceId()方法去获取资源对应的id值,在getResourceId()方法内部是通过Java的反射机制来进行获取的。得到了资源的id后,我们将它转换成InputStream,然后传入到MoviedecodeStream()方法中以解码出Movie对象。如果得到的Movie对象等于null,说明这是一张普通的资源,就不再进行任何特殊处理,因为父类ImageView都帮我们处理好了。如果得到的 Movie对象不等于null,则说明这是一张GIF,接着就要去获取是否允许自动播放、的宽高等属性的值。如果不允许自动播放,还要给播放按钮 注册点击事件,默认是不允许自动播放的。

接下来会进入到onMeasure()方法中。在这个方法中我们进行判断,如果这是一张GIF,则需要将PowerImageView的宽高重定义,使得控件的大小刚好可以放得下这张GIF。

再往后就会进入到onDraw()方法中。在这个方法里同样先判断当前是一张普通的还是GIF,如果是普通的就直接调用 superonDraw()方法交给ImageView去处理就好了。如果是GIF,则先判断该图是否允许自动播放,允许的话就调用 playMovie()方法去播放GIF就好,不允许的话则会先在PowerImageView中绘制该GIF的第一帧,并在上绘制一个播放 按钮,当用户点击了播放按钮时,再去调用playMovie()方法去播放GIF。

下面我们来看看playMovie()方法中是怎样播放GIF的吧。可以看到,首先会对动画开始的时间做下记录,然后对动画持续的时间做下记 录,接着使用当前的时间减去动画开始的时间,得到的时间就是此时PowerImageView应该显示的那一帧,然后借助Movie对象将这一帧绘制到屏 幕上即可。之后每次调用playMovie()方法都会绘制一帧,连贯起来也就形成了GIF动画。注意,这个方法是有返回值的,如果当前时间减去动画 开始时间大于了动画持续时间,那就说明动画播放完成了,返回true,否则返回false。

完成了PowerImageView的编写,下面我们就来看一看如何使用它吧,其实非常简单,打开或新建activity_mainxml,代码如下所示:

<relativelayout p=""

android:layout_width="match_parent"

android:layout_height="match_parent" >

<comexamplepowerimageviewtestpowerimageview p=""

android:id="@+id/image_view"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerInParent="true"

android:src="@drawable/anim"

/>

可以看到,PowerImageView的用法和ImageView几乎完全一样,使用android:src属性来指定一张即可,这里指定的anim就是一张GIF。然后我们让PowerImageView在布局里居中显示MainActivity中的代码都是自动生成的,这里就不再贴出来了。在AndroidManifestxml中还有一点需要注意,有些40 以上系统的手机启动了硬件加速功能之后会导致GIF动画播放不出来,因此我们需要在AndroidManifestxml中去禁用硬件加速功能,可以通过指定android:hardwareAccelerated属性来完成,代码如下所示:

<xml version="10" encoding="utf-8">

<manifest p=""

package="comexamplepowerimageviewtest"

android:versionCode="1"

android:versionName="10" >

<uses-sdk p=""

android:minSdkVersion="14"

android:targetSdkVersion="17" />

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme"

android:hardwareAccelerated="false"

>

android:name="comexamplepowerimageviewtestMainActivity"

android:label="@string/app_name" >

现在可以来运行一下代码了,一打开程序你就会看到GIF的第一帧,点击之后就可以播放GIF动画了。

然后我们还可以通过修改activity_mainxml中的代码,给它加上允许自动播放的属性,代码如下所示:

<relativelayout p=""

xmlns:attr=">

自定义 Activity 过渡效果

符合材料设计的应用中的 Activity 过渡效果,在不同状态之间,通过常用元素之间的动作和转换,提供了视觉连接。你可以为 Activity 之间出入过渡和共享元素过渡效果指定自定义动画。

进入过渡效果决定了 activity 中的视图组是如何进入屏幕的。例如,在explode 进入过渡效果中,视图从外面进入屏幕,并飞入屏幕中心。

退出过渡效果决定了 activity 中的视图组是如何退出屏幕的。例如,在explode 退出过渡效果中,视图是从中心位置退出屏幕的。

共享元素过渡效果决定了两个 activity 之间共享的视图在这些 activity 之间是如何过渡的。例如,如果两个 activity 拥有不同的位置和尺寸的相同的,共享元素的 changeImageTransform 过渡效果将在这些 activity 之间顺滑地平移和缩放这些。

Android 50(API 21) 支持这些出入过渡效果:

explode

—从屏幕中心位置移入移出视图;

slide

—从屏幕地边缘位置移入移出视图;

fade

—通过改变视图的透明度从屏幕中添加或删除视图;

任何继承了 Visibility 类的过渡效果都可以作为出入过渡效果。

Android 50(API 21) 支持这些共享元素过渡效果:

changeBounds

—使目标视图的布局边缘变化生成动画效果;

changeClipBounds

—使目标视图的剪裁边缘变化生成动画效果;

changeTransform

—使目标视图的缩放和旋转变化生成动画效果;

changeImageTransform

—使目标视图的尺寸和缩放变化生成动画效果;

当你在应用中使用 activity 过渡效果时,在 Activity 的进入和退出之间默认的交错退色效果被激活。

以上就是关于Android-浅谈原生动画全部的内容,包括:Android-浅谈原生动画、Android开发之ImageView播放GIF动画实例、如何在Android中自定义动画等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zz/10140511.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-05
下一篇 2023-05-05

发表评论

登录后才能评论

评论列表(0条)

保存