I have a ListView which is supposed to become a menu with two drawables and two text views per row.
Activity Code:
ArrayList<MenuItem> itemArray = new ArrayList<MenuItem>();
itemArray.add(new MenuItem("Headertexxt", "subbtexdt"));
itemArray.add(new MenuItem("asf", "asf"));
ListView listView = (ListView) findViewById(R.id.listViewCM);
String[] array = getResources().getStringArray(R.array.buttonsCM);
int[] images = new int[] { R.drawable.btn_car, R.drawable.btn_star, R.drawable.btn_bag};
listView.setAdapter(new HomeScreenButtonsAdapterSubtext(this, R.layout.row,
itemArray, images, R.drawable.list_arrow));
Utils.setListViewHeightBasedOnChildren(listView);
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
findViewById(R.id.buttonCreditCompilation).performClick();
break;
case 1:
findViewById(R.id.buttonYourCredits).performClick();
break;
}
}
});
Adapter code:
public class HomeScreenButtonsAdapterSubtext extends ArrayAdapter<MenuItem> {
private Drawable[] drawables;
private Drawable arrowDrawable;
private ArrayList<MenuItem> items;
public HomeScreenButtonsAdapterSubtext(Context context, int resourceId,
ArrayList<MenuItem> items, int[] images, int arrowImage) {
super(context, resourceId, items);
this.items = items;
Resources resources = context.getResources();
if (images != null) {
drawables = new Drawable[images.length];
int i = 0;
for (int id : images) {
Drawable drawable = resources.getDrawable(id);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
drawables[i++] = drawable;
}
}
arrowDrawable = resources.getDrawable(arrowImage);
arrowDrawable.setBounds(0, 0, arrowDrawable.getIntrinsicWidth(),
arrowDrawable.getIntrinsicHeight());
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
if (v instanceof TextView) {
Drawable dr = drawables != null ? drawables[position %
drawables.length] : null;
((TextView) v).setCompoundDrawables(dr, null, arrowDrawable, null);
Utils.setFont((TextView) v);
}
// View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
MenuItem station = items.get(position);
if (station != null) {
TextView tt = (TextView) v.findViewById(R.id.headerText);
TextView bt = (TextView) v.findViewById(R.id.subText);
if (tt != null) {
tt.setText(station.getHeaderText());
}
if (bt != null) {
bt.setText(station.getSubText());
}
}
return v;
}
The problem I have right now is that I can't set the listview height based on the children. I'm trying to do that here: Utils.setListViewHeightBasedOnChildren(listView); but getting the error: arrayadapter requires the resource id to be a textview at this row. Does anyone know a solution for this?
Can I use some other method for setting the ListView height?
Thanks
but getting the error: arrayadapter requires the resource id to be a
textview at this row.
R.layout.row is a layout file which it doesn't have just a TextView. If you call the super constructor you have used and you also call the super.getView(in the getView method) method in the adapter, then ArrayAdapter will complain as it expects a single widget in the layout file passed to it(a single TextView).
I don't understand why you have that piece of code in the getView method(with the super call) when you know precisely that the row can't be an instance of Textview .
I'm not sure about setting the height of the ListView either, if you're trying to show all the rows of the ListView, don't do it(as you make the ListView basically useless). If you still want to do this, then it's better to lose the ListView and build the row layouts manually.
In fact it does not really make sense to set the height of ListView depending on its content.
Because the whole point of a ListView is to make its content scrollable (however big it is)...so it is supposed to have a fixed height.
Related
So I have a List View that when you click on a row it opens up a new activity. In the new activity there's a checkbox. If you check the check box and then go back to the listview activity it should set a checkmark next to the list view item that was initially clicked.
Whats happening right now is when I check the checkbox and return to the listview every row has a checkmark next to it regardless of which row the checkbox was checked from.
heres my mainactivity with the listview and on click listener that starts the second checkbox activity
//fill list view with xml array of routes
final CharSequence[] routeListViewItems = getResources().getTextArray(R.array.routeList);
//custom adapter for list view
ListAdapter routeAdapter = new CustomAdapter(this, routeListViewItems);
final ListView routeListView = (ListView) findViewById(R.id.routeListView);
routeListView.setAdapter(routeAdapter);
routeListView.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int listViewItemPosition = position;
CharSequence route = routeListViewItems[position];
int imageId = (int) image.getResourceId(position, -1);
if (route.equals(routeListViewItems[position]))
{
Intent intent = new Intent(view.getContext(), RouteDetails.class);
intent.putExtra("route", routeDetail[position]);
intent.putExtra("imageResourceId", imageId);
intent.putExtra("routeName", routeListViewItems[position]);
intent.putExtra("listViewItemPosition", listViewItemPosition);
startActivity(intent);
}
}
}
);
}
then heres what im passing from the second activity back to the listview activity
#Override ///////for back button///////
public void onBackPressed() {
super.onBackPressed();
////////sets checkmark next to listview item//////
if (routeCheckBox.isChecked())
{
Intent check = new Intent(RouteDetails.this,MainActivity.class);
check.putExtra("checkImageResource", R.drawable.checkmark);
check.putExtra("listViewItemPosition", listViewItemPosition);
startActivity(check);
}
}
and heres back in my listview activity where I recieve the info from my checkbox activity and set the checkmark
edited to include full adapter class
edited to include code that I found to solve my issue!
class CustomAdapter extends ArrayAdapter<CharSequence> {
public CustomAdapter(Context context, CharSequence[] routes) {
super(context, R.layout.custom_row ,routes);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater routeInflater = LayoutInflater.from(getContext());
View customView = convertView;
if(customView == null){customView = routeInflater.inflate(R.layout.custom_row, parent, false);}
CharSequence singleRoute = getItem(position);
TextView routeText = (TextView) customView.findViewById(R.id.routeText);
routeText.setText(singleRoute);
/////////////set check mark/////////////
ImageView checkImageView = (ImageView) customView.findViewById(R.id.checkImageView);
int checkImageResourceId = ((Activity) getContext()).getIntent().getIntExtra("checkImageResource",0);
int listViewItemPosition = ((Activity) getContext()).getIntent().getIntExtra("listViewItemPosition",0);
/////my solution was just setting where listviewitemposition == position in my getview method//////
if (listViewItemPosition == position)
{
checkImageView.setImageResource(checkImageResourceId);}
//////////////////////////////////////////////////
return customView;
}
thanks for any help!
The solution was I just needed use an if statement to set listviewItemPosition == position in my getView method
/////my solution was just setting where listviewitemposition == position in my getview method//////
if (listViewItemPosition == position)
{
checkImageView.setImageResource(checkImageResourceId);}
//////////////////////////////////////////////////
There is ListView with correct values:
public class FragmentTab1 extends SherlockFragment {
ListView list;
LazyAdapter adapter;
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
list = (ListView) getActivity().findViewById(android.R.id.list); //also I tried view.findViewById(android.R.id.list)
............
adapter = new LazyAdapter(getActivity(), mSource);
list.setAdapter(adapter);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View rootView = inflater.inflate(R.layout.fragmenttab1, container, false);
return rootView;
}
when I try:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId(); //correct
int itemCount = list.getCount(); // 10 ps as show Logcat
if (R.id.save == id) {
CheckBox cb;
for(int i = itemCount - 1; i >= 0; i--) {
cb = (CheckBox)list.getChildAt(i).findViewById(R.id.checkBox1); //Error here
}
}
return true;
}
xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save to database"
android:id="#+id/checkBox1" /> // same id
and adapter is next:
public class LazyAdapter extends BaseAdapter {
private Activity activity;
ArrayList<Bitmap> bitmapArray = new ArrayList<Bitmap>();
private ArrayList<Data> mObjects;
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;
public LazyAdapter(Activity a, ArrayList<Data> mObjects1) {
activity = a;
mObjects = mObjects1;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public int getCount() {
return mObjects.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
Data item = mObjects.get(position);
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.item_internet, null);
TextView text=(TextView)vi.findViewById(R.id.title1);
ImageView image=(ImageView)vi.findViewById(android.R.id.icon);
text.setText(item.getmTitle());
bitmapArray.add(imageLoader.getBitmap());
imageLoader.DisplayImage(item.getmImageUrl(), image);
return vi;
}
I receive correct ListView, but receive error when I try click save button from action bar.
Probably, I should init CheckBox in adapter?
Somebody can help me?
list.getChildAt(i) will be null if the child item is not visible. So check for null before use.
So you cannot retrieve all checked items in this way.
Please post complete .xml and the definition of <Data>.
I'd think you'd get an indexoutofbounds but since it's null, this might be why: ListView getChildAt returning null for visible children
Also, put a log statement in your for loop to display the value of all variables concerned, so: i and itemCount etc.
And set a breakpoint just before the loop and run debug mode to step over to check the values as it loops through and you'll see what i value caused the nullpointer in the debugger or if you miss it, it will be in logcat
I know this is very old post. But I'm answering because people are still looking for a work around on ListView getChildAt() null pointer exception.
This is because the ArrayApdater is REMOVING and RECYCLING the views that are not visible yet on the ListView because of height. So that if you have 10 item views, and ListView can display 4 - 5 at a the time :
The Adapter REMOVE the item views at position 5 to 9, so that any attempt to adapter.getChildAt(5... to 9) will cause null pointer exception
The Adapter also RECYCLE the item view, so that any reference you made on position 3 for example will be lost when you scroll down to 5 to 9, and also any Input that you make on position 3 (EditText, Checkbox, etc.) will be recycled when you scroll down to 5 to 9 and will be reused at another position later (ex position 1, 2 or 3, etc.) with the same value
The only way I found to control this is to forget about getting the View and to have :
Attribute HashMap<Integer, Boolean> cbValues or any type you want for handling the values you want to use for each item on the list. The first type must be unique for item like item->getId() or position. Initialize it with new HashMap<>() in the Constructor;
Add InputListener for Input Views, (addTextChangedListener for EditText, setOnCheckedChangeListener for Checkbox, etc.) And on input, update the HashMap key (item.getId() or position) and value (editable.toString() or true or false). Ex. on #Override public void onCheckedChanged, put boolean result cbValues.put(item.getId(), b);
Prevent Adapter from using recycled convertView, remove condition if(convertView == null) so that adapter always inflate a brand new view instance. Because the view instance is new each time, you must set the value from HashMap each time also like if it already contains the key if(cbValues.containsKey(item.getId())){cbItem.setChecked(cbValue.get(cbItem.getId()))};. Probably in this case there is not tons of Items, so that smooth scrolling won't be a must.
And finally create public methods to get the Values outside of Adapter passing item->getId() Integer as parameter. Ex : 'public bool getCheckboxValueForItemId(int itemId) { return cbValues.get(itemId); }` . It will be easy then to select Item from Adapter
Here is the Codes at the end :
public class LazyAdapter extends BaseAdapter {
private Activity activity;
ArrayList<Bitmap> bitmapArray = new ArrayList<Bitmap>();
private ArrayList<Data> mObjects;
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;
HashMap<Integer, Boolean> cbValues;
public LazyAdapter(Activity a, ArrayList<Data> mObjects1) {
activity = a;
mObjects = mObjects1;
cbValues = new HashMap<>();
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public int getCount() {
return mObjects.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
Data item = mObjects.get(position);
View vi=convertView;
// Remove convertView condition
//if(convertView==null)
vi = inflater.inflate(R.layout.item_internet, null);
TextView text=(TextView)vi.findViewById(R.id.title1);
ImageView image=(ImageView)vi.findViewById(android.R.id.icon);
Checkbox cbItem = (Checkbox) vi.findViewById(android.R.id.checkbox1);
text.setText(item.getmTitle());
bitmapArray.add(imageLoader.getBitmap());
imageLoader.DisplayImage(item.getmImageUrl(), image);
if(cbValues.containsKey(item.getId())) {
cbItem.setChecked(cbValue.get(cbItem.getId()))};
}
cbItem.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
cbValues.put(item.getId(), b);
}
});
return vi;
}
// itemId : unique identifier for an Item, not the position of Item in Adapter
public bool getCheckboxValueForItemId(int itemId) {
return cbValues.get(itemId);
}
}
public class SongListAdapter_AddMode extends ArrayAdapter{
Context context;
ArrayList<Song> songs;
public SongListAdapter_AddMode(Context context, ArrayList<Song> songs) {
super(context, R.layout.list_item1_addmode, songs );
this.songs = songs;
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
ViewHolder holder;
final int pos = position;
Log.d("TAG", "position :" + position);
Song currentSong = songs.get(position);
Log.d("TAG", "position : " + position );
if( convertView == null ){
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate( R.layout.list_item1_addmode, parent, false );
holder.titleLabel = (TextView) convertView.findViewById( R.id.topLabel );
holder.artistLabel = (TextView) convertView.findViewById( R.id.bottomLabel );
holder.cover = (ImageView) convertView.findViewById( R.id.list_image );
holder.button = (ImageView) convertView.findViewById(R.id.addButton);
holder.button.setOnClickListener( new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("TAG", "pos" + pos );
}
});
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
Uri coverPath = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), currentSong.getID() );
Picasso.with(context).load( coverPath ).error( R.drawable.untitled ).into( holder.cover );
holder.titleLabel.setText( currentSong.getName() );
holder.artistLabel.setText( currentSong.getArtistName() );
return convertView;
}
private class ViewHolder {
TextView titleLabel;
TextView artistLabel;
ImageView cover;
ImageView button;
}
}
Well this is a small example of what an ArrayAdapter might look like. As you can see in the getView() method, I get the currentSong from a list which is actually very huge. It contains 1600~ Songs. When I print the position to the LogCat, it says 0 up to 7. How am I getting the correct position in the song list but when I print it, it is totally different?
I ALWAYS get the correct song. Even in the 900~ and above. But the position(LogCat) is always from 0 up to 7...
I need to get the current row. I want to add an onClickListener() to a button from the current View in this method and when I click it, I want to do something which I cannot do with an onItemClickListener() on the ListView.
It seems that you set the button click listener to print the position of the recyclable views thus you get 0-7.
Instead set the listener outside the if (convertview == null) check.
I ALWAYS get the correct song
of course since the code is correct
But the position(LogCat) is always from 0 up to 7...
This is due to how the ListView recycles its rows. 7 items are visible on the screen of your test device. See this post
You should override getCount:
#Override
public int getCount(){
return songs.size();
}
Because it is essential for correct displaying the list, but returns 0 b default.
When your view has wrong position ids or displays wrong items in your autocomplete dropdown view, then instantiate your view like this (Kotlin):
val view: View = convertView ?: LayoutInflater.from(context).inflate(resourceId, parent, false)
I have made a ListView which is populated with elements of an ArrayList. I do not have an XML file with this ListView, it is only in Java. Given this, how would I change the background color of the ListView as well as change the color of the text of the ListView?
This is the code for the ListView:
setListAdapter(new ArrayAdapter<String>(CollegeList.this, android.R.layout.simple_list_item_1, nameList));
In case you want to customize each line of the listview you have to use a custom adapter with custom listview item. Then you can use the "getView" Method to catch each item and position to change colors.
Here is a sample:
public class ListViewAdapter extends ArrayAdapter<Item> {
private Context context;
private int itemResourceId;
private ArrayList<Item> items;
public ListViewAdapter(Context context, int layoutId, ArrayList<Item> items) {
super(context, layoutId, items);
this.context = context;
this.itemResourceId = layoutId;
this.items = items;
}
#Override
public View getView(int position, View view, ViewGroup parent) {
ViewHolder holder = null;
if (view == null) {
holder = new ViewHolder();
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(itemResourceId, null);
holder.listItem = (TextView) view.findViewById(R.id.item);
view.setTag(holder);
}
else
holder = (ViewHolder) view.getTag();
if (position % 2 == 0)
view.setBackgroundColor(context.getResources().getColor(R.color.listItemEven));
else
view.setBackgroundColor(context.getResources().getColor(R.color.listItemOdd));
Item item = items.get(position);
holder.listItem.setText((position+1) + ". " + item.sTitle);
return view;
}
static class ViewHolder {
TextView listItem;
}
}
android.R.layout.simple_list_item_1
You have to add an extra parameter to your ArrayAdapter, this will be a CUSTOM XML OF YOUR OWN and the next one will be a textview that is inside that custom layout, that way the array adapter will know which layout to fill and which textview needs to show the data you want to show.
from that xml you can modify background and color. will look something like this:
setListAdapter(new ArrayAdapter<String>(CollegeList.this, R.layout.your_custom_layout, R.id.the-text-view-in-that-layout, nameList));
You can get the xml for simple_list_item_1 here. You can copy it to your project and modify it, just change the code for your listview to setListAdapter(new ArrayAdapter<String>(CollegeList.this, R.layout.simple_list_item_1, nameList)); You can also create one yourself, since simple_list_item_1 is nothing but a textview. Just make sure that the id is android:id="#android:id/text1" otherwise it won't work with the default adapter.
I have a Layout which contains a textview and editText on the top, then after a listView and at the bottom i have 4 buttons.
I want my listview items to contain a imageView and 4 textViews.
I have the main layout with all the views in the 1st statement and a seperate list_item.xml (linear layout) which contains all the elements (imageview and 4 textviews) which each listview item must contain.
How can I populate the listview in my main layout with list_item.xml layout.
I have populated a listview using a list_item.xml which contains only TextView tag (not a linear or relative layout).
I also populated some other listview using a custom array adapter, but here the main layout contains only the list view item layout but not the top textview and edit view and bottom 4 buttons.
I think my problem is a combination of both point1 and point2. can anyone explain how to achieve this?
You need to implement a custom adapter like this...
private class myAdapter extends ArrayAdapter<yourclass> {
private ArrayList<yourclass> items;
public myAdapter(Context context, int textViewResourceId, ArrayList<yourclass> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_item, null);
}
yourclass o = items.get(position);
if (o != null) {
TextView tt = (TextView) v.findViewById(R.id.txt1);
TextView bt = (TextView) v.findViewById(R.id.txt2);
//similarly for 2 more textviews and a imageview
if (tt != null) {
tt.setText("Name: "+o.getitemName()); }
if(bt != null){
bt.setText("Status: "+ o.getitemStatus());
//similarly...and getitemName ,getitemStatus are functions of your class.. to get the values..
}
}
return v;
}
}