Radio Buttons in ListView recycling and causing issues - android

So I have a listview that is built to have each view in the list to have 2 radio buttons. The problem is when the list gets longer than the page the list starts to recycle the views and it also takes along the checked radio buttons. I understand what is causing the problem but I don't really know how to solve this issue and the answers I've looked up online aren't really helping. My problem is a bit unique because I'm using Parse with my adapter so looking up solutions specifically for parse is pretty hard.
Here is my code for my adapter:
// Set up a customized query
ParseQueryAdapter.QueryFactory<AnywallPost> factory =
new ParseQueryAdapter.QueryFactory<AnywallPost>() {
public ParseQuery<AnywallPost> create() {
Location myLoc = (currentLocation == null) ? lastLocation : currentLocation;
ParseQuery<AnywallPost> query = AnywallPost.getQuery();
query.include("user");
query.orderByDescending("PostScore");
query.whereWithinKilometers("location", geoPointFromLocation(myLoc), radius
* METERS_PER_FEET / METERS_PER_KILOMETER);
query.setLimit(MAX_POST_SEARCH_RESULTS);
return query;
}
};
// Set up the query adapter
postsQueryAdapter = new ParseQueryAdapter<AnywallPost>(this, factory) {
#Override
public View getItemView(AnywallPost post, View view, ViewGroup parent) {
if (view == null) {
view = View.inflate(getContext(), R.layout.anywall_post_item, null);
}
//TextView DetailsView = (TextView) view.findViewById(R.id.content_view);
TextView contentView = (TextView) view.findViewById(R.id.content_view);
TextView usernameView = (TextView) view.findViewById(R.id.username_view);
TextView postscoreView = (TextView) view.findViewById(R.id.PostScore);
RadioButton upvote = (RadioButton) view.findViewById(R.id.Upvote);
RadioButton downvote = (RadioButton) view.findViewById(R.id.DownVote);
//DetailsView.setText(post.getDetails());
contentView.setText(post.getText());
usernameView.setText(post.getUser().getUsername());
postscoreView.setText(post.getInt().toString());
upvote.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
post.increment("PostScore", 1);
post.saveInBackground();
}
});
downvote.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//post.decrement();
}
});
return view;
}
};
postsQueryAdapter.setAutoload(false);
postsQueryAdapter.setPaginationEnabled(false);
// Attach the query adapter to the view
ListView postsListView = (ListView) findViewById(R.id.posts_listview);
postsListView.setAdapter(postsQueryAdapter);
So I need these two radio buttons to stay with the post made the entire time but I am still a beginner so I'm not entirely sure how I would go about that. Could anyone provide a solution and explain how it worked? If you need anymore code just let me know. Thank you for you time.

Essentially when you get a new cell, be sure to clear both the radio buttons, then re-check them only if it meets your condition for it to be checked (likely checking the count on the post object). Hard to tell exactly how this will look without knowing more about how you are storing data, but I'd guess something like this:
upvote.setChecked(post.getInt() > 0);
downvote.setChecked(post.getInt() < 0);

Along with the list items I'd carry a list with the state of radio buttons.
So even when the views get recycled you use your own state to check the correct radio button.
A good choice would be a list of Boolean (the boolean wrapper). Where you could keep true for upvote, false for downvote and null for neither.

Related

buttons in listView not always triggered

I have a listview and in each cell it has a RelativeLayout with 7 buttons.
before the list is scrolled all the buttons work fine (all trigger when clicked) for all visible listView items, but after listView was scrolled some items turn to not clickable (no matter which button in the item I click), and it's random, after another scroll the same item can turn clickable, and other which was before turns to not clickable.
I have noticed that it usually happens (item turns not clickable) after scrolling all the way up.
Another thing that i have noticed that seldom (after 4-5 unsuccessful clicks in a row) the button triggers a few times in a row (like it was delayed). But usually it's not happening after a number of unsuccessful clicks.
In my original code I created an arrayList of RelativeLayouts (each for listView Item), and put the arrayList into adapter. For every 7 buttons (for each cell) I set 7 ids corresponding to their's place in arraylist.
In that way I implemented the OnClick event in the main class.
Here is 3 buttons (out of 7):
for (int i = 0; i < EXPEND_BUTTONS.length; i++) {
if (view.getId() == EXPEND_BUTTONS[i]) {
handleEmojiPanel(i);
break;
}
if (view.getId() == BUTTONS[i] || view.getId() == IMAGES[i]) {
ShowTopItem item = new ShowTopItem(getActivity(), i);
item.show();
break;
}
}
Because of the problem I change the code.
I handled the OnClick event for the buttons in the adapter itself in the getView method (for 2 buttons only):
public View getView(final int position, View convertView, ViewGroup parent) {
pos = position;
Button btn = (Button) listOfObjects.get(position).getChildAt(0);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ShowTopItem item = new ShowTopItem(getActivity(), position + listChosen);
item.show();
}
});
Button imageBtn = (Button) listOfObjects.get(position).getChildAt(2);
imageBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ShowTopItem item = new ShowTopItem(getActivity(), position + listChosen);
item.show();
}
});
return listOfObjects.get(position);
}
I have the same result. Nothing changed.
I have looked all over the internet, and it seems that I'm the only one who encountered such issue.
Id anybody knows what can be the issue here?
If some other code is needed, please feel free to ask.
I did not find the reason, but I changed ListView to ScrollView, and all works fine now.
Maybe there is some kind of bug in ListView, but in this case, I wonder why I did not find any complains regarding it.
Anyway, works perfect with ScrollView.

disabled checkboxes get enabled radomly after recycling view through the adapter in android app

i have the following problem. i have set three checkboxes in my app in a way so when i check one of them the other two get disabled. the problem is that when i scroll up or down namely when the views get recycled some of the disabled checkboxes get enabled which of course i dont want to happen. could you please help me to solve that problem?
the code in the adapter is this :
#NonNull
#Override
public View getView(final int position1, View convertView1, ViewGroup parent1) {
View listItemView1 = convertView1;
if (listItemView1 == null) {
listItemView1 = LayoutInflater.from(getContext()).inflate(R.layout.list_item1, parent1, false);
}
final ColorQuiz currentColorQuiz = getItem(position1);
TextView questionTextView = (TextView) listItemView1.findViewById(question_text_view);
questionTextView.setText(currentColorQuiz.getQuestionHeader());
final CheckBox box1 = (CheckBox) listItemView1.findViewById(R.id.check_Box_view1);
box1.setText(currentColorQuiz.getCheckBoxTextA());
final CheckBox box2 = (CheckBox) listItemView1.findViewById(R.id.check_Box_view2);
box2.setText(currentColorQuiz.getCheckBoxTextB());
final CheckBox box3 = (CheckBox) listItemView1.findViewById(R.id.check_Box_view3);
box3.setText(currentColorQuiz.getCheckBoxTextC());
box1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (box1.isChecked() ) {
thesisA[position1]=true;
box2.setEnabled(false);
box3.setEnabled(false);
} else {
thesisA[position1]=false;
box2.setEnabled(true);
box3.setEnabled(true);
}
int getPositionA = (Integer) compoundButton.getTag(); // Here we get the position that we have set for the checkbox using setTag.
colorQuizs.get(getPositionA).setSelectedA(compoundButton.isChecked());
}
});
box1.setTag(position1);
box1.setChecked(colorQuizs.get(position1).isSelectedA());
.
.
.
and continues the same way with the next two checkboxes (box2, box3).
maybe while you calling box1.setChecked then one of the old listeners is called and it is causing problems (as it still has old tag, and propably setting wrong data to your model ).
Try to add at the beggining to make sure that something wrong does not happen:
box1.setOnCheckedChangeListener(null);
box2.setOnCheckedChangeListener(null);
box3.setOnCheckedChangeListener(null);
This will tell you if not calling callback while property does not change is reason of this problem:
box1.setChecked(!colorQuizs.get(position1).isSelectedA());
box1.setChecked(colorQuizs.get(position1).isSelectedA());

Android dynamically drawing more than 1000 text view is causing full application to not responding

This is my function which will fetch some 1300 record from SQLite database and draw a series of text view inside the table layout. But when this function starts executing full application is not responding for some time. Table layout is again inside a Scroll View.
Basically I want it to load in back ground.so that remaining application is still responsive
public void LoadAlarmNotifications() {
int SerNo = 0 ;
TextView SerNoTxtVw;
TextView AlarmTxtVw;
TextView TimeOccuredTxtVw;
TextView UploadTxtTxtVw;
Notificationtablelayout.setGravity(Gravity.CENTER);
pastEvent_tbleList = oDatabaseHandler.FetchPastEventDetails(sDiagnosisID);
if(pastEvent_tbleList != null)
{
for(Pastevents_tble PEvents_tble: pastEvent_tbleList)
{
final TableRow row = new TableRow(this.getApplicationContext());
nAlarmType = PEvents_tble.GetAlarmID();
nRowID = PEvents_tble.GetEventNo();
SerNo = PEvents_tble.GetEventNo();
SetAlarmType(nAlarmType); // Set Alarm
tAlarmTime = PEvents_tble.GetStrtTime();
sUploadStatus = PEvents_tble.GetUploadStatus();
row.setId(nRowID);
SerNoTxtVw =new TextView(this.getApplicationContext());
AlarmTxtVw =new TextView(this.getApplicationContext());
TimeOccuredTxtVw=new TextView(this.getApplicationContext());
UploadTxtTxtVw =new TextView(this.getApplicationContext());
SerNoTxtVw.setText(Integer.toString(SerNo));
SerNoTxtVw.setWidth(80);
SerNoTxtVw.setHeight(40);
SerNoTxtVw.setGravity(Gravity.CENTER);
SerNoTxtVw.setPadding(2, 2,2,2);
SerNoTxtVw.setTextColor(Color.parseColor("#FFAADDFF"));
row.addView(SerNoTxtVw);
AlarmTxtVw.setText(sAlarmName);
AlarmTxtVw.setWidth(100);
AlarmTxtVw.setHeight(40);
AlarmTxtVw.setGravity(Gravity.LEFT);
AlarmTxtVw.setPadding(2,2,2,2);
// AlarmTxtVw.setBackgroundColor(Color.LTGRAY);
row.addView(AlarmTxtVw);
TimeOccuredTxtVw.setText(tAlarmTime);
TimeOccuredTxtVw.setWidth(140);
TimeOccuredTxtVw.setHeight(40);
TimeOccuredTxtVw.setGravity(Gravity.CENTER);
TimeOccuredTxtVw.setPadding(2,2,2,2);
// TimeOccuredTxtVw.setBackgroundColor(Color.LTGRAY);
row.addView(TimeOccuredTxtVw);
UploadTxtTxtVw.setText(sUploadStatus);
if(sUploadStatus.equals("Y")){
UploadTxtTxtVw.setText("✔");
UploadTxtTxtVw.setTextColor(Color.parseColor("#FF00FF00"));
}
else if(sUploadStatus.equals("N")){
UploadTxtTxtVw.setText("X");
UploadTxtTxtVw.setTextColor(Color.parseColor("#FFFF0000"));
}else
{
UploadTxtTxtVw.setText("✔");
UploadTxtTxtVw.setTextColor(Color.parseColor("#FFAADDFF"));
}
UploadTxtTxtVw.setWidth(100);
UploadTxtTxtVw.setHeight(40);
UploadTxtTxtVw.setGravity(Gravity.CENTER);
UploadTxtTxtVw.setPadding(2,2,2,2);
// UploadTxtTxtVw.setBackgroundColor(Color.LTGRAY);
row.addView(UploadTxtTxtVw);
Notificationtablelayout.addView(row);
nProgressCounter++;
row.setOnClickListener(new OnClickListener() {
#Override
public void onClick(final View SelectedRowVw) {
if(!bSwitchPopOpen)
{
}
});
}
LoadingTxtView.setVisibility(View.GONE);
bEpisodeLoad = true;
} else
{
bEpisodeLoad = true;
LoadingTxtView.setText("No Snapshot available...");
}
}
pastEvent_tbleList size is 1335. Any idea how to optimize this. Instead of text view is there any other light control?
You should use the Loader pattern to do this so that you only load content that the user will actually see, and you should also reuse the text views on the screen. This is what ListView / CursorLoader were created to solve, so you really ought to reuse these components if at all possible.
You should use listview, because it loads visible area so application will not stop doing calculations and you can scroll up and down and see your texviews

Saving state of a dynamically added view through an adapter

I've been struggling with this problem for two days and haven't found anything helpful online.
My setup is like this, I've a Gridview which I am populating with images through API. Now each cell of GridView contains a button , when clicked is supposed to add an overlay to the cell. This is being done dynamically depending on the values through webservice. Now the problem is when I scroll up or down the overlay that was added shifts to other cells.How can I tackle this problem?
What I've tried:
I've tried the SparseBooleanArray solution which is used to save the state of the checkboxes through saving the position,but this approach will not solve the problem as I am struggling with keeping the state of the view intact.
What I suspect:
The code adds the overlay through a method which is called on click of the button. I am using the clicked position to get the cell through gridView.getChildat(position) and adding the overlay as a child to it. I think my convertView is not updating that's why the overlay keeps on drawing on different cell positions.
Relevant code:
This is inside my getView method,
if (convertView == null) {
convertView = inflater.inflate(R.layout.grid_image_row, null);
holder = new Holder();
holder.iv_product_image = (ImageView) convertView.findViewById(R.id.id_iv_product_image) ;
RelativeLayout.LayoutParams iv_product_image_params = new RelativeLayout.LayoutParams(imageWidth,imageHeight) ;
holder.iv_product_image.setLayoutParams(iv_product_image_params) ;
iv_product_image_params = null ;
holder.iv_loader_image = (ImageView) convertView.findViewById(R.id.id_iv_loader_image) ;
holder.bt_expand_save_overlay = (CheckBox) convertView.findViewById(R.id.id_bt_expand_save_overlay) ;
holder.tv_productname = (TextView) convertView.findViewById(R.id.id_tv_productname) ;
customFonts.SetSoureLightFont(holder.tv_productname) ;
holder.tv_sellingprice = (TextView) convertView.findViewById(R.id.id_tv_sellingprice) ;
customFonts.SetSoureLightFont(holder.tv_sellingprice) ;
holder.tv_price = (TextView) convertView.findViewById(R.id.id_tv_price) ;
customFonts.SetSoureRegularFont(holder.tv_price) ;
holder.parent_cell = null;
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
EDIT : This is how I am setting tags with the positions...
if (session.isLoggedIn()) {
holder.bt_expand_save_overlay.setTag("1,"+position) ;
int i=0;
for (i=0; i<savedItemsHashMap.size(); i++) {
HashMap<String,String> savedItems = new HashMap<String, String>() ;
savedItems = savedItemsHashMap.get(i) ;
if (savedItems.get(GlobalVariables.KEY_PRODUCT_ID).equals(mapStr.get(GlobalVariables.KEY_ID))) {
holder.bt_expand_save_overlay.setButtonDrawable(R.drawable.wishlist_saved_drawable) ;
break ;
}
}
if (i==savedItemsHashMap.size()) {
holder.bt_expand_save_overlay.setButtonDrawable(R.drawable.wishlist_unsaved_drawable) ;
}
} else {
holder.bt_expand_save_overlay.setTag("0,"+position) ;
}
This is how I am binding click listener inside getView,I am saving the position in the tags.
holder.bt_expand_save_overlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
String getTagString = (String)v.getTag() ;
String [] items = getTagString.split(",");
selectedExpandButton = Integer.valueOf(items[1]) ;
ExpandSaveForLaterOverlay (selectedExpandButton) ;
}
The ExpandSaveForLaterOverlay adds the view to the selected cell but this does not remain on the same child when scrolled.
Thanks in advance for any help or tips!
Update: I checked the google play music application and they have the same setup. They have a gridview with a button which shows a menu overlay on click. But before scrolling the menu goes invisible, and only after that it scrolls. Is there no way to save the overlay in the view instance itself?
Try to use setTag() on imageview in adapter like
holder.imageView.setTag(position);
And getTag() when you are adding the overlay as a child like
holder.imageView.getTag();
then add overlay.
Ok finally my idea about android architecture was wrong. What I needed was to show the added layout as dropdown. And the added layout should be scrolled when the gridview is scrolled.Which is not possible in any way by available android sdk. I might have to make my own view for such implementations which will keep track of the virtual y and x position of the added overlay.

Duplicated layout reference in list view

There is an really weird thing happening with my listview. I am creating an ListView with buttons and an editText.
It's disposed like this: [Button] [EditText] [Button], The buttons works like an "incrementer" and "decrementer" updating the numerical value of EditText in 1 unit per click.
The problem is, when I click in an button, almost every time an editText of another list view element is changed (the editText of the clicked item is also changed). And if I click in a button of this erroneous changed item, it also changes the editText of the first one. They basically have the same reference of buttons and editText, although they have textViews with data, and this data is different between they.
To accomplish that I created and custom adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = mInflater.inflate(R.layout.lastproduct_row, null);
holder = new ViewHolder();
holder.btnAddQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_add_qtd);
holder.btnSubQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_sub_qtd);
holder.etQuantidade = (EditText) convertView.findViewById(R.lastproduct_row.et_quantidade);
TextView tv;
holder.tvList = new TextView[PRODUCTROW_INT_KEY.length];
for(int i = 0; i < PRODUCTROW_INT_KEY.length; i++) {
tv = (TextView) convertView.findViewById(PRODUCTROW_INT_KEY[i]);
holder.tvList[i] = tv;
}
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
HashMap<String, String> hm = productsList.get(position);
String key = hm.get(CODIGO_KEY);
for(int i = 0; i < PRODUCTROW_INT_KEY.length; i++) {
holder.tvList[i].setText(hm.get(PRODUCTROW_STR_KEY[i]));
}
holder.btnAddQtd.setTag(key+QTD_FLAG+ADD_ACTION);
holder.btnSubQtd.setTag(key+QTD_FLAG+SUB_ACTION);
holder.btnAddQtd.setOnClickListener(handle);
holder.btnSubQtd.setOnClickListener(handle);
if(novosEstoques.containsKey(key)) {
holder.etQuantidade.setText(MyParseFunctions.parseCentesimal(novosEstoques.get(key).getQuantidade()));
}
return convertView;
}
class ViewHolder {
private TextView []tvList;
private Button btnAddQtd, btnSubQtd;
private Button btnAddQtVol, btnSubQtVol;
private EditText etQuantidade, etQtVolume;
}
I added onClick listenners to the buttons, setting their tags with my listView element ID (concatenated with another informations). Then in my event listener I just get the button parent View (an LinearLayout) and get the EditText from that using getViewAt():
#Override
public void onClick(View v) {
String tag = (String) v.getTag();
if(tag.contains(QTD_FLAG)) {
String []info = ((String) v.getTag()).split(QTD_FLAG);
float qtd;
LinearLayout ll = (LinearLayout) v.getParent();
ll.setBackgroundColor(Color.rgb(0, 128, 30));
EditText et = (EditText) ll.getChildAt(2);
qtd = Float.parseFloat(et.getText().toString().replace(",", "."));
if(info[1].equals(ADD_ACTION)) {
qtd++;
}
else if(info[1].equals(SUB_ACTION)) {
if(qtd > 0)
qtd--;
}
Log.d("TESTE", "MODIFICAR KEY = "+info[0]);
et.setText(qtd+"");
}
}
I'm using an setBackgroundColor in this example to confirm that the LinearLayout instance is duplicated in the lisView. When I click an Button, it's painted in 2 different list view item.
Anyone can point me what could be doing this? I have found people with an duplicated ListView item, I don know if that is my case, cause I have TextView's inside my ListView, and they are not equal, only the LinearLayout portion with buttons and editText is "shared".
I make some changes in my getView method and it's working now! It seems that every time the getView method is called i have not guarantee at all that my editTexts will be filled properly and I didn't realize that. So every getView call I make I set the editText value, if the user edit an ET value, I store it in a HashMap to restore in getView, if there is no entry in HashMap for the given editText, then I set it to the default value (zero):
...
if(convertView == null) {
holder = new ViewHolder();
holder.btnAddQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_add_qtd);
holder.btnSubQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_sub_qtd);
holder.etQuantidade = (EditText) convertView.findViewById(R.lastproduct_row.et_quantidade);
//Now it is easier to get etQuantidade reference in button
//click handle, I just have to do:
// public onClick(View v) {
// EditText etButtonAssociated = (EditText) v.getTag();
// ...
// }
holder.btnAddQtd.setTag(holder.etQuantidade);
holder.btnSubQtd.setTag(holder.etQuantidade);
holder.btnAddQtd.setOnClickListener(handle);
holder.btnSubQtd.setOnClickListener(handle);
...
}
else {
...
}
holder.etQuantidade.setTag(key);
if(novosEstoques.containsKey(key)) {
holder.etQuantidade.setText(MyParseFunctions.parseCentesimal(novosEstoques.get(key).getQuantidade()));
}
else {
holder.etQuantidade.setText("0");
}
return convertView;
Israel,
After looking over your code, I was wondering about an implementation decision you have made. Since each Button is "bound" to a particular EditText, have you considered setting the Tag of those Buttons to the EditText? The Tag may be any Object including a UI element. This is especially useful for dynamic UI elements, such as a runtime populated list.
Since this is handled in your Adapter you wouldn't have to worry about duplicate Parents and such. Additionally, you could avoid having to worry about "finding" the control in your onClick() because you would have it (It's the tag). I'm not sure exactly what your project needs are, but this seems like a potentially viable solution, unless you need those Buttons to accomplish other tasks.
Note of Caution
Just make sure that you erase the Tags' references to the EditText when you are done. Otherwise, you run the risk of leaking some memory.
FuzzicalLogic

Categories

Resources