Android:片段和光标加载器中的ListView没有接收数据?

时间:2017-01-17 18:42:29

标签: android android-fragments android-asynctask loader simplecursoradapter

我对Android很新,在这个阶段我的代码正在编译,数据正在加载到数据库中(据我可以看到调试器),但不会显示在ListView中。我已经搜索了很长一段时间,但我的代码中的所有内容似乎都没问题。所以我的问题是:我可能错过什么?先感谢您! 下面是我的片段代码。它应该获取AsyncTask提取的数据。 AsyncTask从服务器获取(天气)数据并将其插入数据库。根据我的理解,当插入数据时,应该通知用户界面有关更改,但在那里(或其他地方)出现问题。

public class ForecastFragment extends Fragment implements LoaderManager.LoaderCallbacks <Cursor>  {

private String mLocation;
private static final int FORECAST_LOCADER = 0;

public static SimpleCursorAdapter simpleCursorAdapter;

// column names for the projection
private static final String[] FORECAST_COLUMNS = {
        WeatherContract.WeatherEntry.TABLE_NAME + "." + WeatherContract.WeatherEntry._ID,
        WeatherContract.WeatherEntry.COLUMN_DATE_TEXT,
        WeatherContract.WeatherEntry.COLUMN_SHORT_DESC,
        WeatherContract.WeatherEntry.COLUMN_MAX_TEMP,
        WeatherContract.WeatherEntry.COLUMN_MIN_TEMP,
        WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING
};

private OnFragmentInteractionListener mListener;

public ForecastFragment() {}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_forecast, container, false);
    return view;
}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnFragmentInteractionListener) {
        mListener = (OnFragmentInteractionListener) context;
    } else {
        throw new RuntimeException(context.toString()
                + " must implement OnFragmentInteractionListener");
    }
}

@Override
public void onDetach() {
    super.onDetach();
    mListener = null;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    simpleCursorAdapter = new SimpleCursorAdapter(getActivity(),
            R.layout.list_item_forecast,
            null,
            new String[]{
                    WeatherContract.WeatherEntry.COLUMN_DATE_TEXT,
                    WeatherContract.WeatherEntry.COLUMN_SHORT_DESC,
                    WeatherContract.WeatherEntry.COLUMN_MAX_TEMP,
                    WeatherContract.WeatherEntry.COLUMN_MIN_TEMP
            },
            new int[] {
                    R.id.list_item_date_textview,
                    R.id.list_item_forecast_textview,
                    R.id.list_item_high_textview,
                    R.id.list_item_low_textview
            },
            0
    );

    ListView lv = (ListView) getView().findViewById(R.id.listview_forecast);
    lv.setAdapter(simpleCursorAdapter);

    getLoaderManager().initLoader(FORECAST_LOCADER, null, this);
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    String startDate = WeatherContract.getDbDateString(new Date());
    String sortOrder = WeatherContract.WeatherEntry.COLUMN_DATE_TEXT + " ASC";
    mLocation = Utility.getPreferredLocation(getActivity());
    Uri weatherForLocationUri = WeatherContract.WeatherEntry.buildWeatherLocationWithStartDate(mLocation, startDate);
    return new CursorLoader(
            getActivity(),
            weatherForLocationUri,
            FORECAST_COLUMNS,
            null,
            null,
            sortOrder
    );
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    simpleCursorAdapter.swapCursor(data);
    Log.i("onLoadFinished", "" + data.getCount());
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    simpleCursorAdapter.swapCursor(null);
}

public interface OnFragmentInteractionListener {
    // TODO: Update argument type and name
    void onFragmentInteraction(Uri uri);
}
}

在上面的代码中,日志消息“onLoadFinished”显示0,我不确定这是否是预期值,因为我安装的断点显示此过程在获取数据之前运行。对于ListView,当我使用带有虚拟数据的ArrayAdapter时,显示了数据,但是使用simpleCursorAdapter它到目前为止还不起作用。

在MainActivity中调用AsyncTask:

public class MainActivity extends AppCompatActivity  implements ForecastFragment.OnFragmentInteractionListener {
//... 
@Override
public void onFragmentInteraction(Uri uri) {    }
@Override
public void onStart() {
    super.onStart();
    updateWeather();
}

public void updateWeather() {
    FetchWeatherTask fetchWeatherTask = new FetchWeatherTask(getApplicationContext());
    String userLocationPreference = Utility.getPreferredLocation(getApplicationContext());
    String userUnitsPreference = Utility.getPreferredUnits(getApplicationContext());
    fetchWeatherTask.execute(userLocationPreference, userUnitsPreference);
} 

以下是AsyncTask的代码:

class FetchWeatherTask extends AsyncTask<String, Void, Void> {
     private Context mContext;
     public FetchWeatherTask(Context context) {
         mContext = context;
     }
@Override
protected Void doInBackground(String... params) {
    ...// fetching data
    cVVector.add(weatherValues);                
    if (cVVector.size() > 0) {
        int nRecordsInserted = addWeatherData(cVVector);
    }
...//
}
//function inserting data into db
private int addWeatherData(Vector <ContentValues> cvValues) {
    ContentValues[] values = new ContentValues [cvValues.size()];
    for (int i = 0; i < cvValues.size(); i++) {
        values[i] = cvValues.get(i);
        Log.d("addWeatherData", values[i].toString());
    }
    Uri uri = WeatherContract.WeatherEntry.CONTENT_URI;
    int nRecords = -1;
    nRecords = mContext.getContentResolver().bulkInsert(uri, values);
    Log.i("insert records", "" + nRecords + " records inserted");
    return nRecords;
}

在上面的代码中,日志消息打印数据并说出插入了14条记录(如预期的那样)。 db:中的bulkInsert代码:

@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
    final SQLiteDatabase db = dbHelper.getWritableDatabase();
    final int match = sUriMatcher.match(uri);
    int returnCount = 0;
    switch (match) {
        case WEATHER: {
            db.beginTransaction();
            try {
                for (ContentValues value: values) {
                    long _id = db.insert(WeatherContract.WeatherEntry.TABLE_NAME, null, value);
                    if (_id != -1) {
                        returnCount++;
                    }
                }
                db.setTransactionSuccessful();
            }
            catch (Exception err) {
                Log.e("bulkInsert", err.getMessage());
            }
            finally {
                db.endTransaction();
                getContext().getContentResolver().notifyChange(uri, null);
            }
            break;
        }
        default: {
            returnCount = super.bulkInsert(uri, values);
            break;
        }
    }
    Log.i("bulkInsert", "" + returnCount + " records inserted");
    return returnCount;
}

据我所知(我已经使用调试器检查过),事务成功完成,应该通知UI更改 getContext().getContentResolver().notifyChange(uri, null);但在应用程序中我看到空白屏幕并且没有显示任何记录。所以我不知道该怎么做,请帮忙!提前谢谢!

UPD:,如评论中所述,我还要添加WeatherContract类的完整代码:

public class WeatherContract {

public static final String CONTENT_AUTHORITY = "com.example.irina.sunshine.app";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);

public static final String PATH_WEATHER = "weather";
public static final String PATH_LOCATION = "location";

public static final String DATE_FORMAT = "yyyyMMdd";

public static class LocationEntry implements BaseColumns {
 // description of location table
 public static final Uri CONTENT_URI =
            BASE_CONTENT_URI.buildUpon().appendPath(PATH_LOCATION).build();
    public static final String CONTENT_TYPE =
            "vnd.android.cursor.dir/" + CONTENT_AUTHORITY + "/" + PATH_LOCATION;

    public static final String CONTENT_ITEM_TYPE =
            "vnd.android.cursor.item/" + CONTENT_AUTHORITY + "/" + PATH_LOCATION;
    public static final String TABLE_NAME = "location";
    public static final String COLUMN_LOCATION_SETTING = "setting";
// other fields
}

public static class WeatherEntry implements BaseColumns {
    public static final Uri CONTENT_URI =
            BASE_CONTENT_URI.buildUpon().appendPath(PATH_WEATHER).build();
    public static final String CONTENT_TYPE =
            "vnd.android.cursor.dir/" + CONTENT_AUTHORITY + "/" + PATH_WEATHER;
    public static final String CONTENT_ITEM_TYPE =
            "vnd.android.cursor.item/" + CONTENT_AUTHORITY + "/" + PATH_WEATHER;
    public static final String TABLE_NAME = "weather";
    //foreign key for location table
    public static final String COLUMN_LOC_KEY = "location_id";
    //date stored as text in format yyyy-MM-dd
    public static final String COLUMN_DATE_TEXT = "date";
    //weather id as returned by API to identify the icon
    public static final String COLUMN_WEATHER_ID = "weather_id";
    //weather data as returned by API
    //e.g. "clear" vs "sky is clear"
    public static final String COLUMN_SHORT_DESC = "short_desc";
    //min and max temp stored as floats
    public static final String COLUMN_MIN_TEMP = "min";
    public static final String COLUMN_MAX_TEMP = "max";
    //stored as float representing percentage
    public static final String COLUMN_HUMIDITY = "humidity";
    //stored as float representing percentage
    public static final String COLUMN_PRESSURE = "pressure";
    //stored as float representing mph
    public static final String COLUMN_WIND_SPEED = "wind";
    //meteorological degrees, e.g 0 is north, 180 is south, stored as floats
    public static final String COLUMN_DEGREES = "degrees";

    public static Uri buildWeatherUri(long id) {
        return ContentUris.withAppendedId(CONTENT_URI, id);
    }

    public static Uri buildWeatherLocation(String locationSetting) {
        return CONTENT_URI.buildUpon().appendPath(locationSetting).build();
    }

    // the function used to build Cursor Loader in Fragment
    public static Uri buildWeatherLocationWithStartDate(String locationSetting, String startDate) {
        return CONTENT_URI.buildUpon()
                .appendPath(locationSetting)
                 //+ just added to create another / symbol in path, but it still doesn't match the URI
                .appendEncodedPath("")
                //-
                .appendQueryParameter(COLUMN_DATE_TEXT, startDate)
                .build();
    }


    public static Uri buildWeatherLocationWithDate(String locationSetting, String date) {
        return CONTENT_URI.buildUpon()
                .appendPath(locationSetting)
                .appendPath(date)
                .build();
    }

    public static String getLocationSettingFromUri(Uri uri) {
        return uri.getPathSegments().get(1);
    }

    public static String getDateFromUri(Uri uri) {
        return uri.getPathSegments().get(2);
    }

    public static String getStartDateFromUri(Uri uri) {
        return uri.getQueryParameter(COLUMN_DATE_TEXT);
    }
}
}

因此,CursorLoader是在上面列出的函数buildWeatherLocationWithStartDate中使用URI构建创建的。

以下是自定义ContentProvider的代码(我将修改bulkInsert函数,因为我已将其列在问题的主要部分中):

public class WeatherProvider extends ContentProvider {

private static final int WEATHER = 100;
private static final int WEATHER_WITH_LOCATION = 101;
private static final int WEATHER_WITH_LOCATION_AND_DATE = 102;
private static final int LOCATION = 300;
private static final int LOCATION_ID = 301;

private static final UriMatcher sUriMatcher = buildUriMatcher();
private WeatherDbHelper dbHelper;

private static final SQLiteQueryBuilder queryBuilder;

static {
    queryBuilder = new SQLiteQueryBuilder();
    queryBuilder.setTables(
            WeatherContract.WeatherEntry.TABLE_NAME + " INNER JOIN "
            + WeatherContract.LocationEntry.TABLE_NAME
            + " ON " +  WeatherContract.WeatherEntry.TABLE_NAME
            + "." + WeatherContract.WeatherEntry.COLUMN_LOC_KEY
            + " = " + WeatherContract.LocationEntry.TABLE_NAME
            + "." + WeatherContract.LocationEntry._ID
    );
}

private static final String sLocationSettingSelection =
        WeatherContract.LocationEntry.TABLE_NAME
        + "." + WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING + " = ? ";

private static final String sLocationSettingWithStartDateSelection =
        WeatherContract.LocationEntry.TABLE_NAME
                + "." + WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING + " = ? AND "
        + WeatherContract.WeatherEntry.COLUMN_DATE_TEXT + " >= ? ";

private static final String sLocationSettingAndDateSelection =
        WeatherContract.LocationEntry.TABLE_NAME
                + "." + WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING + " = ? AND"
                + WeatherContract.WeatherEntry.COLUMN_DATE_TEXT + " = ? ";


private Cursor getWetatherByLocationSetting(Uri uri, String[] projection, String sortOrder) {
    String locationSetting = WeatherContract.WeatherEntry.getLocationSettingFromUri(uri);
    String startDate = WeatherContract.WeatherEntry.getStartDateFromUri(uri);
    String[] selectionArgs;
    String selection;

    if (startDate == null) {
        selection = sLocationSettingSelection;
        selectionArgs = new String[]{locationSetting};
    } else {
        selectionArgs = new String[]{locationSetting, startDate};
        selection = sLocationSettingWithStartDateSelection;
    }
    return queryBuilder.query(dbHelper.getReadableDatabase(),
            projection, selection, selectionArgs, null, null, sortOrder);
}

private Cursor getWetatherByLocationAndDateSetting(Uri uri, String[] projection, String sortOrder) {
    String locationSetting = WeatherContract.WeatherEntry.getLocationSettingFromUri(uri);
    String date = WeatherContract.WeatherEntry.getDateFromUri(uri);
    String[] selectionArgs;
    String selection;

    if (date == null) {
        selection = sLocationSettingSelection;
        selectionArgs = new String[]{locationSetting};
    } else {
        selection = sLocationSettingAndDateSelection;
        selectionArgs = new String[]{locationSetting, date};
    }
    return queryBuilder.query(dbHelper.getReadableDatabase(),
            projection, selection, selectionArgs, null, null, sortOrder);
}

private static UriMatcher buildUriMatcher() {
    final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    final String authority = WeatherContract.CONTENT_AUTHORITY;
    //just changed the order of URIs, but it still doesn't affect the result
    sUriMatcher.addURI(authority, WeatherContract.PATH_WEATHER + "/*/*", WEATHER_WITH_LOCATION_AND_DATE);
    sUriMatcher.addURI(authority, WeatherContract.PATH_WEATHER + "/*", WEATHER_WITH_LOCATION);
    sUriMatcher.addURI(authority, WeatherContract.PATH_WEATHER, WEATHER);

    sUriMatcher.addURI(authority, WeatherContract.PATH_LOCATION, LOCATION);
    sUriMatcher.addURI(authority, WeatherContract.PATH_LOCATION + "/#", LOCATION_ID);
    return sUriMatcher;
}

@Override
public boolean onCreate() {
    dbHelper = new WeatherDbHelper(getContext());
    return true;
}

@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    Cursor cursor =  null;
    switch (sUriMatcher.match(uri)) {
        case WEATHER_WITH_LOCATION_AND_DATE: {
            cursor = getWetatherByLocationAndDateSetting(uri, projection, sortOrder);
            break;
        }
        case WEATHER_WITH_LOCATION: {
            cursor = getWetatherByLocationSetting(uri, projection, sortOrder);
            break;
        }
        case WEATHER: {
            cursor = dbHelper.getReadableDatabase().query(
                    WeatherContract.WeatherEntry.TABLE_NAME,
                    projection,
                    selection,
                    selectionArgs,
                    null,
                    null,
                    sortOrder
            );
            break;
        }
        case LOCATION_ID: {
            cursor = dbHelper.getReadableDatabase().query(
                    WeatherContract.LocationEntry.TABLE_NAME,
                    projection,
                    WeatherContract.LocationEntry._ID  + " = '" + ContentUris.parseId(uri) + "'",
                    null,
                    null,
                    null,
                    sortOrder
            );
            break;
        }
        case LOCATION: {
            cursor = dbHelper.getReadableDatabase().query(
                    WeatherContract.LocationEntry.TABLE_NAME,
                    projection,
                    selection,
                    selectionArgs,
                    null,
                    null,
                    sortOrder
            );
            break;
        }
        default: {
            throw new UnsupportedOperationException(" Not supported uri! " + uri.toString());
        }
    }
    cursor.setNotificationUri(getContext().getContentResolver(), uri);
    return cursor;
}

@Nullable
@Override
public String getType(Uri uri) {
    final int match = sUriMatcher.match(uri);
    switch (match) {
        case WEATHER_WITH_LOCATION_AND_DATE: {
            return WeatherContract.WeatherEntry.CONTENT_ITEM_TYPE;
        }
        case WEATHER_WITH_LOCATION: {
            return WeatherContract.WeatherEntry.CONTENT_TYPE;
        }
        case WEATHER: {
            return WeatherContract.WeatherEntry.CONTENT_TYPE;
        }
        case LOCATION_ID: {
            return WeatherContract.LocationEntry.CONTENT_ITEM_TYPE;
        }
        case LOCATION: {
            return WeatherContract.LocationEntry.CONTENT_TYPE;
        }
        default: {
            throw new UnsupportedOperationException(" Not supported uri! " + uri.toString());
        }
    }
}

@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
    final SQLiteDatabase db = dbHelper.getWritableDatabase();
    Uri insertUri;
    final int matcher = sUriMatcher.match(uri);
    switch (matcher) {
        case WEATHER: {
            long _id = db.insert(WeatherContract.WeatherEntry.TABLE_NAME, null, values);
            if (_id > 0) {
                insertUri = WeatherContract.WeatherEntry.buildWeatherUri(_id);
            } else {
                throw new SQLiteException(" Couldn't insert row in db " + uri);
            }
            break;
        }
        case LOCATION: {
            long _id = db.insert(WeatherContract.LocationEntry.TABLE_NAME, null, values);
            if (_id > 0) {
                insertUri = WeatherContract.LocationEntry.buildLocationUri(_id);
            } else {
                throw new SQLiteException(" Couldn't insert row in db " + uri);
            }
            break;
        }
        default: {
            throw new UnsupportedOperationException(" Not supported uri! " + uri.toString());
        }
    }
    getContext().getContentResolver().notifyChange(uri, null);
    return insertUri;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    final SQLiteDatabase db = dbHelper.getWritableDatabase();
    int nRowsAffected = 0;
    final int matcher = sUriMatcher.match(uri);
    switch (matcher) {
        case WEATHER: {
            nRowsAffected = db.delete(WeatherContract.WeatherEntry.TABLE_NAME, selection, selectionArgs);
            if (nRowsAffected <= 0) {
                Log.d("Deleting rows ", " 0 rows affected by " + uri);
            }
            break;
        }
        case LOCATION: {
            nRowsAffected = db.delete(WeatherContract.LocationEntry.TABLE_NAME, selection, selectionArgs);
            if (nRowsAffected <= 0) {
                Log.d("Deleting rows ", " 0 rows affected by " + uri);
            }
            break;
        }
        default: {

            throw new UnsupportedOperationException(" Not supported uri! " + uri.toString());
        }
    }
    if ((nRowsAffected != 0) && (selection != null)){
        getContext().getContentResolver().notifyChange(uri, null);
    }
    return nRowsAffected;
}

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    final SQLiteDatabase db = dbHelper.getWritableDatabase();
    int nRowsAffected = 0;
    final int matcher = sUriMatcher.match(uri);
    switch (matcher) {
        case WEATHER: {
            nRowsAffected = db.update(WeatherContract.WeatherEntry.TABLE_NAME, values, selection, selectionArgs);
            if (nRowsAffected <= 0) {
                Log.d("Updating rows ", " 0 rows affected by " + uri);
            }
            break;
        }
        case LOCATION: {
            nRowsAffected = db.update(WeatherContract.LocationEntry.TABLE_NAME, values, selection, selectionArgs);
            if (nRowsAffected <= 0) {
                Log.d("Updating rows ", " 0 rows affected by " + uri);
            }
            break;
        }
        default: {
            throw new UnsupportedOperationException(" Not supported uri! " + uri.toString());
        }
    }
    if (nRowsAffected != 0) {
        getContext().getContentResolver().notifyChange(uri, null);
    }
    return nRowsAffected;
}

@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
    // listed in the main part of the question
}

我希望这能帮助你找出问题所在。谢谢!

UPD2:所以,感谢评论, 1)我已经了解了Uri中路径和查询参数之间的区别(查询参数不是路径的一部分,因此Uri Matcher忽略它们)。 2)当我查看我的db代码的内幕时,结果发现我的查询没有返回值,因为我将错误的数据记录为我的外键。好吧,现在一切正常。

0 个答案:

没有答案