Создать настоящий экран-заставку - PullRequest
11 голосов
/ 29 июня 2011

Как сделать настоящий Android-заставку?Я не хочу таймеры или задержки.Просто заставка, которая отображается, пока ваше приложение не загрузится.

Ответы [ 7 ]

8 голосов
/ 18 апреля 2012

Ниже представлено решение с кодом, которое, по-видимому, работало безупречно на ряде тестовых устройств в течение последних шести недель.

Однако есть несколько предварительных условий, которые следует рассмотреть, прежде чем погрузиться в полный экран-заставку.

Прежде всего, если вы можете избежать необходимости в заставке, немедленно открыв основной вид вашего приложения и предоставив пользователю немедленный доступ к вашему приложению, это ваш самый лучший вариант.

Зачастую это можно сделать, немедленно вызывая графику основного дисплея, а затем создавая рабочий поток для выполнения любых трудоемких задач инициализации, таких как загрузка таблицы, которая всегда используется приложением.

Однако, возможно, что графика вашего основного вида сами занимает много времени для настройки и отображения, и вы хотите, чтобы во время этой инициализации было видно что-то еще.

Теперь, если ваша основная деятельность имеет простой (например, по умолчанию), светлый или черный непрозрачный фон, это немедленно предоставит подтверждение того, что по крайней мере что-то происходит, когда пользователь запускает ваше приложение , Фоновые темы, которые я лично обнаружил в качестве примитивных «всплывающих» дисплеев, включают следующее (для добавления в тег активности вашей основной активности в файле манифеста):

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.

Если у кого-то есть лучший метод переопределения для той же цели, я был бы рад услышать об этом. Я был удивлен, что мне не удалось найти какое-либо переопределение, специально предназначенное для срабатывания, когда представление закончилось, и поэтому, если оно существует, и я почему-то пропустил его, пожалуйста, оставьте комментарий об этом ниже. Комментарии, подтверждающие, что этот подход будет работать также приветствуются!

3 голосов
/ 29 июня 2011

Что-то вроде

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 голосов
/ 29 июня 2011

Это не так сложно;вы просто создаете представление, которое будете использовать в качестве заставки (с простым макетом и не требующим большого количества измерений), устанавливаете его в качестве контента для действия с помощью setContentView;

Затем снова вызовите setContentView для действия со сложным макетом, который требует времени для сборки.Вы даже можете использовать Asynctask для загрузки данных перед повторным вызовом setContent со сложным макетом.Это зависит от того, связаны ли вы загрузкой данных или просмотром здания.

1 голос
/ 08 ноября 2012

Иногда, с помощью заставки, приложению требуется несколько миллисекунд или секунд для загрузки содержимого действия.

Если вы хотите использовать только «фоновое изображение» как обычный экран-заставку.Я думаю, что лучше всего использовать Темы.

Используя SherlockActionBar, например:

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

, где заставка может быть файлом .9 для заполнения экрана.Активность в Манифесте должна быть чем-то вроде

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

, тогда вам не нужна строка setContent (View) в вашем коде.И тема будет загружаться быстрее, чем содержимое.

Это позволяет вам иметь заставку с самого начала загрузки приложения.Без черных окон или ActionBars или чего-то в этом роде.

1 голос
/ 30 апреля 2012

Лучший способ создать заставку:

  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);
        }
    } }
1 голос
/ 29 июня 2011
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.

         }

}
0 голосов
/ 25 июля 2014

// код для 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);

    }

}
...