Sub arraylist output repeating itself after n elements - java

Newbie. I'm coding a quiz app full code on Github that loads an arrayList with four arguments:
question
image (from drawables)
key answer
possible answers presented in a radioGroup (sub-arrayList)
from the strings.xml as below
...
<string name="questionOne">Who is the "Modern Love" rock star singer?</string>
<string name="answerOne">David Bowie</string>
<string-array name="celebrityOne">
<item>Jaimie Hendrix</item>
<item>David Bowie</item>
<item>Jim Morrison</item>
<item>Elvis Presley</item>
</string-array>
...
Below is how the arguments are loaded in MainActivity (The third argument is a sub-arraylist)
ArrayList<Object> arrayList = new ArrayList<>();
loaddata()
...
public void loadData() {
arrayList.add(new Quiz(getResources().getString(R.string.questionOne),
getResources().getDrawable(R.drawable.celebrity_one_image, null),
new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.celebrityOne))),
getResources().getString(R.string.answerOne)));
arrayList.add(new Quiz(getResources().getString(R.string.questionTwo),
getResources().getDrawable(R.drawable.celebrity_two_image, null),
new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.celebrityTwo))),
getResources().getString(R.string.answerTwo)));
...
}
The issue is after N iterations, the sub-arrayList starts repeating itself (See image below).
Also I think maybe the source of the problem is in the Adapter, where for each string in sub-array is assigned to a radioButton;
void createRadioButtons(String[] arrayAnswer) {
if (mRadioGroup.getChildAt(0) != null)
return;
for (int i = 0; i < arrayAnswer.length; i++) {
mRadioGroup.addView(createRadioButtonAnswerAndSetOnClickListener(arrayAnswer[i]));
}
}
RadioButton createRadioButtonAnswerAndSetOnClickListener(String string) {
RadioButton radioButton = new RadioButton(mContext);
radioButton.setText(string);
radioButton.setOnClickListener(this);
return radioButton;
}
My situation might be similar to this but I have no static fields and arrayList is initialized as new so no need to clear().

From Documentation:
The RecyclerView creates only as many view holders as are needed to display the on-screen portion of the dynamic content, plus a few extra. As the user scrolls through the list, the RecyclerView takes the off-screen views and rebinds them to the data which is scrolling onto the screen.
This means RecyclerView reuses already created view holders when you are scrolling it(that is why your data repeats), and you must repopulate views with new data. So, instead of returning from createRadioButtons method, when mRadioGroup.getChildAt(0) != null, you must change RadioButtons texts to your new data from arrayAnswer.

in your adapter just change this:
if (mRadioGroup.getChildAt(0) != null)
return;
To this:
if (mRadioGroup.getChildAt(0) != null)
mRadioGroup.removeAllViews();
At some moment your adapter, began to reuse view holders which were created at the top of the recyclerView, but it was already filled with data, so when you call return, you just leave your old data, while you need to delete it and then add new data...

Related

Null pointer on Viewholder

I am trying to save the state of checkboxes in my main activity rather than in my custom adapter. I have checked that the correct data is being stored in the sharedprefs and can retrieve the information successfully but when I try and mark the checkboxes on opening the app the viewholder is null. In theory I understand what the problem is but I have no clue how to fix it.
Here is the part of code from the main activity I am having issues with:
for (int i= 0; i<results.size(); i++) {
result = (Results) customAdapter.getItem(i);
if (result != null) {
String RESULT = result.getTitle();
if (sharedPref.getBoolean(result.getTitle(), false)){
Log.i("Saved", result.getTitle());
CustomAdapter.ViewHolder viewHolder = (CustomAdapter.ViewHolder) listView.getTag(i);
Log.i("viewHolder", String.valueOf(viewHolder));
viewHolder.getCheckBox().setChecked(sharedPref.getBoolean(RESULT, false));
}
}
}
You are preparing a List<Results> to pass to adapter right? First add a extra boolean variable in Results model class to represent the state of checkbox. Now at the time you prepare the list in activity, set the value of this new boolean for each item from the preference. So that your List<Results> contain state of the checkbox.

Inflating views in for loop with different identity?

I am inflating layout in view in a for loop and then adding views in Array like:
ArrayList<View> views = new ArrayList();
for (Result datalist : arraylist){
View view = layoutinflater.inflate(R.layout.viewlayout, null);
/*View modification code here....
.............
.............
.............*/
views.add(view);
};
The problem is when i get views from arraylist even by index like views.get(i) it returning only last view. I want to get all views to be able to work on all views.
Here is the code where i am getting views from arrays.
for (View v : views) {
// if (insertPoint != null) {
// if (views != null) {
// if (rel_leftright != null) {
Rect scrollBounds = new Rect();
insertPoint.getHitRect(scrollBounds);
if (v.getLocalVisibleRect(scrollBounds)) {
rel_leftright.setVisibility(GONE);
} else {
rel_leftright.setVisibility(VISIBLE);
}
// }
// }
// }
}
But this code returning only working on last view in array and i need to act on all views.
Please Help me to get out of this. Thanks!!!
First I am missing something:
- Why are you trying to work with the views (/*View modification code here.... ) before you have created all of them? First craete them in the loop and the iterate over views Array and there call the modification code.
- Are you sure you don't want to pass a parent?
View view = layoutinflater.inflate(R.layout.viewlayout, null);
Even if you don't want to be attached you can call:
View view = layoutinflater.inflate(R.layout.viewlayout, parent, false);
And in general it is not possible to fill ArrayList with values and not to find them... Please log the size of datalist, then the size of views. The most probably should match.
And also please note that by the line: /*View modification code here.... not all views are created. You first need to finish the loop.

How to populate different object with same fields or properties in same recyclerview in android

I am going to develop soccer app, and having following json response.(link)
I have two different class with different names, but with same properties or fields and want to display one same single RecyclerView.
Here I have worldmatch and world having same fields like matchTime, startTime and endTime. I have created pojo class using jsontopojo plugin.
Here is main things, I want to display worldmatch in position 0 and rest of other position world based on club. You can see more details in picture.
This has to be first tab(world) and similarly to other tab like Europe , Asia with respective similar pattern.
Tab1(World)
---------------position 0 (All match)------------------
|
| 930 1100 1130 and so on ( horizontal recyclerview)
|
-------------------------------------------
---------------position 1(Barcelona)------------------
|
| 1130 1230 1330 1430 and so on ( horizontal recyclerview)
|
-------------------------------------------
---------------position 2(Chelsea)------------------
|
| 1300 1400 1500 and so on ( horizontal recyclerview)
|
-------------------------------------------
.
.
.
so on
(vertical recyclerview)
Details explanation picture view:
I have two Recyclerview Adapter, first one is display clubname and pass the respective data to other recycler view which gonna display horizontal view with respetive matchTime.
Populated the Outer Recyclerview position 0, worldmatch data and it reflect all the others, how do i pass the populated both worldmatch and world data in same recyclerview and how to filter out all the tab.
Display matchTime field only from WorldMatch list in position 0 and World list in below 0.(horizontal recyclerview)
Any one have any idea, which is really helpful for me and highly appreciate any idea behind this.
Thanks in advance.
It is possible. Now you can set the value to another class in response parsing time or setting Adapter time. I also did this way.
For example:
setmatches(List<club.wordlWild> ww){
List<WorldWild> list = new ArrayList<>();
for(club.worlWide cw : ww){
WorldWild w = new WorldWild ();
w.setMatchTime(cw.getMatchtimie);
..
...// set values
list.add(w);
}
}
Now you can get add club.worlWide values to WorldWild.
If you want change do vise versa.
You can have the worldMatch as the header of your vertical RecyclerView. You do not really need two different adapters for your RecyclerView in your case. Just add the header in your RecyclerView with the worldMatch class will suffice.
If you want to take a look at how you will be going for adding a header or footer in your RecyclerView, you might take a look at the answer here.
Once you are done with adding the worldMatch as the header of your vertical RecyclerView you will be passing the world data in the adapter of your RecyclerView and show them accordingly.
Let me know if you have any confusions.
I encourage you to create a TabData class that you will use for each tab.
public class TabData {
private List<TimeData> regionTimes;
private List<Pair<String, List<TimeData>>> clubTimes = new ArrayList<>();
public void setRegionTimes(List<TimeData> list) {
this.regionTimes = list;
}
public void putClubTimes(String clubName, List<TimeData> times) {
clubTimes.add(new Pair<>(clubName, times);
}
// Getters
}
This would be the TimeData class.
public class TimeData {
int matchTime;
String startTime;
String endTime;
}
Then, for example, to show the first tab (World tab) you should do the following:
TabData worldTabData = new TabData();
worldTabData.setRegionTimes(jsonData.getSoccer().getWorldMatch());
for (ClubData clubData : jsonData.getSoccer().getClubs()) {
String clubName = clubData.getClubProfile().getClubName();
List<TimeData> times = clubData.getWorld();
worldTabData.putClubTimes(clubName, times);
}
regionAdapter = new RegionAdapter(worldTabData);
And this would be the adapter for the outer RecyclerView (vertical):
public class RegionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private TabData data;
public RegionAdapter(TabData data) {
this.data = data;
}
public int getItemCount() {
return 1 + data.getClubTimes().size(); // One row per club plus another one for position 0
}
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (position == 0) {
// Set adapter for horizontal recyclerview with data.getRegionTimes()
} else {
Pair<String, List<TimeData>> clubTime = data.getClubTimes(position - 1);
// Set textview with club.first
// Set adapter for horizontal recyclerview with clubTime.second
}
}
}
Hope you get the idea!
Let me know.
Thanks.
I did something similar via a parent class to use in the adapter but in your case it might be possible to just have the same class for each match.

Removed Images from ArrayList are still showing

I am working on a 2D game in which sprites for player are stored in array list. Since player has multiple states, I want to remove previous states upon use. I have an ArrayList as:
List<Image> x = new ArrayList();
Now I use images from this list as: someImageView.setImage(x.getImage(y));
And I remove first (say 5: 0,1,2,3,4,5th images) states as:
for(int i = 0; i < 6; i++)
x.remove(y);
But these images still show instead of the next image set.
Could it be some sort of Caching? If so how can I delete this cache so my new images are applied?
My sprite images declaration is as follows (shown for one image, but its similar for all images):
Image someImg = new Image("path", true, false, true);
I don't know whether I understand you properly.
So, you use e.g. the first element of a list to be set on an ImageView (it is set properly), then you remove this element from the list.
At this point do you expect that the image will be removed from the ImageView also?
Why is shall be removed?
The list contains references to objects (pointers). When you set an image from the list the reference is passed to the ImageView, so now it will also point to that object. If you remove the reference from the list, it does not matter, the ImageView will still point to that reference.
So you can call setImage explicitly after you removed the elements, or use an ObservableList and set the image on remove (something like this):
ObservableList<Image> iList = FXCollections.observableArrayList();
iList.addListener(new ListChangeListener<Image>()
{
#Override
public void onChanged(javafx.collections.ListChangeListener.Change<? extends Image> change) {
while(change.next())
{
if(change.wasRemoved())
imgView.setImage(iList.get(0));
}
});
}

get the View at the position X of a ListView

How can I retrieve the View at the position X in a ListView? I dont want to inflate a new one, just retrieve the cell visible in the screen for change some parameters programmatically
Since views in ListView are re-used/re-cycled. There is no direct way of getting a view reference from the ListView.
If you want to access a view you need to extend ArrayAdapter and then override getView. There you should call the super.getView and write your own custom code.
If we you really need to control more than try extending BaseAdapter or CursorAdapter.
Found a dirty solution:
You should be able of identify each row generated. For example adding a TextView with visibility=gone and writing a unique value there when generating (or recycling the row)
In the listactivity call to getListView.setSelection(position) to the desired cell
Survey the listview list for the row (until displayed)
lv=getListView();
for (int i=0;i <lv.getChildCount();i++){
if (((TextView)lv.findViewById(R.id.my_hidden_textview)).getText.equals(mykey)){
// view found
} else {
// schedule another survey "soon"
}
}
For the schedule you can use something like:
final int RETRY_DELAY=100;
new Handler(){
public void handleMessage(Message msg){
if (msg.what<0) return; //something went wrong and retries expired
lv=getListView();
for (int i=0;i <lv.getChildCount();i++){
if (((TextView)lv.findViewById(R.id.my_hidden_textview)).getText.equals(mykey)){
//result = lv.findViewById(R.id.my_hidden_textview);
} else {
this.sendEmptyMessageDelayed(msg.what-1,RETRY_DELAY);
}
}
}
}.sendEmptyMessageDelayed(10,RETRY_DELAY);
As I said is a very ugly solution but it works
I didn't clearly understand your problem. But to what I've understood I would suggest you use a frame layout within a linear layout. You can use another frame layout to do your manipulations.

Categories

Resources