mangkool mangkool - 3 years ago 136
Android Question

Why item position in recyclerview change when update database sqlite use content provider?

I am updating my sqlite database using cursorloader inside a recyclerview where i do it by checking checkbox, the value updated but somehow the view became a mess. some row also checked but the value still 0 which should be not checked. This also happen when I insert a new row.

here is my MainActivity where i Updated the database

public class MainActivity extends AppCompatActivity implements
TaskAdapter.OnItemClickListener, LoaderManager.LoaderCallbacks<Cursor>{

private TaskAdapter mAdapter;
private FloatingActionButton floatingActionButton;

private ArrayList<Task> task = new ArrayList<>();

private static final int TASKS_LOADER_ID = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

mAdapter = new TaskAdapter(this);
mAdapter.setOnItemClickListener(this);

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(mAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

floatingActionButton = (FloatingActionButton) findViewById(R.id.fab);


/* Click events in floating action button */
floatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(), AddTaskActivity.class);
startActivity(intent);
}
});

//initialize the loader
getSupportLoaderManager().initLoader(TASKS_LOADER_ID, null, this);
}

@Override
protected void onResume() {
super.onResume();
Log.v("dipanggil", "dipanggil jka");
// re-queries for all tasks
getSupportLoaderManager().restartLoader(TASKS_LOADER_ID, null, this);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}

/* Click events in RecyclerView items */
@Override
public void onItemClick(View v, int position) {
//TODO: Handle list item click event
Intent intent = new Intent(this, TaskDetailActivity.class);
/*b.putLong("ID", task.get(position).getId());
b.putString("DESCRIPTION", task.get(position).getDescription());
b.putInt("PRIORITY", task.get(position).getPriority());
b.putInt("COMPLETE", task.get(position).getComplete());
b.putLong("DUEDATE", task.get(position).getDueDateMillis());*/
intent.putExtra("ID", String.valueOf(task.get(position).getId()));

startActivity(intent);
Log.v("testti", ""+v.getTag()+" apakah sama "+task.get(position).getId());
}

/* Click events on RecyclerView item checkboxes */
@Override
public void onItemToggled(boolean active, int position) {
//TODO: Handle task item checkbox event
ContentValues values = new ContentValues();

String stringId = String.valueOf(task.get(position).getId());
Log.v("test = ", ""+task.get(position).getId());

Uri uri = DatabaseContract.CONTENT_URI;
uri = uri.buildUpon().appendPath(stringId).build();

if(active) {
Log.v("test","tersentuh");
//values.put(DatabaseContract.TaskColumns.IS_COMPLETE, 1);
values.put(DatabaseContract.TaskColumns._ID, task.get(position).getId());
values.put(DatabaseContract.TaskColumns.DESCRIPTION, task.get(position).getDescription());
values.put(DatabaseContract.TaskColumns.IS_PRIORITY, task.get(position).getPriority());
values.put(DatabaseContract.TaskColumns.IS_COMPLETE, 1);
values.put(DatabaseContract.TaskColumns.DUE_DATE, task.get(position).getDueDateMillis());
getContentResolver().update(uri,
values,
null,
null);
}else{
Log.v("test","tidak tersentuh");
values.put(DatabaseContract.TaskColumns._ID, task.get(position).getId());
values.put(DatabaseContract.TaskColumns.DESCRIPTION, task.get(position).getDescription());
values.put(DatabaseContract.TaskColumns.IS_PRIORITY, task.get(position).getPriority());
values.put(DatabaseContract.TaskColumns.IS_COMPLETE, 0);
values.put(DatabaseContract.TaskColumns.DUE_DATE, task.get(position).getDueDateMillis());
getContentResolver().update(uri,
values,
null,
null);
}
// re-queries for all tasks
//mAdapter.notifyDataSetChanged();
getSupportLoaderManager().restartLoader(TASKS_LOADER_ID, null, this);
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

return new AsyncTaskLoader<Cursor>(this) {

// Initialize a Cursor, this will hold all the task data
Cursor mTaskData = null;

// onStartLoading() is called when a loader first starts loading data
@Override
protected void onStartLoading() {
if (mTaskData != null) {
// Delivers any previously loaded data immediately
deliverResult(mTaskData);
} else {
// Force a new load
forceLoad();
}
}

@Override
public Cursor loadInBackground() {
try {
return getContentResolver().query(DatabaseContract.CONTENT_URI,
null,
null,
null,
DatabaseContract.DEFAULT_SORT);

} catch (Exception e) {
e.printStackTrace();
return null;
}
}

public void deliverResult(Cursor data) {
mTaskData = data;
super.deliverResult(data);
}
};

}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {

while(data.moveToNext()){
long id = data.getLong(data.getColumnIndex(DatabaseContract.TaskColumns._ID));
String description = data.getString(data.getColumnIndex(DatabaseContract.TaskColumns.DESCRIPTION));
long date = data.getLong(data.getColumnIndex(DatabaseContract.TaskColumns.DUE_DATE));
int priority = data.getInt(data.getColumnIndex(DatabaseContract.TaskColumns.IS_PRIORITY));
int complete = data.getInt(data.getColumnIndex(DatabaseContract.TaskColumns.IS_COMPLETE));

try{
Task tasks = new Task(data);
task.add(tasks);
}catch (Exception e){
e.printStackTrace();
}
}

mAdapter.swapCursor(data);
}

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


}

this is the recyclerview adapter

public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskHolder> {

/* Callback for list item click events */
public interface OnItemClickListener {
void onItemClick(View v, int position);

void onItemToggled(boolean active, int position);
}

/* ViewHolder for each task item */
public class TaskHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TaskTitleView nameView;
public TextView dateView;
public ImageView priorityView;
public CheckBox checkBox;

public TaskHolder(View itemView) {
super(itemView);

nameView = (TaskTitleView) itemView.findViewById(R.id.text_description);
dateView = (TextView) itemView.findViewById(R.id.text_date);
priorityView = (ImageView) itemView.findViewById(R.id.priority);
checkBox = (CheckBox) itemView.findViewById(R.id.checkbox);

itemView.setOnClickListener(this);
checkBox.setOnClickListener(this);
}

@Override
public void onClick(View v) {
if (v == checkBox) {
completionToggled(this);
} else {
postItemClick(this);
}
}
}

private Cursor mCursor;
private OnItemClickListener mOnItemClickListener;
private Context mContext;

public TaskAdapter(Context mContext) {
this.mContext = mContext;
}

public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}

private void completionToggled(TaskHolder holder) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemToggled(holder.checkBox.isChecked(), holder.getAdapterPosition());
}
}

private void postItemClick(TaskHolder holder) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(holder.itemView, holder.getAdapterPosition());
}
}

@Override
public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mContext = parent.getContext();
View itemView = LayoutInflater.from(mContext)
.inflate(R.layout.list_item_task, parent, false);

return new TaskHolder(itemView);
}

@Override
public void onBindViewHolder(TaskHolder holder, int position) {

//TODO: Bind the task data to the views
// Indices for the _id, description, and priority columns
int idIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns._ID);
int descriptionIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns.DESCRIPTION);
int isCompleteIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns.IS_COMPLETE);
int isPriorityIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns.IS_PRIORITY);
int dueDateIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns.DUE_DATE);

//move cursor to wanted data
mCursor.moveToPosition(position);

// Determine the values of the wanted data
final int id = mCursor.getInt(idIndex);
String description = mCursor.getString(descriptionIndex);
int isComplete = mCursor.getInt(isCompleteIndex);
int isPrior = mCursor.getInt(isPriorityIndex);
long dueDate = mCursor.getLong(dueDateIndex);
Log.v("adapter", " "+DateUtils.getRelativeTimeSpanString(mContext, dueDate)+" "+dueDate);
CharSequence date = DateUtils.getRelativeTimeSpanString(mContext, dueDate);

holder.itemView.setTag(id);

//determine whether to show date or not
if(dueDate != Long.MAX_VALUE){
holder.dateView.setVisibility(View.VISIBLE);
holder.dateView.setText(date);
}else{
holder.dateView.setVisibility(View.GONE);
}
//determine the priority icon
if(isPrior == 0){
holder.priorityView.setImageResource(R.drawable.ic_not_priority);
}else{
holder.priorityView.setImageResource(R.drawable.ic_priority);
}
//determine the text color and description
holder.nameView.setText(description);
holder.nameView.setState(isComplete);

if(isComplete == 1){
holder.checkBox.setChecked(true);
holder.nameView.setState(isComplete);
}

//to chek if due date has passed or not
Calendar now = Calendar.getInstance();
Calendar tasksDate = Calendar.getInstance();
tasksDate.setTimeInMillis(dueDate);
int result = now.compareTo(tasksDate);

if(result >= 0){
holder.nameView.setState(2);
}
else if(result < 0){
holder.nameView.setState(0);
}


}

@Override
public int getItemCount() {
return (mCursor != null) ? mCursor.getCount() : 0;
}

/**
* Retrieve a {@link Task} for the data at the given position.
*
* @param position Adapter item position.
*
* @return A new {@link Task} filled with the position's attributes.
*/
public Task getItem(int position) {
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("Invalid item position requested");
}

return new Task(mCursor);
}

@Override
public long getItemId(int position) {
return getItem(position).id;
}

public Cursor swapCursor(Cursor cursor) {
if (mCursor == cursor) {
return null; // bc nothing has changed
}
Cursor temp = mCursor;
this.mCursor = cursor; // new cursor value assigned

//check if this is a valid cursor, then update the cursor
if (cursor != null) {
this.notifyDataSetChanged();
}
return temp;
}


}

how to solve this ? thanks!

Answer Source

In the following part of your code you haven't provided the else condition.

if (isComplete == 1){
    holder.checkBox.setChecked(true);
    holder.nameView.setState(isComplete);
}

You should handle else condition to uncheck the check box because RecyclerView uses old views.

holder.checkBox.setChecked(isComplete == 1);
holder.nameView.setState(isComplete);

Also, you should clear old items before repopulating the list again.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download