最近需求多了个日夜模式变换,但是大多页面都是WindowManager的弹窗。每次都要写大量的代码来动态设置背景文字颜色啥的(弹窗不管悬浮在哪,都要实时和日夜模式保持一致),感觉太浪费时间。所以最近抽空研究了一下,最后想了个办法,解析xml获取属性,传个RootView和xml,让他自己去干这些重复设置样式的活。
public interface UIModeListener {
void uiModeChanged(boolean isLight);
}
public class UIModeManager {
private List<UIModeListener> uiModeListenerList = new ArrayList<>();
private static UIModeManager instance = new UIModeManager();
public static UIModeManager getInstance() {
return instance;
}
public void registerUIModeListener(UIModeListener uiModeListener) {
boolean hava = false;
for (int i = 0; i < uiModeListenerList.size(); i++) {
if (uiModeListenerList.get(i) == uiModeListener) {
hava = true;
}
}
if (!hava) {
uiModeListenerList.add(uiModeListener);
}
}
public void unRegisterUIModeListener(UIModeListener uiModeListener) {
uiModeListenerList.remove(uiModeListener);
}
public void broadCastUiModeChanged(boolean isLight) {
for (int i = 0; i < uiModeListenerList.size(); i++) {
uiModeListenerList.get(i).uiModeChanged(isLight);
}
}
}
/**
* @author XHD
* Date 2022/08/25
* Description:View上必须设置Id,src textColor background如果引用了资源文件会重新更新
*/
public class LayoutUtils {
private static final String TAG = LayoutUtils.class.getSimpleName() + "-------->";
public static void updateResUI(Context context, @LayoutRes int layoutId, View rootView) {
XmlResourceParser xmlParser = context.getResources().getLayout(layoutId);
try {
int event = xmlParser.getEventType();
while (event != XmlPullParser.END_DOCUMENT) {
switch (event) {
case XmlPullParser.START_DOCUMENT:
Log.i(TAG, "xml解析开始");
break;
case XmlPullParser.START_TAG:
String id = xmlParser.getAttributeValue("http://schemas.android.com/apk/res/android", "id");
if (checkAttr(id)) {
id = id.replace("@", "");
View view = rootView.findViewById(Integer.parseInt(id));
String background = xmlParser.getAttributeValue("http://schemas.android.com/apk/res/android", "background");
String src = xmlParser.getAttributeValue("http://schemas.android.com/apk/res/android", "src");
String textColor = xmlParser.getAttributeValue("http://schemas.android.com/apk/res/android", "textColor");
if (checkAttr(background)) {
background = background.replace("@", "");
view.setBackground(context.getResources().getDrawable(Integer.parseInt(background)));
Log.e(TAG, "update background");
}
if (view instanceof ImageView && checkAttr(src)) {
src = src.replace("@", "");
((ImageView) view).setImageDrawable(context.getResources().getDrawable(Integer.parseInt(src)));
Log.e(TAG, "update src");
}
if (view instanceof TextView && checkAttr(textColor)) {
textColor = textColor.replace("@", "");
((TextView) view).setTextColor(context.getResources().getColor(Integer.parseInt(textColor)));
Log.e(TAG, "update textColor");
}
}
break;
case XmlPullParser.TEXT:
Log.d(TAG, "Text:" + xmlParser.getText());
break;
case XmlPullParser.END_TAG:
break;
default:
break;
}
event = xmlParser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static boolean checkAttr(String attr) {
if (!TextUtils.isEmpty(attr) && !attr.equals("null") && attr.contains("@")) {
return true;
}
return false;
}
}
MainApplication中注册日夜模式变化监听
public class MainApplication extends Application {
public static MainApplication instance;
@Override
public void onCreate() {
super.onCreate();
instance = this;
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int currentNightMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
log("onConfigurationChanged: uiMode=白天模式");
UIModeManager.getInstance().broadCastUiModeChanged(true);
break;
case Configuration.UI_MODE_NIGHT_YES:
log("onConfigurationChanged: uiMode=黑夜模式");
UIModeManager.getInstance().broadCastUiModeChanged(false);
break;
}
}
private void log(String msg) {
Log.e(MainApplication.class.getSimpleName() + "-------->", msg);
}
}
Activity中进行注册
public class MainActivity extends AppCompatActivity implements UIModeListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UIModeManager.getInstance().registerUIModeListener(this);
}
@Override
public void uiModeChanged(boolean isLight) {
Log.e(MainActivity.class.getSimpleName() + "------->", "uiModeChanged: isLight=" + isLight);
LayoutUtils.updateResUI(MainApplication.instance, R.layout.activity_main, getWindow().getDecorView());
}
@Override
protected void onDestroy() {
super.onDestroy();
UIModeManager.getInstance().unRegisterUIModeListener(this);
}
}
最后又利用插件化思路,改进了上面的方法,可以直接加载插件apk,获取插件的Resource,再从插件的Resource中获取资源文件或样式,这样就可以实现通用的动态换肤了。
最后附上完整demo地址