"Simple" is "Best"

開発時に詰まったところや 調べた結果日本語での情報が無かったり古かったりした場合に自分用のメモとして高校生(?)が更新していくブログ

AndroidのThemeを無理矢理動的に変更したよっていう話

TwitterのアプリだとNight Modeっていう機能がついてる

暗いところでは目に優しくないから、色を落ち着かせようっていうもの

黒系が好きな私にとっては、是非とも自作アプリに組み込みたい

この場合、Themeを動的に切り替えるのが一番しっくり来るので実装してみると

つまずいてしまったのでメモ

 

こちらを参考にさせてもらった

stackoverflow.com

簡単にまとめると

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>

特別なにかっていうものはない

今回の件とは関係ないが、

  1. IDはスネークケースで書く
  2. Padding・Marginは面倒臭がらずそれぞれ書く
  3. 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

f:id:bigbuddha:20170809193530g:plain

 

SecondとThirdの見分けが少々難しい

上にバーがあるかないかで判断して欲しい

以下にGithubリポジトリを貼っておく

github.com