I have a recyclerview of tasks. When user will swipe an element I want to change one parameter(variable "isChecked" from false to true - that will mean the task is completed) in that task.
I created new ItemTouchHelper instance in my activity:
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
Sorted current = adapter.getSortedAtPosition(viewHolder.getAdapterPosition());
int time = current.getSortedDuration() + current.getSortedTimeBegin();
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
int minute = Calendar.getInstance().get(Calendar.MINUTE);
if (hour > time/60){
//change
Toast.makeText(ShowSortedActivity.this, "check1", Toast.LENGTH_SHORT).show();
}
else if (hour == time/60){
if (minute > time % 60){
//change
Toast.makeText(ShowSortedActivity.this, "check2", Toast.LENGTH_SHORT).show();
}
else{
//do nothing
Toast.makeText(ShowSortedActivity.this, "uncheck1", Toast.LENGTH_SHORT).show();
}
}
else{
//do nothing
Toast.makeText(ShowSortedActivity.this, "uncheck2", Toast.LENGTH_SHORT).show();
}
}
}).attachToRecyclerView(showSorted);
}
Here I'm getting the task of class "Sorted" and check if the ending time of the task is less than the current time of the day. If so, I want to change the variable.
My adapter class:
public class SortedAdapter extends RecyclerView.Adapter<SortedAdapter.SortedViewHolder> {
private List<Sorted> list = new ArrayList<>();
#NonNull
#Override
public SortedViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.tasks2_layout , parent, false);
return new SortedAdapter.SortedViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull final SortedViewHolder holder, final int position) {
final Sorted data = list.get(position);
holder.title.setText(data.getSortedName());
holder.date.setText(data.getSortedDate());
holder.category.setText(String.valueOf(data.getSortedCategory()));
holder.attach.setText(String.valueOf(data.isSortedAttach()));
holder.to.setText(String.valueOf(toTime(data.getSortedDuration() + data.getSortedTimeBegin())));
holder.from.setText(String.valueOf(toTime(data.getSortedTimeBegin())));
holder.isChecked.setText(String.valueOf(data.isChecked()));
}
public void setSortedData(List<Sorted> sortedList){
this.list = sortedList;
notifyDataSetChanged();
}
public Sorted getSortedAtPosition(int position){
return list.get(position);
}
public void setSorted(Sorted data){
}
#Override
public int getItemCount() {
return list.size();
}
static class SortedViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TextView title;
private TextView date;
private TextView from;
private TextView to;
private TextView category;
private TextView attach;
private TextView isChecked;
SortedViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.tv_title1);
date = itemView.findViewById(R.id.tv_date1);
from = itemView.findViewById(R.id.tv_from3);
to = itemView.findViewById(R.id.tv_to3);
category = itemView.findViewById(R.id.tv_category1);
attach = itemView.findViewById(R.id.tv_attach1);
isChecked = itemView.findViewById(R.id.tv_isChecked);
}
}
private static String toTime(int a) {
String s = "";
int b = a/60;
int c = a%60;
if (c < 10) {
s = b + " : " + 0 + c;
}
else {
s = b + " : " + c;
}
return s;
}
}
Sorted class:
#Entity
public class Sorted {
#PrimaryKey(autoGenerate = true)
public int id;
public String name;
public int timeBegin;
public int duration;
public int category;
public boolean attach;
public String date;
public String categoryChart;
public boolean checked;
public Sorted(String name, int timeBegin, int duration, int category, boolean attach, String date, String categoryChart, boolean checked) {
this.name = name;
this.timeBegin = timeBegin;
this.duration = duration;
this.category = category;
this.attach = attach;
this.date = date;
this.categoryChart = categoryChart;
this.checked = checked;
}
public void setSortedId(int id) {
this.id = id;
}
public String getSortedName() {
return name;
}
public int getSortedTimeBegin() {
return timeBegin;
}
public int getSortedDuration() {
return duration;
}
public int getSortedCategory() {
return category;
}
public boolean isSortedAttach() {
return attach;
}
public String getSortedDate() {
return date;
}
public String getSortedCategoryChart() {
return categoryChart;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
}
My problem is that I don't really understand how to do it. Is there any way to update the data, not create a new task? Or do I need to delete the task I got and insert a new one, with one parameter changed? Maybe I could do it the same way I update the list? :
public void setSortedData(List<Sorted> sortedList){
this.list = sortedList;
notifyDataSetChanged();
}
Or maybe I don't need to deal with my adapter, only with database? (I'm using Room).
Thanks in advance for help. If it's needed, I will add more information to the question.
In method onSwipe to change UI you only need call some method to set for model current and call adapter.notifyItemChange(viewHolder.getAdapterPosition()) no need create new object or new list
I know you using Room don't forget update it to database. Additional if you using RxJava or Flow and LiveData one thing you need is update entity. Room auto update new list for you
Related
I am new to android studios I am currently changing my FirestoreRecyclerOptions to a regular recycler view because I want to add native ads every 5 posts. The current issue I am facing is that the method getItemViewType what I want it to return is if it's either an ad or regular post using instance of. In the tutorial videos they do something along the line of
#Override
public int getItemViewType(int position) {
if (noteList.get(position) instanceof UnifiedNativeAd) {
return TYPE_AD;
}else {
return TYPE_REG;
}
}
But the condition inside the if statement is giving me this error
error: incompatible types: Note cannot be converted to UnifiedNativeAd
if (noteList.get(position) instanceof UnifiedNativeAd) {
CLASS
public class Note {
public int timestamp;
public List<String> replies;
public String ownerName;
public String ownerId;
public String text;
public String imageURL;
public List<String> usersLiked;
public String message;
public String timePosted;
public int likes;
public int replyCount;
public Note() {
}
public Note(int timestamp, String ownerId, String text, String ownerName, String imageURL, List<String> replies, List<String>
usersLiked, String message, String timePosted, int likes, int replyCount) {
this.timestamp = timestamp;
this.ownerId = ownerId;
this.text = text;
this.ownerName = ownerName;
this.imageURL = imageURL;
this.replies = replies;
this.usersLiked = usersLiked;
this.message = message;
this.timePosted = timePosted;
this.likes = likes;
this.replyCount = replyCount;
}
public int getTime() {
return timestamp;
}
public String getOwnerName() {
return ownerName;
}
public String getId() {
return ownerId;
}
public String getPost() {
return text;
}
public List<String> getreplies() {
return replies;
}
public String getImageURL() {
return imageURL;
}
public List<String> getUsersLiked() {
return usersLiked;
}
public String getMessage() {
return message;
}
public String getTimePosted() {
return timePosted;
}
public int getLikes() {
return likes;
}
public int getReplyCount() {
return replyCount;
}
}
FULL RECYCLERVIEW
ublic class adapterRegular extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final static int TYPE_AD=0;
private final static int TYPE_REG=1;
private Context context;
private List<Note> noteList;
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
if(viewType == TYPE_AD){
View view = LayoutInflater.from(context).inflate(R.layout.reclyerads,parent,false);
return new AdTemplateViewHolder(view);
}
View view = LayoutInflater.from(context).inflate(R.layout.list_row,parent,false);
return new noteHolder(view);
}
#Override
public int getItemCount() {
return noteList.size();
}
public class noteHolder extends RecyclerView.ViewHolder{
TextView ownerName;
TextView timestamp;
TextView text;
ImageView imageURL;
TextView replies;
TextView likes;
ImageView heart;
public noteHolder(#NonNull View itemView) {
super(itemView);
ownerName = itemView.findViewById(R.id.userName);
timestamp = itemView.findViewById(R.id.time);
text = itemView.findViewById(R.id.post);
imageURL = itemView.findViewById(R.id.profilePic);
replies = itemView.findViewById(R.id.REPLY);
likes = itemView.findViewById(R.id.likes);
heart = itemView.findViewById(R.id.heart);
}
}
public class AdTemplateViewHolder extends RecyclerView.ViewHolder{
TemplateView templateView;
public AdTemplateViewHolder(#NonNull View itemView) {
super(itemView);
templateView = itemView.findViewById(R.id.my_template);
NativeTemplateStyle style = new NativeTemplateStyle.Builder()
.withMainBackgroundColor(new ColorDrawable(Color.parseColor("#FFFFFF"))).build();
templateView.setStyles(style);
}
public void setUnifiedNativeAd(UnifiedNativeAd ads){
templateView.setNativeAd(ads);
}
}
#Override
public int getItemViewType(int position) {
if (noteList.get(position) instanceof UnifiedNativeAd) {
return TYPE_AD;
}else {
return TYPE_REG;
}
}
}
ANY HELP OR SUGGESTION IS APPERCIATED
Change this
private List<Note> noteList;
To this
private List<Object> noteList;
Problem
I am trying to develop a gallery selector page for my app. A user can select multiple images/videos from the listed items (from the users phone). The user can select up to 10 items at once and the order of selection should be shown for each item. The selection order should also adjust based on selection and deselection.
eg : If user deselects selection 1 then all other selection after selection 1 should decrement by one.
What I have done
I have already made the recyclerview adapter with diffUtils and handled the multiple selection using recyclerview-selection library. But I can't find a way to show the selection order and adjusting it based on user action.
What an trying to achieve
Code
GalleryAdapter
public class GalleryViewAdapter extends ListAdapter<GalleryThumbnailsModel,GalleryViewAdapter.ViewHolder> {
private Context context;
private ArrayList<GalleryThumbnailsModel> selectedItemModels;
private Interaction interaction;
private SelectionTracker<Long> selectionTracker;
private static int POST_TYPE_IMAGE = 1;
private static int POST_TYPE_VIDEO = 3;
public GalleryViewAdapter(GalleryViewDiffCallback diffCallback,Context context,ArrayList<GalleryThumbnailsModel> selectedItemModels) {
super(diffCallback);
this.context = context;
this.selectedItemModels = selectedItemModels;
setHasStableIds(true);
}
#NonNull
#Override
public GalleryViewAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_gallery_view,parent,false);
return new GalleryViewAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final GalleryViewAdapter.ViewHolder holder, final int position) {
GalleryThumbnailsModel item = getItem(position);
holder.bind(item,selectionTracker.isSelected((long) position));
}
#Override
public long getItemId(int position) {
return (long) position;
}
public void setSelectionTracker(SelectionTracker<Long> selectionTracker) {
this.selectionTracker = selectionTracker;
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView postImage;
ImageView postTypeVideo;
ProgressBar progressBar;
TextView postOrderCount;
FrameLayout selectedIcon;
public ViewHolder(View itemView) {
super(itemView);
postImage = itemView.findViewById(R.id.post_gallery_image);
postTypeVideo = itemView.findViewById(R.id.user_post_video);
progressBar = itemView.findViewById(R.id.progress_loader);
selectedIcon =itemView.findViewById(R.id.gallery_selected_item);
postOrderCount = itemView.findViewById(R.id.selected_post_order);
}
public void bind(GalleryThumbnailsModel item, boolean selected) {
new GlideImageLoader(context,postImage,progressBar).load(item.getThumbnail(),null);
if(selected){
selectedIcon.setVisibility(View.VISIBLE);
interaction.onItemSelected(getAdapterPosition(),item);
}else {
selectedIcon.setVisibility(View.INVISIBLE);
interaction.onItemDeselected(getAdapterPosition(),item);
}
if(item.getMediaType() == POST_TYPE_VIDEO){
postTypeVideo.setVisibility(View.VISIBLE);
}else {
postTypeVideo.setVisibility(View.INVISIBLE);
}
}
public ItemDetailsLookup.ItemDetails<Long> getItemDetails(){
return new ItemDetailsLookup.ItemDetails<Long>() {
#Override
public int getPosition() {
return getAdapterPosition();
}
#Nullable
#Override
public Long getSelectionKey() {
return getItemId();
}
#Override
public boolean inSelectionHotspot(#NonNull MotionEvent e) {
return true;
}
};
}
}
public static class GalleryViewDiffCallback extends DiffUtil.ItemCallback<GalleryThumbnailsModel>{
#Override
public boolean areItemsTheSame(#NonNull GalleryThumbnailsModel oldItem, #NonNull GalleryThumbnailsModel newItem) {
return oldItem.getUriPath().equals(newItem.getUriPath());
}
#Override
public boolean areContentsTheSame(#NonNull GalleryThumbnailsModel oldItem, #NonNull GalleryThumbnailsModel newItem) {
return oldItem.equals(newItem);
}
}
public interface Interaction{
void onItemSelected(int position,GalleryThumbnailsModel item);
void onItemDeselected(int position,GalleryThumbnailsModel item);
}
public void setInteraction(Interaction interaction) {
this.interaction = interaction;
}
}
Setting adapter in Fragment
private void setAdapter() {
galleryViewAdapter = new GalleryViewAdapter(new GalleryViewAdapter.GalleryViewDiffCallback(),context,selectedItemsModels);
gridLayoutManager = new GridLayoutManager(context,3,RecyclerView.VERTICAL,false);
galleryViewAdapter.submitList(galleryThumbnailsModels);
galleryViewRecycler.setLayoutManager(gridLayoutManager);
galleryViewRecycler.setHasFixedSize(true);
galleryViewRecycler.setAdapter(galleryViewAdapter);
selectionTracker = new SelectionTracker.Builder<Long>(
"selection",
galleryViewRecycler,
new StableIdKeyProvider(galleryViewRecycler),
new RecyclerSelectionLookup(galleryViewRecycler),
StorageStrategy.createLongStorage()
).withSelectionPredicate(new SelectionTracker.SelectionPredicate<Long>() {
#Override
public boolean canSetStateForKey(#NonNull Long key, boolean nextState) {
// 10 - max selection size
return !nextState || selectionTracker.getSelection().size() < 10;
}
#Override
public boolean canSetStateAtPosition(int position, boolean nextState) {
return false;
}
#Override
public boolean canSelectMultiple() {
return true;
}
})
.build();
selectionTracker.addObserver(new SelectionTracker.SelectionObserver<Long>() {
#Override
public void onSelectionChanged() {
super.onSelectionChanged();
int selectedItemCount = selectionTracker.getSelection().size();
if(selectedItemCount == 0){
addMediaLayout.setVisibility(View.INVISIBLE);
}else if(selectedItemCount > 0 && selectedItemCount < 11){
selectedCountTextView.setText(String.valueOf(selectedItemCount));
addMediaLayout.setVisibility(View.VISIBLE);
}
}
});
galleryViewAdapter.setSelectionTracker(selectionTracker);
galleryViewAdapter.setInteraction(new GalleryViewAdapter.Interaction() {
#Override
public void onItemSelected(int position,GalleryThumbnailsModel item) {
item.setSelectedOrder(selectedItemsModels.size()+1);
selectedItemsModels.add(item);
}
#Override
public void onItemDeselected(int position,GalleryThumbnailsModel item) {
}
});
}
I am only adding the adapter code here. Otherwise the question would become too large.I will share the complete code if this doesn't provide necessary information.
you can pass selectionTracker.selection.size() to bind function in your ViewHolder class, so you will have always the latest count
#Override
public void onBindViewHolder(#NonNull final GalleryViewAdapter.ViewHolder holder, final int position) {
int count = selectionTracker.selection.size()
holder.bind(item,count,selectionTracker.isSelected((long) position));
}
I am trying to get the details of the Recipe from which Recipe I have clicked in recycler view. I am using this to go to an edit/delete feature. Here is the code for my main activity.
The details that I am trying to get is getting the Name, Ingredients and method.
public class MainActivity extends AppCompatActivity implements RecipeListAdapter.OnItemClickListener {
private RecipeViewModel mRecipeViewModel;
public static final int NEW_WORD_ACTIVITY_REQUEST_CODE = 1;
public String Name;
public String Ingredients;
public String Method;
private RecipeListAdapter mAdapter;
private RecipeDao recDao;
private LiveData<List<Recipe>> RecipeList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
final RecipeListAdapter adapter = new RecipeListAdapter(this);
recyclerView.setAdapter(adapter);
adapter.setOnItemClickListener(MainActivity.this);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecipeViewModel = new ViewModelProvider(this).get(RecipeViewModel.class);
mRecipeViewModel.getAllRecipes().observe(this, new Observer<List<Recipe>>() {
#Override
public void onChanged(#Nullable final List<Recipe> recipes) {
// Update the cached copy of the words in the adapter.
adapter.setWords(recipes);
}
});
void onItemClick(int position) {
//Delete Below test to pass data through
Recipe recipe = new Recipe("Test", "Yeet", "Jim");
// showAlertDialogBox();
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
alertDialog.setTitle("Edit or Delete...");
alertDialog.setPositiveButton("Edit", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent update = new Intent(MainActivity.this, UpdateRecipeActivity.class);
update.putExtra("Name", recipe.getName());
update.putExtra("Ingredients", recipe.getIngredients());
update.putExtra("Method", recipe.getMethod());
startActivity(update);
}
});
alertDialog.setNegativeButton("Delete", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
//Delete
}
});
alertDialog.show();
}
Here is the Recipe.class if you needed it!
#Entity(tableName = "recipe_table")
public class Recipe {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name= "recipeId")
private int RecipeId;
private String name;
private String Ingredients;
private String Method;
#Ignore
public Recipe(String name, String Ingredients, String Method) {
this.RecipeId = RecipeId;
this.name = name;
this.Ingredients = Ingredients;
this.Method = Method;
}
public Recipe(String name) {
this.name = name;
}
public void changeText1(String text){
name = text;
}
//Add Image somehow!
public int getRecipeId() {
return RecipeId;
}
public void setRecipeId(int recipeId) {
RecipeId = recipeId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMethod() {
return Method;
}
public void setMethod(String method) {
Method = method;
}
public String getIngredients() {
return Ingredients;
}
public void setIngredients(String ingredients) {
Ingredients = ingredients;
}
}
If you need anymore files, these are the files I have:
- RecipeListAdapter
- RecipeDao
- RecipeRepository
- RecipeRoomDatabase
- RecipeViewModel
Recipe Adapter code
public class RecipeListAdapter extends RecyclerView.Adapter {
private OnItemClickListener mListener;
private List recipeList;
public interface OnItemClickListener{
void onItemClick(int position, Recipe recipe);
}
public void setOnItemClickListener(OnItemClickListener listener){
mListener = listener;
}
class RecipeViewHolder extends RecyclerView.ViewHolder {
private final TextView recipeItemView;
private RecipeViewHolder(View itemView) {
super(itemView);
recipeItemView = itemView.findViewById(R.id.textView);
itemView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
if (mListener != null){
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION){
mListener.onItemClick(position,
recipeList.get(getAdapterPosition()));
}
}
}
});
}
}
private final LayoutInflater mInflater;
private List<Recipe> mRecipes; // Cached copy of words
RecipeListAdapter(Context context) {
mInflater = LayoutInflater.from(context);
this.recipeList = recipeList;
}
#Override
public RecipeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = mInflater.inflate(R.layout.recyclerview_item, parent,
false);
return new RecipeViewHolder(itemView);
}
#Override
public void onBindViewHolder(RecipeViewHolder holder, int position) {
if (mRecipes != null) {
Recipe current = mRecipes.get(position);
holder.recipeItemView.setText(current.getName());
} else {
// Covers the case of data not being ready yet.
holder.recipeItemView.setText("No Recipes");
}
}
void setWords(List<Recipe> recipes){
mRecipes = recipes;
notifyDataSetChanged();
}
// getItemCount() is called many times, and when it is first called,
// mWords has not been updated (means initially, it's null, and we can't
return null).
#Override
public int getItemCount() {
if (mRecipes != null)
return mRecipes.size();
else return 0;
}
public interface OnNoteListener{}
}
Inside the onItemClick there is one more parameter need to add.
void onItemClick(int position, Recipe recipe) {
//Delete selected recipe from recipe list
arrayList.remove(recipe)
}
The onItemClick method will get called from adapter, from there you have to pass the selected receipe. In the adapter you have to use recipeList.get(getAdapterPosition()) to get the clicked receipe and pass this to the interface method, onItemClick along with the position.
So your code will look like this way inside the adapter,
itemClickListener.onItemClick(position,
recipeList.get(getAdapterPosition()))
Just as a note, please ensure instead of List, you need to take ArrayList to perform remove operation.
I try to persist the position of items of a todo list app after I vertically draged & droped the todo items. I use a RecyclerView/FirestoreRecylerAdapter/ItemTouchHelper/Firestore. In the onMove()-Method of the ItemTouchHelper.Callback I access Firestore I guess with asyncronious calls. This leads to unwanted content changes in my RecyclerView. Imagine I have a list of 4 items with Titles A, B, C, D. Now I drag the Item with title A from position 0 (at the top) to position 1. Like this item B's title also changes to A. But this is only on the UI and not in the DB. When I change to another activity and come back then the titles are correct again. I guess it has todo with the asyncronious calls.
//part of MainActivity onCreate()
mFirestore = FirebaseFirestore.getInstance();
FirebaseFirestore.setLoggingEnabled(true);
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setPersistenceEnabled(true)
.build();
mFirestore.setFirestoreSettings(settings);
mRecyclerView = findViewById(R.id.recycler_view);
mUser = FirebaseAuth.getInstance().getCurrentUser();
if(mUser == null){
startSignIn();
}else{
Query mQuery = mFirestore.collection("todos").whereEqualTo("author", mUser.getUid()).whereEqualTo("done", false).orderBy("position", Query.Direction.ASCENDING);
FirestoreRecyclerOptions<Todo> options = new FirestoreRecyclerOptions.Builder<Todo>()
.setQuery(mQuery, Todo.class)
.build();
mAdapter = new TodoAdapter(options);
initRecyclerView();
//ItemTouchHelper.Callback method as part of initRecyclerView()
#Override
public boolean onMove(final RecyclerView recyclerView, final RecyclerView.ViewHolder source, final RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()) {
return false;
}
int fromPos = source.getAdapterPosition();
int toPos = target.getAdapterPosition();
//Change items position when an item was draged & droped vertically downwards
if(toPos > fromPos) {
DocumentReference docTo = mAdapter.getSnapshots().getSnapshot(fromPos).getReference();
docTo.update("position", toPos);
int current = fromPos+1;
while(current <= toPos){
DocumentReference docCurrent = mAdapter.getSnapshots().getSnapshot(current).getReference();
docCurrent.update("position", current-1);
current ++;
}
//Change items position when an item was draged & droped vertically upwards
}else if (toPos < fromPos){
//TODO implement
} else {
return false;
}
mAdapter.notifyItemMoved(fromPos, toPos);
return true;
}
//adapter class
public class TodoAdapter extends FirestoreRecyclerAdapter<Todo, TodoAdapter.TodoHolder> {
private static final String TAG = "DOIT_DEBUG: ";
private OnItemClickListener listener;
private View viewItem;
public TodoAdapter(#NonNull FirestoreRecyclerOptions<Todo> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull TodoHolder holder, int position, #NonNull Todo model) {
holder.textViewTitle.setText(model.getTitle());
holder.textViewDescription.setText(model.getDescription());
}
#NonNull
#Override
public TodoHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
viewItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_todo,
parent, false);
return new TodoHolder(viewItem);
}
public void deleteItem(final int position) {
getSnapshots().getSnapshot(position).getReference().delete();
}
public void setDoneItem(int position){
getSnapshots().getSnapshot(position).getReference().update("done", true);
viewItem.setEnabled(false);
}
public void setOnItemClickListener(OnItemClickListener listener){
this.listener = listener;
}
public interface OnItemClickListener{
void onItemClick (DocumentSnapshot documentSnapshot, int position);
}
public class TodoHolder extends RecyclerView.ViewHolder {
private TextView textViewTitle;
private TextView textViewDescription;
private String documentID;
public TodoHolder(View itemView) {
super(itemView);
textViewTitle = itemView.findViewById(R.id.todo_title);
textViewDescription = itemView.findViewById(R.id.todo_description);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION && listener != null){
listener.onItemClick(getSnapshots().getSnapshot(position), position);
}
}
});
}
}
//model class
public class Todo {
private String title;
private String description;
private String author;
private boolean done;
private int position;
private Date created;
public Todo(){} // Needed for Firebase
public Todo(String title, String description, String author, boolean done, int position, Date created) {
this.title = title;
this.description = description;
this.author = author;
this.done = done;
this.position = position;
this.created = created;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getAuthor() {
return author;
}
public boolean getDone() {
return done;
}
public void setTitle(String title) {
this.title = title;
}
public void setDescription(String description) {
this.description = description;
}
public void setAuthor(String author) {
this.author = author;
}
public void setDone(boolean done) {
this.done = done;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
#ServerTimestamp
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
//DB structure
Collection "todos"
Document fields: author string, created timestamp, description string, done boolean, position number, title string
I expect the drag & drop position change to be persistet in Firestore and that the RecyclerView only displays the correct move of the items and no other changes.
The way I was able to achieve this was by storing the position and updating it when necessary. If you give each item a ranking, you just need to write to an item when the item is affected.
I just have a reorder function that writes the new ranking each time the item is moved, if I move item 15 to position 1, I need to change all positions of 1-15. Depending on the frequency of the functionality, you can get clever like storing the ranking and only updating it after a few seconds of no other change (to avoid a user making 50 changes very quickly and updating each time).
It's not optimal, but persisting via the database seems to require a dedicated field and therefore this approach.
I am planning to save this ImageView :
to my RealmDatabase, but the image is not appearing when I want to retrieve it. it should appear here:
here is the my onSaveExpense code
public void onSaveExpense() {
//TODO - BITMAP EXPENSE ICONS
if (mCategoriesSpinnerAdapter.getCount() > 0 ) {
if (!Util.isEmptyField(etTotal)) {
Category currentCategory = (Category) spCategory.getSelectedItem();
String total = etTotal.getText().toString();
String description = etDescription.getText().toString();
Bitmap bitmap = BitmapFactory.decodeByteArray(currentCategory.getImgico(),0,currentCategory.getImgico().length);
ByteArrayOutputStream imgbyte = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, imgbyte);
byte[] expenseIcons = imgbyte.toByteArray();
if (mUserActionMode == IUserActionsMode.MODE_CREATE) {
//TODO - CONSTRUCTOR expenseIcons
RealmManager.getInstance().save(new Expense(description, selectedDate,expenseIcons,mExpenseType, currentCategory, Float.parseFloat(total)), Expense.class);
} else {
Expense editExpense = new Expense();
editExpense.setId(mExpense.getId());
editExpense.setTotal(parseFloat(total));
editExpense.setDescription(description);
editExpense.setIconViewer(expenseIcons);
editExpense.setCategory(currentCategory);
editExpense.setDate(selectedDate);
RealmManager.getInstance().update(editExpense);
}
// update widget if the expense is created today
if (DateUtils.isToday(selectedDate)) {
Intent i = new Intent(getActivity(), ExpensesWidgetProvider.class);
i.setAction(ExpensesWidgetService.UPDATE_WIDGET);
getActivity().sendBroadcast(i);
}
getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
dismiss();
} else {
DialogManager.getInstance().showShortToast(getString(string.error_total));
}
} else {
DialogManager.getInstance().showShortToast(getString(string.no_categories_error));
}
}
Here is my CategoriesSpinnerAdapter code
public class CategoriesSpinnerAdapter extends ArrayAdapter<Category> {
Category[] categoriesList = null;
LayoutInflater inflater;
public CategoriesSpinnerAdapter(Activity context, Category[] categoriesList) {
super(context, R.layout.spinner_ui, categoriesList);
this.categoriesList = categoriesList;
this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView, parent);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView, parent);
}
public View getCustomView(int position, View convertView, ViewGroup parent) {
View row = inflater.inflate(R.layout.spinner_ui, parent, false);
Category category = categoriesList[position];
Bitmap bitmap = BitmapFactory.decodeByteArray(category.getImgico(),0,category.getImgico().length);
ImageView imgview = (ImageView)row.findViewById(R.id.spinnerimg);
TextView title = (TextView)row.findViewById(R.id.spinnertext);
title.setText(category.getName());
imgview.setImageBitmap(bitmap);
return row;
}
here is my BaseExpenseAdapter code (The Cardview Result)
public class BaseExpenseAdapter<VH extends RecyclerView.ViewHolder> extends BaseExpenseRecyclerViewAdapter<BaseExpenseAdapter.BaseExpenseViewHolder> {
protected List<Expense> mExpensesList;
protected int lastPosition = -1;
protected int colorExpense;
protected int colorIncome;
protected String prefixExpense;
protected String prefixIncome;
private String titleTransitionName;
protected BaseViewHolder.RecyclerClickListener onRecyclerClickListener;
public BaseExpenseAdapter(Context context, BaseViewHolder.RecyclerClickListener onRecyclerClickListener) {
this.mExpensesList = ExpensesManager.getInstance().getExpensesList();
this.onRecyclerClickListener = onRecyclerClickListener;
this.colorExpense = ExpenseTrackerApp.getContext().getResources().getColor(R.color.colorAccentRed);
this.colorIncome = ExpenseTrackerApp.getContext().getResources().getColor(R.color.colorAccentGreen);
this.prefixExpense = ExpenseTrackerApp.getContext().getResources().getString(R.string.expense_prefix);
this.prefixIncome = ExpenseTrackerApp.getContext().getResources().getString(R.string.income_prefix);
this.titleTransitionName = ExpenseTrackerApp.getContext().getString(R.string.tv_title_transition);
}
#Override
public BaseExpenseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_expense_item, parent, false);
return new BaseExpenseViewHolder(v, onRecyclerClickListener);
}
#Override
public void onBindViewHolder(BaseExpenseViewHolder holder, int position) {
holder.itemView.setSelected(isSelected(position));
final Expense expense = mExpensesList.get(position);
Bitmap bitmaps = BitmapFactory.decodeByteArray(expense.getIconViewer(), 0, expense.getIconViewer().length);
holder.iconExpenses.setImageBitmap(bitmaps);
String prefix = "";
switch (expense.getType()) {
case IExpensesType.MODE_EXPENSES:
holder.tvTotal.setTextColor(colorExpense);
prefix = String.format(prefixExpense, Util.getFormattedCurrency(expense.getTotal()));
break;
case IExpensesType.MODE_INCOME:
holder.tvTotal.setTextColor(colorIncome);
prefix = String.format(prefixIncome, Util.getFormattedCurrency(expense.getTotal()));
break;
}
if (expense.getCategory() != null)holder.tvCategory.setText(expense.getCategory().getName());
if (expense.getDescription() != null && !expense.getDescription().isEmpty()) {
holder.tvDescription.setText(expense.getDescription());
holder.tvDescription.setVisibility(View.VISIBLE);
} else {
holder.tvDescription.setVisibility(View.GONE);
}
holder.tvTotal.setText(prefix);
holder.itemView.setTag(expense);
holder.iconExpenses.setImageBitmap(bitmaps);
ViewCompat.setTransitionName(holder.tvTotal, titleTransitionName);
}
#Override
public int getItemCount() {
return mExpensesList.size();
}
public void updateExpenses(List<Expense> mExpensesList) {
this.mExpensesList = mExpensesList;
notifyDataSetChanged();
}
protected void setAnimation(BaseExpenseViewHolder holder, int position) {
if (position > lastPosition) {
Animation animation = AnimationUtils.loadAnimation(ExpenseTrackerApp.getContext(), R.anim.push_left_in);
holder.itemView.startAnimation(animation);
lastPosition = position;
}
}
public static class BaseExpenseViewHolder extends BaseViewHolder {
public TextView tvCategory;
public TextView tvDescription;
public TextView tvTotal;
public ImageView iconExpenses;
public BaseExpenseViewHolder(View v, RecyclerClickListener onRecyclerClickListener) {
super(v, onRecyclerClickListener);
iconExpenses = (ImageView)v.findViewById(R.id.expenseico);
tvCategory = (TextView)v.findViewById(R.id.tv_category);
tvDescription = (TextView)v.findViewById(R.id.tv_description);
tvTotal = (TextView)v.findViewById(R.id.tv_total);
}
}
and here is my Expense entities
public class Expense extends RealmObject {
#PrimaryKey
private String id;
private byte[] iconViewer;
private String description;
private Date date;
private #IExpensesType int type;
private Category category;
private float total;
public Expense() {
}
public Expense(String description, Date date ,byte[] iconViewer, #IExpensesType int type, Category category, float total) {
this.description = description;
this.iconViewer = iconViewer;
this.date = date;
this.type = type;
this.category = category;
this.total = total;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public byte[] getIconViewer() {
return iconViewer;
}
public void setIconViewer(byte[] iconViewer) {
this.iconViewer = iconViewer;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public float getTotal() {
return total;
}
public void setTotal(float total) {
this.total = total;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}