简体   繁体   English

在Fragment中首次调用时,SharedPreferences为空

[英]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.

相关问题 当在TabLayout中调用其他片段时,第一个片段出现第二秒钟 - First fragment appears for a second when other fragments are called in tablayout 第二次调用时,Bottom Sheet Fragment会导致错误 - Bottom Sheet Fragment causes error when called the second time 第一次启动我的应用程序时片段没有显示 - Fragment showing nothing when starting my app for first time 如何定义第一次调用我的 @Scheduled 方法的时间? - How to define when my @Scheduled method should be called first time? 第一次调用时response.getOutputStream()抛出IllegalStateException - response.getOutputStream() throws IllegalStateException when called for the first time 调用片段时,我需要片段中的listView - I need a listView in a fragment when the fragment is called 片段首次被选中时会“覆盖”另一个片段 - Fragment “overwrites” another fragment when first selected SharedPreferences返回一个空字符串 - SharedPreferences returning an empty string 当其他片段更改第一个片段模型时刷新片段 - Refresh fragment when other fragment changes the first's fragment model 第一次显示时仅加载一次标签(片段),并将其保留以备后用 - Load tab (Fragment) only once when it is shown for the first time and retain it for later use
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM