转载请注明出处:http://blog.csdn.net/lizhongstu/article/details/50779939

前言:大家好,由于公司项目需求,要加一个夜间模式的效果

夜间模式的实现方式有以下几种:

1.直接调整屏幕亮度

2.Window上加一层半透明的View

3.换皮肤式解决方式(一)

每套皮肤使用自己的一套theme,使用attrs.xml+styles.xml+Activity.setTheme()来设置自己的主题以实现换皮肤,要求资源保存在本地。

4.换皮肤式解决方式(二)

图片等资源不在本地,可以由网上下载(可以作为.zip/.apk下载)后加载,但更换起来比较麻烦,需要大量代码配合,相对于.zip/.apk两种方式换肤我比较倾向于.zip方式,因为.apk方式我曾经弄过,很是复杂啊,必须保证.apk皮肤包中的皮肤图片跟主版本的皮肤图片和资源一 一对应起来,这是为了保证两个工程中的R文件中的id要一 一对应,如果皮肤包中的R文件中的id多一个或者少一个就会出现奔溃,反正这种方式没把我给折腾死。

我今天所讲的就是第二种方案实现夜间模式

   第一种调整夜间模式的方式我也使用过,但是不是很好用

1.  如果用户把系统亮度调整到最低了,那你在夜间/白天模式几乎就没什么用了,因为亮度已经最低了。

用第二种方案实现夜间模式就能解决这个问题,亮度调整到最低了再window上加一层半透明的View,亮度就会变暗

网上也有讲解第二种实现夜间模式的方法,但是真正放到项目中去使用会出现很多问题,不知道你们遇到过没?网上都是用写几个简单的demo,demo毕竟不是一个真正上线的项目,真应用到项目中使用还是会有很多问题

 

在Window上加一层半透明的View

创建这种窗体需要向用户申请权限才可以的,因此首先在AndroidManifest.xml中加入

首先在Eclipse中新建一个Android项目,项目名就叫做NightModeDemo

先创建一个BaseActivity的抽象类,所有的activity都继承这个抽象类,在里面加入如下代码:

public abstract class BaseActivity extends FragmentActivity {private WindowManager mWindowManager = null;    private View mNightView = null;    private WindowManager.LayoutParams mNightViewParam;    private boolean mIsAddedView;    /**     * 夜间模式覆盖view 是否可用     * true:可用 false:不可用     */    private boolean nightModeEnable=true;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        int readMode=SpUtil.getinstance(this).getReaderModeCode();        //是否是夜间模式         if( readMode==1 && nightModeEnable){            changeToNight();        }    }    @Override    protected void onStart() {        super.onStart();    }    @Override    public void setContentView(int layoutResID) {        super.setContentView(layoutResID);        initView();        initData();        setListener();    }    @Override    public void setContentView(View view) {        super.setContentView(view);        initView();        initData();        setListener();    }    @Override    public void setContentView(View view, LayoutParams params) {        super.setContentView(view, params);        initView();        initData();        setListener();    }    @Override    protected void onDestroy() {        changeToDay();        super.onDestroy();    }    @Override    protected void onPause() {        super.onPause();    }    @Override    protected void onResume() {        super.onResume();    }    @Override    protected void onSaveInstanceState(Bundle outState) {        super.onSaveInstanceState(outState);    }    @Override    protected void onStop() {        super.onStop();    }    protected abstract void initView();    protected abstract void initData();    protected abstract void setListener();    /**     * 设置夜间模式     */    private void changeToNight() {        if (mIsAddedView == true)            return;        mNightViewParam = new WindowManager.LayoutParams(                WindowManager.LayoutParams.TYPE_APPLICATION,                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,                PixelFormat.TRANSPARENT);        mWindowManager = getWindowManager();        mNightView = new View(this);        mNightView.setBackgroundResource(R.color.night_float_color);        mWindowManager.addView(mNightView, mNightViewParam);        mIsAddedView = true;    }    /**     * 设置白天模式     */    public void changeToDay(){        if (mIsAddedView && mNightView!=null) {            mWindowManager.removeViewImmediate(mNightView);            mWindowManager = null;            mNightView = null;            mIsAddedView=false;        }    }    /**     * 设置夜间模式 添加view是否可用     * 必须在super.onCreate(savedInstanceState);之前调用     */    public void setChangeModeUnEnable(){        nightModeEnable=false;    }}

这里可以看到,BaseActivity的代码非常简单,主要是判断如果是夜间模式的时候添加用windowManage添加view,当activity销毁的时候设置日间模式。

然后写一下布局文件,创建或打开layout目录下的activity_main.xml文件,加入如下代码:

    
这个布局很简单我就不多做解释了,就是三个按钮 一个是开启新的activity,一个是设置夜间模式和日间模式,还有一个是弹出Dialog

创建或打开MainActivity,这个类仍然是程序的主Activity,继承BaseActivity,也是这次demo唯一的Activity,在里面加入如下代码:

public class MainActivity extends BaseActivity implements OnClickListener{   private Button button_start_activity,button_dialog,button_start_night_mode;    private WindowManager mWindowManager = null;    private View mNightView = null;    private WindowManager.LayoutParams mNightViewParam;    private boolean mIsAddedView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overrideprotected void initView() {button_start_activity=(Button)findViewById(R.id.button_start_activity);button_dialog=(Button)findViewById(R.id.button_dialog);button_start_night_mode=(Button)findViewById(R.id.button_start_night_mode);}@Overrideprotected void initData() {// 主Activity中初始化数据  加载数据的操作都放在这里}@Overrideprotected void setListener() {// TODO Auto-generated method stubbutton_start_activity.setOnClickListener(this);button_dialog.setOnClickListener(this);button_start_night_mode.setOnClickListener(this);}@Overridepublic void onClick(View v) {if(v!=null){int id=v.getId();if(id==R.id.button_dialog){DialogBookShelfQuit dialogBookShelfQuit=new DialogBookShelfQuit(this);dialogBookShelfQuit.show();}else if(id==R.id.button_start_activity){Intent intent=new Intent(this,TestActivity.class);startActivity(intent);}else if(id==R.id.button_start_night_mode){int readMode=SpUtil.getinstance(this).getReaderModeCode();        if(readMode==1 ){//夜间模式         button_start_night_mode.setText("开启夜间模式");        SpUtil.getinstance(this).setReaderModeCode(0);                changeToDay();                }else if(readMode==0){        button_start_night_mode.setText("开启日间模式");        SpUtil.getinstance(this).setReaderModeCode(1);                        changeToNight();        }}}}@Overrideprotected void onDestroy() {//恢复日间模式changeToDay();super.onDestroy();}    /**     * 设置夜间模式     */    private void changeToNight() {        if (mIsAddedView == true)            return;        mNightViewParam = new WindowManager.LayoutParams(                WindowManager.LayoutParams.TYPE_APPLICATION,                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,                PixelFormat.TRANSPARENT);        mWindowManager = getWindowManager();        mNightView = new View(this);        mNightView.setBackgroundResource(R.color.night_float_color);        mWindowManager.addView(mNightView, mNightViewParam);        mIsAddedView = true;    }    /**     * 设置白天模式     */    public void changeToDay(){        if (mIsAddedView && mNightView!=null) {            mWindowManager.removeViewImmediate(mNightView);            mWindowManager = null;            mNightView = null;            mIsAddedView=false;        }    }}

主Activity的代码其实也很简单,就是三个 1.开启activity(这个activity代码就不贴了,主要作用是开启夜间模式后点击此按钮是否新的activity也在夜间模式) 2.夜间/日间模式切换 3.弹出dialog等三个按钮


MainActivity主要代码是这段

    /**     * 设置夜间模式     */    private void changeToNight() {        if (mIsAddedView == true)            return;        mNightViewParam = new WindowManager.LayoutParams(                WindowManager.LayoutParams.TYPE_APPLICATION,                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,                PixelFormat.TRANSPARENT);        mWindowManager = getWindowManager();        mNightView = new View(this);        mNightView.setBackgroundResource(R.color.night_float_color);        mWindowManager.addView(mNightView, mNightViewParam);        mIsAddedView = true;    }    /**     * 设置白天模式     */    public void changeToDay(){        if (mIsAddedView && mNightView!=null) {            mWindowManager.removeViewImmediate(mNightView);            mWindowManager = null;            mNightView = null;            mIsAddedView=false;        }    }
看到了吧,设置夜间模式的方法就是changeToNight(),如果跳转日间模式则直接调用changeToDay方法即可,   但是一定要注意WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,

这段代码一定要记得加上,这句代码的意思是为了不让view获取焦点,如果有人对WindowManager不熟悉的话,请先去查询下api。

网上有些做法坑点一:很多做法是不在 设置夜间/日间模式按钮 所属的MainActivity中设置 夜间/日间模式,因为BaseActivity中有了设置夜间/日间模式的代码,

MainActivity又是继承于BaseActivity的,所以网上好多的做法是以下的代码

else if(id==R.id.button_start_night_mode){int readMode=SpUtil.getinstance(this).getReaderModeCode();        if(readMode==1 ){//夜间模式         button_start_night_mode.setText("开启夜间模式");        SpUtil.getinstance(this).setReaderModeCode(0);        //        changeToDay();        recreate();//网上做法的错误做法                }else if(readMode==0){        button_start_night_mode.setText("开启日间模式");        SpUtil.getinstance(this).setReaderModeCode(1);        //        changeToNight();        recreate();//网上做法的错误做法        }}

而recreate方法的执行步骤如下:



它会重新执行onCreate onStart方法,这样如果你的MainActivity中有很多数据库操作,并且Layout比较复杂或者你的MainActivity中嵌入了很多fragement,

会出现黑屏闪动的现象,体验非常之差,所以不建议用这种方法,还有好多人换肤也用这个方法重建activity之后自动换肤,我不知道他们的activity比较简单还是怎么了,

反正我自己尝试会出现闪屏

难道这样就完了,夜间/白天模式就打工告成了?

其实用这种方式会出现一个问题,就是弹出的dialog顶层还是没有半透明的view覆盖

新建一个抽象的AbsDialog,所有的dialog都继承AbsDialog,在AbsDialog中加入以下代码

public abstract class AbsDialog extends Dialog {public AbsDialog(Context context) {super(context);}public AbsDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {super(context, cancelable, cancelListener);}public AbsDialog(Context context, int theme) {super(context, theme);}@Overridepublic void setContentView(int layoutResID) {super.setContentView(layoutResID);initView();initData();setListener();}@Overridepublic void setContentView(View view) {super.setContentView(view);initView();initData();setListener();}@Overridepublic void setContentView(View view, LayoutParams params) {super.setContentView(view, params);initView();initData();setListener();}@SuppressWarnings("deprecation")protected void setProperty(int width, int height) {Window window = getWindow();WindowManager.LayoutParams p = window.getAttributes();//夜间模式int readMode=SpUtil.getinstance(getContext()).getReaderModeCode();        if( readMode==1){p.type=WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;}Display d = getWindow().getWindowManager().getDefaultDisplay();p.height = (int) (d.getHeight() * 1);p.width = (int) (d.getWidth() * 1);window.setAttributes(p);}protected abstract void initView();protected abstract void initData();protected abstract void setListener();}

在新建一个DialogBookShelfQuit,在其中加入以下代码:

public class DialogBookShelfQuit extends AbsDialog implements android.view.View.OnClickListener {private Activity context;public DialogBookShelfQuit(Activity context) {super(context, R.style.dialog_normal);this.context = context;setContentView(R.layout.dialog_bookshelf_quit);setProperty(1, 1);}@Overrideprotected void initView() {}@Overrideprotected void initData() {this.setCancelable(true);this.setCanceledOnTouchOutside(false);}@Overrideprotected void setListener() {}@Overridepublic void onClick(View v) {}}


我研究了dialog的源码发现,其实一切 界面 全都是windowManager添加显示的,通过Dialog以下代码打出的WindwManager.LayouParams

Window window = getWindow();WindowManager.LayoutParams p = window.getAttributes();
的type是跟MainActivity夜间模式设置的type是一样的,都是 WindowManager.LayoutParams. TYPE_APPLICATION,这样就确定了MainActivity设置夜间模式后再开启的dialog肯定在MainActivity加一层半透明view之上 ,而我们需要dialog在半透明view之下,所以通过了以下代码解决:

@SuppressWarnings("deprecation")protected void setProperty(int width, int height) {Window window = getWindow();WindowManager.LayoutParams p = window.getAttributes();//夜间模式int readMode=SpUtil.getinstance(getContext()).getReaderModeCode();        if( readMode==1){p.type=WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;}Display d = getWindow().getWindowManager().getDefaultDisplay();p.height = (int) (d.getHeight() * 1);p.width = (int) (d.getWidth() * 1);window.setAttributes(p);}

改变dialog的WindwManager.LayouParams 参数中的type

网上有些做法坑点二:就是弹出的dialog没有测试,没有夜间模式效果


如果大家还有什么疑问,请在下面留言。

源码下载,请点击这里






更多相关文章

  1. launcher修改--获取屏幕缩略(预览)图
  2. 关于Android中传递数据的一些讨论
  3. Intent 详解
  4. Android(安卓)自定义View - 启航 一般View定义
  5. Android(安卓)更新UI的两种方法——handler和runOnUiThread() -
  6. Android学习之Service(下)
  7. Android(安卓)APP之间共享SharedPreference
  8. Android使用Google提供的地图实现定位时LocationListener的各个
  9. lambda表达式介绍以及Android(安卓)Studio引入lambda

随机推荐

  1. SDK Manager中Extras下没有Android(安卓)
  2. 总结android音频视频操作 (java层代码)
  3. EditText的属性说明
  4. Error:(2, 0) Plugin with id 'com.githu
  5. 如何实现TextView的Marquee效果
  6. Android模拟键盘和键盘监听的一些调研
  7. Android获取SD卡中选中图片的路径(URL)
  8. Android播放GIF动画
  9. Android源码下载出现的问题
  10. android中Wifi的操作