Recyclerview не обновляется

Я работаю над проектом, в котором пользователь добавляет данные во время выполнения. Проблема заключается в том, что всякий раз, когда пользователь вставляет первый элемент, он добавляется в базу данных и отображается в recyclerview. Но когда пользователь добавляет больше данных, recyclerview продолжает отображать первый элемент снова и снова. Если пользователь добавляет Apple, он добавляется в базу данных, а recyclerview отображает Apple. Теперь, если пользователь добавляет orange, он добавляется в базу данных, но recyclerview отображает Apple два раза. Я использую Cursorloader для загрузки данных, Contentprovider для добавления данных и CustomCursoradapter(https://gist.github.com/skyfishjy/443b7448f59be978bc59), чтобы установить данные для повторного просмотра

Метод addDetails() выполняется, когда пользователь добавляет данные

public class AddLog extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{

    TextView TotalAmount,Date;
    EditText name,mobile,city,detailname,detailamount;
    RecyclerView adddetailtolist;
    DetailsAdapter adapter;
    Intent returnback;
    double totamount;
    long logid;
    String Debt = "Debt";
    String Paid = "Paid";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_log);
        Toolbar toolbar = (Toolbar)findViewById(R.id.addlogtoolbar);
        setSupportActionBar(toolbar);
        toolbar.setTitle("Add Log");
        returnback = new Intent(this,MainActivity.class);
        Intent intent = getIntent();
        logid = intent.getExtras().getLong("ID");
        TotalAmount = (TextView)findViewById(R.id.totalAmount);
        Date = (TextView)findViewById(R.id.addlogDate);
        name = (EditText)findViewById(R.id.AddName);
        mobile = (EditText)findViewById(R.id.AddPhone);
        city = (EditText)findViewById(R.id.Addcity);
        detailname = (EditText)findViewById(R.id.Detailname);
        detailamount = (EditText)findViewById(R.id.Detailamount);
        adddetailtolist = (RecyclerView)findViewById(R.id.addloglist);
        Date.setText(getDate());
        getSupportLoaderManager().initLoader(1,null,this);
        adapter = new DetailsAdapter(this,null);
        adddetailtolist.setAdapter(adapter);
        adddetailtolist.setLayoutManager(new LinearLayoutManager(this));

    }
    private String getDate()
    {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE,dd-MMM-yyyy");
        String date = simpleDateFormat.format(calendar.getTime());
        return date;
    }
    public void addDetails(View view)
    {
        String Name = detailname.getText().toString();
        String Amount = detailamount.getText().toString();
        String date = Date.getText().toString();
        ContentValues contentValues = new ContentValues();
        contentValues.put(DataProvider.Dname,Name);
        contentValues.put(DataProvider.DCategory,Debt);
        contentValues.put(DataProvider.Damount,Amount);
        contentValues.put(DataProvider.Ddate,date);
        contentValues.put(DataProvider.Per_In,logid);
        Uri uri = getContentResolver().insert(DataProvider.ContentUri_Details,contentValues);
        Toast.makeText(AddLog.this, uri.toString()+"Value Inserted", Toast.LENGTH_SHORT).show();
        NumberFormat currency = changeamount();
        TotalAmount.setText(currency.format(getAmount()));
    }

    private double getAmount()
    {
        ContentProviderClient client = getContentResolver().acquireContentProviderClient(DataProvider.ContentUri_Details);
        SQLiteDatabase db = ((DataProvider)client.getLocalContentProvider()).sqLiteDatabase;
        String query = "SELECT SUM(DAmount) FROM Details WHERE Per_In = "+logid;
        Cursor cursor = db.rawQuery(query, null);
        cursor.moveToFirst();
        double amount = cursor.getDouble(0);
        cursor.close();
        client.release();
        return amount;
    }


    public NumberFormat changeamount()
    {
        Locale locale = new Locale("en","IN");
        NumberFormat currencyformat = NumberFormat.getCurrencyInstance(locale);
        return currencyformat;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater menuinflate = getMenuInflater();
        menuinflate.inflate(R.menu.addlogmenu,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        switch (item.getItemId())
        {
            case R.id.done:
                saveData();
        }
        return super.onOptionsItemSelected(item);
    }

    private void saveData()
    {
        String name1 = name.getText().toString();
        String phone  = mobile.getText().toString();
        String address = city.getText().toString();
        if (TextUtils.isEmpty(name1)||TextUtils.isEmpty(phone)||TextUtils.isEmpty(address))
        {
            if (TextUtils.isEmpty(name1))
            {
                name.setError("Feild can not be Empty");
            }
           else if (TextUtils.isEmpty(phone))
            {
                mobile.setError("Feild can not be Empty");
            }
           else if (TextUtils.isEmpty(address))
            {
                city.setError("Feild can not be Empty");
            }
        }
        else
        {
            ContentValues values = new ContentValues();
            values.put(DataProvider.Pname,name1);
            values.put(DataProvider.Pphone,phone);
            values.put(DataProvider.Paddress,address);
            values.put(DataProvider.PCategory,"Debt");
            values.put(DataProvider.Pamount,totamount);
            String where = DataProvider.PID+"=?";
            String[] whereargs = {String.valueOf(logid)};
           int a = getContentResolver().update(DataProvider.ContentUri_Person,values,where,whereargs);
            Toast.makeText(AddLog.this, String.valueOf(1)+"Value updated", Toast.LENGTH_SHORT).show();
            startActivity(returnback);
            finish();
        }
    }

    @Override
    public Loader<Cursor> onCreateLoader(final int id, Bundle args)
    {
        String[] Projection = new String[]{DataProvider.DID,DataProvider.Dname,DataProvider.DCategory,DataProvider.Damount,DataProvider.Ddate};
        String selection = DataProvider.Per_In+"=?";
        String[] selectionargs = new String[]{String.valueOf(logid)};
        return new CursorLoader(this,DataProvider.ContentUri_Details,Projection,selection,selectionargs,null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data)
    {
       adapter.swapCursor(data);
    }

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

Класс поставщика контента:

public class DataProvider extends ContentProvider
{
    static final String ProviderName = "com.example.mrudu.accounts.provider";
    static final String URLPerson = "content://"+ProviderName+"/Person_Detail";
    static final String URLDetails = "content://"+ProviderName+"/Details";
    static final Uri ContentUri_Person = Uri.parse(URLPerson);
    static final Uri ContentUri_Details = Uri.parse(URLDetails);
//Tables
private static String PTableName = "Person_Detail";
private static String DTableName = "Details";

public static long insertId = 0;

//Person_Detail Coloumns
public static String PID = "_id";
public static String Pname = "PName";
public static String Pphone = "PMobile";
public static String Paddress = "PCity";
public static String PCategory = "PCategory";
public static String Pamount = "PAmount";

//Details coloumn
public static String DID = "_id";
public static String Dname = "DName";
public static String Damount = "DAmount";
public static String Ddate = "DDate";
public static String DCategory = "DCategory";
public static String Per_In = "Per_In";

private static final int Person = 1;
private static final int Person_ID = 2;
private static final int Details = 3;
private static final int Details_Id = 4;

static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static
{
    uriMatcher.addURI(ProviderName,PTableName,Person);
    uriMatcher.addURI(ProviderName,PTableName+"/#",Person_ID);
    uriMatcher.addURI(ProviderName,DTableName,Details);
    uriMatcher.addURI(ProviderName,DTableName+"/#",Details_Id);
}

public static SQLiteDatabase sqLiteDatabase;
private static String Databasename = "Accounts";
private static int DatabaseVersion = 1;

private class DatabaseHelper extends SQLiteOpenHelper
{

    public DatabaseHelper(Context context)
    {
        super(context, Databasename, null, DatabaseVersion);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase)
    {
        String Create_Person = " Create Table "+PTableName+"("+PID+" INTEGER PRIMARYKEY ,"+Pname+" TEXT ,"+Pphone+" TEXT ,"+Paddress+" TEXT ,"+PCategory+" TEXT ,"+Pamount+" REAL"+")";
        String Create_Details = " Create Table "+DTableName+"("+DID+" INTEGER PRIMARYKEY ,"+Dname+" TEXT ,"+DCategory+" TEXT ,"+Damount+" REAl ,"+Ddate+" TEXT ,"+Per_In+" INTEGER )";
        sqLiteDatabase.execSQL(Create_Person);
        sqLiteDatabase.execSQL(Create_Details);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1)
    {
        sqLiteDatabase.execSQL("Drop TABLE if exists"+PTableName);
        sqLiteDatabase.execSQL("Drop TABLE if exists"+DTableName);
        onCreate(sqLiteDatabase);
    }
}
@Override
public boolean onCreate()
{
    Context context = getContext();
    DatabaseHelper databaseHelper = new DatabaseHelper(context);
    sqLiteDatabase = databaseHelper.getWritableDatabase();
    return (sqLiteDatabase==null)?false:true;
}
@Override
public Uri insert(Uri uri, ContentValues values)
{
    switch (uriMatcher.match(uri))
    {
        case Person:
            long rowId = sqLiteDatabase.insert(PTableName,null,values);
            insertId = rowId;
            if (rowId>0)
            {
               Uri _uri = ContentUris.withAppendedId(ContentUri_Person,rowId);
                getContext().getContentResolver().notifyChange(_uri,null);
                return _uri;
            }
            break;
        case Details:
            long rowId1 = sqLiteDatabase.insert(DTableName,null,values);
            if (rowId1>0)
            {
                Uri _uri = ContentUris.withAppendedId(ContentUri_Details,rowId1);
                getContext().getContentResolver().notifyChange(_uri,null);
                return _uri;
            }
            break;
    }
    return null;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder)
{
    SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder();

    switch (uriMatcher.match(uri))
    {
        case Person_ID:
            sqLiteQueryBuilder.setTables(PTableName);
            sqLiteQueryBuilder.appendWhere(PID+ "="+ uri.getPathSegments().get(1));
            break;
        case Person:
            sqLiteQueryBuilder.setTables(PTableName);
            break;
        case Details_Id:
            sqLiteQueryBuilder.setTables(DTableName);
            sqLiteQueryBuilder.appendWhere(Per_In +"="+ uri.getPathSegments().get(1));
            break;
        case Details:
            sqLiteQueryBuilder.setTables(DTableName);
            break;
        default:
            throw new UnsupportedOperationException("Not yet implemented");
    }
    Cursor cursor = sqLiteQueryBuilder.query(sqLiteDatabase,projection,selection,selectionArgs,null,null,sortOrder);
    cursor.setNotificationUri(getContext().getContentResolver(),uri);
    return cursor;

}
@Override
public int update(Uri uri, ContentValues values, String selection,
                  String[] selectionArgs)
{
    int count = 0;
    switch (uriMatcher.match(uri))
    {
        case Person:
            count = sqLiteDatabase.update(PTableName,values,selection,selectionArgs);
            break;
        case Person_ID:
            count = sqLiteDatabase.update(PTableName,values,PID+" = "+uri.getPathSegments().get(1)+(!TextUtils.isEmpty(selection)?" AND (" + selection + ')':""),selectionArgs);
            break;
        case Details:
            count = sqLiteDatabase.update(DTableName,values,selection,selectionArgs);
            break;
        case Details_Id:
            count = sqLiteDatabase.update(DTableName,values,Per_In+" = "+uri.getPathSegments().get(1)+(!TextUtils.isEmpty(selection)?" AND (" + selection + ')':""),selectionArgs);
            break;
        default:
            throw new IllegalArgumentException("Unknown URI " + uri );
    }
    getContext().getContentResolver().notifyChange(uri,null);
    return count;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    // Implement this to handle requests to delete one or more rows.
    throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public String getType(Uri uri) {
    // TODO: Implement this to handle requests for the MIME type of the data
    // at the given URI.
    throw new UnsupportedOperationException("Not yet implemented");
}

}

Детали класса:

public class Details
{
    String name,date;
    double amount;

    public Details(String name, double amount, String date) {
        this.name = name;
        this.amount = amount;
        this.date = date;
    }

    public Details() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public static Details from(Cursor cursor)
    {
        cursor.moveToFirst();
        do {
            Details details = new Details(cursor.getString(1),cursor.getDouble(3),cursor.getString(4));
            return details;
        }while (cursor.moveToNext());
    }
}

Класс адаптера:

public class DetailsAdapter extends CursorRecyclerViewAdapter<DetailsAdapter.View_Holder>
{

    public DetailsAdapter(Context context, Cursor cursor) {
        super(context, cursor);
    }
    public static class View_Holder extends RecyclerView.ViewHolder
    {
        TextView mName,mAmount,mDate;
        public View_Holder(View itemView)
        {
            super(itemView);
            mName = (TextView)itemView.findViewById(R.id.DetailName);
            mAmount = (TextView)itemView.findViewById(R.id.DetailAmount);
            mDate = (TextView)itemView.findViewById(R.id.DetailDate);
        }
    }
    @Override
    public DetailsAdapter.View_Holder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.addloglistlayout,parent,false);
        View_Holder viewHolder = new View_Holder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(DetailsAdapter.View_Holder viewHolder, Cursor cursor)
    {
        Details details = Details.from(cursor);
        viewHolder.mName.setText(details.getName());
        viewHolder.mAmount.setText(String.valueOf(details.getAmount()));
        viewHolder.mDate.setText(details.getDate());
    }
}

Класс адаптера Cursor Recyclerview:

public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
    private Context mContext;

    private Cursor mCursor;

    private boolean mDataValid;

    private int mRowIdColumn;

    private DataSetObserver mDataSetObserver;

    public CursorRecyclerViewAdapter(Context context, Cursor cursor) {
        mContext = context;
        mCursor = cursor;
        mDataValid = cursor != null;
        mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1;
        mDataSetObserver = new NotifyingDataSetObserver();
        if (mCursor != null) {
            mCursor.registerDataSetObserver(mDataSetObserver);
        }
    }

    public Cursor getCursor() {
        return mCursor;
    }

    @Override
    public int getItemCount() {
        if (mDataValid && mCursor != null) {
            return mCursor.getCount();
        }
        return 0;
    }

    @Override
    public long getItemId(int position) {
        if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
            return mCursor.getLong(mRowIdColumn);
        }
        return 0;
    }

    @Override
    public void setHasStableIds(boolean hasStableIds) {
        super.setHasStableIds(true);
    }

    public abstract void onBindViewHolder(VH viewHolder, Cursor cursor);

    @Override
    public void onBindViewHolder(VH viewHolder, int position) {
        if (!mDataValid) {
            throw new IllegalStateException("this should only be called when the cursor is valid");
        }
        if (!mCursor.moveToPosition(position)) {
            throw new IllegalStateException("couldn't move cursor to position " + position);
        }
        onBindViewHolder(viewHolder, mCursor);
    }

    /**
     * Change the underlying cursor to a new cursor. If there is an existing cursor it will be
     * closed.
     */
    public void changeCursor(Cursor cursor) {
        Cursor old = swapCursor(cursor);
        if (old != null) {
            old.close();
        }
    }

    /**
     * Swap in a new Cursor, returning the old Cursor.  Unlike
     * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
     * closed.
     */
    public Cursor swapCursor(Cursor newCursor) {
        if (newCursor == mCursor) {
            return null;
        }
        final Cursor oldCursor = mCursor;
        if (oldCursor != null && mDataSetObserver != null) {
            oldCursor.unregisterDataSetObserver(mDataSetObserver);
        }
        mCursor = newCursor;
        if (mCursor != null) {
            if (mDataSetObserver != null) {
                mCursor.registerDataSetObserver(mDataSetObserver);
            }
            mRowIdColumn = newCursor.getColumnIndexOrThrow("_id");
            mDataValid = true;
            notifyDataSetChanged();
        } else {
            mRowIdColumn = -1;
            mDataValid = false;
            notifyDataSetChanged();
            //There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
        }
        return oldCursor;
    }

    private class NotifyingDataSetObserver extends DataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            mDataValid = true;
            notifyDataSetChanged();
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            mDataValid = false;
            notifyDataSetChanged();
            //There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
        }
    }
}

Спасибо


person Mallisetti Mrudhuhas    schedule 27.05.2016    source источник
comment
Не могли бы вы показать мне код класса CursorRecyclerViewAdapter?   -  person Vickyexpert    schedule 27.05.2016
comment
@Vickyexpert Я только что добавил это к вопросу   -  person Mallisetti Mrudhuhas    schedule 27.05.2016


Ответы (2)


Вероятно, метод from в Details вызывает проблемы из-за цикла cursor.moveToFirst(); и do-while. изменить его как:

public static Details from(Cursor cursor)
    {
      Details details = new Details(cursor.getString(1),
                                    cursor.getDouble(3),
                                    cursor.getString(4));
      return details;
    }
person ρяσѕρєя K    schedule 27.05.2016
comment
Спасибо, чувак. Это сработало. Но у меня есть другая проблема. Как вы можете видеть в коде, у меня есть два действия MainActivity и Addlogactivity, я обновляю данные в Addlogactivity, которые должны отображаться в mainactivity recyclerview, данные обновляются в базе данных, но значение в mainactivity recyclerview не меняется. Пожалуйста, помогите мне и с этим. - person Mallisetti Mrudhuhas; 27.05.2016
comment
@MallisettiMrudhuhas: хорошо, но вместо того, чтобы делать этот вопрос слишком большим, я предлагаю вам создать новый вопрос для следующего выпуска с соответствующим кодом, включая код MainActivity. и поделитесь ссылкой со мной, я обязательно постараюсь помочь - person ρяσѕρєя K; 27.05.2016
comment
Вот ссылка. .com/questions/37475854/ - person Mallisetti Mrudhuhas; 27.05.2016

Я проверил ваш адаптер и все такое, так что вы уже перемещаете курсор в определенную позицию из адаптера, поэтому вам не нужно устанавливать для него moveToFist, поэтому просто удалите строку cursor.moveToFirst() из класса Details и проверьте результат.

person Vickyexpert    schedule 27.05.2016
comment
у меня есть другая проблема. Как вы можете видеть в коде, у меня есть два действия: MainActivity и Addlogactivity. Я обновляю данные в Addlogactivity, которые должны отображаться в recyclerview mainactivity, данные обновляются в базе данных, но значение в recyclerview mainactivity не меняется. Пожалуйста, можете вы тоже помогаете мне с этим. Я могу задать это как еще один вопрос. Но это говорит о том, что я должен задать вопрос только через 90 минут. - person Mallisetti Mrudhuhas; 27.05.2016
comment
Хорошо, обновляются ли ваши данные в базе данных? - person Vickyexpert; 27.05.2016
comment
хорошо, тогда попробуйте написать ниже код в методе onResume основного действия, а затем проверьте @Override public void onResume(){ super.onResume(); адаптер.notifyDataSetChanged } - person Vickyexpert; 27.05.2016
comment
Вот ссылка stackoverflow.com /вопросы/37475854/ - person Mallisetti Mrudhuhas; 27.05.2016