创建一个真正的启动画面

Wah*_*han 11 android splash-screen

如何在Android中制作真正的启动画面?我不想要定时器或延迟.只是在您的应用程序加载之前显示的启动画面.

Car*_*arl 8

带有代码的解决方案出现在过去六周似乎在许多测试设备上完美运行.

但是,在进入完整的启动画面之前,应该考虑一些预备.

首先,如果您可以通过立即调出应用程序的主视图来避免需要启动画面,让用户立即访问您的应用程序,这是您最好的选择.

您通常可以通过立即调出主显示器的图形,然后创建工作线程来执行任何耗时的初始化任务(例如加载应用程序始终使用的表)来实现此目的.

但是,主视图的图形本身可能需要很长时间才能设置和显示,并且您希望在初始化期间看到其他内容.

现在,如果您的主要活动具有简单(例如,默认),浅色或黑色,非透明背景,那么将立即确认用户启动您的应用时至少发生了某些事情.我个人发现作为原始"启动"显示工作的背景主题包括以下内容(要添加到清单文件中主要活动的活动标记):

android:theme="@style/Theme.Light.NoTitleBar"
android:theme="@style/Theme.Black.NoTitleBar"
Run Code Online (Sandbox Code Playgroud)

在这方面我会注意到,如果您的活动背景需要任何类型的位图,动画或其他可绘制的超出默认主题(或如上所示的简单的浅色或黑色主题),我的经验是活动背景将不会显示直到你的主要观点反正显示的,所以只要改变你的活动本身的背景你的启动画面不(在我的经验)完成比你的主屏幕已经提供了更直接的回应.

虽然上面这些简单的主题可以作为原始的"泼溅"使用,但也许您认为简单的浅色或黑色活动背景太过于提示您的应用已启动的提示,并且您希望显示应用的名称或徽标.用户正在等待.或者,也许您的活动背景必须透明的,因为您希望能够使用您自己的应用程序的视图覆盖其他应用程序(当然,这样的透明背景在启动期间是不可见的,因此不会提示用户您的应用程序已经开始).

如果在考虑了上面提到的所有替代方案之后,你仍然认为你需要一个启动画面,这是我个人认为非常好的方法.

对于这种方法,您需要定义一个扩展LinearLayout的新类.你需要自己的课程的原因是因为你需要得到你的启动画面实际显示的肯定确认,所以你可以立即继续显示你的主视图,而不需要一些计时器,只能猜出它需要多长时间你的启动画面出现了.在这方面我会注意到,如果在显示启动视图后过快地开始显示主视图,则永远不会看到启动视图; 使用这种方法可以避免这种可能性.

以下是此类的示例:

public class SplashView extends LinearLayout {

    public interface SplashEvents {
        //This event is signaled after the splash and all of its child views, 
        // if any, have been drawn.
        // As currently implemented, it will trigger BEFORE any scrollbars are drawn.
        // We are assuming that there will BE no scrollbars on a SplashView.
        public void onSplashDrawComplete();
    }

    private SplashEvents splashEventHandler = null;

    public void setSplashEventHandler(SplashEvents splashEventHandler) {
        this.splashEventHandler = splashEventHandler;
    }

    private void fireSplashDrawCompleteEvent() {
        if(this.splashEventHandler != null) {
            this.splashEventHandler.onSplashDrawComplete();
        }
    }

    public SplashView(Context context) {
        super(context);

        //This is set by default for a LinearLayout, but just making sure!
        this.setWillNotDraw(true);

        //If the cache is not enabled, then I think that helps to ensure that
        // dispatchDraw override WILL
        // get called.  Because if caching were enabled, then the 
        //drawing might not occur.
        this.setDrawingCacheEnabled(false);

        setGravity(Gravity.CENTER);

        //This splices in your XML definition (see below) to the SplashView layout
        LayoutInflater.from(context).inflate(R.layout.splashscreen, this, true);
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        //Draw any child views
        super.dispatchDraw(canvas);

        //Signal client objects (in this case, your main activity) that 
            // we have finished initializing and drawing the view.
        fireSplashDrawCompleteEvent();
    }
}
Run Code Online (Sandbox Code Playgroud)

因为我们从视图中加载XML,所以我们需要使用<merge>标记在XML中定义它,以便在XML定义的元素中"拼接"为SplashView类的子元素.这是一个示例(放在应用程序的res/layout文件夹中),您可以根据自己的需要进行定制:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_horizontal"
    >
    <TextView  android:id="@+id/tvHeading"
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"
        android:gravity="center"
        android:textSize="30dp"
        android:textStyle="bold"
        android:text="Loading..."
        android:layout_weight="1.0"
        android:textColor="#00ff00"
        android:background="#AA000000"
        />
</merge>
Run Code Online (Sandbox Code Playgroud)

请注意,TextView定义为半透明的黑色背景,因此它会导致启动器显示变暗,文本"Loading ..."以绿色叠加在顶部.

剩下的就是在主活动的onCreate()方法中编辑以下内容(及以上):

private Handler uiThreadHandler = new Handler();

@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //Create an instance of the splash view, and perform a setContentView()
    SplashView splashView = new SplashView(this);

    //Doing this allows you to access the "this" pointer of the main 
        // activity inside the Runnable below.
    final main mainThis = this;

    // Set an event handler on the SplashView object, so that as soon 
    // as it completes drawing we are
    // informed.  In response to that cue, we will *then* put up the main view, 
    // replacing the content view of the main activity with that main view.
    splashView.setSplashEventHandler(new SplashView.SplashEvents() {
        @Override
        public void onSplashDrawComplete() {
            //Post the runnable that will put up the main view
            uiThreadHandler.post(new Runnable() {
                @Override
                    public void run() {
                        //This method is where you will have moved 
                        // the entire initialization of your app's 
                        // main display, which normally would have been 
                        // in your onCreate() method prior to adding the 
                        // splash display.
                        launchMainView(mainThis, savedInstanceState);
                    }
            });
        }
    });

    //This will cause your splash view to draw.  When it finishes, it will trigger the code above.
    this.setContentView(splashView);

    //At this point, do *not* move directly on to performing a setContentView() on your main view.
    // If you do, you will never see the splash view at all.
    // You simply wait for the splash view to signal you that it has completed drawing itself, and
    // *then* call launchMainView(), which will itself call setContentView() again, passing it
    // your main view.
}

//Here is a stripped-down version of launchMainView().  You will typically have some additional
// initializations here - whatever might have been present originally in your onCreate() method.
public void launchMainView(main mainThis, Bundle savedInstanceState) {
    myRootView = new MyRootView(mainThis);

    setContentView(myRootView);
}
Run Code Online (Sandbox Code Playgroud)

上述方法对我来说非常有效.我使用它仅针对API级别8,并在运行Android 2.2.1,2.3.3和4.0.1(ICS)的各种设备(包括手机和平板电脑)上测试了该代码.

警告:

上述方法的潜在责任是,对于某些情况的组合,飞溅视图可能表示它已经完成,因此飞溅将"卡在"主显示器上,没有主视图来替换它.这从来没有发生在我身上,但是我想在这里征求关于是否可能无法调用上面SplashView中的dispatchDraw()重写的评论.我对触发dispatchDraw()的代码进行了视觉检查,看起来它总是会被调用,因为我已经在SplashView构造函数中完成了初始化.

如果有人有更好的方法来覆盖同样的目的,我会很感激听到它.令我感到惊讶的是,当视图完成显示时,我无法找到专门用于触发的任何覆盖,因此,如果存在并且我不知何故错过了,请在下面发表评论.评论肯定,这种方法工作也非常欢迎!


nha*_*man 3

就像是

public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.splash);

    handler = new Handler();

    new AsyncTask<Void, Void, Void>() {

    @Override
    protected Void doInBackground(Void... params) {
            //Do some heavy stuff
            return null;
        } 

        @Override
        public void onPostExecute(Void result){
            handler.post(new Runnable(){
                 @Override
                 public void run(){
                     setContentView(R.layout.main);
                 }
            });
        }
   }.execute();
}
Run Code Online (Sandbox Code Playgroud)