昼/夜のテーマを実装する必要があるアプリがあります。残念ながら、スタイルを使用するだけでテーマを作成する簡単な方法はありません。レイアウトの背景、ボタンセレクター、テキストの色、テキストサイズ、画像、アイコン、アニメーションを更新できる必要があります。
私が見るところから、私には2つの選択肢があります。
夜/日ごとに異なるxmlレイアウトファイルがあるため、home_day.xml
/home_night.xml
のようになります。アプリには約30の画面があるため、最終的には60のxmlレイアウトになります。アクティビティ/フラグメントonCreateで、現在の時間に基づいてsetContentView
できます。これにより、xmlファイルがさらに追加されますが、アクティビティにコードが追加されることはありません。
テーマを設定し、現在の昼/夜に基づいて属性を更新するアイテムごとに、昼/夜とアクティビティのonCreate
findviewByIdのレイアウトを1つだけにします。これにより、多くの追加コード、findviewsが生成され、多くのビューに属性が適用される可能性があります。
私は2を目指していますが、あなたからの提案を歓迎します。それで、あなたは何を選びますか、そしてなぜですか?
実際には、テーマを使用してカスタムドローアブルを説明することもできるようです。見てみましょう: Androidでナイトモードとデイモードのテーマを切り替える方法は? 。スタイルブロックを使用してテーマを作成し、xmlレイアウトで?attrを使用してテーマに何かを指定します。次に、次のアクティビティでsetTheme(R.styles.DAY_THEME)を呼び出すことができ、すべてが更新されるはずです。
ナイトモードのリソースセット修飾子として-night
を使用し、ナイト固有のリソースをそこに配置します。
Androidにはすでに ナイトモードの概念 があり、時刻とセンサーに基づいてナイトモードとデイタイムモードを切り替えます。したがって、それを使用することを検討するかもしれません。
たとえば、モードに基づいて異なるテーマを設定するには、res/values/styles.xml
とres/values-night/styles.xml
を作成します。各ファイルに同じ名前のテーマを設定します(例:AppTheme
)が、昼と夜のモードの違いに基づいてテーマを調整します。テーマを名前で参照すると(マニフェストなど)、Androidは適切なリソースに自動的に読み込まれ、Androidは自動的に破棄され、これらのアクティビティの実行中にモードが変更された場合のアクティビティ。
夜をテーマにしたUIを使用するかどうかを手動でユーザーが制御したい場合は、-night
は役に立ちません。
完全なステップバイステップの例については、このチュートリアルを確認してください: ここをクリック
Appcompatv23.2サポートライブラリを使用して自動切り替えDayNightテーマを追加する
_build.gradle
_ファイルに次の行を追加します
_compile 'com.Android.support:appcompat-v7:23.2.0'
_
以下のように_styles.xml
_でテーマスタイルを作成します
_<resources>
<!-- 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>
</resources>
_
次に、アプリ全体のテーマを設定するための次のラインコードonCreate()
メソッドを追加します。
デフォルトの自動切り替えナイトモードを設定するには、
_AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
_
デフォルトのナイトモードを設定するには
_AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
_
デフォルトの日モードを設定するには
_AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
_
これが私の解決策です:
-> NOAAのWebページから、特定の位置と日付を指定して、地平線上の太陽の高さを計算するアルゴリズムを見つけることができます。
->これらのアルゴリズムを使用して、緯度と経度の2つの倍数とカレンダーを指定して地平線上の太陽の高さを計算するメソッドを作成しました
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();
}
}
このメソッドは、使用する現在のスタイルを設定します。私には3つあります。昼と夜に2つ、太陽光が大気中に屈折し始める夕暮れに1つ
<!-- 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>
これはかなりうまく機能しており、夏時間の修正を考慮に入れています。
出典:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);