How may I get all children of an item of ListView ?
There are methods to get child at position but I was not able to find any method which returns collection of all children in an item of ListView.
Update : I just found out that I need ListView items(rows) instead. How may I achieve this ?
What I am doing is, comparing the selected ListView item with all items.
You get all list item from list adapter through iteration as below
for (int i=0;i<adapter.getCount();i++){
adapter.getItem(i);
}
Update : You can compare specific item using index with list adapter all items as below :
for (int i=0;i<adapter.getCount();i++){
if(adapter.get(selectedIndex)==adapter.getItem(i)){
// TODO : write code here item match
break; // after match is good practice to break loop instead compare rest of item even match
}
}
Use this method to get all insight and out-sight view of list view:
for ( int i = 0 ; i < listView.getCount() ; i++){
View v = getViewByPosition(i,listView);
}
public View getViewByPosition(int position, ListView listView) {
final int firstListItemPosition = listView.getFirstVisiblePosition();
final int lastListItemPosition =firstListItemPosition + listView.getChildCount() - 1;
if (position < firstListItemPosition || position > lastListItemPosition ) {
return listView.getAdapter().getView(position, listView.getChildAt(position), listView);
} else {
final int childIndex = position - firstListItemPosition;
return listView.getChildAt(childIndex);
}
}
I guess if your items in the ListView are of type ViewGroup you could do the following
ArrayList<View> children = new ArrayList<View>();
for (int i = item.getChildCount() - 1 ; i>=0; i--) {
children.add(item.getChildAt(i));
}
May be you are looking for something like this. You need to have access to the rootView from which you can get the child views. onItemSelected is only used as it gives the rootview of clicked position.
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
ImageView imgView = view.findViewById(R.id.myImageView);//your imageview that is inflated inside getView() of adapter
//similarly for other views
}
Related
I am creating an ArrayAdapter for my gridView considering header and footer views.
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<GridView
android:id="#+id/activity_main_gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="4" />
</RelativeLayout>
The problem is that when I scroll to the top or the bottom of the grid header/bottom views get over grid items and, when clicking on any item it goes back to the original position (I will put screenshots if necessary)
This is my GridView adapter (params in order: context, arraylist of grid items(without headers/footers), header view for all first row, footer view for all last row, gridview columns number):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGridView = (GridView) findViewById(R.id.activity_main_gridview);
List<String> list = new ArrayList<String>();
for (int i = 0; i < 500; i++) {
list.add("item " + i);
}
View headerView = new View(this);
headerView.setMinimumHeight(70);
headerView.setBackgroundColor(Color.argb(255, 63, 81, 181));
GridHeaderFooterAdapter adapter =
new GridHeaderFooterAdapter(this, list, headerView, headerView, 4);
mGridView.setAdapter(adapter);
}
private final class GridHeaderFooterAdapter extends ArrayAdapter<String> {
private int mNumColumns;
private int mListSize;
private List<String> mList;
private View mHeaderView;
private View mFooterView;
private LayoutInflater mInflater;
public GridHeaderFooterAdapter(ActivityMain context, List<String> list, View headerView, View footerView, int numColumns) {
super(context, 0);
this.mNumColumns = numColumns;
this.mListSize = list.size();
this.mList = list;
this.mHeaderView = headerView;
this.mFooterView = footerView;
mInflater = context.getLayoutInflater();
}
#Override
public int getCount() {
int count = 0;
//headers
count += mNumColumns;
//list items
count += mListSize;
//footers
count += mNumColumns + (mNumColumns - mList.size() % mNumColumns);
Log.w("ActivityMain", "getCount() = " + count);
return count;
}
#Override
public String getItem(int position) {
//discard header items
return mList.get(position - mNumColumns);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//headers
if (position < mNumColumns){
Log.w("ActivityMain", "headers: position = " + position);
return mHeaderView;
}
//listitems
if (position >= mNumColumns && position < mNumColumns + mListSize) {
Log.w("ActivityMain", "listitems: position = " + position);
LinearLayout ll = (LinearLayout) mInflater.inflate(R.layout.listitem_main, null);
TextView tv = (TextView) ll.findViewById(R.id.textView);
tv.setText(getItem(position));
return ll;
}
//footers
if (position >= mNumColumns + mListSize){
Log.w("ActivityMain", "footers: position = " + position);
/*TODO: some footers might be in the same row as listitem, which is wrong because Gridview uses last item of the row to determine row height... */
return mFooterView;
}
return null;
}
}
BEFORE SCROLLING:
AFTER SCROLLING:
As you can see, the target is to add a header to avoid first items being behind the status bar
Manually playing around with the position numbers is a dangerous thing, though quite possible if done correctly. Problem here is that you are trying to bypass how the getView() method operates. getView() will maintain it's own reference to the returned View and recycle or destroy it accordingly. It's already been hugely optimized to provide great performance, and to achieve your end goal, you want to work with it...not against.
Ultimately, you want a spacing for the 1st (header) and last (footer) row in your GridView to avoid being overlapped with the translucent status/navigaion bars. I'll give you a higher overview of what needs to happen.
getCount() needs to return:
mListSize + (mNumColumns * 2)
Why? Because you'll need a header view for every column and a footer view for every column as well.
getItem() needs to return something for all possible positions. That means whatever value returned bygetCount() needs to be able to return something...even if it's an empty string. So something like:
// if a header position or a footer position
if (position < mNumColumns || position > mListSize + mNumColumns) {
return ""
} else {
return mList.get(position - mNumColumns);
}
Note, this may be off by a little considering count is not zero based but position is...but it shows the basic gist of what you need.
getView() needs to render a view as normal. Difference being that if the position number is indicative of a header/footer position, you return a blank view. So for example:
ViewHolder vh = null;
if (convertView == null) {
//inflate your view
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView;
}
//If position is indicative of a header/footer. Alternatively you could see if getItem() returns an empty string...assuming your data can never contain an empty string.
if (position < mNumColumns || position > mListSize + mNumColumns) {
//Don't fill the view with any data. Make any visible elements INVISIBLE
} else {
//populate convertView with data
}
Get rid of the whole notion of passing in a view for the adapter to use. That will def break everything. If performance is very much a concern then a better alternative (and the proper one...more so then what I posted) is to use getItemViewType() and getViewTypeCount(). However that will require you to create a class object that can represent your actual data (eg Strings) and a flag that indicates it's a header, footer, and the data.
I just want to start off by saying the list view works fine, there's no problem with this code.
All I want to know is how I can expand on it. I want to have a label (a bit of text to describe what the items in the list view represent) at the top of the list view before all of the items from the adapter appear. But obviously, because I want it inside the list view I want the label to go out of view when the user starts scrolling through the list.
How do I achieve that?
ListView listView = (ListView) findViewById(R.id.list_task_summary);
TasksSummaryListViewHelper adapter = new TasksSummaryListViewHelper(this, getTasks());
listView.setAdapter(adapter);
listView.setEmptyView(findViewById(R.id.text_empty_list));
And my template:
<ListView
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:id="#+id/list_task_summary"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:divider="#android:color/transparent"
android:dividerHeight="15sp"
android:listSelector="#drawable/list_task_summary_selector_unselected">
</ListView>
In your custom List Adapter , When getView is called, create a title view for position 0 and other position you own list item layout
#Override
public View getView(int position, View contentView , ViewGroup parent){
View row = null;
if(position == 0) {
// inflate any custom layout or just a textView
TextView titleView = new TextView();
titleView.setText("TITLE");
row = titleView;
}
else {
row = mContext.getLayoutInflater().inflate(R.layout.list_item, null);
}
return row;
}
And ,You will see the recyle issue with ListView since it uses the same child view for other items,
So to that you need to Override the getViewTypeCount and return the view type count as 2
#Override
public int getViewTypeCount() {
return 2;
}
Also, Override getItemViewType and return a unique id for position 0 and other views same id.
#Override
public int getItemViewType(int position){
// return a unique number
if(position == 0){
return 0;
}
else {
return 1;
}
}
You can do as Libin suggests, but if it's one static view that needs to scroll with the rest of the list, I think it's better to use listView.addHeaderView() instead. This way you don't need to make any changes to your adapter at all. Note that the position reported in any OnListItemClickListeners will be offset by the number of header views.
At the moment im looping through a dynamic arraylist and building a Listview with Switches and Edittexts from its data.
for (int i = 0; i < response.size(); ++i) {
...
xxx.SetId(i)
...
}
this was my working solution if a Edittext was on the first position:
if (i == 0 ) {
editText.requestFocus();
getWindow().setSoftInputMode (WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
But how to set the focus on the first available Edittext? for example third position after two switches?
(the position changes dynamically based on the arraylist)
Generally you should populate and set any required state inside the Adapter that is attached to the ListView. A simple solution would be something in the lines of:
public class MyAdapter extends Adapter {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = layoutInflator.inflate(R.layout.somelayout, parent, false);
// position is zero-based so 3rd item is at position 2
if (position == 2) {
view.findViewById(R.id.edittext).requestFocus();
}
return view;
}
}
And to scroll the listview to the 3rd item you could call listView.setSelection(2).
In arrayAdptor we use following code:
final LayoutInflater inflater = activity.getLayoutInflater();
row = (LinearLayoutCustom) inflater.inflate(R.layout.row, null);
final TextView label = (TextView) row.findViewById(R.id.title);
label.setText(position + "" + items[position]);
return row;
Now suppose some value are null (for example at position 2 , items[2] = null ) so i dont want to show it in row. i want to hide it. if i use
row.setVisibility(View.GONE)
it leaves a blank space at this row which i dont want. so what should i do?
AFAIK you can't return a null view from getView, but you could just make the view invisible and height 1. Although manipulating using the getCount is probably the preferred way.
view.setVisibility(View.INVISIBLE);
view.getLayoutParams().height = 1;
You'll need to have the adapter return the total number of non-null items with getCount and then keep a mapping of position to your internal data structure.
For example.
You have a list
1 - John
2 - null
3 - Bill
4 - Susan
5 - null
When getCount is called it returns 3.
Then when getView is called on position 1 you return the item at list[1].
getView on position 2 returns list[3] (as it's the 2nd non-null),
and so forth.
This is the only way I've found to do this.
You can use a View that has no height for the "hidden" items so that you don't have to do all the model housekeeping and mapping. For example, suppose you had a "filter" EditText field that when data is entered it only keeps matching items:
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) MyActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
RelativeLayout view = (RelativeLayout) inflater.inflate(R.id.myListLayout, null, false);
...
// if we didn't match filter be GONE and leave
if (filterText.length() > 0 && myModelValueAtPosition.toLowerCase().indexOf(filterText) < 0){
view = (RelativeLayout) inflater.inflate(R.layout.myListLayoutWithZeroHeight, null, false);
view.setVisibility(View.GONE); // this doesn't really do anything useful; I'd hoped it would work by itself, but turns out the zero height layout is the key
return view;
}
view.setVisibility(View.VISIBLE);
...
}
Here you need to write the logic in your getCount(),getItemId() and getItem(),
It will create the no of rows what the getCount return
//How many items are in the data set represented by this Adapter
public int getCount() {
return //Should return the count of rows you need to display (here the count excluding null values)
}
And
//This need to return data item associated with the specified position in the data set.
public Object getItem(int position) {
return //Return the object need to display at position, need the logic to skip null value
}
Edit:So in your getview
public View getView(int position, View convertView, ViewGroup parent) {
----
getItem(position);//Object corresponding to position ,In your case it will not be null since you need to write the logic to skip null object at getItem
----
}
This is the solution I implemented, here is a code example for everyone that is looking it:
class Shipment_Adapter extends ArrayAdapter<Shipment>{
....
ArrayList<Integer> emptyPositions = new ArrayList<>();
public Shipment_Adapter(Context context, int shipment_row, Shipment[] myShipments){
super(context, R.layout.shipment_row,myShipments);
//populate emptyPositions list
for(int i = 0; i< myShipments.length; i++){
if(myShipments[i]==null){
emptyPositions.add(i);
}
}
this.mShipment = myShipments;
this.mContext = context;
}
//corrects size of List
#Override
public int getCount() {
return (mShipment.length - emptyPositions.size());
}
//recursive function that checks if position is not empty until it isn't
public int isEmpty(int positiontocheck){
int newposition;
if(emptyPositions.contains(positiontocheck)){
//true? check that next one is free
return isEmpty(positiontocheck+1);
}else{
newposition = positiontocheck;
}
return newposition;
}
}
public View getView(int position, View convertView, ViewGroup parent) {
//now just need to use isEmpty to get the next not empty position in
//case our real position is empty
position= isEmpty(position);
Shipment shipment = mShipment[position];
...//and so on
}
hope this helps!
I need to find out the pixel position of one element in a list that's been displayed using a ListView. It seems like I should get one of the TextView's and then use getTop(), but I can't figure out how to get a child view of a ListView.
Update: The children of the ViewGroup do not correspond 1-to-1 with the items in the list, for a ListView. Instead, the ViewGroup's children correspond to only those views that are visible right now. So getChildAt() operates on an index that's internal to the ViewGroup and doesn't necessarily have anything to do with the position in the list that the ListView uses.
See: Android ListView: get data index of visible item
and combine with part of Feet's answer above, can give you something like:
int wantedPosition = 10; // Whatever position you're looking for
int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount(); // This is the same as child #0
int wantedChild = wantedPosition - firstPosition;
// Say, first visible position is 8, you want position 10, wantedChild will now be 2
// So that means your view is child #2 in the ViewGroup:
if (wantedChild < 0 || wantedChild >= listView.getChildCount()) {
Log.w(TAG, "Unable to get view for desired position, because it's not being displayed on screen.");
return;
}
// Could also check if wantedPosition is between listView.getFirstVisiblePosition() and listView.getLastVisiblePosition() instead.
View wantedView = listView.getChildAt(wantedChild);
The benefit is that you aren't iterating over the ListView's children, which could take a performance hit.
This code is easier to use:
View rowView = listView.getChildAt(viewIndex);//The item number in the List View
if(rowView != null)
{
// Your code here
}
A quick search of the docs for the ListView class has turned up getChildCount() and getChildAt() methods inherited from ViewGroup. Can you iterate through them using these? I'm not sure but it's worth a try.
Found it here
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
View v;
int count = parent.getChildCount();
v = parent.getChildAt(position);
parent.requestChildFocus(v, view);
v.setBackground(res.getDrawable(R.drawable.transparent_button));
for (int i = 0; i < count; i++) {
if (i != position) {
v = parent.getChildAt(i);
v.setBackground(res.getDrawable(R.drawable.not_clicked));
}
}
}
});
Basically, create two Drawables - one that is transparent, and another that is the desired color. Request focus at the clicked position (int position as defined) and change the color of the said row. Then walk through the parent ListView, and change all other rows accordingly. This accounts for when a user clicks on the listview multiple times. This is done with a custom layout for each row in the ListView. (Very simple, just create a new layout file with a TextView - do not set focusable or clickable!).
No custom adapter required - use ArrayAdapter
int position = 0;
listview.setItemChecked(position, true);
View wantedView = adapter.getView(position, null, listview);
This assumes you know the position of the element in the ListView :
View element = listView.getListAdapter().getView(position, null, null);
Then you should be able to call getLeft() and getTop() to determine the elements on screen position.