创建一个真正的启动画面

时间:2011-06-29 11:08:52

标签: android splash-screen

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

7 个答案:

答案 0 :(得分:8)

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

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

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

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

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

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

android:theme="@style/Theme.Light.NoTitleBar"
android:theme="@style/Theme.Black.NoTitleBar"

在这方面我会注意到,如果您的活动背景需要任何类型的位图,动画或其他可绘制的超出默认主题(或如上所示的简单的浅色或黑色主题),我的经验是活动背景在您的主视图显示之前,不会显示,因此只需将活动的背景更改为 您的启动显示不会(根据我的经验)完成更多操作立即响应,而不是主屏幕已提供的。

虽然上面这些简单的主题可以作为原始的" splashes,"也许你认为一个简单的浅色或黑色活动背景太晦涩难懂,你的应用程序已启动了,并且你想要在用户等待时显示应用程序名称或徽标的东西。或者,您的活动背景必须 透明,因为您希望能够使用自己的应用程序视图覆盖其他应用程序(这样的透明背景是当然,在启动期间不可见,因此不会提示用户您的应用已启动。

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

对于这种方法,您需要定义一个扩展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();
    }
}

因为我们正在从视图中加载XML,所以我们需要使用<merge>标记在XML中定义它,以及#34; splice&#34;在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>

请注意,TextView定义为半透明的黑色背景,因此它会导致启动器显示变暗,文本&#34;正在加载...&#34;以绿色叠加在上面。

剩下的就是在主要活动的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);
}

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

警告:

上述方法的潜在责任可能是,对于某些情况的组合,启动视图可能表示它已完成,因此启动将会卡住&#34;卡住& #34;在主显示屏上,没有主视图来替换它。这从来没有发生在我身上,但我想在此征求关于上面SplashView中的dispatchDraw()的覆盖是否被调用的评论。我对触发dispatchDraw()的代码进行了视觉检查,并且在我给SplashView构造函数完成初始化的过程中,它看起来总是被调用。

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

答案 1 :(得分: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();
}

答案 2 :(得分:2)

它并不那么难;你只需创建一个你将用作启动画面的视图(一个布局简单且不需要大量测量的视图),使用setContentView将其设置为活动的内容;

然后使用复杂的布局再次调用活动的setContentView,这需要一段时间来构建。在使用复杂布局第二次调用setContent之前,甚至可以使用Asynctask加载数据。这取决于您是否受到数据加载或视图构建的约束。

答案 3 :(得分:1)

public class MyLocationListener extends Activity {

    public Handler myHandler = new Handler(){
             public void handlerMessage(Message msg){
                  set you contentView here after splash screen
             }
          } 

    public MyLocationListener(Context context){
        this.context = context;
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                 setContentView(R.layout.splashscreen);
                // don't set Content View here instead start a thread here to do the task yiou want to do. 
                // Now post message from new thread to UI Thread using handlers.

         }

}

答案 4 :(得分:1)

执行启动画面的最佳方法是:

  1. 如果用户按下,则需要快速进入主屏幕。
  2. 没有动画。
  3. 我找到了这个好的解决方案:

    import android.app.Activity; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.view.WindowManager; import br.eti.fml.android.sigame.R;
    
    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class LauncherActivity extends Activity {
        private AsyncTask goingToNextScreen;
        private AtomicBoolean alreadyShown = new AtomicBoolean(false);
    
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.launcher);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    
            //noinspection unchecked
            goingToNextScreen = new AsyncTask() {
    
                @Override
                protected Object doInBackground(Object... objects) {
                    try {
                        Thread.sleep(1500);
                    } catch (InterruptedException e) {
                        // ignores
                    }
    
                    return null;
                }
    
                @Override
                protected void onPostExecute(Object o) {
                    goNext();
                }
            }.execute();
        }
    
        @Override
        public void onBackPressed() {
            if (goingToNextScreen != null) {
                goingToNextScreen.cancel(true);
    
                goNext();
            }
        }
    
        private void goNext() {
            if (alreadyShown.compareAndSet(false, true)) {
                startActivity(new Intent(LauncherActivity.this, HomeActivity.class));
                overridePendingTransition(0, 0);
                finish();
                overridePendingTransition(0, 0);
            }
        } }
    

答案 5 :(得分:1)

有时,使用splash,应用程序需要几毫秒或秒才能加载活动的内容。

如果您只想像往常一样使用“backgorund图像”启动画面。我认为最好的方法是使用主题。

以SherlockActionBar为例:

<style name="SplashTheme" parent="Theme.Sherlock.NoActionBar">
    ...
    <item name="android:windowBackground">@drawable/splash</item>
    ...
</style>

其中,splash可以是.9文件来填充屏幕。

清单中的活动必须是

    <activity
        android:name=".SplashActivity"
        ...
        android:theme="@style/SplashTheme"
        ...>
        ...
     </activity>

然后您不需要代码中的setContent(View)行。而主题的加载速度将超过内容。

这使您可以在应用程序加载开始时拥有启动画面。没有黑色的窗户或动作栏或类似的东西。

答案 6 :(得分:-3)

// Splash代码的代码

public class SplashScreen extends Activity {

    static int SPLASH_TIMEOUT = 5000;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.splash_layout);

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            startActivity(new Intent(SplashScreen.this, MainActivity.class));
            finish();
        }
    }, SPLASH_TIMEOUT);
    }
}

此处 SPLASH_TIMEOUT 将定义您自己的活动应显示的时间,因此请根据需要更改此值。

// MainActivity.class的代码

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

}