嗯,工作中遇到策划提了一个小需求:酷炫的闪屏。。
我们知道unity自带的闪屏。。。真·一张图片,其实没有任何的动画效果,所以来看一下怎么定义自己的闪屏方案吧。
基本思路就是用Android的闪屏内嵌到Unity中,通过jar包和Android资源以及程序启动Activity设置,实现自定义的闪屏。
首先使用Eclipse或Android Studio新建一个Library工程,新建闪屏Activity及布局文件,布局文件很简单,一张match_parent的图片就可以了,闪屏代码包含两个动画,缩放和透明度变化,可以自己定制别的效果,比如旋转动画什么的。
源码:
public class Splash extends Activity {
final String TAG = "Splash";
private final int maxSupport = 5;
AnimationListenerBack animationListener;
// AlphaAnimation alphaAni;
ImageView splashView;
Class<?> next;
private boolean ispause;
private long splashDur = 3000;
private float fromAlpha = 0.1f;
private float toAlpha = 1.0f;
private float fromScale = 1.0f;
private float toScale = 1.2f;
private int splashCount = 1;
private int currentIndex = 1;
private WeakReference<Drawable> splash;//弱引用防止OOM
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setondraweble();
setContentView(R.layout.activity_splash1);
splashView = this.findViewById(R.id.splash);
getSplashCount();
initView();
}
private void getSplashCount() {
// TODO Auto-generated method stub
String count = getString(R.string.splash_count);
splashCount = Integer.parseInt(count);
if(splashCount > maxSupport)
splashCount = maxSupport;
nextLoader(getString(R.string.main_activity));
}
private void nextLoader(String className) {
ClassLoader loader = this.getClassLoader();
try {
;
Log.i(TAG, "loadClass: ===> " + className);
next = loader.loadClass(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.e("Splash", String.format("not find main entry, please check strings setting main_activity"));
}
}
private void initView() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
splash = new WeakReference<Drawable>(getResources().getDrawable(getSplashId(currentIndex)));
} else {
splash = new WeakReference<Drawable>(getResources().getDrawable(getSplashId(currentIndex)));
}
splashView.setImageDrawable(splash.get());
initAnimation();
}
private int getSplashId(int curIndex) {
int id = R.mipmap.sp1;
switch (curIndex) {
case 1:
id = R.mipmap.sp1;
break;
case 2:
id = R.mipmap.sp2;
break;
case 3:
id = R.mipmap.sp3;
break;
case 4:
id = R.mipmap.sp4;
break;
case 5:
id = R.mipmap.sp5;
break;
default:
break;
}
return id;
}
private void initAnimation() {
AnimationSet aniSet = new AnimationSet(true);
WindowManager wm = getWindowManager();
int centerX = wm.getDefaultDisplay().getWidth() / 2;
int centerY = wm.getDefaultDisplay().getHeight() / 2;
Log.i("center", String.format("## centerX:%d, centerY:%d", centerX, centerY));
WeakReference<ScaleAnimation> scaleAnimation = new WeakReference<>(new ScaleAnimation(fromScale, toScale, fromScale, toScale, centerX, centerY));
WeakReference<AlphaAnimation> alphaAnimation = new WeakReference<>(new AlphaAnimation(this.fromAlpha, this.toAlpha));
aniSet.setDuration(splashDur);
aniSet.setFillAfter(true);
aniSet.addAnimation(scaleAnimation.get());
aniSet.addAnimation(alphaAnimation.get());
animationListener = new AnimationListenerBack();
aniSet.setAnimationListener(animationListener);
splashView.startAnimation(aniSet);
}
private void endAnimation(){
WeakReference<AlphaAnimation> end = new WeakReference<>(new AlphaAnimation(toAlpha, 0.01f));
end.get().setFillAfter(true);
end.get().setDuration(1000);
end.get().setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
currentIndex++;
initView();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
splashView.startAnimation(end.get());
}
private void setondraweble() {
requestWindowFeature(Window.FEATURE_NO_TITLE);
int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
Window window = this.getWindow();
window.setFlags(flag, flag);
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
ispause = true;
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
ispause = false;
}
private class AnimationListenerBack implements AnimationListener {
@Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
if(currentIndex < splashCount){
endAnimation();
}else{
Intent intent = new Intent();
if (next == null) {
try {
throw new Exception("not find main entry!");
} catch (Exception e) {
e.printStackTrace();
return;
}
}
intent.setClass(Splash.this, next);
startActivity(intent);
finish();
}
}
@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
}
用到的知识点:
- 安卓动画Animation
- Java 弱引用WeakReference
- Java 反射
适用平台:Andorid
支持工具:
1. Eclipse
2. Android Studio
3. Unity
最大支持闪屏数量:5
关于使用:
在Eclipse中使用:
引用Libarbry工程:SplashUtilsLibrary
设置启动Activity:
将manifest xml文件中的启动入口注释,如图,或者设置为Library工程的Splash为主Activity.
在Android Studio中使用:
导入Module SplashUtilsLibrary:
File>New>Import New Moudle ,选择SplashUtilsLibrary工程即可
并在gradle里进行引用:
设置启动Activity,设置方式和Eclipse中一致.
在Unity中使用:
导入资源:
Res目录: 对应unity目录:plugins/Android/res
闪屏图片资源
将自定义的闪屏图片导入到Unity对应目录中:以sp格式命名
layout:
导入布局文件activity_splash1.xml到unity对应目录
在string.xml中添加以下配置:
<string name="main_activity">dy.cn.splashdemo.MainActivity</string>
<string name="splash_count">3</string>
修改splash_count的值即可修改闪屏数量,最大支持5张.
在AndroidManifest中添加配置:
<activity
android:name="cn.dy.jzcj.Splash"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.unity3d.player.UnityPlayerActivity"
android:screenOrientation="landscape">
<meta-data
android:name="unityplayer.UnityActivity"
android:value="true" />
</activity>
其中cn.dy.jzcj是包名,此包名需与工程 Bundle Identifier ,以及下面说到的jar包包名保持一致。
导入jar包:
在Eclipse中修改包名:
然后build project
拷贝bin目录下生成的jar包到unity 中plugins/Android/libs中。
包名须与Unity工程包名保持一致。
自定义闪屏数量:
修改strings.xml中splash_count,并添加对应闪屏资源即可
更换闪屏资源:
Unity、Eclipse中更新drawable下对应图片资源即可,注意命名格式。
Android Studio中更新mipmap下对应图片资源即可。
自定义跳转Activity入口:
Android Studio、Eclipse中修改strings.xml中main_activity值为闪屏结束需要跳转的Activity,格式为:
包名+Activity名称
Unity中若由自定义闪屏直接跳到Unity程序,则main_activity固定为:com.unity3d.player.UnityPlayerActivity。
若自定义闪屏结束后需跳转到其他Activity,则设置和Eclipse、Android Studio中一致即可。
最后分享一些知识点吧:
可能有人会好奇了,为什么jar包包名,AndroidManifest中配置包名必须和Unity工程Bundle Identifier保持一致呢?
嗯,之前我尝试过jar包不修改包名,也就是和Unity Bundle Identifier无关的一些尝试,但是只有在一种情况下才会成功,那就是Unity中生成的资源Id和Android中资源Id完全一致的情况下,换种说法就是Unity中Plugins/Android/res目录和Android导出jar包完全一致。
总的来说,这其实是Android资源管理机制导致的必然结果,我们知道Android会为所有使用到的资源生成一个对应的资源Id,Eclipse中是./gen/R.java,AS中是./build/intermediates/class/debug/包名/R.class,如图:
我们可以看到资源id从0x7f020000开始,按照drawable 、id、layout、string、style的顺序向下生成。而Unity打包时,plugins/Android/res下或者ids,只要出现一个不对应,或多或少的资源,打包时重新生成的资源Id就和Eclipse中生成的不对应了。
为什么要说这个呢,Android打包后会为每个包名生成对应的目录,包含对应的资源及资源Id等,比如我们使用不一样的包名,下图为使用apktools解包后的包结构:
可以看到不同包名映射生成的资源Id目录也不同,而我们使用的是Unity中的资源,所以应该保持jar包包名和Unity Bundle Identifier一致,这样在jar包和Unity就会处于同一目录下,jar代码中映射资源就可以正确获取到了。
这无疑是个比较蛋疼的地方,不修改包名还好,如果Unity中修改了包名,那我们就需要修改Eclipse工程包名重新导出jar包来用了,我也尝试过导出jar包时将R.java一起导出,但是还是前面说的,资源不对应会导致Id不一致,行不通,目前还是没有想到有什么好的解决方案,如果小伙伴们谁有更好的办法,请记得告诉我,感激不尽。。
end of:源码下载
http://pan.baidu.com/s/1dEX7NZr
密码:qpmn