I use a custom leaderboard adapter to bind the data to a recyclerview. The data is parsed like so (the data is fetched from a database, binded post execute):
for (int i = 0; i < arr.length(); ++i)
{
JSONObject obj = arr.getJSONObject(i);
String fname = obj.getString("fname");
String lname = obj.getString("lname");
int elo = Integer.parseInt(obj.getString("elo"));
int hotstreak = Integer.parseInt(obj.getString("hotstreak"));
// Add to player array
TennisUser player = new TennisUser(fname, lname, elo, hotstreak);
players.add(player);
}
Collections.sort(players, (p1, p2) -> p2.getElo() - p1.getElo());
recyclerView = findViewById(R.id.ladder_recyclerview);
LadderAdapter ladderAdapter = new LadderAdapter(getApplicationContext(), players);
recyclerView.setAdapter(ladderAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyclerView.addItemDecoration(new DividerItemDecoration(getApplicationContext(), DividerItemDecoration.VERTICAL));
If the player is on a hotstreak, indicated by 1, I set the image source to the hotstreak icon in the following code:
#Override
public void onBindViewHolder(#NonNull LadderViewHolder holder, int position) {
holder.rank.setText(String.valueOf(position + 1));
holder.fname.setText(p.get(position).getFname());
holder.lname.setText(p.get(position).getLname());
if (p.get(position).getHotstreak() == 1)
{
holder.hotstreak.setImageResource(R.drawable.hot_streak);
}
}
For testing purposes, only the first user in the list has a hotstreak, I have checked the array values after parsing and the data is correct (only the first user with hotstreak = 1). However for some reason, if anyone in the list has a hotstreak, the app also displays the last in the list with a hotstreak as well. I have debugged the process and it only enters the if statement when the condition is met. The imageview xml is:
<ImageView
android:id="#+id/img_hotstreak"
android:layout_width="20dp"
android:layout_height="22dp"
android:layout_marginStart="2dp"
android:layout_marginLeft="2dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
app:layout_constraintBottom_toTopOf="#+id/lname_text"
app:layout_constraintStart_toEndOf="#+id/fname_text"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="#tools:sample/avatars" />
I can provide any more detail necessary, I'm just not sure if I'm missing something glaringly obvious. Thanks!
CustomAdapter MyClassAdapter.class
private static class ViewHolder {
TextView Id;
ImageView Image;
TextView Name;
TextView Description;
TextView Type;
TextView Cost;
TextView Count;
TextView Comment;
Button Buttonup;
Button Buttondown;
}
public MyClassAdapter(Context context, int textViewResourceId, ArrayList<Plate> items) {
super(context, textViewResourceId, items);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final Plate item = getItem(position);
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(this.getContext())
.inflate(R.layout.item_main, parent, false);
viewHolder = new ViewHolder();
viewHolder.Id = (TextView)convertView.findViewById(R.id.code);
viewHolder.Image = (ImageView) convertView.findViewById(R.id.image);
viewHolder.Name = (TextView) convertView.findViewById(R.id.name);
viewHolder.Description = (TextView) convertView.findViewById(R.id.description);
viewHolder.Cost = (TextView) convertView.findViewById(R.id.price);
viewHolder.Count = (TextView) convertView.findViewById(R.id.count);
viewHolder.Buttonup = (Button) convertView.findViewById(R.id.button_up);
viewHolder.Buttondown = (Button) convertView.findViewById(R.id.button_down);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (item!= null) {
viewHolder.Id.setText(String.format("%d",item.getId()));
viewHolder.Image.setImageURI(item.getImage());
viewHolder.Name.setText(String.format("%s", item.getName()));
viewHolder.Description.setText(String.format("%s", item.getDescription()));
viewHolder.Name.setText(String.format("%s", item.getName()));
viewHolder.Cost.setText(String.format("%s", item.getCost()));
viewHolder.Count.setText(String.format("%d", item.getCount()));
viewHolder.Buttonup.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DBHelper mydb= new DBHelper(getContext());
mydb.AddPlate(item.getId());
item.CountUp();
//update viewholder.Count
}
});
viewHolder.Buttondown.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
DBHelper mydb= new DBHelper(getContext());
mydb.RemovePlate(item.getId());
item.CountDown();
//update viewholder.Count
}
});
}
return convertView;
}
// And calls the custom ArrayAdapter from Activity
ArrayList<Plate> FullMenu;
FullMenu = mydb.getPlates("Entrees");
Plate p;
int i;
MyClassAdapter adapter = new MyClassAdapter(this,0,FullMenu);
ListView listView = (ListView) findViewById(R.id.list);
listView.setAdapter(adapter);
for (i=0; i < FullMenu.size(); i++) {
p = FullMenu.get(i);
adapter.add(p);
}
Related
I am using RecyclerView and new items appear in it only at the top of the list. I want to use default insert animation for this action, but it breaks OnClick() method inside ViewHolder.
If I have inserted new item, OnClick() uses data from the previous item of ArrayList. If I use NotifyDataSetChanged(), data is ok, but of course, there is no animation. Looks like there is problem with ViewHolder.bind() method being not called. How can I update index of every item after NotifyItemInserted(0)?
RecyclerAdapter.java
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
private Context context;
private LayoutInflater inflater;
private ArrayList<Song> data = new ArrayList<>();
//то, что ниже - для анимации
private static final int UNSELECTED = -1;
private RecyclerView recyclerView;
private int selectedItem = UNSELECTED;
public RecyclerAdapter(Context context, ArrayList<Song> data, RecyclerView recyclerView) {
this.context = context;
inflater = LayoutInflater.from(context);
this.data = data;
this.recyclerView = recyclerView;
}
public void insert(Song song)
{
data.add(0, song);
recyclerView.scrollToPosition(0);
notifyItemInserted(0);
}
public void swap(ArrayList<Song> datas){
data.clear();
data.addAll(datas);
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView tv_songName;
TextView tv_songGenreID;
TextView tv_songUserInfo;
ExpandableLayout expandableLayout;
private CardView expandButton;
private int position;
private TextView youtubeButton;
private TextView shareButton;
public ViewHolder(View itemView) {
super(itemView);
tv_songName = (TextView) itemView.findViewById(R.id.tv_songName);
tv_songGenreID = (TextView) itemView.findViewById(R.id.tv_songGenreID);
tv_songUserInfo = (TextView) itemView.findViewById(R.id.tv_songUserInfo);
youtubeButton = (TextView) itemView.findViewById(R.id.youtube_button);
shareButton = (TextView) itemView.findViewById(R.id.share_button);
expandableLayout = (ExpandableLayout) itemView.findViewById(R.id.expandable_layout);
expandableLayout.setInterpolator(new OvershootInterpolator());
expandButton = (CardView) itemView.findViewById(R.id.card_view);
expandButton.setOnClickListener(this);
youtubeButton.setOnClickListener(this);
shareButton.setOnClickListener(this);
}
public void bind(int position) {
this.position = position;
Song current = data.get(position);
StringBuilder songInfo = new StringBuilder();
songInfo.append(context.getString(R.string.genre));
songInfo.append(current.songGenreID);
songInfo.append(System.getProperty("line.separator"));
songInfo.append(context.getString(R.string.album));
this.tv_songName.setText(current.songName);
this.tv_songGenreID.setText(songInfo);
this.tv_songUserInfo.setText(current.songUserInfo);
expandButton.setSelected(false);
expandableLayout.collapse(false);
}
#Override
public void onClick(View view) {
switch (view.getId())
{
case R.id.youtube_button:
String youtubeURL = data.get(position).songName.replaceAll(" ", "%20");
youtubeURL = "https://www.youtube.com/results?search_query=" + youtubeURL;
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(youtubeURL));
context.startActivity(browserIntent);
break;
case R.id.share_button:
String songName = data.get(position).songName;
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, songName);
shareIntent.setType("text/plain");
context.startActivity(shareIntent);
break;
default:
ViewHolder holder = (ViewHolder) recyclerView.findViewHolderForAdapterPosition(selectedItem);
if (holder != null) {
holder.expandButton.setSelected(false);
holder.expandableLayout.collapse();
}
if (position == selectedItem) {
selectedItem = UNSELECTED;
} else {
expandButton.setSelected(true);
expandableLayout.expand();
selectedItem = position;
}
break;
}
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v=inflater.inflate(R.layout.recycler_item, parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(position);
setAnimation(holder.itemView, position);
}
private int lastPosition = -1;
private void setAnimation(View viewToAnimate, int position) {
if (position > lastPosition) {
Animation anim = AnimationUtils.loadAnimation(context, android.R.anim.fade_in);
viewToAnimate.startAnimation(anim);
lastPosition = position;
}
}
#Override
public int getItemCount() {
return data.size();
}
}
OnCreate() of Activity.java
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme_NoActionBar);
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
layout = (CoordinatorLayout)findViewById(R.id.coordinator1);
FloatingActionButton myFab = (FloatingActionButton) findViewById(R.id.floatingActionButton);
mRecyclerView = (RecyclerViewEmptySupport) findViewById(R.id.my_recycler_view);
mRecyclerView.setEmptyView(findViewById(R.id.list_empty));
myFab.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FragmentManager fm = getSupportFragmentManager();
AddSongDialogFragment addSongDialogFragment = new AddSongDialogFragment();
addSongDialogFragment.show(fm, "add_song");
}
});
mAdapter = new RecyclerAdapter(MainActivity.this, data, mRecyclerView);
mLayoutManager = new LinearLayoutManager(MainActivity.this);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(mLayoutManager);
dbHelper = new DBHelper(this);
mAdapter.swap(dbHelper.readFromDB());
RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator();
itemAnimator.setAddDuration(700);
itemAnimator.setRemoveDuration(1000);
mRecyclerView.setItemAnimator(itemAnimator);
}
Use getAdapterPosition() to get correct position of your item from your VIewHolder. That way the positions will not be mixed up after moving/deleting items from the RecyclerView.
NOTE: If the user clicks on the empty space during the animationgetAdapterPosition() might return -1: make sure to handle the case.
Good luck
You can use notifyItemRangeChanged(position, data.size()); to notify the other items after you added the new item.
You can also animate the swap() function if you use the functions notifyItemRemoved() notifyItemInserted() and notifyItemMoved() instead of notifyDataSetChanged(). You only have to check if the new list includes the items of the old list.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
I have a problem, been searching since yesterday, I post the code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_wishes);
mListView = (ListView) findViewById(R.id.listId);
refreshData();
}
private void refreshData() {
wishes.clear();
dbHandler = new DatabaseHandler(getApplicationContext());
ArrayList<MyWishes> wishesFromDB = dbHandler.getWishes();
for (int i = 0; i < wishesFromDB.size(); i++){
String title = wishesFromDB.get(i).getTitle();
String content = wishesFromDB.get(i).getContent();
String date = wishesFromDB.get(i).getRecordDate();
MyWishes myWishes = new MyWishes();
myWishes.setTitle(title);
myWishes.setContent(content);
myWishes.setRecordDate(date);
wishes.add(myWishes);
}
dbHandler.close();
mWishAdapter = new WishAdapter(showWishesActivity.this, R.layout.list_row, wishes);
mListView.setAdapter(mWishAdapter);
mWishAdapter.notifyDataSetChanged();
}
public class WishAdapter extends ArrayAdapter<MyWishes>{
Activity activity;
MyWishes wish;
ArrayList<MyWishes> mData = new ArrayList<>();
int layoutRessource;
public WishAdapter(Activity act, int resource, ArrayList<MyWishes> data) {
super(act, resource, data);
activity = act;
layoutRessource = resource;
mData = data;
notifyDataSetChanged();
}
#Override
public int getCount() {
return mData.size();
}
#Override
public int getPosition(MyWishes item) {
return super.getPosition(item);
}
#Nullable
#Override
public MyWishes getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ViewHolder holder = null;
if (row == null || (row.getTag()) == null){
LayoutInflater inflater = LayoutInflater.from(activity);
row = inflater.inflate(layoutRessource, null);
holder = new ViewHolder();
holder.mTitle = (TextView) findViewById(R.id.titre);
holder.mDate = (TextView) findViewById(R.id.date);
row.setTag(holder);
}else {
holder = (ViewHolder) row.getTag();
}
holder.mMyWishes = getItem(position);
holder.mTitle.setText(holder.mMyWishes.getTitle());
holder.mDate.setText(holder.mMyWishes.getRecordDate());
return row;
}
class ViewHolder{
MyWishes mMyWishes;
TextView mTitle;
TextView mContent;
TextView mDate;
TextView mId;
}
}
on the getView method, I get a NullPointerException on holder.setText.`
I post the xml too:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="12dp"
android:background="#90f96666">
<ImageView
android:id="#+id/imageViewDatabaseId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#android:drawable/ic_menu_agenda"/>
<TextView
android:id="#+id/titre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="The title"
android:textStyle="bold"
android:layout_toRightOf="#+id/imageViewDatabaseId"
android:layout_marginLeft="20dp"/>
<TextView
android:id="#+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="The detail"
android:layout_toRightOf="#+id/imageViewDatabaseId"
android:layout_below="#+id/titleId"
android:layout_marginTop="3dp"
android:layout_marginLeft="40dp"
android:textStyle="italic"/>
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at wishlist.yassine.com.mywishlist.showWishesActivity$WishAdapter.getView(showWishesActivity.java:133)
I dont understand the problem, anyone can help me please, thank you for your time.
Instead of this
holder.mTitle = (TextView) findViewById(R.id.titre);
Try this
holder.mTitle = (TextView) convertView.findViewById(R.id.titre);
if (row == null || (row.getTag()) == null){
LayoutInflater inflater = LayoutInflater.from(activity);
row = inflater.inflate(layoutRessource, null);
holder = new ViewHolder();
holder.mTitle = (TextView) row.findViewById(R.id.titre);
holder.mDate = (TextView) row.findViewById(R.id.date);
row.setTag(holder);
}
Your view is not initialized inside getView. So:
Remove this:
holder.mTitle = (TextView) findViewById(R.id.titre);
holder.mDate = (TextView) findViewById(R.id.date);
With this:
holder.mTitle = (TextView) row.findViewById(R.id.titre);
holder.mDate = (TextView) row.findViewById(R.id.date);
In ur getView use this:
holder.mTitle = (TextView)row.findViewById(R.id.titre);
holder.mDate = (TextView)row.findViewById(R.id.date);
you have miss the view to reference
listview replace every item to your specify layout
then getview method in return view of listview item
you can use like
holder.mTitle = (TextView) row.findViewById(R.id.titre);
row is a view of layout
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ViewHolder holder = null;
if (row == null || (row.getTag()) == null){
LayoutInflater inflater = LayoutInflater.from(activity);
row = inflater.inflate(layoutRessource, null);
holder = new ViewHolder();
holder.mTitle = (TextView) row.findViewById(R.id.titre);
holder.mDate = (TextView) row.findViewById(R.id.date);
row.setTag(holder);
}else {
holder = (ViewHolder) row.getTag();
}
holder.mMyWishes = getItem(position);
holder.mTitle.setText(holder.mMyWishes.getTitle());
holder.mDate.setText(holder.mMyWishes.getRecordDate());
return row;
}
class ViewHolder{
MyWishes mMyWishes;
TextView mTitle;
TextView mContent;
TextView mDate;
TextView mId;
}
I have two button increase and decrease and one textview. When I click on increase button the value in the textview is increases and vice versa but when I scroll the listview its get its default value 0. How can I resolve this?
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.cart_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.mImgItem = (ImageView) convertView.findViewById(R.id.cart_image);
viewHolder.mTvItemName = (TextView) convertView.findViewById(R.id.tv_item_name);
viewHolder.mTvItemPrice = (TextView) convertView.findViewById(R.id.tv_item_price);
viewHolder.mTvNumber = (TextView) convertView.findViewById(R.id.tv_number);
viewHolder.mBtnAdd = (Button) convertView.findViewById(R.id.btn_add);
viewHolder.mBtnMinus = (Button) convertView.findViewById(R.id.btn_sub);
viewHolder.mImgDelete = (ImageView) convertView.findViewById(R.id.img_del);
viewHolder.mUniqueKey = String.valueOf(position);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.mBtnAdd.setTag(viewHolder);
viewHolder.mBtnMinus.setTag(viewHolder);
viewHolder.mTvNumber.setText("0");
viewHolder.mBtnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ViewHolder tagHolder = (ViewHolder) v.getTag();
int mValue = Integer.parseInt(tagHolder.mTvNumber.getText().toString().trim());
mValue++;
tagHolder.mTvNumber.setText("" + mValue);
});
viewHolder.mBtnMinus.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
ViewHolder tagHolder = (ViewHolder) v.getTag();
int mValue = Integer.parseInt(tagHolder.mTvNumber.getText().toString());
if (mValue > 0) {
mValue--;
tagHolder.mTvNumber.setText("" + mValue);
}
});
convertView.setTag(viewHolder);
PojoCart pojoCart = (PojoCart) getItem(position);
viewHolder.mTvItemName.setText(pojoCart.getmItemName());
viewHolder.mTvItemPrice.setText(pojoCart.getmItemPrice());
return convertView;
}
Save the value to PojoCart class and retrieve value from it every time just as you are getting itemName and itemPrice. So create an integer quantity in PojoCart class and in the adapter's getView method :
PojoCart pojoCart = (PojoCart) getItem(position);
viewHolder.mBtnAdd.setTag(pojoCart);
viewHolder.mBtnMinus.setTag(pojoCart);
if(pojoCart.getQuantity() > 0)
{
viewHolder.mTvNumber.setText(""+pojoCart.getQuantity());
}
else
{
viewHolder.mTvNumber.setText(""+0);
}
viewHolder.mBtnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
PojoCart pojoCart = (PojoCart) v.getTag();
int mValue = pojoCart.getQuantity();
mValue++;
tagHolder.mTvNumber.setText("" + mValue);
pojoCart.setQuantity(mValue)
notifyDataSetChanged();
};
You have to save value of textview.
As listview recreate views on scroll it takes default
Just take model class and save values by position
Use adapter.notifyDataSetChanged() to update values in listview when data changes
You need to store the number in each list item for example use this function to change the value of the list item's number:
List<Integer> mList = new ArrayList<>(LIST_SIZE);
int changeNumber(int position, int value){
mList.get(position) += value;
return mList.get(position);
}
u can use this function like:
viewHolder.mBtnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ViewHolder tagHolder = (ViewHolder) v.getTag();
int mValue = Integer.parseInt(tagHolder.mTvNumber.getText().toString().trim());
tagHolder.mTvNumber.setText("" + changeNumber(position,1);
});
viewHolder.mBtnMinus.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
ViewHolder tagHolder = (ViewHolder) v.getTag();
int mValue = Integer.parseInt(tagHolder.mTvNumber.getText().toString());
if (mValue > 0) {
tagHolder.mTvNumber.setText("" + changeNumber(position,-1);
}
});
Can you help me out, I'm learning! ;-)
I have two buttons "+" and "-" and I want them to increase or decrease the amount by one up or one down. How do I make sure that it will only have effect on the right textview using an adapter.
Now all the buttons only effects the first one. I know I have to get the position/id of the array. But I don't know how.
btw
Merk = Brand
Aantal = Amount
public class ListViewDemo2 extends Activity {
private ArrayList<String> data = new ArrayList<String>();
int aantal = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view_demo2);
ListView lv = (ListView) findViewById(R.id.bestel_lijst);
lv.setAdapter(new MyListAdapter(this, R.layout.list_item, data));
generateListContent();
}
private void generateListContent(){
for (int i =0; i < 55; i++){
data.add("this is row number: " + i);
}
}
#Override
public boolean onCreateOptionsMenu (Menu menu){
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings){
return true;
}
return super.onOptionsItemSelected(item);
}
private class MyListAdapter extends ArrayAdapter<String>{
private int layout;
public MyListAdapter(Context context, int resource, List<String> objects) {
super(context, resource, objects);
layout = resource;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder mainViewholder = null;
if(convertView == null){
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(layout, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.Merk = (TextView) convertView.findViewById(R.id.Merk);
viewHolder.Aantal = (TextView) convertView.findViewById(R.id.Aantal);
viewHolder.btnup = (Button) convertView.findViewById(R.id.Bup);
viewHolder.btndown = (Button) convertView.findViewById(R.id.Bdown);
viewHolder.btndown.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
aantal = aantal-1;
displayAantal(aantal);
}
});
viewHolder.btnup.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
aantal = aantal+1;
displayAantal(aantal);
}
});
convertView.setTag(viewHolder);
}
else {
mainViewholder = (ViewHolder)convertView.getTag();
mainViewholder.Merk.setText(getItem(position));
}
return convertView;
}
}
public class ViewHolder {
TextView Merk;
TextView Aantal;
Button btnup;
Button btndown;
}
public void displayAantal(int aantal) {
TextView aantalView = (TextView) findViewById(R.id.Aantal);
aantalView.setText(String.valueOf(aantal));
}
}
your problem seems to be in your displayAantal(int) method, in particular, in this line:
TextView aantalView = (TextView) findViewById(R.id.Aantal);
You do not need to re-initialize that field here, since it is already initialized inside getView():
viewHolder.Aantal = (TextView) convertView.findViewById(R.id.Aantal);
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder mainViewholder = null;
if(convertView == null){
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(layout, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.Merk = (TextView) convertView.findViewById(R.id.Merk);
viewHolder.Aantal = (TextView) convertView.findViewById(R.id.Aantal);
viewHolder.btnup = (Button) convertView.findViewById(R.id.Bup);
viewHolder.btndown = (Button) convertView.findViewById(R.id.Bdown);
convertView.setTag(viewHolder);
}
else {
mainViewholder = (ViewHolder)convertView.getTag();
}
String str = getItem(position);
mainViewholder.Merk.setText(str);
viewHolder.btndown.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
//just handle the data ,then call adapter.notifyDataSetChanged();
}
});
viewHolder.btnup.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
//just handle the data ,then call adapter.notifyDataSetChanged();
}
});
return convertView;
}
I'm developing an app with a custom layout and an arrayAdapter:
The problem is when I check one Box the app automatically check another 2 boxes!
I created a list where I put the positions of checkboxes and show that nothing its wrong! I think the problem is when I do the recycle!
Lets view some code; This is my ArrayAdapter:
class VivzAdapter extends ArrayAdapter<String> implements OnCheckedChangeListener {
Context context;
int[] images;
String[] titlesArray, descrptionArray;
List<Integer> positions = new ArrayList<Integer>();
VivzAdapter(Context context, String[] titles, int[] images, String[] description) {
super(context, R.layout.single_row, R.id.textView1, titles);
this.context = context;
this.images = images;
this.titlesArray = titles;
this.descrptionArray = description;
}
class MyViewHolder {
ImageView myImage;
TextView myTitle;
TextView myDescription;
CheckBox box;
MyViewHolder(View v) {
myImage = (ImageView) v.findViewById(R.id.imageView1);
myTitle = (TextView) v.findViewById(R.id.textView1);
myDescription = (TextView) v.findViewById(R.id.textView2);
box = (CheckBox) v.findViewById(R.id.checkBox1);
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
MyViewHolder holder = null;
if (row == null) {
// 1.ºtime
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//row contem RelativeLayout(root) em single_row.xml
row = inflater.inflate(R.layout.single_row, parent, false);
holder = new MyViewHolder(row);
row.setTag(holder);
//Log.d("VIVZ", "Creating a new Row");
} else {
//reciclamos aqui, qeremos usar antigo objecto holder
holder = (MyViewHolder) row.getTag();
//Log.d("VIVZ", "Recycling stuff");
}
holder.myImage.setImageResource(images[position]);
holder.myTitle.setText(titlesArray[position]);
holder.myDescription.setText(descrptionArray[position]);
holder.box.setTag(position);
holder.box.setOnCheckedChangeListener(this);
return row;
}
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
int position = (Integer)buttonView.getTag();
Toast.makeText(getContext(), ""+position, Toast.LENGTH_SHORT).show();
positions.add(position);
Log.d("VIVZ", "+ "+position);
} else {
int position = (Integer)buttonView.getTag();
Toast.makeText(getContext(), ""+position, Toast.LENGTH_SHORT).show();
Log.d("VIVZ", "- "+position);
positions.remove(position);
}
Log.d("VIVZ", positions.toString());
}
}
I was stucked in same problem and found the solution.
Create a boolean array having size same as no. of elements in the list and initial value false for each list item. Then in getView set position in Id through setId for checkbox. Now set the value of checkbox from boolean array through setChecked method of checkbox. Add onClickListener for checkbox and in this listener change the value in boolean array not checkbox value directly.
I hope this resolve your problem also.
Just replace your adapter class with this code :
class VivzAdapter extends ArrayAdapter<String> implements OnCheckedChangeListener {
Context context;
int[] images;
String[] titlesArray, descrptionArray;
List<Integer> positions = new ArrayList<Integer>();
ArrayList<Boolean> arrChecked;
VivzAdapter(Context context, String[] titles, int[] images, String[] description) {
super(context, R.layout.single_row, R.id.textView1, titles);
this.context = context;
this.images = images;
this.titlesArray = titles;
this.descrptionArray = description;
// initialize arrChecked boolean array and add checkbox value as false initially for each item of listview
arrChecked = new ArrayList<Boolean>();
for (int i = 0; i < titles.size(); i++) {
arrChecked.add(false);
}
}
class MyViewHolder {
ImageView myImage;
TextView myTitle;
TextView myDescription;
CheckBox box;
MyViewHolder(View v) {
myImage = (ImageView) v.findViewById(R.id.imageView1);
myTitle = (TextView) v.findViewById(R.id.textView1);
myDescription = (TextView) v.findViewById(R.id.textView2);
box = (CheckBox) v.findViewById(R.id.checkBox1);
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
MyViewHolder holder = null;
if (row == null) {
// 1.ºtime
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//row contem RelativeLayout(root) em single_row.xml
row = inflater.inflate(R.layout.single_row, parent, false);
holder = new MyViewHolder(row);
row.setTag(holder);
//Log.d("VIVZ", "Creating a new Row");
} else {
//reciclamos aqui, qeremos usar antigo objecto holder
holder = (MyViewHolder) row.getTag();
//Log.d("VIVZ", "Recycling stuff");
}
holder.myImage.setImageResource(images[position]);
holder.myTitle.setText(titlesArray[position]);
holder.myDescription.setText(descrptionArray[position]);
//set position as id
holder.box.setId(position);
//set onClickListener of checkbox rather than onCheckedChangeListener
holder.box.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
int id = v.getId();
if (arrChecked.get(id)) {
//if checked, make it unchecked
arrChecked.set(id, false);
} else {
//if unchecked, make it checked
arrChecked.set(id, true);
}
}
});
//set the value of each checkbox from arrChecked boolean array
holder.box.setChecked(arrChecked.get(position));
return row;
}
}
At the end of getView method you should check if the current position is in the positions array or not, this should fix the problem
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
MyViewHolder holder = null;
if (row == null) {
// 1.ºtime
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//row contem RelativeLayout(root) em single_row.xml
row = inflater.inflate(R.layout.single_row, parent, false);
holder = new MyViewHolder(row);
row.setTag(holder);
//Log.d("VIVZ", "Creating a new Row");
} else {
//reciclamos aqui, qeremos usar antigo objecto holder
holder = (MyViewHolder) row.getTag();
//Log.d("VIVZ", "Recycling stuff");
}
holder.myImage.setImageResource(images[position]);
holder.myTitle.setText(titlesArray[position]);
holder.myDescription.setText(descrptionArray[position]);
holder.box.setTag(position);
holder.box.setOnCheckedChangeListener(this);
////// Here you should update the checlbox status by checking if the current index is in the position array or not ////////////
boolean isFound = false;
for(int i =0;i<position.size();i++){
if(positions.get(i) == position){ // this line might need casting
isFound = true;
break;
}
holder.box.setChecked(isFound);
}
return row;
}
Use this sample project for ur problem. link to download complete project
https://drive.google.com/file/d/0B6C9Pqrvc_CWZHFDcmxKR01rc3c/edit?usp=sharing