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.
Related
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...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = inflater.inflate(resource, null);
}
ImageView imageView;
imageView = (ImageView) convertView.findViewById(R.id.ivGallery);
for(HospitalModel.Images images: hospitalModelList.get(position).getImagesList()) {
Glide.with(getContext()).load(images).into(imageView);
}
return convertView;
}
// image URLs are stored in string ArrayList . I defined getter and setter for array list but still I don't know how to use get method for showing ArrayList images dynamically in ListView
Extend the class from BaseAdapter
Override getCount() method and return here the total amount of images you need to show.
In the getView load with Glide only ONE url (remove the for loop), the getView method will be called N times to show in the list the "total" amount of images you returned in the getCount method, the position parameter in the getView method will run from 0 to (total - 1).
You should map 1 position to 1 url, maybe you will need to change the way you access the objects that contain the urls.
Okay so first: stop using ListView, use RecyclerView.
Next, you need to override getItemCount(). You can either choose to do this using a for loop:
int count = 0;
for(HospitalModel.Images images: hospitalModelList.get(position).getImagesList()) {
count += images.size();
}
Or, what is likely preferable, flatten your model object into just what this adapter actually cares about (which is the image URL strings). Something like this:
ArrayList<String> imageUrls = new ArrayList<String>();
for(HospitalModel model : hospitalModelList) {
imageUrls.addAll(model.getImagesList().getImageUrls());
}
Then pass in the imageUrls ArrayList to the Adapter instead. You should only need to flatten this when the model is updated (whether that's initialization or changed). When that occurs, use the notify... methods on the adapter.
After that, it's just a simple Glide.with(getContext()).load(imageUrls.get(position)).into(imageView); inside getView (or onBindViewHolder if you're using a RecyclerView, which I HIGHLY recommend).
I have a ListView within my project. It has many elements, and it uses a custom adapter, since its populated dynamically from a rails server.
I want to change the content of a ListItem when the item is longpressed. In order to achieve this, I have 2 layouts inside the ListItem, with one visible and one hidden.
The issue is that when I longpress an item, the layout changes (As expected), but other ListItems are also affected, and changed in the same way. This appear to occur once for every 5 items, and I cant figure out why.
This is the LongClickListener I'm using, it is located inside de GetView method on the custom adapter:
View v = convertView;
if (v == null){
LayoutInflater vi =
(LayoutInflater)getActivity().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_item, null);
}
final LinearLayout placeInfo =
(LinearLayout) v.findViewById(R.id.list_item_info);
final RelativeLayout placeBrief =
(RelativeLayout)v.findViewById(R.id.list_item_brief);
v.setOnLongClickListener(new OnLongClickListener(){
#Override public boolean onLongClick(View v) {
placeInfo.setVisibility(View.GONE);
placeBrief.setVisibility(View.VISIBLE);
return false;
}});
I would appreciate any help, many thanks in advance.
ListViews recycle Views, so you only have a few views for all of your items. You're directly changing one of these view instances to switch between the info|brief. What you need is to save the status of the info|brief flag for the affected position somewhere else (e.g. a list of positions that should be "briefs" in the adapter). That way when you come back into getView() you can display the right one.
Is there a better way to create a custom list, for my code here seems to make for a less-responsive list. I can't use any of the standard Android lists because I need two ListView's in a ScrollView.
setContentView(R.layout.alertsview);
inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
topRows = new ArrayList<View>();
topLayout = (LinearLayout)findViewById(R.id.topListLayout);
topLayout.removeAllViews();
topRows.clear();
List<Camera> camList = new ArrayList<Camera>(SitesOrchestrator.getInstance().currentSite.cameraList);
for (int i = 0; i < camList.size(); i++)
{
Camera cam = camList.get(i);
View row = inflater.inflate(R.layout.cameraalertrow, null);
TextView name = (TextView)row.findViewById(R.id.cameraNameAlerts);
CheckBox chk = (CheckBox)row.findViewById(R.id.camAlertChk);
name.setText(cam.name);
chk.setChecked(cam.alertsEnabled);
chk.setOnCheckedChangeListener(this);
String index = Integer.toString(i);
chk.setTag(index);
topRows.add(row);
topLayout.addView(row);
}
If you need to lists in the same layout, you should create your own Adapter (derive from base adapter might be good), and, suppose you have two arraylist:
ArrayList<TypeA> typeAList;
ArrayList<TypeB> typeBList;
#Override
int getViewTypeCount(){ return 2; } // means you have two different views from it
#Override
int getItemViewType(int position){
if (position>typeAList.size()) return 1;
return 0;
}
getView(int pos, View convertView, ViewGroup parent){
// Check the pos
if (getItemViewType(pos) == 0){
// Inflate view and bind for type A
}
else{
// Inflate view and bind for type B
}
}
In general, having two list view vertically is not encouraged in Android, but putting both of your content to one list do the trick. I also have a tutorial about this, though it is done in MVVM with Android-Binding.
Moreover, adding Views one by one to ScrollView to mimic the ListView would, of course, be very inefficient. The way Android ListView works (which should different from desktop frameworks) is with recycling views. Suppose you are scrolling up, once the child is scroll out of sight, the listview will recycle the topmost one and place it to the bottom, and the possibly recycled view will pass as convertView in the above getView code. Inflating is considered to be quite expensive process, and multiple child views are memory consuming as well, that's the reason why your code, compare to standard ListView, is very inefficient.
package net.example.view;
class StatusBar extends View { ... }
I use it in my layout as following:
<net.example.view.StatusBar .../>
I must find it by it's type(class), I mean something like:
myActivityInstance.findViewsByClass(StatusBar.class) // returns View[]
Is it possible? If not, what is the better approach to find an element without having to use findViewById()?
Thanks.
If you can't use the id and your content view is some kind of ViewGroup you can iterate over the children.
ViewGroup vg = (ViewGroup) activity.getContentView();
for(int i=0; i<vg.getChildCount(); i++) {
View v = vg.getChildAt(i);
if(v instanceof StatusBar) {
// do something interesting.
}
}
Also if you know the specific index you can call getChildAt(index) directly.
I don't think activity has a method that will return an array of Views to you based on class type. You could create and add them to your layout dynamically if you want to get an array of them without using findViewById() something like this:
mLayout = (LinearLayout)findViewById(R.id.mLayout);
StatusBar[] mBars = new StatusBar[10];
for(int i = 0; i < mViews.length; i++){
mBars[i] = new StatusBar(yourActivity.this);
mLayout.addView(mBars[i]);
}
That would give you an array full of StatusBar objects that have all been added to your layout. (You could make the array of type View if you wanted also).
If you are wanting to define all of your views in XML I don't think there is a way that you can avoid using findViewById() in a loop similar to this to get an array full of references to all of them. For this method you'd probably have to create an array of ints that is the same size that contains all of the resIDs of your views
ids[0] = R.id.mStatusBar1;
ids[1] = R.id.mStatusBar2;
etc.