[英]SharedPreferences are empty when called for the first time in Fragment
I have a sample android application where depending on the location (zip code) and temperature units set in settings (SharedPreference), the application displays 7 days weather. 我有一个示例Android应用程序,其中根据设置(SharedPreference)中设置的位置(邮政编码)和温度单位,该应用程序显示7天天气。
It seems like when the application fetches temperature for the first time and checks what is the temperature unit set in SharedPreference, it comes as empty and isMetric is set to TRUE. 似乎当应用程序首次获取温度并检查在SharedPreference中设置的温度单位是什么时,它为空并且isMetric设置为TRUE。 Utility.isMetric can be improved to signify that there was no data fetched from SharedPreference but my question is why the SharedPreference is empty when called for first time in Utility.isMetric from ForecastFragment's onCreateView?
Utility.isMetric可以进行改进以表示没有从SharedPreference提取数据,但是我的问题是,为什么从ForecastFragment的onCreateView首次在Utility.isMetric中调用SharedPreference为空?
Utility.isMetric accessing SharedPreference Utility.isMetric访问SharedPreference
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
ForecastFragment calling Utility.isMetric ForecastFragment调用Utility.isMetric
boolean isMetric = Utility.isMetric(getActivity());
I have logcat that shows this behavior, let me know if you would like to see that. 我有显示此行为的logcat,如果您希望看到该消息,请告诉我。
As there is a maximum limit on no. 由于没有限制。 of characters, complete code can be accessed at https://github.com/gosaliajigar/CSC519/tree/master/CSC519-HW6
字符集,可以在https://github.com/gosaliajigar/CSC519/tree/master/CSC519-HW6访问完整代码
ForecastFragment ForecastFragment
public class ForecastFragment extends Fragment implements LoaderCallbacks<Cursor> {
private SimpleCursorAdapter mForecastAdapter;
private static final int FORECAST_LOADER = 0;
private String mLocation;
// For the forecast view we're showing only a small subset of the stored data.
// Specify the columns we need.
private static final String[] FORECAST_COLUMNS = {
// In this case the id needs to be fully qualified with a table name, since
// the content provider joins the location & weather tables in the background
// (both have an _id column)
// On the one hand, that's annoying. On the other, you can search the weather table
// using the location set by the user, which is only in the Location table.
// So the convenience is worth it.
WeatherEntry.TABLE_NAME + "." + WeatherEntry._ID,
WeatherEntry.COLUMN_DATETEXT,
WeatherEntry.COLUMN_SHORT_DESC,
WeatherEntry.COLUMN_MAX_TEMP,
WeatherEntry.COLUMN_MIN_TEMP,
LocationEntry.COLUMN_LOCATION_SETTING
};
// These indices are tied to FORECAST_COLUMNS. If FORECAST_COLUMNS changes, these
// must change.
public static final int COL_WEATHER_ID = 0;
public static final int COL_WEATHER_DATE = 1;
public static final int COL_WEATHER_DESC = 2;
public static final int COL_WEATHER_MAX_TEMP = 3;
public static final int COL_WEATHER_MIN_TEMP = 4;
public static final int COL_LOCATION_SETTING = 5;
public ForecastFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Add this line in order for this fragment to handle menu events.
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.forecastfragment, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_refresh) {
updateWeather();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final String[] columns = {WeatherEntry.COLUMN_DATETEXT,
WeatherEntry.COLUMN_SHORT_DESC,
WeatherEntry.COLUMN_MAX_TEMP,
WeatherEntry.COLUMN_MIN_TEMP
};
final int[] viewIDs = {R.id.list_item_date_textview,
R.id.list_item_forecast_textview,
R.id.list_item_high_textview,
R.id.list_item_low_textview
};
// The SimpleCursorAdapter will take data from the database through the
// Loader and use it to populate the ListView it's attached to.
mForecastAdapter = new SimpleCursorAdapter(
getActivity(),
R.layout.list_item_forecast,
null,
columns,
viewIDs,
0);
mForecastAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
boolean isMetric = Utility.isMetric(getActivity());
switch (columnIndex) {
case COL_WEATHER_MAX_TEMP: {
String high = Utility.formatTemperature(
cursor.getDouble(cursor.getColumnIndex(WeatherEntry.COLUMN_MAX_TEMP)), isMetric);
((TextView) view).setText(high);
return true;
}
case COL_WEATHER_MIN_TEMP: {
// we have to do some formatting and possibly a conversion
String low = Utility.formatTemperature(
cursor.getDouble(cursor.getColumnIndex(WeatherEntry.COLUMN_MIN_TEMP)), isMetric);
((TextView) view).setText(low);
return true;
}
case COL_WEATHER_DATE: {
String dateString = Utility.formatDate(
cursor.getString(cursor.getColumnIndex(WeatherEntry.COLUMN_DATETEXT)));
((TextView) view).setText(dateString);
return true;
}
}
return false;
}
});
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
// Get a reference to the ListView, and attach this adapter to it.
ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast);
listView.setAdapter(mForecastAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
Cursor cursor = mForecastAdapter.getCursor();
if (cursor != null && cursor.moveToPosition(position)) {
Intent intent = new Intent(getActivity(), DetailActivity.class)
.putExtra(DetailActivity.DATE_KEY, cursor.getString(COL_WEATHER_DATE));
startActivity(intent);
}
}
});
return rootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
getLoaderManager().initLoader(FORECAST_LOADER, null, this);
super.onActivityCreated(savedInstanceState);
}
private void updateWeather() {
String location = Utility.getPreferredLocation(getActivity());
// update weather only if location is not an EMPTY string
if (location != null
&& location.length() > 0) {
new FetchWeatherTask(getActivity()).execute(location, SettingsActivity.FORECAST_DAYS);
}
}
@Override
public void onResume() {
super.onResume();
if (mLocation != null && !mLocation.equals(Utility.getPreferredLocation(getActivity()))) {
getLoaderManager().restartLoader(FORECAST_LOADER, null, this);
}
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// fragment only uses one loader, so we don't care about checking the id.
// To only show current and future dates, get the String representation for today,
// and filter the query to return weather only for dates after or including today.
// Only return data after today.
String startDate = WeatherContract.getDbDateString(new Date());
// Sort order: Ascending, by date.
String sortOrder = WeatherEntry.COLUMN_DATETEXT + " ASC";
mLocation = Utility.getPreferredLocation(getActivity());
if (mLocation == null || mLocation.length() == 0) {
mLocation = "00000";
}
Uri weatherForLocationUri = WeatherContract.WeatherEntry.buildWeatherLocationWithStartDate(mLocation, startDate);
Log.d(getString(R.string.app_name), weatherForLocationUri.toString());
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new CursorLoader(
getActivity(),
weatherForLocationUri,
FORECAST_COLUMNS,
null,
null,
sortOrder
);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mForecastAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mForecastAdapter.swapCursor(null);
}
}
Utility 效用
public class Utility {
public static String getPreferredLocation(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getString(context.getString(R.string.pref_location_key),
context.getString(R.string.pref_location_default));
}
public static boolean isMetric(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getString(context.getString(R.string.pref_units_key),
context.getString(R.string.pref_units_metric))
.equals(context.getString(R.string.pref_units_metric));
}
static String formatTemperature(double temperature, boolean isMetric) {
double temp;
if ( !isMetric ) {
temp = 9*temperature/5+32;
} else {
temp = temperature;
}
return String.format("%.0f", temp);
}
static String formatDate(String dateString) {
Date date = WeatherContract.getDateFromDb(dateString);
return DateFormat.getDateInstance().format(date);
}
}
I have put logcat in two files (Utility & ForecastFragment) to demonstrate the problem. 我已将logcat放在两个文件(实用程序和ForecastFragment)中以演示该问题。 Here is the logcat for the very first time when ForecastFragment onCreateView is loaded, when app is installed.
这是在首次安装ForecastFragment onCreateView时安装了应用程序时的日志猫。
07-05 17:35:57.562 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:35:57.563 21604-21604/com.example.android.weather.app D/JIGAR: isMetric true
Here is the logcat when ForecastFragment onCreateView is loaded, after pressing Refresh button. 这是在按下“刷新”按钮后加载ForecastFragment onCreateView时的日志猫。
07-05 17:36:44.433 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.435 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.435 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.436 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.445 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.449 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.450 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.452 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.458 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.458 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.458 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.458 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.459 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.460 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.460 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.460 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.461 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.461 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.461 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.461 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.462 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.462 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.462 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.463 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.463 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.464 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.464 21604-21604/com.example.android.weather.app D/JIGAR: {}
07-05 17:36:44.464 21604-21604/com.example.android.weather.app D/JIGAR: {}
I have removed the Log.d statements for now from the above two locations but you are more than welcome to put log statements. 我现在已经从上述两个位置删除了Log.d语句,但是非常欢迎您放置日志语句。
NOTE: This was a homework app with TO DO shown in the files and its complete application now but while playing with the application and understanding code, I found this bug in the application and trying to understand why its happening. 注意:这是一个家庭作业应用程序,现在在文件及其完整的应用程序中显示了TO DO,但是在使用该应用程序并理解代码时,我在应用程序中发现了此错误并试图了解其发生的原因。 This is
NOT
part of homework or any submission. 这
NOT
家庭作业或任何提交的一部分。
Thanks Dennis and Kaze, my android application is loading the default shared preferences from xml file with help of addPreferencesFromResource(R.xml.pref_general)
in SettingActivity which extends PreferenceActivity
which is a separate activity and it is started when someone clicks on Settings button hence the shared preferences are never populated before pressing settings button. 感谢Dennis和Kaze,我的Android应用程序正在
addPreferencesFromResource(R.xml.pref_general)
帮助下从xml文件加载默认的共享首选项,该extends PreferenceActivity
,这是一个单独的活动,当有人单击“设置”按钮时启动它,因此在按“设置”按钮之前,不会填充共享的首选项。
Your question about populating default preferences made me look into how is it populated and it should be populated in ForecastFragment onCreate
using PreferenceManager.setDefaultValues
as below ... 您关于填充默认首选项的问题使我研究了如何填充默认首选项,应该使用
PreferenceManager.setDefaultValues
将其填充在ForecastFragment onCreate
中,如下所示...
PreferenceManager.setDefaultValues(context, R.xml.pref_general, false);
This has solved my issue. 这解决了我的问题。 I will be voting up your answers!
我会投票支持您的答案! Thanks
谢谢
The reason why your sharedPreferences are empty is because you did not successfully input anything in the first place. 您的sharedPreferences为空的原因是因为您首先没有成功输入任何内容。
I suggest using sharedPreferences this way as this is what worked for me: 我建议以这种方式使用sharedPreferences,因为这对我有用:
import android.content.SharedPreferences;
in your function: 在您的职能:
SharedPreferences preferences = getApplicationContext().getSharedPreferences("AppPref", MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("**Input your data here**", **tag your data/give it a name*);//example editor.putString("userPassword", password);
editor.commit();//this one places your string into the sharedpreferences instance you created
in order to access your shared preferences onCreate: 为了在onCreate上访问您的共享首选项:
private String data; //this receives your data from sharedpref
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
SharedPreferences conn = getSharedPreferences("AppPref", MODE_PRIVATE);
data = conn.getString("**your data tag**","");
}
Hope this helps. 希望这可以帮助。 You need to make an instance of sharedpreferences specifically for the data that is inputted during runtime if you wish to store values from your session.
如果要存储会话中的值,则需要为运行时输入的数据专门创建一个共享首选项实例。 Im not so good at technical explanations so just ask for clarifications if you have any.
我不太擅长技术说明,因此请澄清一下。
You should use following method from PreferenceManager to load the default values from xml (as you described in your comments that default values are loaded from xml). 您应该使用PreferenceManager中的以下方法从xml加载默认值(如注释中所述,默认值是从xml加载的)。
static void setDefaultValues(Context context, int resId, boolean readAgain)
Sets the default values from an XML preference file by reading the values defined by each Preference item's android:defaultValue attribute.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.