SVGA JAVA库在源码AOSP Android.mk中引用及应用
程序员文章站
2022-05-04 07:49:22
...
SVGA JAVA库在源码AOSP Android.mk中引用及应用
SVGA 纯Java库做成
我用Android studio kotlin插件自带的转化工具,这个百度都有就不说了。不过不能百分百转化,有些问题就要自己手动改了。还要有些系统不支持的也要换,比如lambda表达式Java7不支持,就改掉了。依赖的kotlin库也要换成Java的。
然后我把SVGA在mk中配置成静态jar包:svga
SVGA库引用
在Android.mk中配置:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := svgademo
LOCAL_PRIVILEGED_MODULE := true
LOCAL_DEX_PREOPT:=false
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_PROGUARD_FLAG_FILES := proguard-rules.pro
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
frameworks/support/v7/appcompat/res \
vendor/nextev/frameworks/support/3rdparty/svga/res
LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
LOCAL_STATIC_JAVA_LIBRARIES += \
svga \
LOCAL_AAPT_FLAGS := --auto-add-overlay \
--extra-packages android.support.v7.appcompat \
--extra-packages com.opensource.svgaplayer \
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
SVGA使用
layout XML配置
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SimpleActivity">
<com.opensource.svgaplayer.SVGAImageView
android:id="@+id/svgaImage"
android:layout_width="match_parent"
android:layout_height="658dp"
android:layout_alignParentTop="true"
app:autoPlay="true"
app:loopCount="1"
app:clearsAfterStop = "false"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
...
我的Button控件
...
</LinearLayout>
</RelativeLayout>
在 XML 中,允许定义以下这些标签:
用于表示 svga 文件的路径,提供一个在 assets 目录下的文件名,或者提供一个 http url 地址。
source: String
默认为 true,当动画加载完成后,自动播放。
autoPlay: Boolean
默认为 0,设置动画的循环次数,0 表示无限循环。
loopCount: Int
默认为 true,当动画播放完成后,是否清空画布。
clearsAfterStop: Boolean
默认为 true,当 SVGAImageView 触发 onDetachedFromWindow 方法时,是否清空画布。
clearsAfterStop: Boolean
默认为 Forward,可以是 Forward、 Backward。
fillMode: Forward/Backward
Forward 表示动画结束后,将停留在最后一帧。
Backward 表示动画结束后,将停留在第一帧。
资源文件
把.svga文件放到assets文件下,Android.mk中LOCAL_ASSET_DIR配置好路径。
ActivityMain
package com.tecinno.svgaplayer;
import android.app.Activity;
import android.net.http.HttpResponseCache;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.io.File;
import java.io.IOException;
import com.opensource.svgaplayer.SVGAImageView;
public class SimpleActivity extends Activity {
private Button button1, button2, button3, button4;
private SVGAImageView svgaImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
//播放网络动画时需要配置的缓存
try {
File cacheDir = new File(getApplicationContext().getCacheDir(),"http");
HttpResponseCache.install(cacheDir, 1024 * 1024 * 128);
} catch (IOException e) {
e.printStackTrace();
}
initView();
}
private void initView(){
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button4 = findViewById(R.id.button4);
svgaImage = findViewById(R.id.svgaImage);
final SvgaUtils svgaUtils = new SvgaUtils(SimpleActivity.this, svgaImage);
//startAnimator前需要先initAnimator,完成一些监听注册
svgaUtils.initAnimator();
//button监听
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
svgaUtils.startAnimator("alarm", true);
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
svgaUtils.startAnimator("angel", false);
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
svgaUtils.startAnimator("posche", false);
}
});
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
svgaUtils.startAnimator("rose_1.5.0", false);
}
});
}
}
SVGAUtils
showSVGAStep()可以监听手指左右滑动来正反播放动画。
package com.tecinno.svgaplayer;
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.opensource.svgaplayer.SVGACallback;
import com.opensource.svgaplayer.SVGADrawable;
import com.opensource.svgaplayer.SVGADynamicEntity;
import com.opensource.svgaplayer.SVGAImageView;
import com.opensource.svgaplayer.SVGAParser;
import com.opensource.svgaplayer.SVGARange;
import com.opensource.svgaplayer.SVGAVideoEntity;
import java.net.URL;
/**
* author: jieqin.liu
* date: on 2021/02/23.
* describe:SVGA工具类
* 使用时首先调用初始化数据方法,
* 然后再调用开始动画的方法
*/
public class SvgaUtils {
private Context context;
private SVGAImageView svgaImage;
private SVGAParser parser;
private final static String TAG = "SvgaUtils";
private String needLoop;
private SVGARange ran;
private FrameEntry frameEntry;
public SvgaUtils(Context context, SVGAImageView svgaImage) {
this.context = context;
this.svgaImage = svgaImage;
//播放的帧顺序,第一个参数是开始位置,第二个参数是帧数
ran = new SVGARange(9,20);
}
/**
* 初始化数据
*/
public void initAnimator() {
//左右滑动播放动画的灵敏度
final int slideSpeed = 10;
parser = new SVGAParser(context);
//监听大动画的控件周期
svgaImage.setCallback(new SVGACallback() {
@Override
public void onPause() {
Log.e(TAG, "onPause");
}
@Override
public void onFinished() {
Log.e(TAG, "onFinished"+ (needLoop.equals("yes")? ", loop play":" "));
if(needLoop.equals("no")){
stopSVGA();
} else {
//从第几帧开始播放,正序播放
svgaImage.stepToFrame(0, false);
//以给定的顺序播放,倒序播放
// svgaImage.startAnimation(ran,true);
//以给定的顺序播放,正序播放
// svgaImage.startAnimation(ran,false);
}
}
@Override
public void onRepeat() {
Log.e(TAG, "onRepeat");
stopSVGA();
}
@Override
public void onStep(int i, double v) {
Log.e(TAG, "onStep i :"+i+", maxFrame : " + frameEntry.maxFrame);
//保存当前是第几帧
frameEntry.currentFrame = i;
}
});
//滑动监听
svgaImage.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
// 按下
case MotionEvent.ACTION_DOWN:
Log.e(TAG,"ACTION_DOWN X "+ event.getX()+ " Y "+event.getY());
break;
// 移动
case MotionEvent.ACTION_MOVE:
Log.e(TAG,"ACTION_MOVE X "+ event.getX()+ " Y "+event.getY());
float currentPosX = event.getX();
if(currentPosX - frameEntry.mPosX > slideSpeed){
frameEntry.mPosX = currentPosX;
showSVGAStep(false);
} else if(frameEntry.mPosX - currentPosX > slideSpeed){
frameEntry.mPosX = currentPosX;
showSVGAStep(true);
}
break;
// 拿起
case MotionEvent.ACTION_UP:
Log.e(TAG,"ACTION_UP X "+ event.getX()+ " Y "+event.getY());
break;
default:
break;
}
return true;
}
});
}
/**
* 显示动画
*/
public void startAnimator(String svgaName, boolean isLoop) {
Log.e(TAG,"startAnimator");
if (svgaName != null) {
needLoop = isLoop ? "yes" : "no";
//播放本地动画
showSVGA(svgaName);
//播放网络动画
// showNetSVGA("https://github.com/yyued/SVGA-Samples/blob/master/posche.svga?raw=true");
//播放网络动画并插入图片
// showdynamicSVGA("https://github.com/yyued/SVGA-Samples/blob/master/kingset.svga?raw=true");
} else {
Log.e(TAG,"startAnimator svgaName is null");
return;
}
}
/**
* 停止动画
*/
private void stopSVGA() {
if (svgaImage.isAnimating()) {
svgaImage.stopAnimation();
}
}
/**
* 滑动播放动画,向右滑动顺序播放,向左滑动倒序播放
* reserse:播放顺序,顺序播放或者倒放
*/
public void showSVGAStep(boolean reverse){
if(svgaImage == null && frameEntry == null){
Log.e(TAG, svgaImage == null ? "svgaImage is null" : "frameEntry is null");
return;
}
int nextFrame;
SVGARange range;
if(reverse){ //倒序播放
//下一帧
nextFrame = frameEntry.currentFrame - 1;
//配置播放的帧和帧数
range = new SVGARange(nextFrame < 0 ? frameEntry.maxFrame : nextFrame,1);
} else{//顺序播放
//下一帧
nextFrame = frameEntry.currentFrame + 1;
//配置播放的帧和帧数
range = new SVGARange(nextFrame >= frameEntry.maxFrame ? 0:nextFrame, 1);
}
//开始播放
svgaImage.startAnimation(range, reverse);
}
/**
* 播放动画
*/
private void showSVGA(String svagName) {
try {
parser.parse(svagName+".svga", new SVGAParser.ParseCompletion() {
@Override
public void onComplete(SVGAVideoEntity svgaVideoEntity) {
//保存当前动画最大帧数,方便后面随机播放
frameEntry = new FrameEntry(svgaVideoEntity.getFrames());
//解析动画成功,到这里才真正的显示动画
SVGADrawable drawable = new SVGADrawable(svgaVideoEntity);
svgaImage.setImageDrawable(drawable);
svgaImage.startAnimation();
}
@Override
public void onError() {
//停止播放
stopSVGA();
}
});
} catch (Exception e) {
Log.e(TAG,"show svga error : "+e);
}
}
/**
* 播放网络动画
*/
private void showNetSVGA(String svagURL) {
try {
parser.parse(new URL(svagURL), new SVGAParser.ParseCompletion() {
@Override
public void onComplete(SVGAVideoEntity svgaVideoEntity) {
//保存当前动画最大帧数,方便后面随机播放
frameEntry = new FrameEntry(svgaVideoEntity.getFrames());
//解析动画成功,到这里才真正的显示动画
svgaImage.setVideoItem(svgaVideoEntity);
svgaImage.startAnimation();
}
@Override
public void onError() {
//停止播放
stopSVGA();
}
});
} catch (Exception e) {
Log.e(TAG,"show svga error : "+e);
}
}
/**
* 播放动态动画,动画中插入自定义图片
*/
private void showdynamicSVGA(String svagURL) {
try {
parser.parse(new URL(svagURL), new SVGAParser.ParseCompletion() {
@Override
public void onComplete(SVGAVideoEntity svgaVideoEntity) {
//保存当前动画最大帧数,方便后面随机播放
frameEntry = new FrameEntry(svgaVideoEntity.getFrames());
//解析动画成功,到这里才真正的显示动画
SVGADynamicEntity dynamicEntity = new SVGADynamicEntity();
dynamicEntity.setDynamicImage("https://github.com/PonyCui/resources/blob/master/svga_replace_avatar.png?raw=true", "99"); // Here is the KEY implementation.
SVGADrawable drawable = new SVGADrawable(svgaVideoEntity, dynamicEntity);
svgaImage.setImageDrawable(drawable);
svgaImage.startAnimation();
}
@Override
public void onError() {
//停止播放
stopSVGA();
}
});
} catch (Exception e) {
Log.e(TAG,"show svga error : "+e);
}
}
/*
* 随机播放动画时保存的动画和touch位置信息,需要在parser完成时初始化。
*
* mPosX:当前touch X轴位置
* currentFrame:当前播放的是第几帧
* maxFrame:该动画有多少帧
*
* */
public class FrameEntry{
//当前触点X轴位置
public float mPosX;
//当前播放是第几帧
public int currentFrame;
//最大帧数
public int maxFrame;
public FrameEntry(int maxFrame){
mPosX = 0;
currentFrame = 0;
this.maxFrame = maxFrame;
}
}
}
下一篇: JMS 发布/订阅模式案例