我有一个应用程序,我需要实现日/夜主题。不幸的是,没有简单的方法可以通过使用样式进行主题化,我需要能够更新:布局背景,按钮选择器,文本颜色,文本大小,图像,图标,动画。
从我看到的我有两个选择:
为夜晚/日期提供不同的xml布局文件,例如home_day.xml
/ home_night.xml
。应用程序中有大约30个屏幕,因此最终将有60 xml布局。在活动/片段onCreate上,根据当前小时我可以setContentView
。这会增加一些xml文件,但可以避免在活动中添加更多代码
对于我想要主题的每个项目,只有一个日/夜布局和活动的onCreate
findviewById,并根据当前日/夜更新其属性。这可能会为许多视图生成大量额外代码,查找视图和应用属性。
我的目标是2.但我愿意接受你的任何建议。那么,你会选择什么?为什么?
答案 0 :(得分:11)
我使用-night
作为夜间模式的资源集限定符,将夜间特定资源放在那里。
Android已经有the notion of night mode,根据时间和传感器在夜间和白天模式之间切换。因此,您可以考虑使用它。
例如,要根据模式设置不同的主题,请创建res/values/styles.xml
和res/values-night/styles.xml
。在每个文件中都有一个具有相同名称的主题(例如AppTheme
),但根据您希望在白天和夜晚模式之间的差异来定制主题。当您按名称引用主题时(例如,在清单中),Android将自动加载正确的资源,如果模式在这些活动运行时发生变化,Android将自动销毁并重新创建您的活动。
现在,如果您希望手动用户控制是否使用以夜间为主题的UI,-night
将无济于事。
答案 1 :(得分:11)
查看本教程以获取完整的分步示例:click here
使用Appcompat v23.2支持库添加自动切换DayNight主题
在build.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);
答案 2 :(得分:6)
这是我的解决方案:
- &GT;从NOAA网页上可以找到算法来计算给定位置和日期的地平线上的太阳高度。
- &GT;使用这些算法,我创建了一种计算太阳在地平线上的高度的方法,给出了两倍的Latitude&amp;经度和日历
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>
这一直很好,并考虑了夏令时修正。
来源:
答案 3 :(得分:4)
实际上,您似乎也可以使用主题来描述自定义drawable。看看:How to switch between night-mode and day-mode themes on Android?。您可以使用样式块创建主题,然后在xml布局中使用?attr指定主题中的内容。然后你应该可以在下一个活动上调用setTheme(R.styles.DAY_THEME),所有内容都应该更新。
答案 4 :(得分:1)
更新后的答案
启用深色主题:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
强制禁用深色主题:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
根据暗模式的移动设置来设置应用主题,即,如果启用了暗模式,则该主题将设置为暗主题;如果未启用,则 默认主题,但这仅适用于版本> = Android版本Q
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
注释:
“ Theme.AppCompat.DayNight”
喜欢
<style name="DarkTheme" parent="Theme.AppCompat.DayNight">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
可绘制和可绘制的夜晚,
价值观与价值观之夜
答案 5 :(得分:-1)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);