如何手动更改DayNight主题?
问题描述
我已经在我的应用中成功实现了NightMode,但仅在> API29(Android Q)时才实现。在较旧的API中,我没有自动DayNight主题的功能(根据操作系统),因此我使用开关来手动触发主题,然后将其保存在Preferences中,以便在加载主题时用户再次启动活动。这是开关代码(此代码在一个片段内):
DarkSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
if (b) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
p.setBoolean("NightMode", true); //P is an instance of a class I have called PrefUtils that saves the user preferences
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
p.setBoolean("NightMode", false);
}
((MainActivity) getActivity()).recreate();
});
上面的代码工作正常,它以所需的主题重新启动Activity并将布尔值保存在首选项中。但是,主要问题是当我重新启动应用程序时。 “活动”在setContentView()
之前更改了主题,并加载了主题,但是在尝试从AsyncTask访问我的数据库后,它引发了异常:
java.lang.RuntimeException:执行时发生错误doInBackground()原因:java.lang.NullPointerException:尝试执行以下操作:调用虚拟方法'android.database.sqlite.SQLiteDatabaseandroid.content.Context.openOrCreateDatabase(java.lang.String,int,android.database.sqlite.SQLiteDatabase $ CursorFactory,android.database.DatabaseErrorHandler)'上的空对象引用
我的猜测是,以某种方式更改主题可能导致Context为空,并且将此空上下文传递给片段,这些片段试图通过AsyncTask访问数据库,并且引发异常。这很奇怪,因为该应用程序仅在加载活动时崩溃(它不会在第一个代码段中崩溃),并且仅在执行AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
时崩溃,这只会在用户具有首选项“ NightMode”时发生设置为true。
这是我的活动代码:
private void initTheme() {
PrefUtils p = new PrefUtils(this);
if (p.getBoolean("NightMode", false) || AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
getDelegate().applyDayNight();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initTheme();
setContentView(R.layout.activity_main);
initFragments();
}
编辑我通过我的FolderFragment访问数据库值,该文件夹与initFragments()中的活动相同:
public Fragment fragment1 = new FolderFragment();
private void initFragments()
{
fm.beginTransaction().add(R.id.fragmentcontainer, fragment1, "1").commit();
}
这是我的FolderFragment的代码:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.folderfragment, container, false);
new RefreshData(v).execute();
return v;
}
private class RefreshData extends AsyncTask {
private View v;
private String title;
private Drawable drawable;
public RefreshData(View v) {
this.v = v;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
Progress = v.findViewById(R.id.FFProgress); //Progress is my ProgressBar
Progress.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
PAdapter = new ListAdapter(mList, getContext(), Mode);
Pager.setAdapter(PAdapter);
Progress.setVisibility(View.GONE); //Hides the ProgressBar because has finished
}
@Override
protected Void doInBackground(Void... voids) {
mList = getLists();
return null;
}
private void getLists()
{
ArrayList l = new ArrayList();
DatabaseHelper d = new DatabaseHelper(getContext());
Cursor res = d.getAllLists();
try
{
while (res.moveToNext()) {
int ID = res.getInt(0);
int FolderID = res.getInt(1);
String Title = res.getString(2);
String Description = res.getString(3);
String ColorHex = res.getString(4);
int Tag = res.getInt(5);
l.add(new ListItem(ID, FolderID, Title, Description, ColorHex, Tag));
}
}
catch(Exception ignored)
{
}
return l;
}
}
此类"DatabaseHelper"
是管理所有选择和插入语句的类:
public class DatabaseHelper extends SQLiteOpenHelper{
public Cursor getAllLists() {
try
{
SQLiteDatabase db = this.getWritableDatabase(); //This is where throws the exception
return db.rawQuery(Db.Query.SELECT_LISTS, null);
}
catch(Exception e)
{
}
return null;
}
}
思路:
视图未附加到onCreateView
内部的活动/片段(这也是通货膨胀中false
的意思。false
表示不附加,因为从该方法返回视图时,系统将附加)。
开始操作视图并执行使用该视图和上下文的任务本身具有危险。
我将new RefreshData(v).execute();
移到onActivityCreated
。
但是您的问题是您的ASyncTask是非静态的,这就是为什么您可以在其中使用getContext()
方法的原因。因此,问题在于任务要么持有旧的引用,然后变为空值,要么任务在生命周期中过早请求引用,并且空值。
我认为上述移至onActivityCreated
的修复程序可能会对其进行修复,但这仍然不是最佳方法。
然后我将您的ASyncTask更改为静态:
private static class RefreshData extends AsyncTask
然后在您使用getContext()
的任何地方更改为v.getContext()
,即
new DatabaseHelper(getContext());
成为
new DatabaseHelper(v.getContext());
您还可以显式传递上下文,就像传递视图的方式一样。那会更好。实际上最好是这样做:
@Override
public void onActivityCreated (Bundle savedInstanceState) {
DatabaseHelper dbHelper = new DatabaseHelper(getContext());
ProgressBar progressView = findViewById(R.id.FFProgress);
new RefreshData(dbHelper, progressView).execute();
}
private static class RefreshData extends AsyncTask {
private final View progress;
private final DatabaseHelper dbHelper;
private String title;
private Drawable drawable;
public RefreshData(DatabaseHelper dbHelper, View progress) {
this.dbHelper = dbHelper;
this.progress = progress;
}
等等。以便您明确声明任务使用的内容,并将其从活动中传递出去。
旁注:
您有很多以大写字母开头的变量,例如:
String Title = res.getString(2);
您应避免这样做,以免混淆自己。 Java开发人员的idomatic协议是用大写字符(例如DatabaseHelper
)命名类,并用小写字符(例如title
)命名变量。然后,骆驼的任何进一步的话。这样就变成:
String title = res.getString(2);
以上内容就是爱站技术频道小编为大家分享的如何手动更改DayNight主题?看完以上分享之后,大家应该都知道如何手动更改DayNight主题了吧。