【问题标题】:Create a true splash screen创建一个真正的闪屏
【发布时间】:2011-09-25 01:36:58
【问题描述】:

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

【问题讨论】:

标签: android splash-screen


【解决方案1】:

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

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

【讨论】:

  • 如果调用 setContentView() 来显示初始视图,然后立即为主视图再次调用,则在显示第二个视图之前,初始视图将没有机会显示。现在,如果您有一个工作线程在显示启动画面后执行繁重的工作,并且让 that 线程发布到 UI 处理程序以执行最终的 setContentView(),那么只要异步东西需要一段时间。但是,如果您的延迟来自主视图本身(根据滑牛的评论),那么您需要等到启动画面绘制后再触发第二个 setContentView()。
【解决方案2】:

类似

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();
}

【讨论】:

  • 如果重的东西正好是setContentView(R.layout.main)怎么办?
  • 你永远不应该在一个活动上调用 setContentView 两次。我建议而不是 setContentView 调用 startActivity(启动后要加载的活动)。
  • 这对我不起作用。屏幕粘在第一个启动画面上,而不是设置为第二个加载屏幕。我使用了几乎相同的代码,只是我在 run 方法上没有 @Override 标记,因为 eclipse 将其删除为 quickFix 说:new Runnable(){} 类型的方法 run() 必须覆盖超类方法
  • 实际启动 AsyncTask 的是什么?
  • 我在代码中添加了.execute(),一定是在复制粘贴时出错了:)
【解决方案3】:
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】:

    下面出现了一个带有代码的解决方案,过去六周以来,它似乎在许多测试设备上都运行良好。

    但是,在进入完整的初始屏幕之前,应该考虑一些准备工作。

    首先,如果您可以通过立即调出应用的主视图、让用户立即访问您的应用来避免使用启动画面,那么这是您的最佳选择。

    您通常可以通过立即调出主显示器的图形来完成此操作,然后创建一个工作线程来执行任何耗时的初始化任务,例如加载应用程序始终使用的表格。

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

    现在,如果您的主要活动有一个简单(例如,默认)、浅色或黑色、不透明的背景,那么当用户启动您的应用程序。我个人发现用作原始“飞溅”显示的背景主题包括以下内容(将添加到清单文件中主要活动的活动标签中):

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

    在这方面我会注意,如果您的活动背景需要任何类型的位图、动画或其他默认主题(或如上所示的简单浅色或黑色主题)之外的可绘制对象,我的经验是活动背景不会在您的主视图显示之前显示,因此仅仅将您的活动的背景更改为自身您的初始显示不会(根据我的经验)完成更多比您的主屏幕已经提供的即时响应。

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

    如果在考虑了上面介绍的所有替代方案后,您仍然认为需要启动屏幕,那么我个人认为这是一种非常有效的方法。

    对于这种方法,您需要定义一个扩展 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,所以我们需要在 XML 中使用 &lt;merge&gt; 标记将它定义为“拼接”在 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 定义为半透明的黑色背景,这样会导致启动器显示变暗,文本“正在加载...”以绿色叠加在顶部。

    剩下的就是在主要活动的 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) 的各种设备(包括手机和平板电脑)上测试了该代码。

    警告:

    上述方法的潜在责任是,在某些情况下,闪屏视图可能发出已完成的信号,因此闪屏会“卡在”主显示,没有主视图可以替换它。这从来没有发生在我身上,但我想在这里向 cmets 征求关于上面 SplashView 中 dispatchDraw() 的覆盖是否可能不会被调用。我对触发 dispatchDraw() 的代码进行了视觉检查,在我看来,鉴于我在 SplashView 构造函数中所做的初始化,它似乎总是会被调用。

    如果有人有更好的方法来实现同样的目的,我会很高兴听到它。令我惊讶的是,当视图完成显示时,我找不到任何专门为触发而定制的覆盖,因此,如果存在并且我不知何故错过了它,请在下面发表评论。也非常欢迎评论确认这种方法奏效!

    【讨论】:

    • 对我很有用,我喜欢你的解决方案,希望以后不会有错误。谢谢!
    • 同样,我在多个应用程序中使用这种方法没有任何问题,我自己在从 Froyo 到 Jelly Bean 的设备上进行了测试,公开分发了好几个月,没有任何投诉。
    • 为什么不使用“onDraw”来代替,并且只在那里调用一次你的函数(使用一个标志来防止它被多次调用)。
    • splash view 的子视图是在 之后绘制的,splash view 自己的 onDraw() 方法被调用以便出现在它的前面,所以触发 onSplashDrawComplete() 从onDraw() 将在显示启动视图的任何子视图之前启动主视图的显示,因此在主视图出现之前启动不会变得可见。 dispatchDraw() 方法绘制启动视图的所有子视图,因此通过覆盖它并在触发事件之前调用 super.dispatchDraw(),我们可以确保在启动主视图之前绘制子视图。
    • +1 for giving the user immediate access to your app, that is your very best option. 完全同意(也许是工程师?)
    【解决方案5】:

    制作启动画面的最佳方法是:

    1. 如果用户按下返回,您需要快速进入主屏幕。
    2. 没有动画。

    我找到了这个很好的解决方案:

    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);
            }
        } }
    

    【讨论】:

      【解决方案6】:

      有时,使用启动,应用需要几毫秒或几秒的时间来加载 Activity 的内容。

      如果您只需要像往常一样的初始屏幕“背景图像”。我认为最好的方法是使用主题。

      以 SherlockActionBar 为例:

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

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

      Manifest 中的 Activity 必须类似于

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

      那么您的代码中不需要 setContent(View) 行。而且主题的加载速度会比内容快。

      这使您可以在应用程序加载开始时有一个启动画面。没有黑色窗口或 actionBars 或类似的东西。

      【讨论】:

        【解决方案7】:

        //启动代码的代码

        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);
        
            }
        
        }
        

        【讨论】:

        • OP 声明他不想要计时器或延迟。阅读问题。
        猜你喜欢
        • 2012-01-08
        • 2017-07-29
        • 2019-06-14
        • 1970-01-01
        • 2012-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多