我有一个Activity来处理我的电影数据片段的详细信息片段&我想实现收藏功能。但问题是我一次只能收藏一部电影。此外,每次我尝试添加/收藏电影时,它都会在数据库中显示它已被保存但从未在不受欢迎上删除。
这是我的代码:
MovieDetailsActivity
public class MovieDetailsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Set the content of the activity to use the activity_tv_show_details.xml layout file */
setContentView(R.layout.activity_movie_details);
Bundle movieDetails = new Bundle();
/**get the movie's Object from the parent activity**/
Movie movie = getIntent().getParcelableExtra("movie");
movieDetails.putParcelable("movie", movie);
Intent intent = getIntent();
Uri mCurrentMovieUri = intent.getData();
movieDetails.putString("currentMovieUri", mCurrentMovieUri.toString());
/* Check for pre-existing instances of fragments(here explicitly check for savedInstance)
and then begin fragment transaction accordingly */
if (savedInstanceState == null) {
MovieDetailsFragment defaultMovieFragment = new MovieDetailsFragment();
defaultMovieFragment.setArguments(movieDetails);
getSupportFragmentManager().beginTransaction()
.add(R.id.containerMovieDetailActivity, defaultMovieFragment)
.commit();
}
}
}
MovieDetailsFragment
public class MovieDetailsFragment extends Fragment implements LoaderManager.LoaderCallbacks<MovieDetailsBundle> {
private static final int MOVIE_DETAIL_LOADER_ID = 2;
/* Arrays for holding movie details */
Movie movie;
public MovieDetailsFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_movie_detail, container, false);
Bundle bundle = getArguments();
position = bundle.getInt("position");
currentMovieUri = Uri.parse(bundle.getString("currentMovieUri"));
favoriteButton = (ImageButton) rootView.findViewById(R.id.favorite);
if (savedInstanceState == null) {
mReview = new ArrayList<>();
mVideo = new ArrayList<>();
mCredits = new ArrayList<>();
mMovieDetailsBundle = new MovieDetailsBundle();
}
if ((bundle != null)) {
movie = getArguments().getParcelable("movie");
movieDetailTitleTextView.setText(movie.getMovieTitle());
...
String[] projection = {
MoviesEntry._ID,
MoviesEntry.COLUMN_MOVIE_TITLE,
MoviesEntry.COLUMN_MOVIE_RELEASE_DATE,
MoviesEntry.COLUMN_MOVIE_OVERVIEW,
MoviesEntry.COLUMN_MOVIE_POSTER_URL,
MoviesEntry.COLUMN_MOVIE_BACKDROP_URL,
MoviesEntry.COLUMN_MOVIE_RATING};
// Perform a query on the provider using the ContentResolver.
// Use the {@link MoviesEntry#CONTENT_URI} to access the pet data.
Cursor cursor = getActivity().getContentResolver().query(
MoviesEntry.CONTENT_URI, // The content URI of the movies table
projection, // The columns to return for each row
null, // Selection criteria
null, // Selection criteria
null);
try {
// Figure out the index of each column
int idColumnIndex = cursor.getColumnIndex(MoviesEntry._ID);
int titleColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_TITLE);
int releaseDateColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_RELEASE_DATE);
int overviewColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_OVERVIEW);
int posterUrlColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_POSTER_URL);
int backdropUrlColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_BACKDROP_URL);
int ratingColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_RATING);
// Iterate through all the returned rows in the cursor
if (cursor.moveToFirst()){
// Use that index to extract the String or Int value of the word
// at the current row the cursor is on.
currentID = cursor.getInt(idColumnIndex);
currentTitle = cursor.getString(titleColumnIndex);
currentReleaseDate = cursor.getString(releaseDateColumnIndex);
currentOverview = cursor.getString(overviewColumnIndex);
currentposterUrl = cursor.getString(posterUrlColumnIndex);
currentBackdropUrl = cursor.getString(backdropUrlColumnIndex);
currentRatings = cursor.getFloat(ratingColumnIndex);
}
} finally {
// Always close the cursor when you're done reading from it. This releases all its
// resources and makes it invalid.
cursor.close();
}
if (currentTitle!=null) {
//currentTitle = movie.getMovieTitle();
if (currentTitle.equals(movie.getMovieTitle())) {
favoriteButton.setImageResource(R.drawable.starred);
favorite = true;
}
}else{
favoriteButton.setImageResource(R.drawable.unstarred);
favorite = false;
}
/*setting the ratingbar from @link: https://github.com/FlyingPumba/SimpleRatingBar*/
SimpleRatingBar simpleRatingBar = (SimpleRatingBar) rootView.findViewById(R.id.movieRatingInsideMovieDetailsFragment);
simpleRatingBar.setRating((float) (movie.getMovieVoteAverage()) / 2);
favoriteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (favorite) {
// Only perform the delete if this is an existing movie.
if (currentMovieUri != null) {
// Call the ContentResolver to delete the movie at the given content URI.
// Pass in null for the selection and selection args because the mCurrentPetUri
// content URI already identifies the movie that we want.
int rowsDeleted = getActivity().getContentResolver().delete(currentMovieUri, null, null);
// Show a toast message depending on whether or not the delete was successful.
if (rowsDeleted == 0) {
// If no rows were deleted, then there was an error with the delete.
Toast.makeText(getContext(), getString(R.string.delete_movie_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the delete was successful and we can display a toast.
Toast.makeText(getContext(), getString(R.string.delete_movie_successful),
Toast.LENGTH_SHORT).show();
}
}
favoriteButton.setImageResource(R.drawable.unstarred);
} else {
// Create a ContentValues object where column names are the keys,
// and movie attributes from the editor are the values.
ContentValues values = new ContentValues();
values.put(MoviesEntry.COLUMN_MOVIE_TITLE, movie.getMovieTitle());
values.put(MoviesEntry.COLUMN_MOVIE_RELEASE_DATE, movie.getMovieReleaseDate());
values.put(MoviesEntry.COLUMN_MOVIE_OVERVIEW, movie.getMovieOverview());
values.put(MoviesEntry.COLUMN_MOVIE_POSTER_URL, movie.getMoviePosterPath());
values.put(MoviesEntry.COLUMN_MOVIE_BACKDROP_URL, movie.getMovieBackdropPath());
values.put(MoviesEntry.COLUMN_MOVIE_RATING, movie.getMovieVoteAverage() / 2);
// Insert a new movie into the provider, returning the content URI for the new movie.
Uri newUri = getActivity().getContentResolver().insert(MoviesEntry.CONTENT_URI, values);
// Show a toast message depending on whether or not the insertion was successful
if (newUri == null) {
// If the new content URI is null, then there was an error with insertion.
Toast.makeText(getContext(), getString(R.string.insert_movie_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the insertion was successful and we can display a toast.
Toast.makeText(getContext(), getString(R.string.insert_movie_successful),
Toast.LENGTH_SHORT).show();
}
favoriteButton.setImageResource(R.drawable.starred);
}
favorite = !favorite;
}
});
...
@Override
public Loader<MovieDetailsBundle> onCreateLoader(int id, Bundle args) {
Uri baseUri = Uri.parse((UrlsAndConstants.MovieDetailQuery.DEFAULT_URL) + movie.getMovieId());
Uri.Builder uriBuilder = baseUri.buildUpon();
uriBuilder.appendQueryParameter(API_KEY_PARAM, API_KEY_PARAM_VALUE);
uriBuilder.appendQueryParameter(APPEND_TO_RESPONSE, VIDEOS_AND_REVIEWS_AND_CREDITS);
return new DetailsMovieLoader(getActivity().getApplicationContext(), uriBuilder.toString());
}
@Override
public void onLoadFinished(Loader<MovieDetailsBundle> loader, )
...
}
public void updateDurationTextView(MovieDetailsBundle movieDetailsBundle) {
...
movieRunTimeDuration.setText(mMovieDurationString);
}
@Override
public void onLoaderReset(Loader<MovieDetailsBundle> loader) {
}
}
的ContentProvider
public class MovieProvider extends ContentProvider {
/**
* URI matcher code for the content URI for the movies table
*/
private static final int MOVIES = 100;
/**
* URI matcher code for the content URI for a single movie in the movie table
*/
private static final int MOVIE_ID = 200;
/**
* UriMatcher object to match a content URI to a corresponding code.
* The input passed into the constructor represents the code to return for the root URI.
* It's common to use NO_MATCH as the input for this case.
*/
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Static initializer. This is run the first time anything is called from this class.
static {
// The calls to addURI() go here, for all of the content URI patterns that the provider
// should recognize. All paths added to the UriMatcher have a corresponding code to return
// when a match is found.
// The content URI of the form "content://com.example.android.movie/movie" will map to the
// integer code {@link #MOVIES}. This URI is used to provide access to MULTIPLE rows
// of the movie table.
sUriMatcher.addURI(MovieContract.CONTENT_AUTHORITY, MovieContract.PATH_MOVIES, MOVIES);
// The content URI of the form "content://com.example.android.movie/movie/#" will map to the
// integer code {@link #MOVIES}. This URI is used to provide access to ONE single row
// of the movie table.
//
// In this case, the "#" wildcard is used where "#" can be substituted for an integer.
// For example, "content://com.example.android.movie/movie/3" matches, but
// "content://com.example.android.movie/movie" (without a number at the end) doesn't match.
sUriMatcher.addURI(MovieContract.CONTENT_AUTHORITY, MovieContract.PATH_MOVIES + "/#", MOVIE_ID);
}
/**
* Database helper object
*/
private MovieDbHelper mDbHelper;
@Override
public boolean onCreate() {
mDbHelper = new MovieDbHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Get readable database
SQLiteDatabase database = mDbHelper.getReadableDatabase();
// This cursor will hold the result of the query
Cursor cursor;
// Figure out if the URI matcher can match the URI to a specific code
int match = sUriMatcher.match(uri);
switch (match) {
case MOVIES:
// For the MOVIES code, query the movie table directly with the given
// projection, selection, selection arguments, and sort order. The cursor
// could contain multiple rows of the movie table.
cursor = database.query(MoviesEntry.TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder);
break;
case MOVIE_ID:
// For the MOVIE_ID code, extract out the ID from the URI.
// For an example URI such as "content://com.example.android.movie/movie/3",
// the selection will be "_id=?" and the selection argument will be a
// String array containing the actual ID of 3 in this case.
//
// For every "?" in the selection, we need to have an element in the selection
// arguments that will fill in the "?". Since we have 1 question mark in the
// selection, we have 1 String in the selection arguments' String array.
selection = MoviesEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
// This will perform a query on the movie table where the _id equals 3 to return a
// Cursor containing that row of the table.
cursor = database.query(MoviesEntry.TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Cannot query unknown URI " + uri);
}
// Set notification URI on the Cursor,
// so we know what content URI the Cursor was created for.
// If the data at this URI changes, then we know we need to update the Cursor.
cursor.setNotificationUri(getContext().getContentResolver(), uri);
// Return the cursor
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
final int match = sUriMatcher.match(uri);
switch (match) {
case MOVIES:
return insertMovie(uri, contentValues);
default:
throw new IllegalArgumentException("Insertion is not supported for " + uri);
}
}
/**
* Insert a movie into the database with the given content values. Return the new content URI
* for that specific row in the database.
*/
private Uri insertMovie(Uri uri, ContentValues values) {
// Log.v("my_tag", "Received Uri to be matched insert is :"+uri.toString());
Log.i("my_tag", "Received Uri to be matched insert is :"+uri.toString());
// Check that the product name is not null
String title = values.getAsString(MoviesEntry.COLUMN_MOVIE_TITLE);
if (title == null) {
throw new IllegalArgumentException("Product requires a name");
}
// Check that the product name is not null
String releaseDate = values.getAsString(MoviesEntry.COLUMN_MOVIE_RELEASE_DATE);
if (releaseDate == null) {
throw new IllegalArgumentException("Product requires a detail");
}
// If the price is provided, check that it's greater than or equal to 0
String overview = values.getAsString(MoviesEntry.COLUMN_MOVIE_OVERVIEW);
if (overview == null) {
throw new IllegalArgumentException("Product requires valid price");
}
String posterUrl = values.getAsString(MoviesEntry.COLUMN_MOVIE_POSTER_URL);
if (posterUrl == null) {
throw new IllegalArgumentException("Product requires valid quantity");
}
String backdropUrl = values.getAsString(MoviesEntry.COLUMN_MOVIE_BACKDROP_URL);
if (backdropUrl == null) {
throw new IllegalArgumentException("Product requires valid quantity");
}
Integer ratings = values.getAsInteger(MoviesEntry.COLUMN_MOVIE_RATING);
if (ratings != null && ratings < 0) {
throw new IllegalArgumentException("Product requires valid quantity");
}
// Get writeable database
SQLiteDatabase database = mDbHelper.getWritableDatabase();
// Insert the new product with the given values
long id = database.insert(MoviesEntry.TABLE_NAME, null, values);
Log.i("my_tag", "values is :"+values.toString());
// Log.e("my_tag", "values is :"+values.toString());
// Log.v("my_tag", "values is :"+values.toString());
if (id == -1) {
return null;
}
// Notify all listeners that the data has changed for the product content URI
getContext().getContentResolver().notifyChange(uri, null);
// Return the new URI with the ID (of the newly inserted row) appended at the end
return ContentUris.withAppendedId(uri, id);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Get writeable database
SQLiteDatabase database = mDbHelper.getWritableDatabase();
// Track the number of rows that were deleted
int rowsDeleted;
final int match = sUriMatcher.match(uri);
case MOVIES:
// Delete all rows that match the selection and selection args
rowsDeleted = database.delete(MoviesEntry.TABLE_NAME, selection, selectionArgs);
break;
case MOVIE_ID:
// Delete a single row given by the ID in the URI
selection = MoviesEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
Log.i("my_tag", "selection is :"+selection);
// Log.e("my_tag", "selection is :"+selection);
// Log.v("my_tag", "selection is :"+selection);
Log.i("my_tag", "selectionArgs is :"+selectionArgs[0]);
// Log.e("my_tag", "selectionArgs is :"+selectionArgs[0]);
// Log.v("my_tag", "selectionArgs is :"+selectionArgs[0]);
rowsDeleted = database.delete(MoviesEntry.TABLE_NAME, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Deletion is not supported for " + uri);
}
// If 1 or more rows were deleted, then notify all listeners that the data at the
// given URI has changed
if (rowsDeleted != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
// Return the number of rows deleted
return rowsDeleted;
}
@Override
public int update(Uri uri, ContentValues contentValues, String selection,
String[] selectionArgs) {
...
}
@Override
public String getType(Uri uri) {
final int match = sUriMatcher.match(uri);
switch (match) {
case MOVIES:
return MoviesEntry.CONTENT_LIST_TYPE;
case MOVIE_ID:
return MoviesEntry.CONTENT_ITEM_TYPE;
default:
throw new IllegalStateException("Unknown URI " + uri + " with match " + match);
}
}
}