In my program, as soon as I create a ListView and I set a Simpleadapter as adapter, I try to get access of a View in this listView in order to change the background of this view depending on a condition. I do it with the method ListView.getChildAt(position). However, I get a nullPointer Exception and i do not understand why. Here is a part of my code that is concerned.
To better understand the code below: I have actually created 2 listViews in the code and Alarm is a class I have implemented. I simply retrieve some pieces of information through this class.
Java Code:
public class SmartAlarm extends AppCompatActivity {
private ListView list_view_alarms;
private ListView list_view_activates;
private List<HashMap<String, String>> listMapOfEachAlarm;
private List<HashMap<String, Integer>> listMapOfActivates;
private SimpleAdapter adapter_alarms;
private SimpleAdapter adapter_activates;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_smart_alarm);
list_view_alarms = (ListView) findViewById(R.id.list_alarm);
list_view_activates = (ListView) findViewById(R.id.list_activate);
listMapOfEachAlarm = new ArrayList<>();
listMapOfActivates = new ArrayList<>();
adapter_alarms = new SimpleAdapter(this, listMapOfEachAlarm, R.layout.item_alarm,
new String[]{"alarm", "title"}, new int[]{R.id.time, R.id.title});
adapter_activates = new SimpleAdapter(this, listMapOfActivates, R.layout.item_activate, new String[]{"alarm_drawable"}, new int[]{R.id.activate});
for (Alarm alarm : alarmList) {
HashMap<String, String> mapOfTheNewAlarm = new HashMap<>();
mapOfTheNewAlarm.put("alarm", alarm.getTime());
mapOfTheNewAlarm.put("title", alarm.getTitle());
listMapOfEachAlarm.add(mapOfTheNewAlarm);
HashMap<String, Integer> mapOfTheAlarmDrawable = new HashMap<>();
if (alarm.getActivated()) {
mapOfTheAlarmDrawable.put("alarm_drawable", R.drawable.alarm_on);
} else {
mapOfTheAlarmDrawable.put("alarm_drawable", R.drawable.alarm_off);
}
listMapOfActivates.add(mapOfTheAlarmDrawable);
}
list_view_alarms.setAdapter(adapter_alarms);
list_view_activates.setAdapter(adapter_activates);
for(int i=0; i<list_view_alarms.getCount();i++)
{
if(conditionRespected()){
list_view_alarms.getChildAt(i)
.setBackgroundColor(getResources().getColor (R.color.dark)); //The compilation error is here because list_view_alarms.getChildAt(i) is null
}
}
}
}
activity_smart_alarm.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/crazy_alarm"
android:orientation="vertical"
android:weightSum="10">
<CheckBox
android:id="#+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:text="Check the box if you want to activate the game" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ListView
android:id="#+id/list_alarm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="8"
android:choiceMode="singleChoice"
android:divider="#FF0000"
android:dividerHeight="2dp" />
<ListView
android:id="#+id/list_activate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:choiceMode="singleChoice"
android:divider="#FF0000"
android:dividerHeight="2dp" />
</LinearLayout>
</LinearLayout>
For people interested: To change the background of each view in the listView according to a specific condition, I have finally overrided the method getView() like this :
public View getView(int position, View convertView, ViewGroup parent)
{
convertView = super.getView(position, convertView, parent);
if(condition){
convertView.setBackgroundColor(getResources().getColor(R.color.dark));
}
else
{
convertView.setBackgroundColor(getResources().getColor(R.color.bright));
}
return convertView;
}
You aren't setting your main view. Your listview must be created inside of the view that your activity will actually be using. Right inside of your onCreate() add setContentView(R.layout.activity_main);
You also have to create a layout (in this case activity_main) and your listView has to be child of the activity_main layout
This might be what your activity_main looks like
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin">
//and inside of here would be your listview
<android.support.v7.widget.ListViewCompat
android:id="#+id/list_alarm"
android:layout_width=""
android:layout_height=""></android.support.v7.widget.ListViewCompat>
</RelativeLayout>
Related
I have a ArrayList<String> taskList; to which I add strings. Then I connect them to an ArrayAdapter with
if (mAdapter == null) {
mAdapter = new ArrayAdapter<>(this,
R.layout.item_todo,
R.id.task_title,
taskList);
mTaskListView.setAdapter(mAdapter);
} else {
mAdapter.clear();
mAdapter.addAll(taskList);
mAdapter.notifyDataSetChanged();
}
item_todo.xml looks like this
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/task_title"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="Text"
android:layout_weight=".3"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight=".9"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
...
</LinearLayout>
<TextView
android:id="#+id/tvKategorie"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
android:text="Kategorie" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
Before, there was no
<TextView
android:id="#+id/tvKategorie"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
android:text="Kategorie" />
How do I fill not only task_title but also tvKategorie with content?
My ideas:
Create a new class EntryCategory to then create an ArrayList<EntryCategory> taskList; with fields task_title and tvKategorie.
Still the question remains how to assign task_title of taskList and tvKategorie of taskList to mAdapter so it shows in each item in my list?
Best way to achieve this is by using RecyclerView.
But if you still want to use ListView, I am providing one of the many solutions bellow:
mAdapter = new ArrayAdapter<EntryCategory>(this,
R.layout.item_todo, R.id.task_title,
taskList) {
#Override
public View getView (int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView tvTaskTitle = (TextView) view.findViewById(R.id.task_title);
TextView tvKategorie = (TextView) view.findViewById(R.id.tvKategorie);
EntryCategory item = getItem(position);
tvTaskTitle.setText(item.getTaskTitle());
tvKategorie.setText(item.getKategorie());
return view;
}
};
Please excuse any syntax error as I am answering from my mobile.
Update:
Please pass R.id.task_title as the 3rd parameter in the ArrayAdapter's constructor. So, the new code will look like the following,
mAdapter = new ArrayAdapter<EntryCategory>(this,
R.layout.item_todo, R.id.task_title,
taskList) {
.....
}
I want to display a list of items containing 2 textview in one of my fragment. Something really simple since I'm learning how to use listviews.
Here is my code:
public class CommandeFragment extends Fragment{
private BarMenu myMenu;
View myView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
myView = inflater.inflate(R.layout.commande_layout,container, false);
myMenu = (BarMenu) getArguments().getSerializable(MENU_KEY);
List<HashMap<String,String>> aList = new ArrayList<HashMap<String,String>>();
for(int i = 0; i < myMenu.drinks.size(); i++){
HashMap<String,String> hm = new HashMap<String, String>();
hm.put("Name",myMenu.drinks.get(i).drinkName);
hm.put("Price",myMenu.drinks.get(i).drinkPrice.toString());
aList.add(hm);
}
String[] from = {"drinkName","drinkPrice"};
int[] to = {R.id.textView3,R.id.textView4};
SimpleAdapter adapter = new SimpleAdapter(myView.getContext(), aList, R.layout.listview_commande_layout, from, to);
ListView listView = (ListView) myView.findViewById(R.id.listView);
listView.setAdapter(adapter);
return myView;
}
}
When I run this, I got a list of textviews placed correctly but the problem is that the text inside is empty. I've try multiple advice about the xml files online but I'm pretty sure it's fine.
Here it is in case:
listview_commande_layout.xml
<?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">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView3"
android:layout_weight="1" />
<TextView
android:text="TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView4"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
commande_layout.xml
<?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">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:text="Bar Menu Layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="102dp"
android:id="#+id/textView2" />
</RelativeLayout>
The column names you pass to your adapter don't match the column names in your HashMap ("Name" vs. "drinkName"). If you update these to match, it should work.
for(int i = 0; i < myMenu.drinks.size(); i++){
HashMap<String,String> hm = new HashMap<String, String>();
hm.put("Name",myMenu.drinks.get(i).drinkName);
hm.put("Price",myMenu.drinks.get(i).drinkPrice.toString());
aList.add(hm);
}
String[] from = {"Name","Price"}; //changed from {"drinkName","drinkPrice"}
I have the following code for example.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<String> names = new ArrayList<>();
names.add("one");
names.add("two");
names.add("three");
names.add("four");
names.add("five");
names.add("six");
names.add("seven");
names.add("eight");
names.add("nine");
names.add("ten");
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
LinearLayoutManager llm = new LinearLayoutManager(this);
rv.setLayoutManager(llm);
RvAdapter adapter = new RvAdapter(names);
rv.setAdapter(adapter);
}
}
RvAdapter.java
public class RvAdapter extends RecyclerView.Adapter<RvAdapter.PersonViewHolder> {
public static final String TAG = RvAdapter.class.getSimpleName();
List<String> persons;
RvAdapter(List<String> persons) {
this.persons = persons;
}
#Override
public RvAdapter.PersonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new PersonViewHolder(v);
}
#Override
public void onBindViewHolder(RvAdapter.PersonViewHolder holder, int position) {
holder.name.setText(persons.get(position));
Log.d(TAG, "onBindViewHolder: " + position);
}
#Override
public int getItemCount() {
return persons.size();
}
public static class PersonViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView name;
PersonViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
name = (TextView) itemView.findViewById(R.id.name);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mrmayhem.myapplication.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<android.support.v7.widget.CardView
android:id="#+id/cv"
android:layout_width="match_parent"
android:layout_height="200dp">
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v7.widget.CardView>
</LinearLayout>
It works so good and correct. When I scroll through the list I get successive messages from Adapter one by one in Logs: "onBindViewHolder: " + position. But my problem in next. When i try add some View above the RecyclerView, as shown in the code below. Adapter displays all calls of the method onBindViewHolder simultaneously. Wrap the RecyclerView in activity_main.xml by the next code.
activity_main.xml (with header)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mrmayhem.myapplication.MainActivity">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="HEADER" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</RelativeLayout>
RecyclerView is a ListView. It will only display the items that you can currently see, reusing parts as you scroll.
Now you wrap the RecyclerView in a LinearLayout within a NestedScrollView, what happens?
A NestedScrollView needs to know the size of the LinearLayout to handle the scrolling.
How does the LinearLayout know how long it should be, without knowing the RecyclerViews total length? It doesn't.
So the recyclerView gets completely inflated, all views at once, all in a long line. No recycling, no reuse. This is the phenomenon you experience.
Now the LinearLayout can properly tell the NestedScrollView it's height (the total height of the TextView and all the items of the RecyclerView) and the NestedScrollView can handle the scrolling.
This is why nested scrolling is always a bad idea. If possible, try to move your TextView as an item into the RecyclerView and get rid of the wrapping NestedScrollview.
e.g. as seen here: Is there an addHeaderView equivalent for RecyclerView?
Another option would be to remove the recyclerview from the scroll view and put it below. The header would not scroll then, though.
Unless you have a good reason to have a NestedScrollView in your hierarchy, you could achieve a TextView on top with a RecyclerView below with just this layout code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context="com.example.mrmayhem.myapplication.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="HEADER" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
I want to display all items of an ArrayList<String> on a ListView. I use an ArrayAdapter<String> for this. The problem is that only the first item ('foo' in my example) of the ArrayList is shown. Can someone tell me why? Am I missing something?
I've made an minimal example using the generated code (Tabbed Action Bar Spinner Activity) from Android Studio 2.
My FooActivity:
public class FooActivity extends AppCompatActivity {
...
public static class PlaceholderFragment extends Fragment {
private ListView fooListView;
private ArrayAdapter<String> mAdapter;
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_foo, container, false);
fooListView = (ListView) rootView.findViewById(R.id.foo_list);
updateList();
return rootView;
}
private void updateList() {
ArrayList<String> strings = new ArrayList<>();
strings.add("foo");
strings.add("bar");
if (mAdapter == null) {
mAdapter = new ArrayAdapter<>(getContext(),
R.layout.item_foo,
R.id.foo_string,
strings);
fooListView.setAdapter(mAdapter);
} else {
mAdapter.clear();
mAdapter.addAll(strings);
mAdapter.notifyDataSetChanged();
}
}
}
My fragment_foo.xml:
<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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.foo.activities.FooActivity$PlaceholderFragment">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/foo_list"
android:layout_below="#+id/total_activities"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="10dp"
android:layout_alignParentBottom="true" />
</RelativeLayout>
And the item_foo.xml:
<?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">
<TextView
android:id="#+id/foo_string"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="some_string"
android:textSize="20sp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
</LinearLayout>
If it helps I can post the generated activity_foo.xml (I didn't made any changes there) and the complete FooActivity class too.
The Problem actually was my activity_foo.xml which had a NestedScrollView. I've added android:fillViewport="true" and now it shows every item.
My NestedScrollView now looks like this:
<android.support.v4.widget.NestedScrollView
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:fillViewport="true"/>
On any ListView, if you use android:layout_height="wrap_content", you're only going to see one item. Try android:layout_height="match_parent". If the ListView only takes up part of the layout, you'll need a LinearLayout with weights or a RelativeLayout with constraints. But wrap_content won't work for a height on a ListView, ever.
Your Listview look something like this
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/foo_list"
android:layout_marginTop="10dp"/>
I have removed from your list view android:layout_below="#+id/total_activities"
and
android:layout_alignParentBottom="true"
For others: It's okay to nest a ListView inside another layout, and it's also fine to use android:layout_height="wrap_content" on Layouts surrounding the ListView. (So long as the outer-most Layout allows for match_parent)
What is not fine, however, is when the TextView being used to populate the ListView is defined with layout_height="match_parent". It will cover up all of the subsequent rows after the first.
Bad:
<TextView
android:id="#+id/infoRow_textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Good:
<TextView
android:id="#+id/infoRow_textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
Problem
I'm trying to create a ListView with selectable items. I want to be able to click on an item in the ListView and have the item change color in the list, and then go on and do something else with the data from the row.
I'm using a SimpleAdapter.
How do I make it so that when I tap on a row, it turns a different color, and then when I tap on a different row, the new row is selected and changed to a new color, and the old row changes back to normal?
Code
Here is my code so far. The DBTools class is has all of the data that I want to be displayed in my ListView organized and taken care of. The getAllReceivers() method returns an ArrayList of HashMap<String, String>s that have all of my data.
MainActivity.java:
public class MainActivity extends ListActivity {
DBTools dbTools = new DBTools(this);
ArrayList<HashMap<String, String>> receiverList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().hide();
setContentView(R.layout.activity_main);
receiverList = dbTools.getAllReceivers();
dbTools.close();
ListView listView = getListView();
if(receiverList.size() != 0) {
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this,receiverList, R.layout.receiver_entry, new String[] {"receiverId","receiverName", "fullPath"}, new int[] {R.id.receiverId, R.id.receiverName, R.id.fullPath});
setListAdapter(adapter);
}
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/black" >
<TextView
android:id="#+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="My List" />
</TableRow>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black"
android:id="#android:id/list" />
</TableLayout>
receiver_entry.xml
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/tableRow" >
<TextView
android:id="#+id/receiverId"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<TextView
android:id="#+id/receiverName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Robotronics" />
<TextView
android:id="#+id/fullPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="123.45.678.910:8088/robtrox/find" />
</TableRow>
Solution
The solution to this problem is very simple. We need to add an OnItemClickListener to our ListView to listen for clicks and respond accordingly.
So, in the onCreate() method, once you've made sure that you set of data isn't empty, you're going to want to Override the onItemClick() method to listen for the click and change the color. You're also going to want to keep track of which item you selected for the later steps, so add public int selectionId = -1; at the top of your class. Furthermore, you'll need to let the ListAdapter know that you changed something by calling ((SimpleAdapter) getListAdapter()).notifyDataSetChanged().
if(receiverList.size() != 0) {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int index, long id) {
view.setBackgroundColor(Color.RED);
TextView receiverIdTextView = (TextView) view.findViewById(R.id.receiverId);
selectionId = Integer.valueOf(receiverIdTextView.getText().toString());
((SimpleAdapter) getListAdapter()).notifyDataSetChanged();
}
});
SimpleAdapter adapter = getNewAdapter();
setListAdapter(adapter);
}
Great! Now we have a working system that will change the color of the row that you tap. But we're not done yet. We need to make sure that the previous selection changes back to the normal color.
For this, we are going to use override the SimpleAdapter's getView() method, which is called everytime the ListView goes to draw the items being displayed in it.
It only actually displays the items it needs to - the ones that you can see. It does not render the ones above or below your screen. So if you have 200 items in a ListView, only 5 or 6, depending on the size of your screen and the size of the items, are being rendered at a time.
To override the getView() method, go up to where you initialize the adapter and change the code to this:
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this,receiverList, R.layout.receiver_entry, new String[] { "receiverId","receiverName", "fullPath"}, new int[] {R.id.receiverId, R.id.receiverName, R.id.fullPath}) {
#Override
public View getView (int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView receiverIdTextView = (TextView) view.findViewById(R.id.receiverId);
if(receiverIdTextView.getText().toString().equals(String.valueOf(selectionId))) {
view.setBackgroundColor(Color.RED);
} else {
view.setBackgroundColor(Color.WHITE);
}
return view;
}
};
Every time one of the rows is drawn, since the getView() will get called, the ListView will check if the current view has the id of row you selected. If it doesn't, it'll change the background color to white. If it does, it'll change the background color to red.
And voila! That's it! Now you are setting the background color to red when you click on an item in the ListView.
Final Code
MainActivity.java:
public class MainActivity extends ListActivity {
DBTools dbTools = new DBTools(this);
ArrayList<HashMap<String, String>> receiverList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().hide();
setContentView(R.layout.activity_main);
receiverList = dbTools.getAllReceivers();
dbTools.close();
ListView listView = getListView();
if(receiverList.size() != 0) {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int index, long id) {
view.setBackgroundColor(Color.RED);
TextView receiverIdTextView = (TextView) view.findViewById(R.id.receiverId);
selectionId = Integer.valueOf(receiverIdTextView.getText().toString());
((SimpleAdapter) getListAdapter()).notifyDataSetChanged();
}
});
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this,receiverList, R.layout.receiver_entry, new String[] { "receiverId","receiverName", "fullPath"}, new int[] {R.id.receiverId, R.id.receiverName, R.id.fullPath}) {
#Override
public View getView (int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView receiverIdTextView = (TextView) view.findViewById(R.id.receiverId);
if(receiverIdTextView.getText().toString().equals(String.valueOf(selectionId))) {
view.setBackgroundColor(Color.RED);
} else {
view.setBackgroundColor(Color.WHITE);
}
return view;
}
};
setListAdapter(adapter);
}
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/black" >
<TextView
android:id="#+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="My List" />
</TableRow>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black"
android:id="#android:id/list" />
</TableLayout>
receiver_entry.xml
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/tableRow" >
<TextView
android:id="#+id/receiverId"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<TextView
android:id="#+id/receiverName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Robotronics" />
<TextView
android:id="#+id/fullPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="123.45.678.910:8088/robtrox/find" />
</TableRow>