Android: getView method of custom adapter not called - android

I've searched through a lot of other answer for the same problem, but didn't found any solution that works for me. The problem, as the title says, is that the getView method from my custom adapter doesn't get called.
Here's the code (first the fragment):
public class CategoryListFragment extends ListFragment
implements NewCategoryDialogListener {
private GestoreAttivitaDbHelper mDbHelper;
private CategoryListAdapter mAdapter;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mAdapter = new CategoryListAdapter(getActivity());
setListAdapter(mAdapter);
CategoryLoader categoryLoader = new CategoryLoader();
if (mDbHelper == null) {
mDbHelper = new GestoreAttivitaDbHelper(getActivity().getApplicationContext());
}
SQLiteDatabase db = mDbHelper.getReadableDatabase();
mAdapter.addAll(categoryLoader.getAllCategories(db));
mAdapter.notifyDataSetChanged();
mAdapter.getCount();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.category_list, container);
}
Here's the adapter:
public class CategoryListAdapter extends ArrayAdapter<CategoryElement> {
private LayoutInflater mInflater;
public CategoryListAdapter(Context ctx) {
super(ctx, R.layout.category_element);
mInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
Log.d("Adapter", "Restituisco la view per l'elemento");
if (convertView == null) {
view = mInflater.inflate(R.layout.category_element, null);
} else {
view = convertView;
}
//((TextView) view.findViewById(R.id.category_element_text)).setText(getItem(position).getName());
TextView textView = (TextView) view.findViewById(R.id.category_element_text);
textView.setText(getItem(position).getName());
return view;
}
}
And here's my two layout files:
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/category_list"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
and:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/category_element"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="#+id/category_element_text"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
</LinearLayout>
I thought that setting the adapter in the onCreate could be a problem, since it is called before onCreateView, and at that time the fragment isn't already associated with the ListView. So I moved the code from the onCreate to the onStart method, but nothing changed.
Also, the getCount() correctly returns me 6, the precise number of element red from the database.
Any help would be really appreciated!!
Thanks.
Edit:
Solved!
Problem was in the activity, I had the following code:
fragmentTransaction.add(0, categoryListFragment);
that I changed in
fragmentTransaction.add(R.id.activity_main, categoryListFragment);
Without specifying the View id to which the fragment should be attached it never draws it!
In addition, I had to change from this
view = mInflater.inflate(R.layout.category_element, parent);
to this:
view = mInflater.inflate(R.layout.category_element, null);
in the getView method.
PS. I'm editing this cause I can't answer my own question until 8 hours have passed..

I think in your R.layout.category_list file you need to give the ListView the following attribute:
android:id="#android:id/list"
ListFragment (and ListActivity) look for this id to find the ListView when you call methods like setListAdapter().
Also, if all you want is a ListView, you don't have to supply a layout file. Simply do not override onCreateView() and the system will provide a ListView for you automatically. Only if you want a custom layout do you need to inflate one, and if you do, the ListView should have the id stated above.

Related

Choppy ProgressBar performance when used in ListView

I recently encountered an issue where the animation of an indeterminate ProgressBar used inside of a ListView row became choppy. In a nutshell, I have a ListView where each row contains a ProgressBar. The animations look great, until I scroll; from then on, at least one of the ProgressBar will have a choppy animation.
Does anyone know how to fix this problem?
View for the ListView row
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:indeterminate="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Simple custom ArrayAdapter
public class MyAdapter extends ArrayAdapter {
List list;
public MyAdapter(Context context, List objects) {
super(context, 0, objects);
list = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = ((LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.list_row, parent, false);
}
return convertView;
}
}
OnCreate() method for the sample Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
ListView listView = (ListView)findViewById(R.id.list_view);
ArrayList<Integer> data = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,7,8,9,0,11,12,13,14,15,16,17));
MyAdapter adapter = new MyAdapter(this, data);
listView.setAdapter(adapter);
}
Bug logged (contains sample project): https://code.google.com/p/android/issues/detail?id=145569&thanks=145569&ts=1423673226
Try implementing the view holder pattern and check the performance.
Create a static ViewHolder class with a progress bar.
static class ViewHolder {
ProgressBar progress;
}
and in your getView() you get find the view holder from the view only when the convertView is null, otherwise take it from the tag holding the viewHolder. This way you are inflating a new view only when the convertView is null, otherwise you are using the views stored in your viewholder tag.
A simple tutorial can be found here.
it is happening for the first time only, if you close and reopen the app, you will not notice it.
Did you check in older versions like kitkat?
And dont create LayoutInflater in the getView(), create once in the constructor and use it in the constuctor

Android fragment + custom listview

Its my first post here but i am reading this Q&A for years and I always find an answer, but this time i cannot find it, or i cannot combine multiple answers with my problem. I hope you can help.
So I have fragment which overrides onCreateView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// TODO Auto-generated method stub
db = new WineryDatabaseAdapter(getActivity());
bacveList = db.getBacve();
v = inflater.inflate(R.layout.sve_bacve, container, false);
Log.v("onCreateView", "oncreateview");
return v;
}
Then I have method where I set my data
public void getBacveItems(){
ArrayAdapter<Bacve> ad = new BacveListAdapter(getActivity(), bacveList);
lv = (ListView) v.findViewById(R.id.listSveBacve);
lv.setAdapter(ad);
lv.setOnItemClickListener(this);
Log.v("getBacveItems", "getBacveItems");
}
In that method I am calling my Adapter so i can use my listview layout
public class BacveListAdapter extends ArrayAdapter<Bacve>{
List<Bacve> bacve;
Context c;
String[] values = new String[] { "prva","druga" };
public BacveListAdapter(Context c,List<Bacve> l){
//super(c,R.layout.sve_bacve_item,l);
super(c,R.layout.sve_bacve_item,l);
Log.v("BacveListAdapter", "BacveListAdapter");
this.bacve = l;
this.c = c;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View iv = convertView;
TextView tv;
if (iv == null){
iv = (View)((LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.sve_bacve,parent,false);
tv = new TextView(c);
}
else{
Bacve bacva = bacve.get(position);
tv = (TextView) iv.findViewById(R.id.textNazivBacve);
tv.setText(bacva.getIme());
}
return iv;
}
}
But for some reason i am getting errors when trying to access tv. It is always null.
I think its something with views that are created/getted, and I am creating/getting some parent view in higher hierarchy. I have tried to debug it but dont know how to use those IDs in debug mode. Well not how to use it, but how to compare it so I can see is it right view.
Please help :)
I did it like this in this edit but nothing. Is it because i am using swipe views and I am getting wrong view in the first?
This is my sve_bacve_item and it has textview.
I am kind 1 step forward with #Raghunandan solution but now I have problem with convertin String to holder. I am trying to resolve that.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textNazivBacve"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:layout_toRightOf="#+id/bacveIcon"
android:text="#string/BacveNaziv"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ImageView
android:id="#+id/bacveIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignTop="#+id/textNazivBacve"
android:adjustViewBounds="true"
android:contentDescription="#string/Bacve"
android:maxHeight="120dp"
android:maxWidth="120dp"
android:src="#drawable/bacve_item" />
</RelativeLayout>
BR
Use a ViewHolder for perofrmance.
public static ViewHolder
{
TextView tv;
}
Change to
LayoutInflater mInflater;
public BacveListAdapter(Context c,List<Bacve> l){
//super(c,R.layout.sve_bacve_item,l);
super(c,R.layout.sve_bacve_item,l);
mInflater = LayoutInflater.from(c);
this.bacve = l;
this.c = c;
}
Change getView to
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null){
convertView = (View) mInflater.inflate(R.layout.sve_bacve,parent,false);
holder = new ViewHolder();
holder.tv = (TextView) convertView.findViewById(R.id.textNazivBacve);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
Bacve bacva = bacve.get(position);
holder.tv.setText(bacva.getIme());
return convertView;
}
Also you mention
This is my sve_bacve_item and it has textview
So change
convertView = (View) mInflater.inflate(R.layout.sve_bacve,parent,false);
to
convertView = (View) mInflater.inflate(R.layout.sve_bacve_item,parent,false);
Edit:
Your NPE was because you inflated the wrong layout. I got confused the first time.
Also you have commented this
public BacveListAdapter(Context c,List<Bacve> l){
//super(c,R.layout.sve_bacve_item,l);
remove the comment
public BacveListAdapter(Context c,List<Bacve> l){
super(c,R.layout.sve_bacve_item,l);
And do check this to understand ViewHolder pattern to understand why you need it.
A ViewHolder object stores each of the component views inside the tag
field of the Layout, so you can immediately access them without the
need to look them up repeatedly.
http://developer.android.com/training/improving-layouts/smooth-scrolling.html
and also check
How ListView's recycling mechanism works
I'm guessing your layout doesn't have the TextView defined because your code should look like the following:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = (View)((LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.sve_bacve_item,parent,false);
}
TextView tv = (TextView)convertView.findViewById(R.id.textNazivBacve);
Bacve bacva = bacve.get(position);
tv.setText(bacva.getIme());
return iv;
}
And your custom layout should have a text view defined somewhere in your layout file like so.
<TextView android:id="#+id/textNazivBacve"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a TextView" />
UPDATE
Ok it looks like your layout file looks fine. You still do not need to create a TextView the way you were trying. You can simply findView. One thing I noticed, your inflate call is using the wrong layout (sve_bacve not sve_bacve_item), that is why it is unable to find the text view.
This line:
convertView = (View) mInflater.inflate(R.layout.sve_bacve,parent,false);
Should be:
convertView = (View) mInflater.inflate(R.layout.sve_bacve_item,parent,false);

ListView list style

Is it possible to change the "style" of a list view once it's displayed? What I want to do is to start with a list view using simple_list_item_1 but switch to simple_list_item_multiple_choice after the list has been populated.
If this isn't possible, can someone please show me how to populate a list view with a text view and checkbox as I'm finding this very difficult to understand.
As always, thanks in advance.
if the two view "simple_list_item_multiple_choice" and "simple_list_item_1" are just different based on the checkbox, then you can just use the "simple_list_item_multiple_choice" where you can make the checkbox hidden on very startup and when the data is loaded you can make the checkbox visible. Or you can show a activity-indicator when the data is loading and after loading the data you can directly port the custom ListView, think this one is better. Some sample code is provided with explanation.
First of all a custom adapter for the listView. This adapter is for each item of the ListView.
//Custom Adapter
private class DataAdapter extends BaseAdapter {
#Override
public int getCount() {
if (channelListTitles.size()>0){
return dataArrayList.size(); //dataArrayList is a ArrayList<String>
}
else {
return 0;
}
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView==null){
convertView = getActivity().getLayoutInflater().inflate(R.layout.item_view,null); //item_view is the custom view for the items of the ListView
}
if (convertView != null) {
((TextView)convertView.findViewById(R.id.item_view_title)).setText(channelListTitles.get(position)); //item_view_title is the TextView
if(isDataLoaded){ //isDataLoaded is a bool value to indicate if data is loaded
CheckBox checkBox = (CheckBox)convertView.findViewById(R.id.checkbox);
checkBox.setVisibility(View.VISIBLE);
}
}
return convertView;
}
}
Now the item_view for each list-item:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/checkbox"
android:visibility="invisible"/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/item_view_title"
android:layout_gravity="center_vertical"/>
</LinearLayout>
Now finally declare a listView on which the upper custom view will be ported. Posting the whole code of the OnCreateView() for better understanding. Here I am using Fragment, you need to change the corresponding methods if you are using Activity:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
if (rootView != null) {
ListView listView = (ListView) rootView.findViewById(R.id.item_list);
DataAdapter itemDataAdapter = new DataAdapter();
listView.setAdapter(itemDataAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// OnItemClickListener goes here
}
});
}
return rootView;
}
Now the fragment_main:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity$PlaceholderFragment">
<ListView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/item_list"/>
</RelativeLayout>
The Adapter is an important factor when you want to load data on dynamically. Basically Adapter is the bridge between data and view. Once you have the data you can call the adapter to update the view by using itemDataAdapter.notifyDataSetChanged();.
Yes you can do that, You need to make a xml for the row. And make an adapter class and overwrite getView() and inflate the view here. I always extend BaseAdapter.
# adelphia, i thing you want to use custom listview. Follow this tutorials for custom list view (follow 72 - 84 tutorials).

Setting up a button onClickListener for EmptyView android

I am working through the Big Nerd Ranch guide for android programming, and I am at the challenge for Chapter 16. The challenge is to make an EmptyView for a ListView, and then make a button on the EmptyView that adds stuff. I got the EmptyView to work but I can't figure out where I should make my button. Here is my code.
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v= super.onCreateView(inflater, parent, savedInstanceState);
inflater.inflate(R.layout.list_frame_layout, parent);
return v;
}
and here is my XML.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="#android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ListView>
<LinearLayout android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="#string/empty_no_crime" />
<Button
android:id="#+id/empty_new_crime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/empty_new_crime">
</Button>
</LinearLayout>
</FrameLayout>
The book is telling us to use fragments, hence the inflate. I figure the code should be
mNewCrime=(Button)getView().findViewById(R.id.empty_new_crime)
but that isn't working. Any ideas?
Edit*: Hmmm, apparently this also really isn't working that well. When I do add stuff, the EmptyView does not go away, it just gets pushed down while items are listed. Any ideas on how to make the EmptyView go away as soon as I add things?
I had trouble with this challenge at first as well. I over thought it! You have probably solved this issue by now but I thought it would be useful to post an answer for others. The following worked for me:
Create a new XML file specifying the "empty" and "list" views as you have done already.
Modify your existing onCreateView method to inflate the new modified layout which contains the "empty" and "list" views you have defined in your XML.
Create a new button and setup the onClickListener for the button.
Here is the code:
#TargetApi(11)
#Override
// We override the onCreateView to set the subtitle by default if we are rocking >3.0
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
{
super.onCreateView(inflater, parent, savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
if(mSubtitleVisible){
getActivity().getActionBar().setSubtitle(R.string.subtitle);
}// End inner if
}// End if
View v = inflater.inflate(R.layout.empty_layout, parent, false);
mNewCrimeButton = (Button)v.findViewById(R.id.add_crime);
//Define an click event listener for the button and launch the new crime fragment when clicked
mNewCrimeButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
Crime crime = new Crime();
//Get the crimelab from the activity and add the crime
CrimeLab.get(getActivity()).addCrime(crime); //getActivity returns the activity this fragment is attached to
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
startActivityForResult(i,0);
}//End onClick
});
return v;
}// End onCreateView
This should work with your existing xml layout. I hope this helps.
I too struggled initially with this, essentially solving it the same way the above poster did. However my problem was a bit different. I was getting bombed out of the application on startup, because my code that set up the onClick listener looked like this:
Button mCrimeButton = (Button)v.findViewById(R.id.crime_button);
mCrimeButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
initiateCrimeRecord();
}
});
It wasn't until I moved the declaration of mCrimeButton up to the class level making it an instance variable of the class that I was able to successfully execute the app:
public class CrimeListFragment extends ListFragment {
private static final String TAG = "CrimeListFragment";
private ArrayList<Crime> mCrimes;
private boolean mSubtitleVisible;
private Button mCrimeButton;
*
*
*
#TargetApi(11)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_empty_crime_list, parent, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if ( mSubtitleVisible) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
} else {
getActivity().getActionBar().setSubtitle(null);
}
}
// Set the button up on the empty view
mCrimeButton = (Button)v.findViewById(R.id.crime_button);
mCrimeButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
initiateCrimeRecord();
}
});
return v;
}
I then went back and noticed that in all the other examples in the book, the widgets that get manipulated are declared as private instances of the class. Why is this? Android doesn't allow you to just get a local instance to attach the listener?

Multiple choice list with custom view?

I've seen example com.example.android.apis.view.List11 from ApiDemos. In that example, each row takes the view android.R.simple_list_item_multiple_choice. Each such view has a TextView and a CheckBox.
Now I want each view to have 2 TextViews and 1 CheckBox, somewhat similar to the List3 example. I tried creating a custom layout file row.xml like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<CheckBox
android:id="#+id/checkbox"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="fill_parent" />
<TextView
android:id="#+id/text_name"
android:textSize="13px"
android:textStyle="bold"
android:layout_toLeftOf="#id/checkbox"
android:layout_alignParentLeft="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/text_phone"
android:textSize="9px"
android:layout_toLeftOf="#id/checkbox"
android:layout_below="#id/text_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
Then in Activity's onCreate(), I do like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Query the contacts
mCursor = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);
startManagingCursor(mCursor);
ListAdapter adapter = new SimpleCursorAdapter(this,
R.layout.row,
mCursor,
new String[] { Phones.NAME, Phones.NUMBER},
new int[] { R.id.text_name, R.id.text_phone });
setListAdapter(adapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
The result kind of looks like what I want, but it looks like the list doesn't know which item of it is selected. Also, I need to click exactly on the CheckBox. In the List11 example, I only need to click on the item row.
So what do I need to do to make a multiple choice list with my custom view for each row? Many thanks.
You have to make your own RelativeLayout that implements the Checkable interface and have a reference to the CheckBox or to the CheckedTextView (or a list if it's multiple choice mode).
Look at this post:
http://www.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/
The answer of Rahul Garg is good for the first time the list is loaded, if you want some rows to be checked depending on the model data, but after that you have to handle the check/uncheck events by yourself.
You can override the onListItemCLick() of the ListActivity to check/uncheck the rows
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
ViewGroup row = (ViewGroup)v;
CheckBox check = (CheckBox) row.findViewById(R.id.checkbox);
check.toggle();
}
If you do so, do not set the ListView to CHOICE_MODE_MULTIPLE, because it makes strange things when calling the function.
To retrieve the list of checked rows, you have to implement a method yourself, calling getCheckItemIds() on the ListView does not work:
ListView l = getListView();
int count = l.getCount();
for(int i=0; i<count; ++i) {
ViewGroup row = (ViewGroup)l.getChildAt(i);
CheckBox check = (Checked) row.findViewById(R.id.ck1);
if( check.isChecked() ) {
// do something
}
}
Each such view has a TextView and a
CheckBox.
No, it doesn't. It has a CheckedTextView.
So what do I need to do to make a
multiple choice list with my custom
view for each row?
Try making the CheckBox android:id value be "#android:id/text1" and see if that helps. That is the ID used by Android for the CheckedTextView in simple_list_item_multiple_choice.
The solution is to create a custom View that implements the Clickable interface.
public class OneLineCheckableListItem extends LinearLayout implements Checkable {
public OneLineCheckableListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
private boolean checked;
#Override
public boolean isChecked() {
return checked;
}
#Override
public void setChecked(boolean checked) {
this.checked = checked;
ImageView iv = (ImageView) findViewById(R.id.SelectImageView);
iv.setImageResource(checked ? R.drawable.button_up : R.drawable.button_down);
}
#Override
public void toggle() {
this.checked = !this.checked;
}
}
And create a custom layout for the list items using the new widget.
<?xml version="1.0" encoding="utf-8"?>
<ax.wordster.OneLineCheckableListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:background="#drawable/selector_listitem"
android:orientation="horizontal" >
<ImageView
android:id="#+id/SelectImageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="#drawable/button_friends_down" />
<TextView
android:id="#+id/ItemTextView"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:gravity="center"
android:text="#string/___"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="#color/text_item" />
</ax.wordster.OneLineCheckableListItem>
Then create a new custom Adapter using the layout above.
It is possible by some trick
in your ListActivtyClass in method
protected void onListItemClick(ListView l, View v, int position, long id) {
//just set
<your_model>.setSelected(true);
}
now in you custom Adapter
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(textViewResourceId, parent, false);
}
if (<your_model>.isSelected()) {
convertView.setBackgroundColor(Color.BLUE);
} else {
convertView.setBackgroundColor(Color.BLACK);
}
return convertView;
}
this way you can customize the view in adapter when the item is selected in the list.
Simple example how to get a custom layout to work as custom checkbox:
private class FriendsAdapter extends ArrayAdapter<WordsterUser> {
private Context context;
public FriendsAdapter(Context context) {
super(context, R.layout.listitem_oneline);
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final int pos = position;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rv = inflater.inflate(R.layout.listitem_oneline, parent, false);
rv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
boolean checked = friendsListView.isItemChecked(pos);
friendsListView.setItemChecked(pos, !checked);
}
});
WordsterUser u = getItem(position);
TextView itw = (TextView) rv.findViewById(R.id.ItemTextView);
itw.setText(u.userName + " (" + u.loginName + ")");
ImageView iv = (ImageView) rv.findViewById(R.id.SelectButton);
if (friendsListView.isItemChecked(position)) {
iv.setImageResource(R.drawable.downbutton);
} else {
iv.setImageResource(R.drawable.upbutton);
}
return rv;
}
}
I found it very useful this little code: http://alvinalexander.com/java/jwarehouse/apps-for-android/RingsExtended/src/com/example/android/rings_extended/CheckableRelativeLayout.java.shtml
It is a great addition to #ferdy182 's http://www.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/ content.
Got the solution ... You can get the clicks on the views (like checkboxes in custom layouts of row) by adding listener to each of them in the adapter itself while you return the converted view in getView(). You may possibly have to pass a reference of list object if you intent to get any list specific info. like row id.
I want to confirm that the Pritam's answer is correct. You need an onClickListener on each list's item (define it in the adapter's getView()).
You can create a new onClickListener() for each item, or have the adapter implement onClickListener() - in this case the items must be tagged for the listener to know, which item it is operating on.
Relying on the list onItemClickListener() - as someone advised in another thread - will not work as the CheckBox will intercept the click event so the list will not get it.
And finally #Rahul and JVitella:
The situation is that the CheckBox on a list item must be clickable and checkable independently from the list item itself. Therefore the solution is as I just described above.

Categories

Resources