Ниже представлено решение с кодом, которое, по-видимому, работало безупречно на ряде тестовых устройств в течение последних шести недель.
Однако есть несколько предварительных условий, которые следует рассмотреть, прежде чем погрузиться в полный экран-заставку.
Прежде всего, если вы можете избежать необходимости в заставке, немедленно открыв основной вид вашего приложения и предоставив пользователю немедленный доступ к вашему приложению, это ваш самый лучший вариант.
Зачастую это можно сделать, немедленно вызывая графику основного дисплея, а затем создавая рабочий поток для выполнения любых трудоемких задач инициализации, таких как загрузка таблицы, которая всегда используется приложением.
Однако, возможно, что графика вашего основного вида сами занимает много времени для настройки и отображения, и вы хотите, чтобы во время этой инициализации было видно что-то еще.
Теперь, если ваша основная деятельность имеет простой (например, по умолчанию), светлый или черный непрозрачный фон, это немедленно предоставит подтверждение того, что по крайней мере что-то происходит, когда пользователь запускает ваше приложение , Фоновые темы, которые я лично обнаружил в качестве примитивных «всплывающих» дисплеев, включают следующее (для добавления в тег активности вашей основной активности в файле манифеста):
android:theme="@style/Theme.Light.NoTitleBar"
android:theme="@style/Theme.Black.NoTitleBar"
В этой связи я хотел бы отметить, что если фон вашей деятельности требует какого-либо растрового изображения, или анимации, или других элементов, которые можно нарисовать за пределами темы по умолчанию (или простой светлой или черной темы, как показано выше), мой опыт показывает, что фон деятельности не будет отображаться до тех пор, пока ваш основной вид все равно не отобразится, и поэтому простое изменение фона вашей деятельности на * be ваш всплывающий дисплей не (по моему опыту) не дает более немедленного ответа чем ваш главный экран уже обеспечивает.
Хотя перечисленные выше простые темы будут работать как примитивные «всплески», возможно, вы думаете, что простой светлый или черный фон деятельности слишком нечеткий сигнал, который запустило ваше приложение, и вы хотите что-то, что отображает название или логотип вашего приложение, пока пользователь ждет. Или, может быть, ваш фон деятельности должен быть прозрачным , потому что вы хотите иметь возможность накладывать какие-то другие приложения на представления своего приложения (такой прозрачный фон, конечно, невидим во время при запуске, и поэтому не будет подсказывать пользователю, что ваше приложение было запущено).
Если, рассмотрев все альтернативы, представленные выше, вы все еще думаете, что вам нужен заставка, вот подход, который я лично нашел, который работает очень хорошо.
Для этого подхода вам необходимо определить новый класс, расширяющий 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 с помощью тега <merge>
, чтобы "склеить" в определенных 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).
Оговорка:
Потенциальная ответственность вышеупомянутого подхода заключается в том, что при некоторой комбинации обстоятельств всплывающее окно может не сигнализировать о его завершении, и поэтому всплеск будет "зависать" на главном дисплей, без основного вида, чтобы заменить его. Это никогда не случалось со мной, но я хотел бы попросить комментарии о том, может ли когда-либо вызываться переопределение dispatchDraw () в SplashView выше , а не . Я провел визуальный осмотр кода, который вызывает dispatchDraw (), и мне кажется, что он всегда будет вызываться, учитывая инициализации, которые я сделал в конструкторе SplashView.
Если у кого-то есть лучший метод переопределения для той же цели, я был бы рад услышать об этом. Я был удивлен, что мне не удалось найти какое-либо переопределение, специально предназначенное для срабатывания, когда представление закончилось, и поэтому, если оно существует, и я почему-то пропустил его, пожалуйста, оставьте комментарий об этом ниже. Комментарии, подтверждающие, что этот подход будет работать также приветствуются!