I am trying to remove item from database onClick of a button(defined as delbutton) here.
My Adapter looks like(I have included definitions and removed onBindViewHolder for brevity), and the onclick method is inside ViewHolder.
public class PlacesAdapter extends RecyclerView.Adapter<PlacesAdapter.ViewHolder> {
private static final String TAG = "PlaceAdapter";
List<PlaceSaved> items;
PlaceDatabase db;
public PlacesAdapter(List<PlaceSaved> items, PlaceDatabase db) {
this.items = items;
this.db = db;
}
#Override
public PlacesAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.places_list_item, parent, false);
return new ViewHolder(view);
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
public TextView name;
public TextView time;
public TextView longi;
public ImageButton delbutton;
public CardView cview;
public ViewHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.secondLine);
time = itemView.findViewById(R.id.firstLine);
longi = itemView.findViewById(R.id.longitude);
delbutton = itemView.findViewById(R.id.delicon);
cview = itemView.findViewById(R.id.place_view);
itemView.setOnClickListener(this);
delbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Log.e("What", "Get"+getAdapterPosition());
removeItem(getAdapterPosition());
}
});
}
#Override
public void onClick(View view) {
// Clicked postion
int position = getAdapterPosition();
PlaceSaved place = items.get(position);
// Here is the data Do your stuff
Log.e("Will WORK", place.getLongi());
}
}
private void removeItem(int position){
db.databaseInterface().delete(items.get(position));
items.remove(position);
notifyItemRemoved(position);
}
}
Now on clicking delbutton, I am getting the error:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
I know, I need to invoke runOnUIThread somehow, but inside ViewHolder, I cant define it. How can I solve this problem?
create new Thread Executors class like below
public class AppExecutors {
private final Executor mDiskIO;
private AppExecutors(Executor diskIO) {
this.mDiskIO = diskIO;
}
public AppExecutors() {
this(Executors.newSingleThreadExecutor());
}
public Executor diskIO() {
return mDiskIO;
}
}
use that class like this way..make return type of delete interface int type , to check successfully delete item.
private void removeItem(int position) {
new AppExecutors().diskIO().execute(new Runnable() {
#Override
public void run() {
if(db.databaseInterface().delete(items.get(position)) > 0 ){
runOnUiThread(new Runnable() {
#Override
public void run() {
items.remove(position);
notifyItemRemoved(position);
}
});
}
}
});
}
Related
I have some problems with RecyclerView Adapter. I want to do the list with id index: 1,2,3... When an item is removed it should set every next item position to position-1. Like this: (for example delete number 3)
1|2|3|4|5 -> 1|2|3|4
It works perfectly when:
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
But this causes other errors with notifyItemRemoved().
When I set:
#Override
public long getItemId(int position) {
return 0;
}
#Override
public int getItemViewType(int position) {
return 0;
}
It has problems with item order after scrolling, with repeatability of numbers etc.
I'm stuck.
MainActivity.class
public class MainActivity extends AppCompatActivity {
private DatabaseHelper databaseHelper;
private EditText editText;
private Button showButton,saveButton;
private RecyclerView mainListRecyclerView;
private ArrayList<String>arrayList;
private MainListAdapter mainListAdapter;
private RecyclerView.LayoutManager layoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText) findViewById(R.id.editText);
showButton = (Button) findViewById(R.id.showButton);
saveButton = (Button) findViewById(R.id.saveButton);
mainListRecyclerView = (RecyclerView) findViewById(R.id.listView);
databaseHelper = new DatabaseHelper(this);
arrayList = new ArrayList<>(20);
layoutManager = new LinearLayoutManager(this);
mainListRecyclerView.setLayoutManager(layoutManager);
mainListAdapter = new MainListAdapter(arrayList,this);
mainListAdapter.setHasStableIds(false);
mainListRecyclerView.setAdapter(mainListAdapter);
mainListAdapter.notifyDataSetChanged();
showButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, FinalListActivity.class);
intent.putExtra("productList",arrayList);
MainActivity.this.startActivity(intent);
}
});
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if(actionId==EditorInfo.IME_ACTION_DONE) {
addItem();
}
return true;
}
});
}
public void addData(String newItem){
boolean insertData = databaseHelper.addData(newItem);
if(insertData){
toastMessage("Item successfully inserted!");
}
else {
toastMessage("Something is wrong");
}
}
public void removeData(String itemToRemove){
boolean removeData = databaseHelper.removeData(itemToRemove);
if(removeData){
toastMessage("Item successfully deleted!");
}
else {
toastMessage("Something is wrong");
}
}
public void toastMessage(String message){
Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
}
public void addItem(){
String newItem = editText.getText().toString();
if(newItem.length()!=0) {
addData(newItem);
arrayList.add(newItem);
editText.setText("");
editText.requestFocus();
mainListAdapter.notifyItemInserted(arrayList.size()-1);
}
else{
toastMessage("You must put something in the text field");
}
}
public void removeItem(int position){
if(mainListAdapter.getItemCount()!=0){
removeData(arrayList.get(position));
arrayList.remove(position);
mainListAdapter.notifyItemRemoved(position);
}
}
}
Adapter class
public class MainListAdapter extends
RecyclerView.Adapter<MainListAdapter.MyViewHolder> {
private ArrayList<String> mDataset;
private MainActivity mainActivity;
public MainListAdapter(ArrayList<String> mDataset, MainActivity
mainActivity) {
this.mDataset = mDataset;
this.mainActivity = mainActivity;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public int getItemViewType(int position) {
return 0;
}
#Override
public int getItemCount() {
return mDataset.size();
}
#Override
public void onBindViewHolder(final MyViewHolder holder,final int position) {
holder.listTextView1.setText(holder.getAdapterPosition()+"");
holder.listTextView2.setText(mDataset.get(position));
holder.listButtonViewDel.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
int currentPosition = holder.getAdapterPosition();
if(currentPosition!=-1)
mainActivity.removeItem(currentPosition);
}
});
}
public class MyViewHolder extends RecyclerView.ViewHolder {
final public TextView listTextView1;
final public TextView listTextView2;
final public Button listButtonViewDel;
public MyViewHolder(View view) {
super(view);
listTextView1 = (TextView) view.findViewById(R.id.listTextView1);
listTextView2 = (TextView) view.findViewById(R.id.listTextView2);
listButtonViewDel = (Button) view.findViewById(R.id.listButtonViewDel);
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_list_layout, parent, false);
return new MyViewHolder(view);
}
}
Add this lines To removeItem function in MainActivity.java
public void removeItem(int position){
if(mainListAdapter.getItemCount()!=0){
removeData(arrayList.get(position));
arrayList.remove(position);
mainListAdapter.notifyItemRemoved(position);
arrayList.remove(position); // add this line
mainListAdapter.update(arrayList); // add this line
}
}
And add update function to your MainListAdapter.java like this
public void update(ArrayList<String> mDataset){
this.mDataset = mDataset
notifyDataSetChanged();
}
For show animation try this function (I suggest you to set all function about adapter in adapter)
public void update(ArrayList<String> mDataset,int position){
this.mDataset = mDataset
notifyItemRangeChanged(position,mDataset.size);
}
In your removeItem() method,
You have mainListAdapter.notifyItemRemoved(position); and it will only inform the item is removed.
You need to put a line inside this method at end as mainListAdapter.notifyDataSetChanged().
It reminds adapter to reset the list on every change.
you can set null to mainListRecyclerVie wadapter and bind again when remove items !
mainListRecyclerVie.setAdapter(null);
and bind again
I am working with recyclerview and sqlite database. i want to place a button in cardview such that whenever user clicks on that button, the respective record should be deleted from the table.
This is my adapter class for recyclerview
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.CardViewHolder> {
private Context mContext;
private Cursor mCursor;
private butttonsAdapetrListener mlistener;
private RecyclerView.ViewHolder v;
public CardAdapter(Context context, Cursor cursor ,butttonsAdapetrListener listener){
mContext=context;
mCursor=cursor;
mlistener=listener;
}
#NonNull
#Override
public CardViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.mycard,parent,false);
return new CardViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CardViewHolder holder, int position) {
if(!mCursor.moveToPosition(position)){
return;
}
String name = mCursor.getString(mCursor.getColumnIndex("NAME"));
int total = mCursor.getInt(mCursor.getColumnIndex("TOTAL"));
int bunked = mCursor.getInt(mCursor.getColumnIndex("BUNK"));
int color = mCursor.getInt(mCursor.getColumnIndex("COLOR"));
long id= mCursor.getLong(mCursor.getColumnIndex("_id"));
holder.nameText.setText(name);
holder.totalText.setText(Integer.toString(total));
holder.bunkedText.setText(Integer.toString(bunked));
holder.colorText.setBackgroundColor(color);
holder.itemView.setTag(id);
v=holder;
}
#Override
public int getItemCount() {
return mCursor.getCount();
}
public class CardViewHolder extends RecyclerView.ViewHolder{
public TextView nameText;
public TextView totalText;
public TextView bunkedText;
public TextView colorText;
public Button delete;
public CardViewHolder(#NonNull View itemView) {
super(itemView);
nameText= itemView.findViewById(R.id.name);
totalText = itemView.findViewById(R.id.total_num);
bunkedText = itemView.findViewById(R.id.bunked_num);
colorText= itemView.findViewById(R.id.color);
delete= itemView.findViewById(R.id.delete);
delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mlistener.deleteOnClick(v,getAdapterPosition());
}
});
}
}
public interface butttonsAdapetrListener{
void deleteOnClick(RecyclerView.ViewHolder v,long position);
}
public void swapCursor(Cursor newCursor){
if(mCursor!=null){
mCursor.close();
}
mCursor=newCursor;
if(newCursor!=null){
notifyDataSetChanged();
}
}
}
This is my main activity code to delete record from database
public class MainActivity extends AppCompatActivity {
private SQLiteDatabase db;
private CardAdapter mAdapter;
private long id;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SQLiteOpenHelper proBunkerDatabaseHelper = new ProBunkerDatabaseHelper(this);
db = proBunkerDatabaseHelper.getWritableDatabase();
RecyclerView recyclerView = findViewById(R.id.rv);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new CardAdapter(this, getCursor(), new CardAdapter.butttonsAdapetrListener() {
#Override
public void deleteOnClick(RecyclerView.ViewHolder v, long position) {
id = (long)v.itemView.getTag();
db.delete("MYTABLE","_id="+id,null);
mAdapter.swapCursor(getCursor());
}
});
recyclerView.setAdapter(mAdapter);
FloatingActionButton b = findViewById(R.id.fab);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this,AddSubject.class);
startActivity(intent);
}
});
}
public Cursor getCursor(){
return db.query("MYTABLE",null,null,null,null,null,"_id ASC");
}
}
The main problem is the when I click on the delete button in any item it is deleting the item which is last in the cursor.
there are no compilation errors and runtime issues only the records are not deleting in correct order.
Try it like this:
Your interface:
public interface butttonsAdapetrListener{
void deleteOnClick(int id,long adapterPos);
}
Inside your CardViewHolder:
delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int id = (int)view.getTag();
mlistener.deleteOnClick(id,getAdapterPosition());
}
});
In onBindViewHolder
holder.delete.setTag(id);
In MainActivity onCreate
mAdapter = new CardAdapter(this, getCursor(), new CardAdapter.butttonsAdapetrListener() {
#Override
public void deleteOnClick(int id, long position) {
db.delete("MYTABLE","_id="+id,null);
mAdapter.swapCursor(getCursor());
}
});
Another alternative
If you want your activity to be clean and not handle data from within.
Keep it for the adapter to handle the data being deleted and notifying any change.
Just create a listener for the button in the onbindviewholder method.
First, my problem is exactly similar to this, but I still cant solve my problem.
public class PlacesAdapter extends RecyclerView.Adapter<PlacesAdapter.ViewHolder> {
private static final String TAG = "PlaceAdapter";
List<PlaceSaved> items;
PlaceDatabase db;
public PlacesAdapter(List<PlaceSaved> items, PlaceDatabase db) {
this.items = items;
this.db = db;
}
#Override
public PlacesAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.places_list_item,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final PlacesAdapter.ViewHolder holder, final int position) {
holder.name.setText(items.get(position).getTitle());
holder.time.setText(items.get(position).getTime());
holder.longi.setText(items.get(position).getLongi());
holder.delbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View view) {
// removeItem(items.get(holder.getAdapterPosition()));
AsyncTask.execute(new Runnable() {
#Override
public void run() {
AsyncTask.execute(new Runnable() {
#Override
public void run() {
db.databaseInterface().delete(items.get(position));
Snackbar.make(view , items.get(position).getTime()+" deleted, will be updated next time!", Snackbar.LENGTH_LONG).show();
}
});
}
});
}
});
holder.cview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.e("CardView", "Clicked "+items.get(position).getTitle()+", "+items.get(position).getLongi());
Location Saved_Location_onCard = new Location("");
Saved_Location_onCard.setLatitude(Double.parseDouble(items.get(position).getTitle()));
Saved_Location_onCard.setLatitude(Double.parseDouble(items.get(position).getLongi()));
latlang.Lat = (Double.parseDouble(items.get(position).getTitle()));
latlang.Lang = (Double.parseDouble(items.get(position).getLongi()));
Snackbar.make(view, "The new location is "+
items.get(position).getTitle()+", "+items.get(position).getLongi(),
Snackbar.LENGTH_LONG).show();
// Intent intent = new Intent(, SunFragment.class)
SecondFragment sf = new SecondFragment();
sf.updateLocationUI();
// sf.showMap();
}
});
}
After realising that onclick is not a good idea inside onBindViewHolder, I am trying to move it to ViewHolder as:
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener{
public TextView name;
public TextView time;
public TextView longi;
public ImageButton delbutton;
public CardView cview;
public ViewHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.secondLine);
time= itemView.findViewById(R.id.firstLine);
longi = itemView.findViewById(R.id.longitude);
delbutton = itemView.findViewById(R.id.delicon);
cview = itemView.findViewById(R.id.place_view);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view){
//Snackbar.make(this,"Hi", Snackbar.LENGTH_LONG).show();
Log.e("onClick", "Hello"+getLayoutPosition());
}
}
So, I am abel to get the layout's position, but not the values stored in the card, as I was able to do when I defined it inside BindView.
How I can do that?
Also, any idea how to delete and update the delete function?
Get the position with getAdapterPosition().
#Override
public void onClick(View view){
// Clicked postion
int position =getAdapterPosition();
PlaceSaved place=items.get(position);
// Here is the data Do your stuff
}
To delete a specific item with position call.
items.remove(position);
notifyItemRemoved(position);
Read Notify methods for RecyclerView.AdapterHere.
Sorry for my english. I have list objects, it list contains switch btn. When user change some switch i neeed it update in db. I
But when i try change swith, i have error:
java.lang.IllegalStateException: Cannot modify managed objects outside of a write transaction.
I can not understand how create adapter, who can update realtime data
My adapter:
public class DocumentTypeAdapterDB extends RealmRecyclerViewAdapter<DocType, DocumentTypeAdapterDB.ViewHolder> {
RealmList<DocType> docTypes;
public DocumentTypeAdapterDB(#Nullable RealmList<DocType> docTypes, Context context) {
super(docTypes, true);
this.docTypes = docTypes;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_doc_type, null);
RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutView.setLayoutParams(lp);
ViewHolder rcv = new ViewHolder(layoutView);
return rcv;
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final DocType docType = docTypes.get(position);
holder.switch_item.setText(docType.name);
//check box
holder.switch_item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!holder.switch_item.isChecked()) {
docType.is_check = false;
} else {
docType.is_check = true;
}
}
});
}
#Override
public int getItemCount() {
return docTypes.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
SwitchCompat switch_item;
public ViewHolder(View itemView) {
super(itemView);
switch_item = (SwitchCompat) itemView.findViewById(R.id.switch_item);
}
public void clearAnimation()
{ itemView.clearAnimation(); }
}
}
DocType
public class DocType extends RealmObject{
public boolean is_check;
public String name;
//getter and setter
}
You need transaction to modify RealmObjects.
So, you should get Realm instance that docTypes belongs to and then:
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
docType.is_check = holder.switch_item.isChecked();
}
});
I am having trouble removing items from RecyclerView. When I click on delete, the item is removed from RecyclerView, but comes back when I open the app again. I'm hoping it is just a minor issue that someone here can point out or direct me to what area to troubleshoot. The removeItem(String item) in bold is what I think is the issue. You can't see it in this post, but it is "not used".
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {
private List<Grocery> mListData;
private SQLGroceryHelper helper;
RecyclerViewAdapter adapter;
//Adapter's Constructor//
public RecyclerViewAdapter(List<Grocery> mDataList) {
this.mListData = mDataList;
}
//Provide a reference to the views for each contact item//
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView rowItem;
ImageButton purchasedButton;
ImageButton deleteButton;
LinearLayout linearLayout;
public MyViewHolder(View itemView) {
super(itemView);
linearLayout = (LinearLayout) itemView.findViewById(R.id.recycler_row);
rowItem = (TextView) itemView.findViewById(R.id.item_field1);
purchasedButton = (ImageButton) itemView.findViewById(R.id.item_purchased);
deleteButton = (ImageButton) itemView.findViewById(R.id.delete_item);
}
}
//Inflate the view based on the viewtype provided//
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//Create a new view by inflating the row item xml//
View row = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_row, parent, false);
//Set the view to the ViewHolder//
MyViewHolder holder = new MyViewHolder(row);
return holder;
}
//Display data at the specified position//
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
holder.rowItem.setText(mListData.get(position).getTextItem());
holder.purchasedButton.setOnClickListener(new View.OnClickListener() {
//Ignore this click for now//
#Override
public void onClick(View v) {
removeItem(position);
}
});
holder.deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
removeItem(position);
}
});
}
public void **removeItem**(String item) {
int position = mListData.indexOf(item);
if (position != -1) {
mListData.remove(item);
notifyItemRemoved(position);
}
}
public void removeItem(int position) {
mListData.remove(position);
notifyItemRemoved(position);
}
#Override
public int getItemCount() {
if (mListData == null) {
return 0;
}
return mListData.size();
}
}
You are removing the data from local object, mListData I guess the original data object remains intact. Remove the data item from the original data object as well
Declare a interface
public interface AdapterCommunication{
void removeStringItem(int position);
}
then in your adapter
private AdapterCommunication mListener;
public void setOnClickListener(AdapterCommunication listener){
mListener = listener;
}
Then from your activity where you initialize the adapter
RecyclerViewAdapter adapter = new RecyclerViewAdapter(list);
adapter.setOnClickListener(new AdapterCommunication{
public void removeStringItem(int position){
list.remove(position);
adapter.notifyDataSetChanged();
}
});
In your adaper,
holder.deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mListener.remove(position);
}
});