AndroidのThemeを無理矢理動的に変更したよっていう話
TwitterのアプリだとNight Modeっていう機能がついてる
暗いところでは目に優しくないから、色を落ち着かせようっていうもの
黒系が好きな私にとっては、是非とも自作アプリに組み込みたい
この場合、Themeを動的に切り替えるのが一番しっくり来るので実装してみると
つまずいてしまったのでメモ
こちらを参考にさせてもらった
簡単にまとめると
Themeを変更した後にAvtivityを再描画させようぜ
と、いう話
1. styles.xmlを書き換える
今回は
「三種類のThemeをボタンをおすことによって切り替わる」
という感じで作っていく
てことで三種類のThemeを定義していく
res/valuse/styles.xml
<resources>
<!--First Theme-->
<style name="FirstTheme" parent="Theme.AppCompat.Light.DarkActionBar"></style>
<!--Second Theme-->
<style name="SecondTheme" parent="Theme.AppCompat.DayNight.NoActionBar"></style>
<!--Third Theme-->
<style name="ThirdTheme" parent="Theme.AppCompat.DayNight.DarkActionBar"></style>
</resources>
色をわざわざ上書きするのも面倒くさいので、何も触らない
これならいちいち書く必要もないかもしれないが、名前がわかりやすくなるので
次がかなりの確率で忘れるのだが、しっかりAndroidManifest.xmlも変える
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="kyoto.test.freeprojects.oldbigbuddha.changetheme">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/FirstTheme"> ←ここ
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
最初これを書き換えなかったためにビルドが通らなかった
2. レイアウトをちゃちゃっと書く
テストで作っているはずなのに無駄にレイアウトに凝ってしまうあるあるがあるので
そこら辺は程々に(と言いつつ長い)
とりあえずボタンが3つあればいい
layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:text="@string/app_name"
android:textSize="32sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="16dp"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/bt_first"
android:text="First Theme"
android:textAllCaps="false"
android:textSize="24sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"/>
<Button
android:id="@+id/bt_second"
android:text="Second Theme"
android:textAllCaps="false"
android:textSize="24sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"/>
<Button
android:id="@+id/bt_third"
android:text="Third Theme"
android:textAllCaps="false"
android:textSize="24sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"/>
</LinearLayout>
</LinearLayout>
特別なにかっていうものはない
今回の件とは関係ないが、
- IDはスネークケースで書く
- Padding・Marginは面倒臭がらずそれぞれ書く
- Left・Rightではなく、Start・Endで書く
というところを気をつけて書くと幸せになれるかもしれない
3. では、コードを書こう
今回重要になってくるのはsetTheme()
こいつにソースIDをぶち込んでやるとそのThemeを適用してくれる
ただ呼び出す場所が厄介で、setContentView()
の前に書かないといけない
それでは動的に変更できないではないか
そんな気がするが、Activityを再描画させれば大丈夫
そのコードがこちら
finish();
startActivity( new Intent( this, MainActivity.class ));
最初このコードを見た時に動くのかと心配したが、普通に動いた
一回finish()
を呼んでしまってるわけだから
シェアプリで一回選択したThemeを保存する
R.style.~~
の形で取ってきているから、実態はint型のデータ
よって以下の形になる
mEditor = getSharedPreferences("themeData", MODE_PRIVATE).edit;
mEditor.putInt("theme", R.style.~~);
mEditor.commit(); // 注意
最初、mEditor.apply()
と書いてうまくいかなかった
これはapply()
が非同期で書き込んでいることが原因である
下にコードを載せるので見てほしいが
すぐ下にfinish()
が書いてある
なので書き込んでしまう前に破棄されている模様
ちゃんとcommit()
にする
あと紹介してないのは基本的なところだけなので
コードを貼って終了とする
MainActivity.java
package kyoto.test.freeprojects.oldbigbuddha.changetheme;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button mBtFirst, mBtSecond, mBtThird;
private SharedPreferences mPreferences;
private SharedPreferences.Editor mEditor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPreferences = getSharedPreferences("themeData", MODE_PRIVATE);
setTheme( mPreferences.getInt("theme", R.style.FirstTheme) );
setContentView(R.layout.activity_main);
mBtFirst = (Button)findViewById(R.id.bt_first);
mBtSecond = (Button)findViewById(R.id.bt_second);
mBtThird = (Button)findViewById(R.id.bt_third);
mBtFirst.setOnClickListener(this);
mBtSecond.setOnClickListener(this);
mBtThird.setOnClickListener(this);
mEditor = mPreferences.edit();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_first: {
mEditor.putInt("theme", R.style.FirstTheme);
break;
}
case R.id.bt_second: {
mEditor.putInt("theme", R.style.SecondTheme);
break;
}
case R.id.bt_third: {
mEditor.putInt("theme", R.style.ThirdTheme);
break;
}
}
mEditor.commit();
finish();
startActivity( new Intent( this, MainActivity.class ));
}
}
Demo
SecondとThirdの見分けが少々難しい
上にバーがあるかないかで判断して欲しい