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.
Related
I'm learning android programming, following a book's examples, but I'm still a beginner in this field.
The aim of the book is to build a progressively more sophisticated "note taking" app, starting from a very simple one then each time adding features.
I got a working app that allows adding notes, deleting notes: each note added becomes an item in a listview, and has a title, a text, and some boolean flag like "idea", "todo" and "important". Adding notes having the "important" boolean flag set, makes an "important" icon visible.
Internally, notes are stored in a List (see code later).
Each note, tapping its listitem, can be also opened in a modal showing its properties, which has a button to delete the shown note.
Pressing that delete button, the modal is dismissed, the note is deleted, and the item in the listview is removed.
This works.
Now I'm following the next step: adding a "flashing" animation for items (notes) with the "Important" flag set.
Regular notes' items are not animated, but "important" ones are constantly "flashing" when they appear in the listview.
This works.
Removing the notes, one by one, still works, and each item is removed.
But, if I remove all notes, and the last one is flashing
- this last note is effectively "removed" from the list
- but the listview still shows (only) its animation!?!
But the flashing animation, showing the last note (deleted) title and text in the listitem, cannot be clicked... inspecting the code it is effectively gone fro the List, but not its flashing animation.
Btw, the book at this point doesn't cover deleting notes, and that's a feature I added on my own.
the modal showing the note has, as said a btnDelete, that works like this:
btnDelete.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
new AlertDialog.Builder(getContext())
.setMessage("Really delete this note?")
.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
//nothing
}
}
)
.setPositiveButton("Ok",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
MainActivity mainActivity = (MainActivity) getActivity();
mainActivity.deleteNote(getNoteId());
String msg = "Note " + getNoteId() + " deleted (" + mNote.getTitle() + ")";
Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
dismiss();
}
})
.show();
}
}
);
I tried adding and deleting many notes, flashing and not, but the only way to get this "last" flashing ghost is to remove all notes but remain with one last flashing note.
Then, after removing that last flashing note, the flashing ghost remains.
I can have any number of flashing notes, and maybe one normal note but if I delete all notes leaving a normal one, that is deleted just fine: leave a last flashing note and, even if it is deleted, the flashing ghost remains.
When this ghost flashing animation lasts forever, if I add a new note, I can see the new listitem note text and icons over the flashing ghost animation, since it is added at first place... there are two animation overimposed, one is the old "ghost" and the other is the "regular" new note...
Now, some more code:
The listview is
listNote = (ListView) findViewById(R.id.listView);
and is "ruled" by a mNoteAdapter object
listNote.setAdapter(mNoteAdapter);
which extends BaseAdapter, overriding needed methods and also has two methods to add and remove notes
public void addNote(Note n) {
noteList.add(n);
notifyDataSetChanged();
saveNotes();
}
public void deleteNote(int i) {
//(I tried getting the view and canceling animation, setting it to null.. nothing works)
//View listItem = listNote.getAdapter().getView(i, null, listNote);
noteList.remove(i);
notifyDataSetChanged();
saveNotes();
}
the added notes are stored in a list:
List<Note> noteList;
the main overridden method is getView, which is "magically" called by the adapter when a node is added, and creates the listitem view, including the flashing animation if necessary.
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater)
getSystemService(getApplicationContext().LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.listitem, viewGroup, false);
}
TextView txtTitle = (TextView) view.findViewById(R.id.txtSettingsTitle);
TextView txtDescription = (TextView) view.findViewById(R.id.txtDescription);
ImageView ivImportant = (ImageView) view.findViewById(R.id.imageViewImportant);
ImageView ivTodo = (ImageView) view.findViewById(R.id.imageViewTodo);
ImageView ivIdea = (ImageView) view.findViewById(R.id.imageViewIdea);
Note tmpNote = noteList.get(i);
if (mAnimationSpeed != SettingsActivity.NONE && tmpNote.isImportant()) {
view.setAnimation(mAnimFlash);
} else {
view.setAnimation(mAnimFadeIn);
}
if (!tmpNote.isIdea()) ivIdea.setVisibility(View.GONE);
if (!tmpNote.isImportant()) ivImportant.setVisibility(View.GONE);
if (!tmpNote.isTodo()) ivTodo.setVisibility(View.GONE);
txtTitle.setText(tmpNote.getTitle());
txtDescription.setText(tmpNote.getDescription());
return view;
}
I don't need the flashing feature, of course, but I wish to understand what's happening and possibly solve it.
the "flashing" animation (mAnimFlash) xml is:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="0.0"
android:toAlpha="1.0"
android:interpolator="#android:anim/accelerate_interpolator"
android:repeatMode="reverse"
android:repeatCount="infinite"/>
</set>
while the "fading in" (mAnimFadeIn, which is not causing any issue) animation xml is:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="500"
android:interpolator="#android:anim/accelerate_interpolator">
</alpha>
</set>
Can anyone help me understand why that ghost animation remains, just in that case, and how can I better implement this kind of thing?
This is a bit speculation on my part, I'm not exactly sure but my guess is that this behavior is the result of the interaction between:
The way ListView recycles and caches views for items that are no longer visible
That the animation lasts 'forever'
The property being animated is one of the properties used to 'hide' the cached views that ListView is saving for later.
A ListView is a container that arranges child views so that it 'seems' that all the items are visible. The items that would fall outside the ListView are not really there. This is done for performance reasons (speed and to save memory). When the user scrolls and a view falls out of view it is 'recycled' and put in stand-by until another item comes into view to be reused for that new item. It gets passed as the 2nd parameter of the getView() function.
This means that usually there are more views than visible items. There can be space for 10 items, and there will be 10 visible items and 1-3 hidden extra ones that will be used for items that come into view. (see How ListView's recycling mechanism works).
One drawback of ListView is that there is no easy way to detect when a view is being recycled (it becomes hidden). Usually we have to wait until it's reused for another item. RecyclerView does have events to handle when a view gets recycled.
In your case we can see this happening when you're setting the animation. If you set the mAnimFlash on an item, the only time it gets stopped is when the view is reused for another item that not 'important' and it gets the mAnimFadeIn animation (replacing the flashing animation).
If you commented out the call to view.setAnimation(mAnimFadeIn) you would notice that as you scroll down some items are blinking when they shouldn't. That's because there's no place where the view is told to stop blinking, even after being assigned to another item in the list.
Now to item 3 of the list above, the 'alpha' property. You are animating the 'alpha' property of the view. So if the ListView is making the view transparent to hide it from view, then after the ListView hides it by making it transparent, the animation is making it visible again by changing the values of the alpha transparency.
More thoughts on this:
If the animation worked on a property of a child item of the view, instead of the 'main' view, probably the view would remain hidden. Or the animation changed something else like the text color.
Additional info/topics:
Further investigation can be done with the Layout Inspector, which you can use to check where are the views and their properties. So you could for example check what children views are inside the ListView and what are their properties.
You can use different layouts for different items. See the getItemViewType() and related functions of the Adapter class. You can specify for example that there are 2 types of views: 'important' and 'not important' and the views passed as convertView will match the type for that item, and you won't have to make the choices in getView(), but isolate the decision of type in getItemViewType().
Also take a look at RecyclerView, since it has better handling for a few situations where ListView falls short. For example, the adapter for a RecyclerView has a method onViewRecycled() that lets you know that a view is no longer used, and you can stop animations there.
I have a simple navigation drawer layout setup. I'm using the design support library so I have one a navigation view that slides in above the support library's toolbar. However, the header layout is responding to taps in the UI. I'm getting that ripple effect appearing behind the header layout when I tap it. How can I do either of the following:
Disable the tap interaction all together? (so no ripple effect occurs)
Respond to the tap interaction.
I can't seem to find any documentation on this.
I noticed the same issue. This component uses a ListView internally (called menuView below), and unfortunately there is no option to disable the header selection because it's hard-coded:
this.mHeader = (LinearLayout)this.mLayoutInflater.inflate(layout.design_navigation_item_header, this.mMenuView, false);
this.mMenuView.addHeaderView(this.mHeader);
this.mMenuView.setAdapter(this.mAdapter);
this.mMenuView.setOnItemClickListener(this);
Adding that to the fact that the NavigationView also has an icons coloring bug (in version 22.2.0), I would suggest to not use it for the moment.
This can be a workaround for the issue:
View headerView = getLayoutInflater().inflate(R.layout.header_nav, mNavigationView, false);
mNavigationView.addHeaderView(headerView);
//Use this if you don't want the click listener,
//be sure to use the appropriate background color
//((ViewGroup) headerView.getParent()).setBackgroundResource(R.color.background_material_light);
//If you want your header to respond to clicks, this should suffice
headerView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Open to your implementation
}
});
You can override the layout resource "design_navigation_item_header" and add background color and then you will not see the ripple.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:orientation="vertical"
android:paddingBottom="#dimen/navigation_separator_vertical_padding"/>
If you wanted to respond to the click you could look up the view by id and add an OnClickListener.
Taking hints from razzledazzle's post, simply consuming the onClick works
View headerView = LayoutInflater.from(getActivity()).inflate(R.layout.drawer_header_view, menuItemsNavigationView, false);
headerView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Consume input from header view. This disables the ripple effect
}
});
menuItemsNavigationView.addHeaderView(headerView);
I just dealt with this in my own app. A NavigationView is a ScrimInsetsFrameLayout wrapped around a ListView and it explicitly ignores (throws out) clicks on the first item of the list, which is the header.
However, you can retrieve them yourself by wrapping the NavigationView’s onItemClickListener with your own:
ListView menuList = (ListView) navigationView.getChildAt(0);
final AdapterView.OnItemClickListener nativeListener = menuList.getOnItemClickListener();
menuList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0) {
drawerLayout.closeDrawer(GravityCompat.START);
onUserClicked(prefs.getUser());
}
nativeListener.onItemClick(parent, view, position, id);
}
});
Normally I don’t endorse this kind of relying on the internals of Android widgets, but in this case it’s the support library, which is packaged and shipped with your app. It’s probably better to build your own custom navigation view in the long run, but sometimes you need a quick fix!
None of the solutions on this page worked for me, and probably with good reason. As the accepted answer by BladeCoder states, the listener is hard-coded into the NavigationDrawer ListView, and the header view is the 0th item of the ListView's Adapter. Simply overriding the onClickListener for the inflated header view won't work if you want to actually disable it, as you need to reference the onClickListener for the ListView's Adapter item instead of just the inflated view.
The below solution works for me to disallow clicking/tapping the header view, but it does not disable focusability (with a d-pad or keyboard for example). I can't seem to figure out how just yet.
//get the listview within the navigation drawer (0th child)
ListView menuList = (ListView) mNavigationView.getChildAt(0);
//get the header view (0th item in listview adapter)
final View headerView = menuList.getAdapter().getView(0, null, null);
if (headerView != null) {
headerView.setEnabled(false); //doesn't work, should
headerView.setFocusable(false); //doesn't work, should
headerView.setFocusableInTouchMode(false); //doesn't work, should
headerView.setClickable(false); //doesn't work, should
headerView.setOnClickListener(null); //WORKS! works to disallow clicking! is still focusable via keyboard
}
I have set up a listview with one edit text widget. The listview is populated with a string file and I wanted to make changes on each item by clicking on the item. code builds and the listview shows as planned. When the item is clicked it gets focus but then looses focus after the keyboard pops up without any entries.
els_edit_tiles.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="match_parent"
android:orientation="vertical" >
<EditText
android:id="#+id/listText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" >
</EditText>
This is the onclicklistener, I assume the changes will need to be here and did not post the adapter code.
private class ListClickhandler implements OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> Adapter, View view, int position, long arg3) {
TextView listText = (TextView) view.findViewById(R.id.listText);
String text = listText.getText().toString();
// Toast.makeText(context, text + " " + position, Toast.LENGTH_LONG).show();
}
}
I assume you mean the item loses its highlight, rather than focus. Focus goes wherever you tap and is there whether the highlight shows or not. So, I'll explain some options for the disappearing highlight.
For better or worse, they've designed it this way. You have a few choices. One is to subclass ListView and try to figure out how to add a sticky highlight. (I tried that and got it to work, but abandoned it for another reason - I wanted to put the list in a ScrollView, which doesn't work with a ListView).
Another choice: use a spinner (aka a drop down list). This doesn't exactly do what you're asking but it does show you which item was selected -- it is the one that shows when the spinner collapses.
Yet a third choice (I implemented this) - Use a LinearLayout and add each item as a child of the layout. You have to implement your own selection and highlighting, but that takes relatively little code (I can help if you need). This effectively gives you a list that shows all items - it doesn't scroll. You can include it in a ScrollView along with other elements to scroll the whole collection together.
I'm using a custom view expanded from a XML layout in a horizontal scroll view as a sort of horizontal image list but I'm not sure how to get them to appear clickable/tappable (ie they highlight when tapped) or how to capture these events. I think I've tried setOnClickHandler without it working. I'm also trying to get a simple TextView to do the same. I've also tried setting android:clickable="true" but that hasn't helped either. Any ideas?
To take care of the visual feedback use an xml Selector, and set it as the View's background.
To handle click events use
mView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
//your code here
}
});
I have a ListView. The data behind it is fetched from the Internet, in sets of 10-30 items whenever the user scrolls all the way to the bottom. In order to indicate that it is loading more items, I used addFooterView() to add a simple view that displays a "Loading..." message and a spinner. Now, when I'm out of data (no more data to fetch), I want to hide that message. I tried to do:
loadingView.setVisibility(View.GONE);
Unfortunately, while that does hide the view, it leaves space for it. I.e. I end up with a big blank space where the "Loading" message used to be. How can I go about properly hiding this view?
I can't use removeFooterView() because I may need to show it again, in which case I can't call addFooterView() again because an adapter has already been set on the ListView, and you can't call addHeaderView() / addFooterView() after setting an adapter.
It seems that you are allowed to call addHeaderView() / addFooterView() after setAdapter() as long as you call one of those methods at least once before. That is a rather poor design decision from Google, so I filed an issue. Combine this with removeFooterView() and you have my solution.
+1 for the other two answers I got, they're valid (and arguably more correct) solutions. Mine, however, is the simplest, and I like simplicity, so I'll mark my own answer as accepted.
Try setting the footer's height to 0px or 1px before hiding it. Alternatively, wrap the footer view in a wrap_content height FrameLayout and hide/show the inner view, leaving the FrameLayout visible; the height should wrap properly then.
in my case addFooterView / removeFooterView() cause some artefacts.
And I found other solution. I used FrameLayout as FooterView. And when I want to add Footer I called mFrameFooter.addView(myFooter); and mFrameFooter.removeAllViews(); for remove.
FrameLayout frameLayout = new FrameLayout(this);
listView.addFooterView(frameLayout);
......
......
//For adding footerView
frameLayout.removeAllViews();
frameLayout.addView(mFooterView);
//For hide FooterView
frameLayout.removeAllViews();
The Droid-Fu library has a class designed for having a loading footer show and hide: ListAdapterWithProgress.
Works well in my project:
1.Add footer view first
mListView.addFooterView(mFooterView);
mListView.setAdapter(mAdapter);
2.Set visibility
mFooterView.setVisibility(View.GONE);
mFooterView.setPadding(0, 0, 0, 0);
3.Set invisibility
mFooterView.setVisibility(View.GONE);
mFooterView.setPadding(0, -1*mFooterView.getHeight(), 0, 0);
As #YoniSamlan pointed out, it can be achieved in a simple way. You have to specify
android:layout_height="wrap_content"
in the ViewGroup that contains the "Load More" button. Doesn't have to be FrameLayout, see below for a simple -working- example that uses a LinearLayout.
Both images show a screen that is scrolled all the way to the bottom. First one has a visible footer that wraps around the "load more" button. Second images shows that the footer collapses if you set button's visibility to GONE.
You can show again the footer (inside some callback) by changing the visibility:
loadMore.setVisibility(View.VISIBLE); // set to View.GONE to hide it again
Perform listView initialization as usual
// Find View, set empty View if needed
mListView = (ListView) root.findViewById(R.id.reservations_search_results);
mListView.setEmptyView(root.findViewById(R.id.search_reservations_list_empty));
// Instantiate footerView using a LayoutInflater and add to listView
footerView = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.load_more_footer_view, null, false);
// additionally, find the "load more button" inside the footer view
loadMore = footerView.findViewById(R.id.load_more);
loadMore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fetchData();
}
});
// add footer view to the list
mListView.addFooterView(footerView);
// after we're done setting the footerView, we can setAdapter
adapter = new ReservationsArrayAdapter(getActivity(), R.layout.list_item_reservations_search, reservationsList);
mListView.setAdapter(adapter);
load_more_footer_view.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">
<Button
android:id="#+id/load_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="9dp"
android:gravity="center"
android:layout_gravity="center"
android:background="#drawable/transparent_white_border"
android:textColor="#android:color/white"
android:text="#string/LOAD_MORE"/>
It should be a bug of Android.
You don't need to remove or add footer view dynamically. You just need to create an unspecified height parent Layout (either inflate it from an xml file or create it programatically) and then add your view which you want to hide or show into it.
And you can set the view, but NOT the parent Layout, to VISIBLE or GONE or something else now. It works for me.
Used
footer.removeAllViews();
This does not remove footer but flushes children.
You again have to repopulate children. Can check by
footer.getChildCount()<2
I also found that is possible call onContentChanged() (if you use ListActivity) to force recreate ListView if I need add HeaderView to them after setAdapter() call, but it is very ugly hack.
I have created a ListView that handles this. It also has an option to use the EndlessScrollListener I've created to handle endless listviews, that loads data until there's no more data to load.
You can see these classes here:
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java
- Helper to make it possible to use the features without extending from SimpleListViewWithLoadingIndicator.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java
- Listener that starts loading data when the user is about to reach the bottom of the ListView.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java
- The EndlessListView. You can use this class directly or extend from it.
I have small hack way to resolve this problem for everywhere.
Put listview and footer view (just sub layout) in parent layout like LinnearLayout, remember that footerview below listview.
Controller this footer view gone and visibility like nomal view. And done!
first I am adding my footer to the listview,like this
listView.addFooterView(Utils.b);
Then on button click , I remove the view,
listView.removeFooterView(Utils.b);
I am adding the footer everytime when I am hitting the async,and theus the're no duplicate entry.I could aslo check for the count and so it like this,
if(listView.getFooterViewsCount() > 0){//if footer is added already do something}
When you want to remove the footer in ListView just call
listView.addFooterView(new View(yourContext));
It will add a dummy empty view which will not reserve any space