Android databinding in an expandable listview - java

I have a very specific Question. I am using the android databinding library:
https://developer.android.com/topic/libraries/data-binding/index.html
I have a datamodel like this:
Class Participant
Public String Name;
//and so on
Public ObservableArrayList<Drink> DrinkList;
End Class
Class Drink extends BaseObservable
#Bindable
Public String Name;
#Bindable
Public Float Price;
End Class
ViewModel:
Class ParticipantList
public ObservableArrayList<Participant> list = new ObservableArrayList<>();
//some Methods to add items and concstructor
I use also Getter and Setter for all #Bindable fields.
Now I have an activity with an expandablelistview where I bind an adapter to the expandablelistview via XML and an custom #BindingAdapter like this:
//in XML for the layout
<layout xmlns:bind="http://schemas.android.com/apk/res-auto"
<data>
<variable name="Participants" type="com.example.ParticipantList"/>
</data>
<ExpandableListview>
<bind:participants=Particpants.list>
In a binding helper Class this static method
#BindingAdapter("participants")
public static void bindList(ExpandableListView view, ObservableArrayList<Participant> list) {
ParticipantListAdapter adapter = new ParticipantListAdapter(list);
view.setAdapter(adapter);
}
and finally my adapter:
Class ParticpantListAdapter Extends ExpandableListAdapter {
public ObservableArrayList<Participant> list;
#Override
public View getGroupView(..)
ParticipantListItemBinding binding = DataBindingUtil.inflate(inflater,R.layout.participant_list_item, parent, false);
binding.setParticipant(list.get(groupposition));
return binding.getRoot();
}
#Override
public View getChildView(..) {
DrinkListItemBinding binding = DataBindingUtil.inflate(inflater, R.layout.drink_list_item, parent, false);
binding.setDrink(list.get(goupposition).DrinkList.get(childposition));
return binding.getRoot();
}
I have 2 more xml LayoutFiles with data binding layout.drink_list_item + layout.participant_list_item which are bound to the participant and to the drink class.
I skipped a lot of code which is not really important here.
All is working fine except that I have a button in my layout.participant_list_item which will open a new activity where I can add a new drink to my drinklist in my dataclasses. When I finish the activity a new child should be added to the group. But it will only show after I collapse and expands the group once. Usually databinding does this for me when I call NotifyPropertychanged for a #Bindable field or if I add a new item to a directly bound ObservableArrayList. But this is not the case. I add an item to an ObservableArrayList of an item in an ObservableArrayList. Is there a way to refresh only the children of that group? Or is my approach somehow wrong?
My dirty solution is at the moment that I tell my main binding of the activity in its onResume event that if a certain condition is met, that it should refresh all bindings which leads to a complete redraw of the ExpandableListView Activity and all items will be collapsed again (which is not a desired behavior).
I wrote all the code from my memory since I m not at my development place at moment. But this problem bothers me since days and I could not find a decent example of Databinding and ExpandableListView so far. If you need more information just ask.
I am glad for any hints which helps me to find a nice solution.
This is my first question here so please be kind if the post is not meeting all the requirements.
Thank you in advance
//Edit. Edited some Code. Is anybody here that has any helpful comment? Do you need more code or how can I get some tips..

Try this..
public class IExpandableListViewAdapter extends BaseExpandableListAdapter {
#Override
public int getGroupCount() {
return 0;
}
#Override
public int getChildrenCount(int i) {
return 0;
}
#Override
public Object getGroup(int i) {
return null;
}
#Override
public Object getChild(int i, int i1) {
return null;
}
#Override
public long getGroupId(int i) {
return 0;
}
#Override
public long getChildId(int i, int i1) {
return 0;
}
#Override
public boolean hasStableIds() {
return false;
}
#Override
public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) {
if(view == null) {
parentBinding = DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.basket_list_layout, viewGroup, false);
view = parentBinding.getRoot();
}
else {
parentBinding = (BasketListLayoutBinding)view.getTag();
}
parentBinding.setBasketParentModel(parent.get(i));
view.setTag(parentBinding);
return view;
}
#Override
public View getChildView(int i, int i1, boolean b, View view, ViewGroup viewGroup) {
final int index = i;
if(view == null) {
childBinding = DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.basket_detail_layout, viewGroup, false);
view = childBinding.getRoot();
}
else {
childBinding = (BasketDetailLayoutBinding) view.getTag();
}
childBinding.setBasketChildModel(parent.get(i).getBasketChildModel());
view.setTag(childBinding);
return view;
}
#Override
public boolean isChildSelectable(int i, int i1) {
return false;
}
}

Related

RecyclerView row flashes/blinks when item inserted

Any time I insert an item into my Room database, my recycler view flashes the row containing the newly inserted view holder (the rest of the list does not flash). I am using LiveData to keep my list automatically updated by calling submitList(list) in onChanged() of the observed ViewModel method. My adapter extends ListAdapter and I am using DiffUtil to track changes in the list. That being said, I don't call notifyItemInserted(position) directly as DiffUtil should do this for me. There are 2 instances where an item gets inserted (1) a completely new item gets inserted at the end of the list (2) a deleted item gets reinserted into the list. Both cases the item will insert itself then flash. I have read numerous posts where people suggest disabling animations on the recycler view but this is not an option for me as I rely on animations elsewhere in my code. Any other suggestions would be appreciated. I'm trying to keep the posted code brief but I can post more if it would be helpful.
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initComponents();
initRecyclerView();
setListeners();
setListObserver();
createItemTouchHelper();
}
private void setListObserver() {
viewModel.getAllItems().observe(this, new Observer<List<ListItem>>() {
#Override
public void onChanged(List<ListItem> newList) {
adapterMain.submitList(newList);
}
});
}
...
// Inserts a new ListItem when MainActivity's EditText is used
public void onClick(View v) {
if (v.getId() == R.id.img_add_item_main) {
String itemName = String.valueOf(edtAddItem.getText());
if (!itemName.trim().equals("")) { // Insert new list item only if the EditText is not empty
ListItem item = new ListItem();
item.setItemName(itemName);
viewModel.insert(item);
}
...
// SnackBar to allow a user to undo a delete operation
public void showUndoSnackBar(ListItem deletedItem) {
Snackbar undoSnackBar = Snackbar.make(constraintLayout, "Undo deleted Item",
Snackbar.LENGTH_LONG).setAction("UNDO", new View.OnClickListener() {
#Override
public void onClick(View v) {
// Restore deleted item to its original position in the list if UNDO is clicked
viewModel.insert(deletedItem);
}
});
undoSnackBar.show();
}
RecyclerAdapterMain.java
public class RecyclerAdapterMain extends ListAdapter<ListItem, RecyclerAdapterMain.ListItemHolder> {
public RecyclerAdapterMain() {
super(DIFF_CALLBACK);
}
private static final DiffUtil.ItemCallback<ListItem> DIFF_CALLBACK = new DiffUtil.ItemCallback<ListItem>() {
#Override
public boolean areItemsTheSame(#NonNull ListItem oldItem, #NonNull ListItem newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull ListItem oldItem, #NonNull ListItem newItem) {
return oldItem.equals(newItem);
}
#Override
public ListItemHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_item_layout_main, parent, false);
return new ListItemHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ListItemHolder holder, int position) {
ListItem item = getItem(position);
holder.txtItemName.setText(item.getItemName());
holder.checkBox.setChecked(item.getIsChecked());
if(item.getIsChecked()) {
holder.txtItemName.setTextColor(Color.LTGRAY);
} else {
holder.txtItemName.setTextColor(Color.BLACK);
}
}
...
ListItem.java (POJO)
#Entity(tableName = "list_item_table")
public class ListItem {
#PrimaryKey(autoGenerate = true)
private long id;
private String itemName;
private boolean isChecked;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
... other getters and setters
public boolean equals(#Nullable ListItem listItem) {
return this.itemName.equals(listItem.getItemName()) && this.isChecked == listItem.getIsChecked();
}
}
I determined that the DefaultItemAnimator class (which is the animator for RecyclerView at the time of this writing) has a method called animateAdd(final RecyclerView.ViewHolder holder) that sets the holder's alpha to 0 then animates it to 1 over time. I verified that this was the cause of the blinking by changing the default setting to 1. I solved the problem by a using a combination of the accepted answer from this StackOverflow post and the documentation for DefaultItemAnimator.
First, I created a new animator class that extends DefaultItemAnimator in order to override the animateAdd() method. Under the section for animateAdd(), the documentation states, "Called when an item is added to the RecyclerView. Implementors can choose whether and how to animate that change, but must always call dispatchAddFinished(RecyclerView.ViewHolder) when done, either immediately (if no animation will occur) or after the animation actually finishes." I called dispatchAddFinished() immediately to avoid the add animation. Instead of disabling animations altogether, all animations are present except for the problematic one.
MyRecyclerViewAnimator.java
public class MyRecyclerViewAnimator extends DefaultItemAnimator {
#Override
public boolean animateAdd(RecyclerView.ViewHolder holder) {
dispatchAddFinished(holder); // this is what bypasses the animation
return true;
}
/* this is the default implementation of animateAdd() in DefaultItemAnimator
#Override
public boolean animateAdd(final RecyclerView.ViewHolder holder) {
resetAnimation(holder);
holder.itemView.setAlpha(0); // this is what caused the flashing/blinking
mPendingAdditions.add(holder);
return true;
}
*/
}
MainActivity.java
...
recyclerMain.setItemAnimator(new MyRecyclerViewAnimator()); // set the default animator to your extended class
...

Removing an item from a nested RecyclerView

I've been duelling with this problem for a good few hours now. I have a nested RecyclerView (i.e. a RecyclerView that encompasses an inner Recycler view). Both the parent and child recycler view's are dynamic. The problem I encounter is that I cannot find a way to correctly notify the child (inner) recycler view when a CRUD, in particular a delete, occurs. At first it works ok, but then I get all sorts of random errors from "You must be a direct descend view" or getAdapterPosition returning -1 or just simply incorrect positions. I think my implementation is pretty standard so I ask what is the correct way to notify the inner recycler view.
I am pretty close to returning to my former implementation which involved an array of fragments each containing a recycling view, but I question about the performance of such design. My code is as follows:
Parent RecyclerView
public class RecipeRecyclerAdapter extends RecyclerView.Adapter<RecipeRecyclerAdapter.ViewHolder>
{
public interface OnRecipeRecyclerListener
{
//--------------------------- Proxy methods for OnDishRecyclerListener -----------------
void renameDish(int DishPosition, int RecipePosition);
void deleteDish(int DishPosition, int RecipePosition);
//--------------------------- OnRecipeRecyclerListener methods ----------------------------
void deleteRecipe(int RecipePosition);
void renameRecipe(int RecipePosition);
}
//Recycler Pool and tools
private RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
//Recycler Parameters
private ArrayList<Recipe> allRecipes;
private Context context;
//Listener
#Setter
private OnRecipeRecyclerListener onRecipeRecyclerListener;
public RecipeRecyclerAdapter(Context context, ArrayList<Recipe> allRecipes)
{
this.allRecipes = allRecipes;
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_Recipe, parent, false);
return new RecipeRecyclerAdapter.ViewHolder(view, onRecipeRecyclerListener, context);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position)
{
Recipe Recipe = allRecipes.get(position);
holder.RecipeName.setText(Utils.colourFirstLetter(context, Recipe.getRecipeName(), R.color.progressFxBar));
holder.RecipeDate.setText(Utils.getDate(Recipe.getTimestamp()));
// Create layout manager with initial prefetch item count
LinearLayoutManager layoutManager = new LinearLayoutManager(
holder.DishsRecycler.getContext(),
LinearLayoutManager.VERTICAL,
false
);
layoutManager.setInitialPrefetchItemCount(Recipe.getDishs().size());
DishRecyclerAdapter DishsRecyclerAdapter = new DishRecyclerAdapter(Recipe.getDishs(), holder, context);
holder.DishsRecycler.setLayoutManager(layoutManager);
holder.DishsRecycler.setAdapter(DishsRecyclerAdapter);
holder.DishsRecycler.setRecycledViewPool(viewPool);
}
#Override
public int getItemCount()
{
return allRecipes.size();
}
static class ViewHolder extends RecyclerView.ViewHolder implements DishRecyclerAdapter.OnDishRecyclerListener
private OnRecipeRecyclerListener onRecipeRecyclerListener;
private Context context;
TextView RecipeName, RecipeDate;
ImageView addDish;
//The Dishs Recycler
RecyclerView DishsRecycler;
public ViewHolder(#NonNull View itemView, OnRecipeRecyclerListener onRecipeRecyclerListener, Context context)
{
super(itemView);
this.onRecipeRecyclerListener = onRecipeRecyclerListener;
this.context = context;
RecipeName = itemView.findViewById(R.id.RecipeName);
RecipeDate = itemView.findViewById(R.id.RecipeDate);
addDish = itemView.findViewById(R.id.addDish);
DishsRecycler = itemView.findViewById(R.id.DishsRecyclerView);
loadListeners(itemView);
}
private void loadListeners(#NonNull View initView)
{
RecipeName.setOnClickListener(v ->
{
PopupMenu popup = new PopupMenu(context, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.Recipe_floating_menu, popup.getMenu());
popup.show();
popup.setOnMenuItemClickListener(item ->
{
switch (item.getItemId())
{
case R.id.menuDeleteRecipe:
onRecipeRecyclerListener.deleteRecipe(getAdapterPosition());
return true;
case R.id.menuRenameRecipe:
onRecipeRecyclerListener.renameRecipe(getAdapterPosition());
return true;
case R.id.menuRecipeProps:
onRecipeRecyclerListener.RecipeProps(getAdapterPosition());
return true;
default:
return false;
}
});
});
addDish.setOnClickListener(v ->
{
onRecipeRecyclerListener.addDish(getAdapterPosition());
});
}
//******************************* OnDishRecyclerListener *******************************
#Override
public void renameDish(int position)
{
onRecipeRecyclerListener.renameDish(position, getAdapterPosition());
}
#Override
public void deleteDish(int position)
{
onRecipeRecyclerListener.deleteDish(position, getAdapterPosition());
}
}
}
Child (inner) RecyclerView
public class DishRecyclerAdapter extends RecyclerView.Adapter<DishRecyclerAdapter.ViewHolder>
{
public interface OnDishRecyclerListener
{
void renameDish(int position);
void deleteDish(int position);
}
private OnDishRecyclerListener onDishRecyclerListener;
private ArrayList<Dish> allDishs;
private Context context;
public DishRecyclerAdapter(ArrayList<Dish> allDishs, OnDishRecyclerListener onDishRecyclerListener, Context context)
{
this.onDishRecyclerListener = onDishRecyclerListener;
this.allDishs = allDishs;
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_Dishs, parent, false);
return new ViewHolder(context, view, onDishRecyclerListener);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position)
{
Dish Dish = allDishs.get(position);
holder.DishName.setText(Dish.getDishName());
}
#Override
public int getItemCount()
{
return allDishs.size();
}
public class ViewHolder extends RecyclerView.ViewHolder
{
private Context context;
TextView DishName; //plus a bunch of other Views I just removed for the sake of simplicity
OnDishRecyclerListener onDishRecyclerListener;
public ViewHolder(Context context, #NonNull View itemView, OnDishRecyclerListener onDishRecyclerListener)
{
super(itemView);
this.context = context;
DishName = itemView.findViewById(R.id.DishName);
this.onDishRecyclerListener = onDishRecyclerListener;
loadListeners(itemView);
}
private void loadListeners(#NonNull View v)
{
//Rename an Dish
DishName.setOnClickListener(view ->
{
PopupMenu popup = new PopupMenu(context, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.Dish_floating_menu, popup.getMenu());
popup.show();
popup.setOnMenuItemClickListener(item ->
{
switch (item.getItemId())
{
case R.id.menuDeleteDish:
onDishRecyclerListener.deleteDish(getAdapterPosition());
return true;
case R.id.menuRenameDish:
onDishRecyclerListener.renameDish(getAdapterPosition());
return true;
case R.id.menuDishProps:
return true;
default:
return false;
}
});
});
}
}
}
An extraction of the fragment calling the parent recycler view:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_Recipe_panel, container, false);
recyclerRecipe = view.findViewById(R.id.RecipeRecyclerView);
SimpleItemAnimator simpleItemAnimator = (SimpleItemAnimator) recyclerRecipe.getItemAnimator();
if(simpleItemAnimator !=null)
{
simpleItemAnimator.setSupportsChangeAnimations(true);
}
RecipeAdapter = new RecipeRecyclerAdapter(getContext(), allRecipes);
RecipeAdapter.setOnRecipeRecyclerListener(this);
//recyclerRecipe.setHasFixedSize(true);
recyclerRecipe.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerRecipe.setAdapter(RecipeAdapter);
return view;
}
public void createRecipe(String RecipeName)
{
Recipe Recipe = new Recipe(RecipeName, getContext());
allRecipes.add(0,Recipe);
RecipeAdapter.notifyItemInserted(0);
}
#Override
public void deleteRecipe(int RecipePosition)
{
allRecipes.remove(RecipePosition);
RecipeAdapter.notifyItemRemoved(RecipePosition);
}
#Override
public void addDish(int RecipePosition)
{
allRecipes.get(RecipePosition).getDishs().add(new Dish(DishName));
RecipeAdapter.notifyItemChanged(RecipePosition);
}
#Override
public void deleteDish(int DishPosition, int RecipePosition)
{
Recipe Recipe = allRecipes.get(RecipePosition);
Dish Dish = Recipe.getDishs().get(DishPosition);
Dish.getTimer().destroyTimer();
Recipe.getDishs().remove(DishPosition);
RecipeAdapter.notifyItemChanged(RecipePosition);
}
I figured out what the problem was (after LOADS OF HOURS). I needed to notify first the parent recycler and then the child recycler in that order.
//adding an item to the inner list
recipeAdapter.notifyItemChanged(recipePosition);
dishsRecycler.getAdapter().notifyItemInserted(recipe.getDishs().size()-1);
//deleting an inner list item
recipeAdapter.notifyItemChanged(recipePosition);
dishsRecycler.getAdapter().notifyItemRemoved()
However the biggest culprit was having a common recyclerPool for all the inner recyclerviews, so removed this line from the code
//REMOVED THESE LINES
private RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
holder.DishsRecycler.setRecycledViewPool(viewPool);
Also, I refrained from using notifyDataSet() as that for some reason throws NO_POSITION (-1).
I'm implementing a similar case.
I have 2 RecyclerViews, one nested. Where you can delete items either from nested or parent RecyclerView.
It guess you must update Recyclers every time an item changed or removed.
For comprehension I read this article first:
https://medium.com/android-news/recyclerview-optimisations-a4b141dd433d
And I agree answer by Ken John, when he said you need to notify RecyclerView updates first to parent then to nested; otherwise you get an error and your app will crash.
However, other important thing is how to do the notification updates.
For the nested RecyclerView, I used
// for items updated
notifyItemChanged(position);
// for items deleted
notifyItemRemoved(position);
but the mentioned above not working fine for parent RecyclerView, really I'm not sure why, but I solved as follow:
// for items updated
notifyItemChanged(position);
// for items deleted
notifyItemRemoved(position); // this line does not work for me
notifyDataSetChanged(); // it works fine
The last instruction spend a more bit of time, but works fine.
Note: I don't know yet why notifyItemRemoved(position) doesn't work for parent, and I have call notifyDataSetChanged()

Strange NullPointerException in my own ArrayAdapter

I'm beginner in programming in Android Studio and I'm making now some kind of messenger via bluetooth. So I have my own ArrayAdapter class which extends ArrayAdapter class and it is for outgoing and incoming messages. I want incoming messages to be at the left side ang outgoing ones at the right, so I made two layouts for this Adapter. I know, that on stackoverflow there is a lot of solutions to make ArrayAdapter with few diffrent layouts for each row, but every one of them doesn't work - changing layouts cause change view of every row. So my solution is to make another ArrayList of booleans, and in getView() I check what I have in this List - true or false - and use right layout on that row in ArrayAdapter (I'm checking it by position field from getView()). And when I send a lot of messages to second device and try to response to first device there is NullPointerException in line with
(TextView)row.findViewById(R.id.singleIncomingMessage);
or
(TextView)row.findViewById(R.id.singleOutgoingMessage);
This exceptions seems to appear in random situation, but of course there must be some pattern. Here's the whole code. What it's wrong? And I'm sorry for my language if there is some misspells ;)
public class MyArrayAdapter extends ArrayAdapter<String> {
public MyArrayAdapter(Context context, ArrayList<String> list) {
super(context, 0, list);
this.list =list;
this.context=context;
}
ArrayList<String> list;
Context context;
#Override
public int getCount() {
return list.size();
}
#Override
public String getItem(int position) {
return list.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
if(Messenger.inOut){
return 1;
}
else{
return 0;
}
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int type=getItemViewType(position);
View row=convertView;
if(row==null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(Messenger.inOutList.get(position)==0){
row=inflater.inflate(R.layout.outgoing_message_layout, parent, false);
}
if(Messenger.inOutList.get(position)==1){
row=inflater.inflate(R.layout.incoming_message_layout, parent, false);
}
}
String message=getItem(position);
TextView label;
if(Messenger.inOutList.get(position)==0){
label=(TextView)row.findViewById(R.id.singleOutgoingMessage);
label.setText(message);
}
if(Messenger.inOutList.get(position)==1){
label=(TextView)row.findViewById(R.id.singleIncomingMessage);
label.setText(message);
}
return row;
}
}
I think the issue is from the case where row is non-null. It may have previously been inflated as an outgoing message layout, now you are recycling it and trying to treat it as an incoming message layout, so it can't find the TextView.
It's hard to say for sure this is the issue though since I don't really know how the Messenger.* calls behave in your code. Since you already get type in getView you should use that rather than Messenger.inOutList.get(position)==X to determine which view logic to use if you keep it this way. This question has some good answers on how to do this consistently.
Also keep in mind that for this to work getItemViewType must always return the same type for a given position, or else you have to detect the change and inflate a new layout in getView. If Messenger.inOut is constant, there's not reason to use this format (multi-layout format). If it's not a constant and it gets changed, then you need to detect this in getView
Okay, thank you very much, I finally did it. I was using two list (of booleans and messages) and after I read about ArrayAdapter I decided to make class with this two fields and make a list of its object. That error was probably because of that second list of booleans. Here's below my final code if anyone have similiar problem.
public class MyArrayAdapter extends ArrayAdapter<MessageWithType> {
public MyArrayAdapter(Context context, ArrayList<MessageWithType> list) {
super(context, 0, list);
this.list =list;
this.context=context;
}
public static final int TYPE_OUT = 0;
public static final int TYPE_IN = 1;
ArrayList<MessageWithType> list;
Context context;
#Override
public int getCount() {
return list.size();
}
#Override
public MessageWithType getItem(int position) {
return list.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
if(list.get(position).inOut){
return 1;
}
else{
return 0;
}
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MessageWithType item=getItem(position);
int type=getItemViewType(position);
View row=convertView;
ViewHolder viewHolder=null;
if(row==null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(type==TYPE_OUT){
row=inflater.inflate(R.layout.outgoing_message_layout, parent, false);
}
if(type==TYPE_IN){
row=inflater.inflate(R.layout.incoming_message_layout, parent, false);
}
TextView label=(TextView)row.findViewById(R.id.singleMessage);
viewHolder=new ViewHolder(label);
row.setTag(viewHolder);
}
else{
viewHolder=(ViewHolder)row.getTag();
}
viewHolder.textView.setText(item.message);
return row;
}
public class ViewHolder{
TextView textView;
public ViewHolder(TextView textView){
this.textView=textView;
}
}
}

Android: Is there a way to add an object to a spinner and display only a property of it?

I have an array called spinnerItems populated by objects of the type DateSpinnerItem. I am passing it to an adapter to be used by a Spinner.
My DataSpinnerItem objects have three properties: Label, Date1 and Date2.
My question is, is there a way for me to add them to an adapter, but in the options show only the object.Label?
My DataSpinnerItem class:
public class DateSpinnerItem {
public String Label;
public String dateInicioStringValue;
public String dateFinalStringValue;
public DateSpinnerItem(){
}
}
My adapter:
ArrayAdapter<DateSpinnerItem> adapter = new ArrayAdapter<DateSpinnerItem>(this, android.R.layout.simple_spinner_dropdown_item) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
//((TextView) v).setGravity(Gravity.CENTER);
return v;
}
#Override
public int getCount() {
return super.getCount();
}
public View getDropDownView(int position, View convertView,ViewGroup parent) {
View v = super.getDropDownView(position, convertView,parent);
((TextView) v).setGravity(Gravity.CENTER);
return v;
}
};
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
for (int i=0; i<spinnerItems.length; i++){
adapter.add(spinnerItems[i]);
}
sp.setAdapter(adapter);
sp.setSelection(adapter.getCount()-1); //display hint
Thanks in advance!
Found a solution! It was actually a lot simpler than I thought.
Just had to override the toString method from my custom class (DataSpinnerItem) to return the Label:
public class DateSpinnerItem {
public String Label;
public Date dateInicioStringValue;
public Date dateFinalStringValue;
public DateSpinnerItem(){
}
#Override
public String toString() {
return Label;
}
}
That way, I can just add the DataSpinnerItems to the adapter and thanks to the toString() override it shows the label for me representing the added object!

How to handle checkboxes in a listview and send information back from a custom base adapter

I have a list view and each item contains a check box and other various text views. In the Main Activity I have an ArrayList of objects called listOfStuff. From the main activity I'm defining and using a custom base adapter. In the getView method I defined a listener for the check box like so:
holder.cbCompletionStatus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(holder.cbCompletionStatus.isChecked()){
listOfStuff.get(position).setComplete(1);
} else {
listOfStuff.get(position).setComplete(0);
};
}
});
My problem is that I don't know how to access the listOfStuff and the objects within it to modify the information within. The code in the if/else statement hopefully gives an idea what I was trying to do. Just a quick warning, I'm not only new to Android and Java, but to programming field on the whole. Thanks.
UPDATE:
So I ended up figuring this out on my own. I just had to make the listOfStuff Arraylist a static in the Main Activity. Then I could call a static function in the Main Activity to manipulate whatever data in the Array list I needed like so:
MainActivity.checkBoxClicked(result, position);
Here is my class:
class ImageInfoAdapter extends BaseAdapter{
#Override
public int getCount() {
if(viewcount == 0){
return 0;
}
return viewcount;
}
#Override
public Object getItem(int position) {
return isSentAlList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View view, ViewGroup parent) {
final ViewHolder viewHolder;
View rowView=view;
if(rowView==null){
LayoutInflater layoutinflate = LayoutInflater.from(ListPictures.this);
rowView=layoutinflate.inflate(R.layout.listviewayout, parent, false);
viewHolder = new ViewHolder();
viewHolder.textViewisSentFlag = (TextView)rowView.findViewById(R.id.textViewisSentFlag);
viewHolder.imageViewToSent = (ImageView)rowView.findViewById(R.id.imageViewToSent);
viewHolder.checkBoxToSend = (CheckBox)rowView.findViewById(R.id.checkBoxToSend);
rowView.setTag(viewHolder);
} else{
viewHolder = (ViewHolder) rowView.getTag();
}
viewHolder.ref = position;
Log.i("InfoLog","viewHolder.ref = position; "+viewHolder.ref);
viewHolder.textViewisSentFlag.setText(isSentAlList.get(position));
Bitmap blob = BitmapFactory.decodeByteArray(imageAlList.get(position), 0, imageAlList.get(position).length);
viewHolder.imageViewToSent.setImageBitmap(blob);
viewHolder.checkBoxToSend.setClickable(true);
if(checked.containsKey(""+viewHolder.ref)){ ///if this id is present as key in hashmap
Log.i("InfoLog","checked.containsKey "+viewHolder.ref);
if(checked.get(""+viewHolder.ref).equals("true")){ //also check whether it is true or false to check/uncheck checkbox
Log.i("InfoLog","checked.get(position) "+viewHolder.ref);
viewHolder.checkBoxToSend.setChecked(true);
} else
viewHolder.checkBoxToSend.setChecked(false);
} else
viewHolder.checkBoxToSend.setChecked(false);
viewHolder.checkBoxToSend.setOnCheckedChangeListener(new OncheckchangeListner(viewHolder));
return rowView;
}//End of method getView
}//End of class ImageInfo
class ViewHolder{
private TextView textViewisSentFlag = null;
private ImageView imageViewToSent = null;
private CheckBox checkBoxToSend = null;
int ref;
}//End of class ViewHolder
/////////////////////////
and here is my oncheckchangedlistener
////////////////////////
class OncheckchangeListner implements OnCheckedChangeListener{
ViewHolder viewHolder = null;
public OncheckchangeListner(ViewHolder viHolder) {
viewHolder = viHolder;
}
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if(viewHolder.checkBoxToSend.equals(buttonView)) {
if(!isChecked) {
Log.i("InfoLog","checked.get before "+checked.get(""+viewHolder.ref));
checked.put(""+viewHolder.ref,"false");
Log.i("InfoLog","checked.get after "+checked.get(""+viewHolder.ref));
} else
checked.put(""+viewHolder.ref,"true");
} else
Log.i("InfoLog","i m in checkchange ");
}
}

Categories