День / ночь тема для android app

у меня есть приложение, в котором мне нужно реализовать "день / ночь" тема. К сожалению, нет простого способа сделать тематику, просто используя стили, мне нужно иметь возможность обновлять: фон макета, селекторы кнопок, цвет текста, размер текста, изображения, значки, анимации.

из того что я вижу у меня 2 варианта:

  1. имеют разные файлы макета xml для ночи / дня, так что что-то вроде home_day.xml / home_night.xml. Есть около 30 экранов в приложении, так что в конце концов есть будет 60 xml макетов. На activity / fragment onCreate, основываясь на текущем часе, я мог бы setContentView. Это добавляет еще несколько xml-файлов, но позволяет избежать добавления кода в activities

  2. есть только один макет для дня / ночи и на активности onCreate findviewById для каждого элемента, который я хочу тема и обновить его атрибуты на основе текущего дня / ночи. Это может создать много дополнительного кода, findviews и применить атрибуты для многих представлений.

Я стремлюсь для 2. но я готов выслушать любые ваши предложения. Итак, что бы вы выбрали и почему ?

4 ответов


на самом деле, похоже, вы можете использовать темы для описания пользовательских чертежей. Взгляните на: Как переключаться между ночным и дневным режимами на Android?. Вы создаете свои темы с помощью блока стиля, а затем в своем XML-макете указываете что-то в своей теме с помощью ?достопри. Тогда вы сможете вызвать setTheme (R. styles.DAY_THEME) о следующем действии, и все должно быть обновлено.


Я хотел бы использовать -night в качестве квалификатора набора ресурсов для ночного режима, помещая туда свои ночные ресурсы.

Android уже есть понятие ночного режима, переключение между ночным и дневным режимами на основе времени суток и датчиков. Следовательно, вы можете использовать его.

например, чтобы иметь другую тему, основанную на режиме, создайте res/values/styles.xml и res/values-night/styles.xml. Имейте тему с тем же именем в каждом файле (например, AppTheme), но адаптировать тему основываясь на любых различиях, которые вы хотите иметь между дневным и ночным режимами. Когда вы ссылаетесь на свою тему по имени (например, в манифесте), Android автоматически загружает нужные ресурсы, а Android автоматически уничтожает и воссоздает ваши действия, если режим изменяется во время выполнения этих действий.

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


проверьте этот учебник для полного пошагового примера:нажмите здесь

добавить автоматическое переключение DayNight тему с помощью Appcompat v23.2 библиотека поддержки

добавьте следующую вещь в свою сборку.файл gradle

compile 'com.android.support:appcompat-v7:23.2.0'

сделайте свой стиль темы как ниже

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:textColorPrimary">@color/textColorPrimary</item>
    <item name="android:textColorSecondary">@color/textColorSecondary</item>
</style>

теперь добавьте следующий код одной строки в метод onCreate () для установки темы для всего приложения

По Умолчанию Автоматически Переключающ Режим

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);

Для Установки Ночного Режима По Умолчанию

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);

Для Установки Режима Дня По Умолчанию

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);

enter image description here


вот мое решение:

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

-> С веб-страниц NOAA можно найти алгоритмы для расчета высоты солнца над горизонтом с учетом определенной позиции и даты.

--> используя эти алгоритмы, я создал метод, который вычисляет высоту солнца над горизонтом с учетом двух двойных широты и долготы и Календарь

public class SolarCalculations {

    /**
     * Calculate height of the sun above horizon for a given position and date
     * @param lat Positive to N
     * @param lon Positive to E
     * @param cal Calendar containing current time, date, timezone, daylight time savings
     * @return height of the sun in degrees, positive if above the horizon
     */
    public static double CalculateSunHeight(double lat, double lon, Calendar cal){

        double adjustedTimeZone = cal.getTimeZone().getRawOffset()/3600000 + cal.getTimeZone().getDSTSavings()/3600000;

        double timeOfDay = (cal.get(Calendar.HOUR_OF_DAY) * 3600 + cal.get(Calendar.MINUTE) * 60 + cal.get(Calendar.SECOND))/(double)86400;

        double julianDay = dateToJulian(cal.getTime()) - adjustedTimeZone/24;

        double julianCentury = (julianDay-2451545)/36525;

        double geomMeanLongSun = (280.46646 + julianCentury * (36000.76983 + julianCentury * 0.0003032)) % 360;

        double geomMeanAnomSun = 357.52911+julianCentury*(35999.05029 - 0.0001537*julianCentury);

        double eccentEarthOrbit = 0.016708634-julianCentury*(0.000042037+0.0000001267*julianCentury);

        double sunEqOfCtr = Math.sin(Math.toRadians(geomMeanAnomSun))*(1.914602-julianCentury*(0.004817+0.000014*julianCentury))+Math.sin(Math.toRadians(2*geomMeanAnomSun))*(0.019993-0.000101*julianCentury)+Math.sin(Math.toRadians(3*geomMeanAnomSun))*0.000289;

        double sunTrueLong = geomMeanLongSun + sunEqOfCtr;

        double sunAppLong = sunTrueLong-0.00569-0.00478*Math.sin(Math.toRadians(125.04-1934.136*julianCentury));

        double meanObliqEcliptic = 23+(26+((21.448-julianCentury*(46.815+julianCentury*(0.00059-julianCentury*0.001813))))/60)/60;

        double obliqueCorr = meanObliqEcliptic+0.00256*Math.cos(Math.toRadians(125.04-1934.136*julianCentury));

        double sunDeclin = Math.toDegrees(Math.asin(Math.sin(Math.toRadians(obliqueCorr))*Math.sin(Math.toRadians(sunAppLong))));

        double varY = Math.tan(Math.toRadians(obliqueCorr/2))*Math.tan(Math.toRadians(obliqueCorr/2));

        double eqOfTime = 4*Math.toDegrees(varY*Math.sin(2*Math.toRadians(geomMeanLongSun))-2*eccentEarthOrbit*Math.sin(Math.toRadians(geomMeanAnomSun))+4*eccentEarthOrbit*varY*Math.sin(Math.toRadians(geomMeanAnomSun))*Math.cos(2*Math.toRadians(geomMeanLongSun))-0.5*varY*varY*Math.sin(4*Math.toRadians(geomMeanLongSun))-1.25*eccentEarthOrbit*eccentEarthOrbit*Math.sin(2*Math.toRadians(geomMeanAnomSun)));

        double trueSolarTime = (timeOfDay*1440+eqOfTime+4*lon-60*adjustedTimeZone) % 1440;

        double hourAngle;
        if(trueSolarTime/4<0)
            hourAngle = trueSolarTime/4+180;
        else
            hourAngle = trueSolarTime/4-180;

        double solarZenithAngle = Math.toDegrees(Math.acos(Math.sin(Math.toRadians(lat))*Math.sin(Math.toRadians(sunDeclin))+Math.cos(Math.toRadians(lat))*Math.cos(Math.toRadians(sunDeclin))*Math.cos(Math.toRadians(hourAngle))));

        double solarElevation = 90 - solarZenithAngle;

        double athmosphericRefraction;
        if(solarElevation>85)
            athmosphericRefraction = 0;
        else if(solarElevation>5)
            athmosphericRefraction = 58.1/Math.tan(Math.toRadians(solarElevation))-0.07/Math.pow(Math.tan(Math.toRadians(solarElevation)),3)+0.000086/Math.pow(Math.tan(Math.toRadians(solarElevation)),5);
        else if(solarElevation>-0.575)
            athmosphericRefraction = 1735+solarElevation*(-518.2+solarElevation*(103.4+solarElevation*(-12.79+solarElevation*0.711)));
        else
            athmosphericRefraction = -20.772/Math.tan(Math.toRadians(solarElevation));
        athmosphericRefraction /= 3600;

        double solarElevationCorrected = solarElevation + athmosphericRefraction;

        return solarElevationCorrected;

    }


    /**
     * Return Julian day from date
     * @param date
     * @return
     */
    public static double dateToJulian(Date date) {

        GregorianCalendar calendar = new GregorianCalendar();
        calendar.setTime(date);

        int a = (14-(calendar.get(Calendar.MONTH)+1))/12;
        int y = calendar.get(Calendar.YEAR) + 4800 - a;

        int m =  (calendar.get(Calendar.MONTH)+1) + 12*a;
        m -= 3;

        double jdn = calendar.get(Calendar.DAY_OF_MONTH) + (153.0*m + 2.0)/5.0 + 365.0*y + y/4.0 - y/100.0 + y/400.0 - 32045.0 + calendar.get(Calendar.HOUR_OF_DAY) / 24 + calendar.get(Calendar.MINUTE)/1440 + calendar.get(Calendar.SECOND)/86400;

        return jdn;
    } 
}

тогда в MainActivity у меня есть метод, который проверяет каждые 5 минут высоту солнца в данном положении:

 if(displayMode.equals("auto")){
        double sunHeight = SolarCalculations.CalculateSunHeight(lat, lon, cal);
        if(sunHeight > 0 && mThemeId != R.style.AppTheme_Daylight)
        {//daylight mode
            mThemeId = R.style.AppTheme_Daylight;   
            this.recreate();
        }
        else if (sunHeight < 0 && sunHeight >= -6 && mThemeId != R.style.AppTheme_Dusk)
        {//civil dusk
            mThemeId = R.style.AppTheme_Dusk;
            this.recreate();
        }
        else if(sunHeight < -6 && mThemeId != R.style.AppTheme_Night)
        {//night mode
            mThemeId = R.style.AppTheme_Night;
            this.recreate();
        }
    }

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

<!-- Application theme. -->
<style name="AppTheme.Daylight" parent="AppBaseTheme">
    <item name="android:background">@color/white</item>
    <item name="android:panelBackground">@color/gray</item>
    <item name="android:textColor">@color/black</item>
</style>

<style name="AppTheme.Dusk" parent="AppBaseTheme">
    <item name="android:background">@color/black</item>
    <item name="android:panelBackground">@color/gray</item>
    <item name="android:textColor">@color/salmon</item>
</style>

<style name="AppTheme.Night" parent="AppBaseTheme">
    <item name="android:background">@color/black</item>
    <item name="android:panelBackground">@color/gray</item>
    <item name="android:textColor">@color/red</item>
</style>

это работает довольно хорошо и учитывает коррекцию летнего времени.

источники:

NOAA восход закат

Юлианский День