I am working on a Custom list view with a single choice mode. I have followed the below tutorial and successfully achieved it.
Custom Single Choice ListView
I have a use case where I want to set a particular item is the list to be checked by default,
I have tried to do setChecked(true) at the require position in the adapter but it didnt work.
Can anybody help me how to achieve it.
Thanks in advance.
This source says:
Basically, the single-choice ListView expects the widgets you provide it with to implement the Checkable interface. LinearLayout et al don't. So you need to create a custom layout that inherits LinearLayout (or whatever layout you want to use for your items) and implements the necessary interface.
And from this source you can set it by using custom adapter inside your Activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// set the choice mode
final ListView list = getListView();
list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
// custom adapter
list.setAdapter(new ArrayAdapter<String>(this, R.layout.list_item,
R.id.title, text) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
ImageView icon = (ImageView) v.findViewById(R.id.img);
if (list.isItemChecked(position)) {
icon.setImageResource(R.drawable.checked);
} else {
icon.setImageResource(R.drawable.unchecked);
}
return v;
}
});
}
However this version has some performance issues - findViewById and setImageResource are relatively time-consuming operations so you should consider using some caching.
Hope it helps for you.
Related
I've seen all the related questions, and tried their answers. I have a ListView inside a fragment and it's onItemClick method is called when inside one activity, but not called when in another one. Everything else is the same. I've tried:
Changing android:clickable explicitly.
Changing android:focusable and android:focusableInTouchMode explicitly.
Calling listView.setItemsCanFocus.
Adding android:descendantFocusability="blocksDescendants" attribute both on fragment and activity root.
Still, it's not working. It's the same fragment with same adapter, which doesn't have conditionals about being in which activity. However, in one activity it works perfectly, and in another, onItemClick is not called. I'm on ICS. Why would this happen?
UPDATE:
Here is the relevant code in my fragment:
dataSource = (ArrayList<Map<String, Object>>) task.getResult();
FeedAdapter adapter = new FeedAdapter(getActivity(), dataSource, getUser());
ListView list = (ListView) rootView.findViewById(R.id.listView);
list.setItemsCanFocus(true);
//just trying these
list.setOnItemClickListener(self);
list.setAdapter(adapter);
adapter.notifyDataSetChanged();
if (dataSource.size() == 0) {
noPostsLabel.setVisibility(View.VISIBLE);
}
And in my adapter:
public View getView(int position, View convertView, ViewGroup parent) {
View view;
final Map<String, Object> post = objects.get(position);
LayoutInflater inflater = LayoutInflater.from(getContext());
if (convertView == null || convertView.getId() == R.id.headerRoot) {
view = inflater.inflate(R.layout.list_item_post_layout, parent, false);
} else {
view = convertView;
}
view.setClickable(false); //just trying these two now, they weren't here originally
view.setFocusable(false);
//populate
view.setTag(post);
[...] //populate the cell. very long code, redacted.
return view;
}
UPDATE 2:
I've also realized some cells are also not selectable in my "working" activity too, when they have a visible HorizontalScrollView within the cell (I have file attaching feature and it's only visible when there are files. Otherwise, it's in GONE visibility state). I have no idea why it's causing such trouble.
UPDATE 3:
I've also found out that views inside the cell are responsive. It's just the cell view itself which is not taking input.
UPDATE 4:
I've ended up moving the tap handler logic to the cell layout itself, instead of relying on list view's handler. I know it's not a good practice but I had to meet a deadline. Besides, it's working pretty smooth now. I'm not closing/answering the question as the technical problem is still present and I haven't found a real solution to it. I've just used a workaround to meet my project deadline.
I am not sure of your problem and I don't see the full code to debug.
I'll submit a sample code which normally should work in a Fragment.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
ListView list = (ListView) view.findViewById(R.id.listView);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int positi on, long id) {
Log.d(TAG, "onItemClick");
}
});
Notes:
I don't use root or any other cached object. I use the view parameter for calling findViewById().
I don't know self also. Instead I instantiated a new view or AdapterView.
Have you tried setting callbacks for the fragments? That's the way Android recommends it. You can check out - https://developer.android.com/training/basics/fragments/communicating.html#Deliver
I've got a ListView in my application that's rendered in a ListFragment's onActivityCreated()using setListAdapter(). Passed in to this setListAdapter()are my implementation of ArrayAdapter. At some times this adapter can be empty, and that's fine, but at those moments I would like to show a message in the list telling that there are no items, instead of just an empty view. However I don't really know how to achieve this, as for I have researched most people to show lists in a ListActivityand by doing that you can setEmptyView() but this doesn't seem doable when using a ListFragment. Another way of doing this was to change view in the ListFragment if the ArrayAdapter has no item's in it, then change view to another showing the message, but this seems to me a bit hacky.
Whats really the proper way of doing what I want to achieve?
Tried to setEmptyView() to my ListView but that didn't work either, see code on how views are inflated in my ListFragment:
public class MyFragment extends ListFragment {
#SuppressLint("InflateParams")
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ListView listView = (ListView) inflater.inflate(R.layout.my_list, null);
listView.setEmptyView(inflater.inflate(R.layout.list_items_missing, null));
return listView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
MyItemArrayAdapter adapter = new MyItemArrayAdapter(getActivity());
// Populate adapter with items...
setListAdapter(adapter);
}
}
Shouldn't this result in the empty view beeing shown if no items exists in my adapter?
1) Try putting backgroundImage for ListView inside xml. If no item, show backgroundImage, if there is item, put background color.
2) You can also do what as #kgandroid suggested and here, setEmptyView() which you set custom view.
Example:
Another:
You can customize your adapter to show the layout R.layout.list_items_missing when (item == null) instead of inflating the normal custom list item layout that you implement.
Something like this:
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder; //asuming you have this in your custom adapter
if (convertView == null) {
holder = new ViewHolder();
if(MyItemArrayList.get(position)!=null)
{
convertView = this.inflater.inflate(R.layout.layout_list_item,
parent, false);
else
{
convertView = this.inflater.inflate(R.layout.list_items_missing,
parent, false);
}
//the rest of the adapter logic
}
I'm trying to get a ListView to toggle the background color of its elements when they are selected (this is for selecting songs to add to a playlist). I've been mostly successful, but for some reason whenever the ListView is longer than the screen, selecting either the first or last item also toggles the background of the other. I keep track of whether an item is selected or not in an array of booleans called selectedStatus, and there's not a problem there, so it's purely a UI problem.
Here's the relevant section of the code:
boolean selectedStatus{} = new boolean[songsList.size()];
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
lv = getListView();
// listening for song selection
lv.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
int viewPosition = position - lv.getFirstVisiblePosition();
if(selectedStatus[position]){
selectedStatus[position] = false;
View currentEntry = lv.getChildAt(viewPosition);
currentEntry.setBackgroundResource(R.color.footercolor);
}
else{
selectedStatus[position] = true;
View currentEntry = lv.getChildAt(viewPosition);
currentEntry.setBackgroundResource(R.color.selected);
}
}
});
}
There must be some implementation detail about ListViews that I'm missing, but I can't figure out why this would be happening.
EDIT: I've realized with more testing that it actually links all list elements with positions which are equal mod 12, I just wasn't looking at a long enough list. This seems much less weird, it's just reusing elements, and I'll have to look into this ViewHolder idea.
Since a few people asked, this is all the code I have for making an adapter and populating the list:
// Adding items to ListView
ListAdapter adapter = new ArrayAdapter<String>(getActivity(),
R.layout.playlist_builder_item, songnamesList);
setListAdapter(adapter);
Sounds like you might need to create a proper ListAdapter and implement a ViewHolder.
The ViewHolder avoids the layout reuse that Android ListView implements, so as you can do slightly more complicated things by relying on the Views being the same as before.
You should hold onto the View that you're changing in a static class. For example:
static class ViewHolder {
ImageView backgroundItem;
}
Then in your Adapter's getView method you want to get hold of that ViewHolder and if we are creating a new view, we set it to be new, otherwise we reuse the old view that we have set as a tag to the original.
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView == null){
// Inflate the view as we normally would
// Create a new ViewHolder
// Set our ImageView to be equal to viewHolder's backgroundItem
// final step
convertView.setTag(viewHolder);
}else{
// use the original ViewHolder that was already saved as a tag
viewHolder = (ViewHolder) convertView.getTag();
}
// Set the background as per your own code
// Return the convertView
return convertView;
}
Don't forget to set your Adapter to your listview by calling the ListView setAdapter(ListAdapter adapter) method
A decent tutorial which incorporates creating an Adapter and a ViewHolder can be found here.
Further reading:
Vogella article on ListViews
Android Developer post on ListViews
I have been developing an android application and i am stuck in the middle.
The problem is i am using SimpleAdapter to do the adapter stuff and show items in the Listview and as far as i know i cannot override the getView() method of SimpleAdapter class to bound click listeners to the items.
There is a other way to handle click events of sub items like using XML, you can write in the XML like android:clickable="true"and android:onClick="clicklistenr", using this i can get the item but my problem is if i use this then i cannot get the position of the adapter which i need to get adapter item values and handle other tasks. So i am stuck here any help would be appreciable. thanks.
For example i have a ListView which contains one image, TextView, like Button, share Button in each of its items. And there is no way i can find that either its image or button clicked using setOnItemClickListener. So i need a way to handle click events of these sub items of a ListView, i am using SimpleAdapter.
Just call listView.setOnItemClickListener() with your implementation of the listener.
and use like
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
}
});
Where list=(ListView)findViewById(R.id.list); and list.setAdapter(your_adapter);
For More details Follow: http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/
Hope It Will Help You.. :)
You can use like
myListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
HashMap<String, Object> obj = (HashMap<String, Object>) adapter.getItem(position);
String result= (String) obj.get("name");
Log.d("Yourtag", name);
}
});
First of all your problem is how to handle the items of a custom listview.. not sub. Anyways...
If you are using SimpleAdapter , you may use getView().But if you are using SimpleAdapter you don't need to use the getView() as the it handles the mapping of data to your layout via the resource. For inforamtion check the SimpleAdapter in developer site.
Another thing is there is not mandatory for using any particular adapter. You can create any adapter class which will extends the BaseAdapter which will implement the getView().
Inside getView() you can able to inflate your custom layout(which contains the image,button etc..) to your view or convertview.something like:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
vi = convertView;
if (convertView == null)
vi = inflater.inflate(R.layout.yourcustomlayout, null);
//Initialize your custom views here
TextView someText= (TextView) vi.findViewById(R.id.tvSometext);
Button likeButton = (Button ) vi.findViewById(R.id.btnLike);
//put data or call onclicklistener or
//whatever you want to do with custom views
likeButton .setOnClickListener(...){
//on click
}
}
Ans simply call this through your constructor with some data and appropriate context.
Amir,
I am not sure you found the solution or not. You can do this way:
#Override
public View getView(int position, View view, ViewGroup viewGroup) {
//for each subitem, set click listener & set tag having position as
// value.
ImageView imv = (ImageView)view.findViewById(R.id.live_view);
imv.setTag(i);
imv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("INFO","Live view clicked");
//This will give you position in listview.
int position = (int) v.getTag();
}
});
}
I hope it will help you and others looking to find relevant answer.
May be there is some other solution too, but it will surely work.
I am using a ListView to display the main screen of my application.
The main screen is essentially a menu to get into the different sections of application. Currently, I have the ListView whose contents are added programmatically in the onCreate method.
Here is the code snippet that does this:
String[] mainItems = {
"Inbox", "Projects", "Contexts", "Next Actions"
}
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1, mainItems));
registerForContextMenu(getListView());
}
So the menu is essentially just a bunch of nodes with the text contained in the mainItems array. I know that I can create an XML layout (i.e. R.layout.mainMenu_item) that has an ImageView and TextView in it, but I am unsure how to set the ImageView's icon. I have seen that there is a setImageResouce(int resId) method, but the way to use this when generating with an ArrayAdapter is eluding me. Is there a better way to do this?
What I typically do for a ListView is to implement my own Adapter by extending the handy BaseAdapter class. One of the abstract methods you'll implement will be getView() as the previous poster mentioned. From there you can inflate a layout containing an ImageView, get a reference to it using findViewById, and set the image to whatever drawable you've added into your resources.
public View getView(int position, View convertView, ViewGroup parent) {
View row = inflater.inflate(R.layout.menu_row, null);
ImageView icon = (ImageView) row.findViewById(R.id.icon);
icon.setImageResource(..your drawable's id...);
return view;
}
From the google docs for ArrayAdapter.
To use something other than TextViews
for the array display, for instance,
ImageViews, or to have some of data
besides toString() results fill the
views, override getView(int, View,
ViewGroup) to return the type of view
you want.