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.
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...
I have a RecyclerView that shows different lists depending on which option I pick in my Spinner. I can add items to a list dynamically, but when I add items to the top option on the Spinner, two items appear everytime I add, when it is only supposed to add just one and when I choose a different option in the Spinner and then back to the top option, all items that are supposed to be in it are gone. However, when I repeat the same actions in another option, one item is added at a time, and they are still there if i switch to another and back, so it is only in the first option where I experience these problems.
Here are pictures that shows what happens. First picture shows the entire Spinner, second is the top option selected with items added, third is the second Spinner option selected and fourth is back to the top option and now the items are gone.
This method is used to add a Item to one of the lists. Each option in the Spinner is used to get an element in listOfLists where each element is an ArrayList<Item>(). mItemList is the list which the RecyclerView prints out.
public void addItem(){
mItemList.add(0, new Item(idText.getText().toString(), addAmountField.getText().toString()));
listOfLists.get(dropdown.getSelectedItemPosition()).add(0, new Item(idText.getText().toString(), addAmountField.getText().toString()));
System.out.println("ListSize: " + listOfLists.get(dropdown.getSelectedItemPosition()).size());
addAmountField.setVisibility(View.GONE);
btnAdd.setVisibility(View.GONE);
mAdapter.notifyDataSetChanged();
saveData2();
}
This is the code for switching the lists when I choose another option from the Spinner. What I do here is that I clear the mItemList and then I fill it with one of the ArrayList<Item> inside listOfLists.
dropdown.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
mItemList.clear();
for (int i = 0; i < listOfLists.get(position).size(); i++) {
mItemList.add(listOfLists.get(position).get(i));
}
mAdapter.notifyDataSetChanged();
}
#Override
public void onNothingSelected(AdapterView<?> parentView) {
// your code here
}
});
How can I resolve this so that the top option of the Spinner will work just like the other options where it adds one item at a time and still has the items after I switch to another option?
I managed to solve it so for anyone who faces similar problems this was the problem:
Unlike the other lists in listOfLists where I set mItemList to the chosen list like this
mItemList.clear();
for (int i = 0; i < listOfLists.get(position).size(); i++) {
mItemList.add(listOfLists.get(position).get(i));
}
mItemList is always set to the first list when I run the app like this
mItemList = listOfLists.get(dropDown.getSelectedItemPosition());
So I just changed it so it sets mItemList the same way it is done with the other lists and now it is working.
I have created a custom ListView, and populated with an ArrayList of objects. It also has a checkbox, to allow the user to select certain items, and then on a button click do something with them.
My question is: How to iterate through the ListView code-side?
I've looked at the docs for ListView but can't find a method that looks as if it iterates through. Do I need to implement this in my CustomArrayAdapter??
You can use this. You shoudn't implement nothing from nessesary methods in CustomAdapter.
private void ButtonClick() {
View v;
CheckBox chBox;
for (int i = 0; i < myList.getCount(); i++) {
v = myList.getAdapter().getView(i, null, null);
chBox = (chBox) v.findViewById(R.id.checkBox);//your xml id value for checkBox.
if (chBox.isChecked()) {
doSomething();
}
}
....
}
I would not advice to use Anatol's method as this will create new views for each item. The best way to approach this would be to handle the click of the check box and update your model accordingly. Then you can do something like this:
ListAdapter adapter = myList.getAdapter();
for (int i = 0; i < adapter.getCount(); i++) {
if (adapter.getItem(i).isChecked()) {
doSomething();
}
}
Note that isChecked need to be created in your model class. Please let me know if you have any questions.
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.
I have an Edittext and a CheckedTextView in a listview item (the layout implements Checkable). When I click on the EditText, the bindView() method of the CursorAdapter calls ListView.setItemChecked() (I do this to restore CheckMarks that may have to be restored after clearing a filter). However, ListView.setItemChecked() seems to immediately take away the focus (or something else happening here?) from the EditText I just clicked, so now I can't use the EditText anymore.
I there a way to call ListView.setItemChecked() inside bindView() without such a sideeffect? I already tried calling ListView.setItemChecked() after adapter.getFilter().filter() (outside of bindView()) but it seems in this case the new item list is not available just yet, because the filtering is done in a background thread.
Any suggestions?
Found the answer: use FilterListener class like this
adapter.getFilter().filter("", new FilterListener() {
#Override
public void onFilterComplete(int count) {
for (int i = 0; i < adapter.getCount(); i++) {
if (selected.contains((int) adapter.getItemId(i)))
listView.setItemChecked(i, true);
}
}
});
EDIT: strictly speaking this is not an answer because I still don't know why setItemChecked can't be used in bindView() without intering with the edittext