How to highlight ListView item on touch? - android

I have a simple ListView and I want each of it items to be highlighted on user's touch. I thought this should happen by default but it isn't. Can you advice?
ListView xml:
<ListView
android:id="#+id/list_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:divider="#206600"
android:dividerHeight="2dp"
android:smoothScrollbar="true"
android:background="#ffffff"
>
</ListView>
And code of my Adapter:
private class MyAdapter extends ArrayAdapter<Task> {
private LayoutInflater mInflater;
public MyAdapter(Context context, int resource, List<Task> list) {
super(context, resource, list);
mInflater = LayoutInflater.from(context);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
v = mInflater.inflate(R.layout.list_item, null);
}
Task task = taskList.get(position);
/* Setup views from your layout using data in Object here */
return v;
}

You may want to post your actual row layout code, but I suspect the problem will be that you set a background color on your list row. By default, the selectors are drawn behind the list items (which looks nicer, since the highlight color is behind the text). Either don't set a background color on your list items, or set it to draw the selector on top instead with ListView's drawSelectorOnTop XML attribute.
EDIT: If you really must have an opaque background for the default state and don't want to use drawSelectorOnTop, you can also try this: Set a background on your list rows, but use a StateListDrawable to use #android:drawable/list_selector_background for all but the default state (you can define an xml file in your drawables folder for this; see the StateList documentation).
You could also nest a layout inside your outer backgrounded row layout with its background set to #android:drawable/list_selector_background; that way the background would draw on top of your background, but below the content.

ListViews do not retain a visual indication of focus (or selection) while in touch mode. You will only see this when you use the hardware keyboard or controls to navigate your UI.
See the Google Touch Mode Android Blog article for more details.
So, if you are only using touch mode, you will never see focus or selection on ListViews.

I believe this has to do with the "Enabled" attribute of the items in the ListAdapter.
If your Adapter contains the code:
#Override
public boolean areAllItemsEnabled() {
return true;
}
Then each item should be clickable (and therefore should highlight on being touched).
Could you post details (and possibly code) of what kind of Adapter you're using for this list?

I struggled with this for a few days. In the end, I have to create a widget that supports Checkable interface and return that widget/view in my adapter's getiew() function. And the listview needs to be in ListView.CHOICE_MODE_SINGLE (or possibly any other mode specified by android:choiceMode) for it to keep track of the choice made on the UI.
So in essence, all the following needs to be in place for the listview item to stay highlighted:
ListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
ListAdapter.getView() return a view that implements Checkable interface
ListView.OnItemClickListener should call setItemChecked(position, true) to mark the item to be checked (and thus highlight it in the listview)
Hope this can help someone who is also struggling with this.

Related

ListView sub-element background changes when list item clicked

I'm working on a Fragment that has a custom expandable ListView. When the ListView is selected, elements are added to two LinearLayouts within the selected list item and these layouts are made visible. Each of these new elements are selectable and have the following properties:
android:clickable="true"
android:background="?android:attr/selectableItemBackground"
I noticed that the handling of this background selector state is different on my two devices. I've attached two sets of screenshots for these devices to compare the handling of the same code.
Android 4.3.1 | CyanogenMod 10.2.1
Expanded, Sub-Element Clicked, List Item Clicked
Android 4.0.4 | 2.6.38.8-aokp-bravo-ics
Expanded, Sub-Element Clicked, List Item Clicked
As you can see on the 4.0.4 device when the whole list item is selected all the sub-elements get the focus highlight as well which is undesirable. How do I get around this?
I got tired of dealing with the inconsistencies (or maybe I never properly learnt) and did the following, which may or may not be correct or suit your scenario (treat the code below more like pseudo code than Java).
class SomeAdapter extends BaseAdapter {
final int colorItemDefault;
final int colorItemSelected;
final int colorSubItemDefault;
final int colorSubItemSelected;
public SomeAdapter (final Context c) {
final Resources res = c.getResources();
colorItemDefault = res.getColor (R.color.item_default);
colorItemSelected = res.getColor (R.color.item_selected);
colorSubItemDefault = res.getColor (R.color.sub_item_default);
colorSubItemSelected = res.getColor (R.color.sub_item_selected);
}
public View getView (final int position, final View convertView, final ViewGroup parent...) {
final View view = makeNewViewOrRecycleExisting (...);
final Item item = getItem (position);
// bind content, etc...
view.setBackgroundColor (isSelected (item) ? colorItemSelected : colorItemDefault);
// similar logic for the sub items
}
}
The color that you are seeing is because of the list view's listSelector property. The behavior of which effect will be rendered on list items depends upon the Android version and the theme that you have used for the application. To get around the problem either you set the listSelector to transparent or provide your own selector. You can set the listSelector in you xml or programmatically using either of them. Sets transparent color
android:listSelector="#00000000"
yourListView.setSelector(new ColorDrawable(0x0));

Android limit the no of items displayed in a spinner's dropdown list

I have a spinner item bound to an array adapter which might have 0 or more items at any time. I want the spinner dropdown list to only show three items at a time, the rest of the items being scrollable.
I have tried wrapping the spinner within a layout with a fixed width but the spinner drop down list still takes up the entire screen(if there are that many items in the array adapter) to display the list.
I was looking at the Spinner source code and it seems like you can't do that with a spinner.
The Spinner has its own private interface called SpinnerPopup which defines how dropdown items can be shown. This is currently based on the spinnerMode allowing for a dropdown or dialog list.
Both options are also implemented inside the Spinner class as private classes: DialogPopup and DropdownPopup. Since you can't access them, it seems to me your only options at this point are:
Implement your own custom spinner based on other widgets such as in this example.
Copy the code from the Spinner class which seems pretty self-contained and implement your version of a spinner with it, modifying whatever you like in it.
I'm sorry I couldn't be of more help.
Good luck!
EDIT:
If you choose option 2, I think all you need to do is add your mode implementing the SpinnerPopup interface. Then inside the constructor Spinner(Context context, AttributeSet attrs, int defStyle, int mode) add another case to the switch checking for the modes to instantiate your own popup. Doesn't seem hard.
This answer doesn't work anymore, please look elsewhere.
Here's the ultimately simple solution.. just copy this line in Spinner
tag...
android:dropDownHeight="100dp" <!--change 100dp to your requirement-->
you can also modify width...
android:dropDownWidth="100dp" <!--change 100dp to your requirement-->
and you know what it works on AutoCompleteTextView too and with
atleast api 16...
The recommended solutions are not really interesting because they hard code the height of drop down this is bad because font size are actually different in different phones, I handled it dynamic and reliable like this :
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.drop_down_text_view,
new String[]{"A", "B", "C", "D", "E"}) {
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
TextView dropDownTextView = (TextView) super.getView(position, convertView, parent);
dropDownTextView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
myDropDownMenuOrSpinnerOrAutoCompleteTextView.setDropDownHeight(dropDownTextView.getHeight() * 3);
dropDownTextView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
return dropDownTextView;
}
};

Android ListView with EditText focus issues [duplicate]

I've spent about 6 hours on this so far, and been hitting nothing but roadblocks. The general premise is that there is some row in a ListView (whether it's generated by the adapter, or added as a header view) that contains an EditText widget and a Button. All I want to do is be able to use the jogball/arrows, to navigate the selector to individual items like normal, but when I get to a particular row -- even if I have to explicitly identify the row -- that has a focusable child, I want that child to take focus instead of indicating the position with the selector.
I've tried many possibilities, and have so far had no luck.
layout:
<ListView
android:id="#android:id/list"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
/>
Header view:
EditText view = new EditText(this);
listView.addHeaderView(view, null, true);
Assuming there are other items in the adapter, using the arrow keys will move the selection up/down in the list, as expected; but when getting to the header row, it is also displayed with the selector, and no way to focus into the EditText using the jogball. Note: tapping on the EditText will focus it at that point, however that relies on a touchscreen, which should not be a requirement.
ListView apparently has two modes in this regard:
1. setItemsCanFocus(true): selector is never displayed, but the EditText can get focus when using the arrows. Focus search algorithm is hard to predict, and no visual feedback (on any rows: having focusable children or not) on which item is selected, both of which can give the user an unexpected experience.
2. setItemsCanFocus(false): selector is always drawn in non-touch-mode, and EditText can never get focus -- even if you tap on it.
To make matters worse, calling editTextView.requestFocus() returns true, but in fact does not give the EditText focus.
What I'm envisioning is basically a hybrid of 1 & 2, where rather than the list setting if all items are focusable or not, I want to set focusability for a single item in the list, so that the selector seamlessly transitions from selecting the entire row for non-focusable items, and traversing the focus tree for items that contain focusable children.
Any takers?
This helped me.
In your manifest :
<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
Sorry, answered my own question. It may not be the most correct or most elegant solution, but it works for me, and gives a pretty solid user experience. I looked into the code for ListView to see why the two behaviors are so different, and came across this from ListView.java:
public void setItemsCanFocus(boolean itemsCanFocus) {
mItemsCanFocus = itemsCanFocus;
if (!itemsCanFocus) {
setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
}
}
So, when calling setItemsCanFocus(false), it's also setting descendant focusability such that no child can get focus. This explains why I couldn't just toggle mItemsCanFocus in the ListView's OnItemSelectedListener -- because the ListView was then blocking focus to all children.
What I have now:
<ListView
android:id="#android:id/list"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:descendantFocusability="beforeDescendants"
/>
I use beforeDescendants because the selector will only be drawn when the ListView itself (not a child) has focus, so the default behavior needs to be that the ListView takes focus first and draws selectors.
Then in the OnItemSelectedListener, since I know which header view I want to override the selector (would take more work to dynamically determine if any given position contains a focusable view), I can change descendant focusability, and set focus on the EditText. And when I navigate out of that header, change it back it again.
public void onItemSelected(AdapterView<?> listView, View view, int position, long id)
{
if (position == 1)
{
// listView.setItemsCanFocus(true);
// Use afterDescendants, because I don't want the ListView to steal focus
listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
myEditText.requestFocus();
}
else
{
if (!listView.isFocused())
{
// listView.setItemsCanFocus(false);
// Use beforeDescendants so that the EditText doesn't re-take focus
listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
listView.requestFocus();
}
}
}
public void onNothingSelected(AdapterView<?> listView)
{
// This happens when you start scrolling, so we need to prevent it from staying
// in the afterDescendants mode if the EditText was focused
listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}
Note the commented-out setItemsCanFocus calls. With those calls, I got the correct behavior, but setItemsCanFocus(false) caused focus to jump from the EditText, to another widget outside of the ListView, back to the ListView and displayed the selector on the next selected item, and that jumping focus was distracting. Removing the ItemsCanFocus change, and just toggling descendant focusability got me the desired behavior. All items draw the selector as normal, but when getting to the row with the EditText, it focused on the text field instead. Then when continuing out of that EditText, it started drawing the selector again.
My task was to implement ListView which expands when clicked. The additional space shows EditText where you can input some text. App should be functional on 2.2+ (up to 4.2.2 at time of writing this)
I tried numerous solutions from this post and others I could find; tested them on 2.2 up to 4.2.2 devices.
None of solutions was satisfactionary on all devices 2.2+, each solution presented with different problems.
I wanted to share my final solution :
set listview to android:descendantFocusability="afterDescendants"
set listview to setItemsCanFocus(true);
set your activity to android:windowSoftInputMode="adjustResize"
Many people suggest adjustPan but adjustResize gives much better ux imho, just test this in your case. With adjustPan you will get bottom listitems obscured for instance. Docs suggest that ("This is generally less desirable than resizing"). Also on 4.0.4 after user starts typing on soft keyboard the screen pans to the top.
on 4.2.2 with adjustResize there are some problems with EditText focus. The solution is to apply rjrjr solution from this thread. It looks scarry but it is not. And it works. Just try it.
Additional 5. Due to adapter being refreshed (because of view resize) when EditText gains focus on pre HoneyComb versions I found an issue with reversed views:
getting View for ListView item / reverse order on 2.2; works on 4.0.3
If you are doing some animations you might want to change behaviour to adjustPan for pre-honeycomb versions so that resize doesnt fire and adapter doesn't refresh the views. You just need to add something like this
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
All this gives acceptable ux on 2.2 - 4.2.2 devices.
Hope it will save people some time as it took me at least several hours to come to this conclusion.
This saved my life--->
set this line
ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
Then in your manifest in activity tag type this-->
<activity android:windowSoftInputMode="adjustPan">
Your usual intent
We're trying this on a short list that does not do any view recycling. So far so good.
XML:
<RitalinLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ListView
android:id="#+id/cart_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"
/>
</RitalinLayout>
Java:
/**
* It helps you keep focused.
*
* For use as a parent of {#link android.widget.ListView}s that need to use EditText
* children for inline editing.
*/
public class RitalinLayout extends FrameLayout {
View sticky;
public RitalinLayout(Context context, AttributeSet attrs) {
super(context, attrs);
ViewTreeObserver vto = getViewTreeObserver();
vto.addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
#Override public void onGlobalFocusChanged(View oldFocus, View newFocus) {
if (newFocus == null) return;
View baby = getChildAt(0);
if (newFocus != baby) {
ViewParent parent = newFocus.getParent();
while (parent != null && parent != parent.getParent()) {
if (parent == baby) {
sticky = newFocus;
break;
}
parent = parent.getParent();
}
}
}
});
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override public void onGlobalLayout() {
if (sticky != null) {
sticky.requestFocus();
}
}
});
}
}
this post was matching exactly my keywords. I have a ListView header with a search EditText and a search Button.
In order to give focus to the EditText after loosing the initial focus the only HACK that i found is:
searchText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// LOTS OF HACKS TO MAKE THIS WORK.. UFF...
searchButton.requestFocusFromTouch();
searchText.requestFocus();
}
});
Lost lots of hours and it's not a real fix. Hope it helps someone tough.
If the list is dynamic and contains focusable widgets, then the right option is to use RecyclerView instead of ListView IMO.
The workarounds that set adjustPan, FOCUS_AFTER_DESCENDANTS, or manually remember focused position, are indeed just workarounds. They have corner cases (scrolling + soft keyboard issues, caret changing position in EditText). They don't change the fact that ListView creates/destroys views en masse during notifyDataSetChanged.
With RecyclerView, you notify about individual inserts, updates, and deletes. The focused view is not being recreated so no issues with form controls losing focus. As an added bonus, RecyclerView animates the list item insertions and removals.
Here's an example from official docs on how to get started with RecyclerView: Developer guide - Create a List with RecyclerView
some times when you use android:windowSoftInputMode="stateAlwaysHidden"in manifest activity or xml, that time it will lose keyboard focus. So first check for that property in your xml and manifest,if it is there just remove it. After add these option to manifest file in side activity android:windowSoftInputMode="adjustPan"and add this property to listview in xml android:descendantFocusability="beforeDescendants"
Another simple solution is to define your onClickListener, in the getView(..) method, of your ListAdapter.
public View getView(final int position, View convertView, ViewGroup parent){
//initialise your view
...
View row = context.getLayoutInflater().inflate(R.layout.list_item, null);
...
//define your listener on inner items
//define your global listener
row.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
doSomethingWithViewAndPosition(v,position);
}
});
return row;
That way your row are clickable, and your inner view too :)
The most important part is to get the focus working for the list cell.
Especially for list on Google TV this is essential:
setItemsCanFocus method of the list view does the trick:
...
mPuzzleList = (ListView) mGameprogressView.findViewById(R.id.gameprogress_puzzlelist);
mPuzzleList.setItemsCanFocus(true);
mPuzzleList.setAdapter(new PuzzleListAdapter(ctx,PuzzleGenerator.getPuzzles(ctx, getResources(), version_lite)));
...
My list cell xml starts like follows:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/puzzleDetailFrame"
android:focusable="true"
android:nextFocusLeft="#+id/gameprogress_lessDetails"
android:nextFocusRight="#+id/gameprogress_reset"
...
nextFocusLeft/Right are also important for D-Pad navigation.
For more details check out the great other answers.
I just found another solution. I believe it's more a hack than a solution but it works on android 2.3.7 and android 4.3 (I've even tested that good old D-pad)
init your webview as usual and add this: (thanks Michael Bierman)
listView.setItemsCanFocus(true);
During the getView call:
editText.setOnFocusChangeListener(
new OnFocusChangeListener(View view,boolean hasFocus){
view.post(new Runnable() {
#Override
public void run() {
view.requestFocus();
view.requestFocusFromTouch();
}
});
Just try this
android:windowSoftInputMode="adjustNothing"
in the
activity
section of your manifest.
Yes, it adjusts nothings, which means the editText will stay where it is when IME is opening. But that's just an little inconvenience that still completely solves the problem of losing focus.
In my case, there is 14 input edit text in the list view. The problem I was facing, when the keyboard open, edit text focus lost, scroll the layout, and as soon as focused view not visible to the user keyboard down. It was not good for the user experience. I can't use windowSoftInputMethod="adjustPan". So after so much searching, I found a link that inflates custom layout and sets data on view as an adapter by using LinearLayout and scrollView and work well for my case.

ListView onItemLongClickListener is not working properly

I am working on a ListView and I have used setBackgroundColor inside onItemLongClickListener on the selected item. My problem is that when I am doing this and scrolling, it is setting color of some invisible child of ListView too. How can it be solved.
Try putting the following attributes in your xml:
`
<ListView
android:dividerHeight="1dp"
android:scrollingCache="false" >
`
this is caused since a listview uses old views in order to avoid re-creation of views when you scroll.
in fact , this is common to all of the adapterView classes .
in order to handle this , store the status of the position of the view (using an arrayList or whatever collection you wish) and on the getView , if the position is set in the list to be of this background , use this background , otherwise use the default background.
for more information about listview , either read the API , or (and i highly recommend it) watch the video "the world of listView" .
In your adapter class:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = inflater.inflate(...);
}
convertView.setBackgroundColor(defaultcolor);
...
}
This however will overwrite the background you set in the onlongclicklistener when that view would be redrawn. So you might want to keep a list of the positions of the clicked items so you can set these in the getView method.

How to overlay rows of Android Listview

I am trying to implement the below Android ListView. The each yellow box represents one row of ListView. If you see the rows, each row is over lapped by previous and next row. I have searched Google, but I could not find any proper solutions. If anyone has clues, please let me know.
Thanks in advance.
Think outside the box. Imagine this list not having the rows overlap, but just having diagonal lines on it. In your code, set the Listview's divider height to 0:
ListView listView = (ListView)findViewById(R.id.your_listview);
listView.setDivider(null);
listView.setDividerHeight(0);
Then create two drawables for the rows- one for odd rows, another for even rows:
and
(don't use these two images, as they are not properly/evenly sized, but create the ones for your specific list).
Then create an adapter for your list view - and in its getView method, set the corresponding background to your element:
#override
public void getView(int position, View convertView, ViewGroup parent) {
MyView row;
if(convertView == null) {
row = (MyView)convertView;
}
else {
row = ... //create your view - inflate or create in code
}
row.setBackgroundResource(position%2==1 ? R.drawable.row_odd : R.drawable.row_even);
... //populate the rest of the row with whatever you need
return row;
}
And voila! You get what the effect you need (note, on this schematic the black lines represent the boundaries between rows - these are for schematic purposes only - in your final result you will not have them):
Finally, please note that if you want highlight on your "rows" when an item is selected, you'll have to implement custom state change listeners, which would change the background of the cell that's being selected and also the ones above and below it as to create the proper visual effect.
I got the same issue but I managed to find a solution
In the ListView in the xml you can set a negative dividerHeight, it works for me.
<ListView
...
android:dividerHeight="-5dp" />
I hope it can help someone :)

Categories

Resources