嗯,工作中遇到策划提了一个小需求:酷炫的闪屏。。
我们知道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

android 加闪屏 手机闪屏设计_闪屏

设置启动Activity:

将manifest xml文件中的启动入口注释,如图,或者设置为Library工程的Splash为主Activity.

android 加闪屏 手机闪屏设计_unity_02

在Android Studio中使用:

导入Module SplashUtilsLibrary:

File>New>Import New Moudle ,选择SplashUtilsLibrary工程即可

android 加闪屏 手机闪屏设计_自定义闪屏_03


并在gradle里进行引用:

android 加闪屏 手机闪屏设计_闪屏_04


设置启动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包包名保持一致。

android 加闪屏 手机闪屏设计_android 加闪屏_05

导入jar包:

在Eclipse中修改包名:

android 加闪屏 手机闪屏设计_自定义闪屏_06

然后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,如图:

android 加闪屏 手机闪屏设计_闪屏_07

我们可以看到资源id从0x7f020000开始,按照drawable 、id、layout、string、style的顺序向下生成。而Unity打包时,plugins/Android/res下或者ids,只要出现一个不对应,或多或少的资源,打包时重新生成的资源Id就和Eclipse中生成的不对应了。
为什么要说这个呢,Android打包后会为每个包名生成对应的目录,包含对应的资源及资源Id等,比如我们使用不一样的包名,下图为使用apktools解包后的包结构:

android 加闪屏 手机闪屏设计_android_08

可以看到不同包名映射生成的资源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