技术交流学习或者有任何问题欢迎加群 : 154514123 
优点:正儿八经的夜间模式,配色看着舒服
缺点:规模较大的应用,需要随theme变化的属性会很多,都需要逐一定义,有点麻烦,另外一个缺点是要使得新theme生效,一般需要restartActivity来切换UI,会导致切换主题时界面闪烁
核心思路:自定义一个颜色属性名 A,A在日间和夜间模式下都有具体的颜色代码,页面布局文件只管引用A,至于是日间还是夜间,由后台主题决定。
附上一二中方法的图
(原图)
(第一种实现)
(第二种实现)
好,下面来讲讲具体的实现步骤,本环节使用的开发环境是Eclipse demo地址http://www.oschina.net/code/snippet_2702417_55892
attrs.xml(声明属性的类型,布局xml中用) reference可以使用系统的资源ID,比如R.color.gray; color可以直接使用#ffffff颜色代码

<attr name="colorValue" format="color" /> <attr name="floatValue" format="float" /> <attr name="integerValue" format="integer" /> <attr name="booleanValue" format="boolean" /> <attr name="dimensionValue" format="dimension" /> <attr name="stringValue" format="string" /> <attr name="referenceValue" format="color|reference" /> <attr name="imageValue" format="reference"/> <attr name="curVisibility"> <!-- 相当于View的Visiblity --> <enum name="show" value="0" /> <!-- Not displayed, but taken into account during layout (space is left for it).相当于View的unVisiblity --> <enum name="inshow" value="1" /> <!-- Completely hidden, as if the view had not been added.相当于View的GONE --> <enum name="hide" value="2" /> </attr>
colors.xml(调色板,集中管理颜色hex)遵循优秀格式规范,即调色板模式,避免使用btn1,btn2,fontTitle,fontText之类的颜色名。
<?xml version="1.0" encoding="utf-8"?><resources> <color name="title">#101115</color> <color name="night_title">#FFFFFF</color> </resources>string.xml
<string name="app_name">dayandNight</string> <string name="hello_world">Hello world!</string> <string name="action_settings">白模式</string> <string name="night_action_settings">夜模式</string>styles.xml(日间、夜间主题)
<resources> <style name="AppBaseTheme" parent="android:Theme.Light"> <!-- Theme customizations available in newer API levels can go in res/values-vXX/styles.xml, while customizations related to backward-compatibility can go here. --> </style> <!-- application theme. --> <style name="APPTheme" parent="AppBaseTheme"> <!-- All customizations that are NOT specific to a particular API-level can go here. --> </style> <style name="DayTheme" parent="AppTheme"> <item name="colorValue">@color/title</item> <item name="floatValue">0.35</item> <item name="integerValue">33</item> <item name="booleanValue">true</item> <item name="dimensionValue">16dp</item> <!-- 如果string类型不是填的引用而是直接放一个字符串,在布局文件中使用正常,但代码里获取的就有问题 --> <item name="stringValue">@string/action_settings</item> <item name="referenceValue">@drawable/bg</item> <item name="imageValue">@drawable/day</item> <item name="curVisibility">show</item> </style> <style name="NightTheme" parent="AppTheme"> <item name="colorValue">@color/night_title</item> <item name="floatValue">1.44</item> <item name="integerValue">55</item> <item name="booleanValue">false</item> <item name="dimensionValue">18sp</item> <item name="stringValue">@string/night_action_settings</item> <item name="referenceValue">@drawable/night_bg</item> <item name="imageValue">@drawable/night</item> <item name="curVisibility">hide</item> </style></resources>2activity的使用
PRotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(MyApp.isLightMode()){ this.setTheme(R.style.NightTheme); }else{ this.setTheme(R.style.DayTheme); } setContentView(R.layout.activity_one); } public void onClick(View view){ MyApp.setIslightMode(!MyApp.isLightMode()); recreate(); }xml的布局:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/referenceValue" android:orientation="vertical" android:gravity="center" > <TextView android:id="@+id/setting_Color" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" android:textColor="?attr/colorValue" /> <CheckBox android:id="@+id/setting_show_answer_switch" android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="?attr/booleanValue" /> <TextView android:id="@+id/setting_Title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="pszwzy" android:textColor="?attr/colorValue" android:textSize="?attr/dimensionValue" /> <TextView android:id="@+id/setting_Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="?attr/stringValue" /> <ImageView android:id="@+id/setting_Image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="?attr/imageValue" /> <View android:id="@+id/setting_line" android:layout_width="match_parent" android:layout_height="1dp" android:visibility="?attr/curVisibility" /> <Button android:onClick="onClick" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="模式切换" android:textColor="?attr/colorValue" /></LinearLayout>注:遇到放回activity闪屏的问题可以(1)在BaseActivity中创建下面的方法
public static void updateTheme(Activity activity,isNight){ MyApp.setNightMode(isNight); activity.recreate();}2:使用一个带黑色带透明度的View,盖在现有的activity上,效果类似你带上墨镜,看着太阳不刺眼。
优点:不用重启activity,不闪屏;加上透明度过渡动画,模式之间切换非常舒服,解决了1中,白底图片依旧刺眼的问题。;
缺点:配色没变化,就算带上墨镜,白天依旧是白天。
核心思路:使用WindowManager,在当前activity上,通过addView,添加一个黑色带透明度的View。
//第二中方式,只是提供一种思路,逻辑没有去实习 public void onClickT(View view){ LayoutParams mNightViewParam = new LayoutParams( LayoutParams.TYPE_APPLICATION, LayoutParams.FLAG_NOT_TOUCHABLE | LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); View mNightView = new View(this); mWindowManager.addView(mNightView, mNightViewParam); mNightView.setBackgroundResource(R.color.night_mask);}3:通过修改Theme来切换夜间模式,不过不会出现闪屏()的情况。
和第一种方式一样需要theme的设置从上到下的代码
<?xml version="1.0" encoding="utf-8"?><resources> <attr name="gray_3_double" format="color" /> <attr name="source_bg" format="reference" /></resources></pre><pre code_snippet_id="1671755" snippet_file_name="blog_20160504_8_4007554" name="code" class="html"><?xml version="1.0" encoding="utf-8"?><resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="AppTheme_Night" > <item name="gray_3_double">@color/gray_3_night</item> <item name="source_bg">@drawable/source_bg_night</item> </style> </resources><?xml version="1.0" encoding="utf-8"?><resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="AppTheme_Light" > <item name="gray_3_double">@color/gray_3_light</item> <item name="source_bg">@drawable/source_bg_light</item> </style></resources>先看下BaseActivitypublic abstract class BaseActivity extends Activity {protected int skin;public Context mContext;SharedPreferences sp;@Overrideprotected void onCreate(Bundle savedInstanceState) {<span style="white-space:pre"> </span>setThemeMode(getSkinTypeValue());<span style="white-space:pre"> </span>super.onCreate(savedInstanceState);<span style="white-space:pre"> </span>init();<span style="white-space:pre"> </span>}<span style="white-space:pre"> </span>public abstract void init();<span style="white-space:pre"> </span>protected void setThemeMode(SkinType skinType) {<span style="white-space:pre"> </span> switch (skinType) {<span style="white-space:pre"> </span> case Light:<span style="white-space:pre"> </span>setTheme(R.style.AppTheme_Light);<span style="white-space:pre"> </span> break;<span style="white-space:pre"> </span> case Night:<span style="white-space:pre"> </span>setTheme(R.style.AppTheme_Night);<span style="white-space:pre"> </span> break;<span style="white-space:pre"> </span> default:<span style="white-space:pre"> </span>setTheme(R.style.AppTheme_Light);<span style="white-space:pre"> </span> break;<span style="white-space:pre"> </span>}}//获取用户选取的模式protected SkinType getSkinTypeValue() {<span style="white-space:pre"> </span>if (sp == null) {<span style="white-space:pre"> </span>sp = getSharedPreferences("AppSkinType", Context.MODE_PRIVATE);<span style="white-space:pre"> </span>}<span style="white-space:pre"> </span>int i = sp.getInt("AppSkinTypeValue", 0);<span style="white-space:pre"> </span>switch (i) {<span style="white-space:pre"> </span> case 0:<span style="white-space:pre"> </span>return SkinType.Light; case 1:<span style="white-space:pre"> </span>return SkinType.Night;<span style="white-space:pre"> </span> default:<span style="white-space:pre"> </span> break;<span style="white-space:pre"> </span>}<span style="white-space:pre"> </span>return SkinType.Light;}//保存用户是选取的模式public void saveSkinValue(int skin) {<span style="white-space:pre"> </span>if (sp == null) {<span style="white-space:pre"> </span>sp = getSharedPreferences("AppSkinType", Context.MODE_PRIVATE);<span style="white-space:pre"> </span>}<span style="white-space:pre"> </span>Editor editor = sp.edit();<span style="white-space:pre"> </span>editor.putInt("AppSkinTypeValue", skin);<span style="white-space:pre"> </span>editor.commit();}在看下第一个activity的使用的public class MainActivity extends BaseActivity { private CheckBox cbSetting; private Button btnForward; private TextView tv_title; private View llNight; @Override public void init() { setContentView(R.layout.activity_main); llNight = findViewById(R.id.ll_night); tv_title = (TextView) findViewById(R.id.tv_title); btnForward = (Button) findViewById(R.id.forward); cbSetting = (CheckBox) findViewById(R.id.cb_setting); cbSetting.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { // 夜间模式 saveSkinValue(1); setTheme(R.style.AppTheme_Night);//防止getTheme()取出来的值是创建时候得值 initView(); } else { // 白天模式 saveSkinValue(0); setTheme(R.style.AppTheme_Light);//防止getTheme()取出来的值是创建时候得值 initView(); } } }); btnForward.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,SecondActivity.class); startActivity(intent); } }); } //改变模式后手动修改UI,不然出现闪屏 private void initView(){ <span style="color:#ff6666;">TypedValue typedValue = new TypedValue(); Theme theme = getTheme(); theme.resolveAttribute(R.attr.gray_3_double, typedValue, true); btnForward.setTextColor(getResources().getColor(typedValue.resourceId)); tv_title.setTextColor(getResources().getColor(typedValue.resourceId)); theme.resolveAttribute(R.attr.source_bg, typedValue, true); llNight.setBackgroundDrawable(getResources().getDrawable(typedValue.resourceId));</span> } }大体就是这样实现,附上demo链接http://www.oschina.net/code/snippet_2702417_55886优点:
bug少,用户体验好
缺点:
UI控件多的时候,改起来工作量不小。代码量很大
4 通过模式库changesKin
http://www.codeceo.com/article/android-changeskin-usage.html 注:这里提到的都是资源内嵌在Apk中的方案,像QQ通过下载方式实现夜间的方式可以去百度一下。参考:http://m.oschina.net/blog/668384
新闻热点
疑难解答