I want to have a MvxSpinner with a drop down template that has a button in it. The button click brings up a webbrowser with information about that item. The button click works fine, but now the item cannot be selected. Is there a way around this?
I figured out a way to make this work. Not sure if it is best practices, but it works for me.
I made the ItemsSource for the Spinner an enumerable of view models. There is a command for the button click and an event to handle any other touches in the item. I subclassed MvxSpinner and MvxAdapter. In the subclassed adapter I assign the event to a handler that programmatically clicks the back button to close the drop down list. I also routed a method from the adapter to the spinner in order to set the selected item.
public class ButtonSpinner : MvxSpinner
{
public ButtonSpinner(Context context, IAttributeSet attrs) : base(context, attrs)
{
var adapter = new ButtonAdapter(context, routedItemClick);
adapter.ItemTemplateId = Adapter.ItemTemplateId;
adapter.DropDownItemTemplateId = Adapter.DropDownItemTemplateId;
adapter.SimpleViewLayoutId = Adapter.SimpleViewLayoutId;
this.Adapter = adapter;
}
private void routedItemClick(object sender, ItemClickEventArgs e)
{
this.SetSelection((int)sender);
}
}
public class ButtonAdapter : MvxAdapter
{
EventHandler<Android.Widget.AdapterView.ItemClickEventArgs> _routedItemClick;
View _parent;
public override IEnumerable ItemsSource {
get {
return base.ItemsSource;
}
set {
base.ItemsSource = value;
if (value != null)
{
foreach(var item in value)
{
var dialectItem = item as DialectItemViewModel;
dialectItem.DialectSelected += (object obj, EventArgs args) => {
var dialectItems = ItemsSource as List<DialectItemViewModel>;
_routedItemClick(dialectItems.IndexOf(dialectItem), null);
_parent.RootView.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.Back));
_parent.RootView.DispatchKeyEvent(new KeyEvent(KeyEventActions.Up, Keycode.Back));
};
}
}
}
}
public ButtonAdapter(Context context, EventHandler<Android.Widget.AdapterView.ItemClickEventArgs> routedItemClick) : base(context)
{
_routedItemClick = routedItemClick;
}
protected override Android.Views.View GetView (int position, Android.Views.View convertView, Android.Views.ViewGroup parent, int templateId)
{
if (_parent == null)
_parent = parent;
return base.GetView (position, convertView, parent, templateId);
}
}
Related
I've used many examples for implementing search in listview but none of them work. I have a listview binding from a JSON array, I want to implement a simple way to search items in a listview. I found some examples on the internet but they doesn't work.
I am using Xamarin Android.
mainActivity:
DaftarGaleri = FindViewById<ListView>(Resource.Id.dataList);
itemGaleri = new List<Galeri>();
var webClient = new WebClient();
webClient.DownloadStringAsync(BaseUri);
progress = FindViewById<ProgressBar>(Resource.Id.progressBar);
//Showing loading progressbar
progress.Visibility = ViewStates.Visible;
webClient.DownloadStringCompleted += WebClient_DownloadStringCompleted;
}
private void WebClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
RunOnUiThread(() =>
{
itemGaleri = JsonConvert.DeserializeObject<List<Galeri>>(e.Result);
CustomListAdapter adapter = new CustomListAdapter(this, itemGaleri);
DaftarGaleri.Adapter = adapter;
progress.Visibility = ViewStates.Gone;
DaftarGaleri.ItemClick += DaftarGaleri_ItemClick;
}
);
}
customlistadapter class:
public class CustomListAdapter : BaseAdapter<Galeri>
{
public CustomListAdapter()
{
}
Activity context;
List<Galeri> list;
public CustomListAdapter(Activity _context, List<Galeri> _list) : base()
{
this.context = _context;
this.list = _list;
}
public override int Count
{
get { return list.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override Galeri this[int index]
{
get { return list[index]; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
// re-use an existing view, if one is available
// otherwise create a new one
if (view == null)
view = context.LayoutInflater.Inflate(Resource.Layout.item, parent, false);
Galeri item = this[position];
view.FindViewById<TextView>(Resource.Id.productname).Text = item.meta_title;
view.FindViewById<TextView>(Resource.Id.category).Text = item.model;
view.FindViewById<TextView>(Resource.Id.description).Text = item.description;
using (var imageView = view.FindViewById<ImageView>(Resource.Id.Photo))
{
string url = Android.Text.Html.FromHtml("http://www.termamed.com/store/image/"+item.image).ToString();
//Download and display image
Koush.UrlImageViewHelper.SetUrlDrawable(imageView,url, Resource.Drawable.products);
}
return view;
}
can anyone help.. thanks
xamarin android listview filter using searchview or edittext
You could refer to Cheesebaron's SearchView-Sample: showing how to Filter ListView's using SearchView and custom Filter implementation.
And don't forget to add the ObjectExtensions class in your project.
Some other useful link:
Searching Through RecyclerView - Xamarin.Droid
Filterable adapter with custom object
AutoCompleteTextView lets users choose a string from a list of valid values. Like, I imagine, every developer who wants to use this yoke, I am much more interested in the id of the user's selection than its string label. Is there any way to retrieve the id property of a chosen object, or its index in the source array?
The following C# code let's users pick from a list of SomeObject. I'm working in Xamarin, but don't let this put you off. Fix my problem in java and I'll happily make it work in C#
public class AutoCompleteField : PhysicalField
{
protected AutoCompleteTextView actv;
public AutoCompleteField(IList<SomeObject> choices, LogicalField logical, string id)
: base(logical, id)
{
_choices = choices;
}
protected ArrayAdapter<SomeObject> _adapter;
public override void addToView(LayoutInflater inflater)
{
var ctx = App_NotMobility.CurrentActivity;
actv = new AutoCompleteTextView(ctx);
actv.Id = _form.generateId();
// test choices
var _choices = new List<SomeObject>();
_choices.Add(new SomeObject(234, "Oranges"));
_choices.Add(new SomeObject(456, "Apples"));
_choices.Add(new SomeObject(789, "Bananas"));
_adapter = new ArrayAdapter<SomeObject>(ctx, Android.Resource.Layout.SimpleDropDownItem1Line, _choices);
actv.Adapter = _adapter;
actv.ItemClick += delegate(object sender, AdapterView.ItemClickEventArgs e)
{
// HOW DO I ACCESS THE ID OR THE INDEX OF USER'S SELECTION ?????????????????
};
_form.AddView(actv);
}
}
public class SomeObject
{
public int Id { get; set; }
public string Label { get; set; }
public SomeObject(int id, string label)
{
Id = id;
Label = label;
}
public override string ToString()
{
return Label;
}
}
Once you have initialized the adapter and overdid the item click, all you need to do is get the particular object from your adapter at that particular position of item which you clicked.
In java it would be somewhat similar to,
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
SomeObject someObject = (SomeObject) adapter.getItem(position);
int id = someObject.getId();
}
Thats all you would need. I am not sure about your code in xamarin, how you would get the position because i don't see any method where the position is input variable, may be you could add the tag to your view and can get the tag on your click method.
var position = ((View)sender).Tag;
But i would recommend , if you can create a class extend the base adapter, that way you will the method GetView to override and can easily do what you are looking for. You constructor could be like this for start,
List<SomeObjects> items;
Activity context;
public CustomAdapter(Activity context, List<SomeObjects> items)
: base()
{
this.context = context;
this.items = items;
}
Ankush's answer worked. I'm posting the C# code here because there are some subtleties with casting and generics...
public class myActv : AutoCompleteTextView, AdapterView.IOnItemClickListener
{
PhysicalField _physical;
public myActv(Activity ctx, PhysicalField physical) : base(ctx)
{
OnItemClickListener = this;
_physical = physical;
}
public void OnItemClick(AdapterView parent, View view, int position, long id)
{
// This is the punchline...
SomeObject whatIwant = ((ArrayAdapter<SomeObject>)this.Adapter).GetItem(position);
}
}
Here searchText is an Autocompletetextview..
searchText.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
int position = position; //Adapter selection position
}
});
I have implemented a custom adapter and listItemView. The adapter sets an onlclick listener to a button that is on the listItemView. The onclick listener simply calls a private method I have in the adapter and passes it the position of the item to be removed. I know the position is correct because the database removes the proper item. I have found similar questions but have not been able to adapt the answers to work for me. Ideas and thoughts are greatly appreciated. Thanks.
Here is the full adapter class
public class FoodListAdapter extends ArrayAdapter<FoodListItem> {
//private
private int type;
public FoodListAdapter(Context context, ArrayList<FoodListItem> _objects) {
super(context, 0, _objects);
type = 0;
}
public FoodListAdapter(Context context, ArrayList<FoodListItem> _objects, int _type) {
super(context, 0, _objects);
type = _type;
}
#Override
public View getView(int position, View reusableView, ViewGroup parent)
{
//Cast the reusable view to a listAdpaterItemView
FoodListItemView listItemView = (FoodListItemView) reusableView;
//Check if the listAdapterItem is null
if(listItemView == null)
{
//If it is null, then create a view.
listItemView = FoodListItemView.inflate(parent, this, type);
}
if (type == 2)
{
Button deleteButton = (Button) listItemView.findViewById(R.id.listItemViewDeleteBTN);
deleteButton.setTag(new Integer(position));
}
//Now we need to set the view to display the data.
listItemView.setData(getItem(position));
return listItemView;
}
}
Here is a portion of my code used in fragment. Note that I have a private variable decalred in the class for listAdapter, though I don't think I need that.
private void displayListForDate(Calendar _date)
{
//get the list view
ListView listView = (ListView) getView().findViewById(1);
//Clear the listview by removing the listadapter and setting it to null.
//listView.setAdapter(null);
//First we must get the items.
Global global = (Global) getActivity().getApplicationContext();
DietSQLiteHelper database = global.getDatabase();
//Create a list to hold the items we ate. This list will then be added to the listView.
final ArrayList<FoodListItem> consumedList;
//Add the items to the array.
consumedList = database.getConsumed(_date.getTimeInMillis());
//Create an adapter to be used by the listView
listAdapter = new FoodListAdapter(getActivity().getBaseContext(), consumedList, 2);
//Add the adapter to the listView.
listView.setAdapter(listAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
consumedList.remove(position);
listAdapter.notifyDataSetChanged();
}
});
}
If you didn't implement "equals" method of FoodListItem, try to implements it.
I would suggest,
that you just update the underlying data, in your case its ArrayList<FoodItems>.
In your Adapter make this simple method and change :
private List<FoodListItem> myList = new ArrayList<FoodListItem>();
public FoodListAdapter(Context context, List<FoodListItem> myList) {
super(context, 0, myList);
type = 0;
this.myList = myList;
}
public FoodListAdapter(Context context, List<FoodListItem> myList, int _type) {
super(context, 0, myList);
type = _type;
this.myList = myList;
}
// Also update your getView() method to use myList!
#Override
public View getView(int position, View reusableView, ViewGroup parent)
{
...
listItemView.setData(myList.get(position));
public void removeItem(int positio){
if(myList != null){
myList.remove(position);
}
}
And then in class, you are creating the adapter (Activity/Fragment), just call the method.
// Update the underlying ArrayAdapter
adapter.removeItem(position);
// Notify the adapter, the data has changed
adapter.notifyDataSetChanged();
Also, you shouldnt open connection to your SQLiteDatabase on UI thread, because you are blocking it. You never know, how fast is the reading from disk going to be. If it takes too long, user can think, that your application froze and therefore, he leaves, which you dont want. I would suggest to use AsyncTask, you will find a lot of examples.
I went through and cleaned up my code and it now works, here is the working code. I really don't know exactly the difference other than I updated the IDs that I was using to assign and get views. If anyone can explain the cause for the issue I was having I would appreciate it.
Here is the snippet from my fragment where I create the list view and assign an adapter.
private void displayListForDate(Calendar _date)
{
//get the list view
ListView listView = (ListView) getView().findViewById(R.id.listView);
//Clear the listview by removing the listadapter and setting it to null.
//listView.setAdapter(null);
//First we must get the items.
Global global = (Global) getActivity().getApplicationContext();
DietSQLiteHelper database = global.getDatabase();
//Create a list to hold the items we ate. This list will then be added to the listView.
ArrayList<FoodListItem> consumedList;
//Add the items to the array.
consumedList = database.getConsumed(_date.getTimeInMillis());
//Create an adapter to be used by the listView
listAdapter = new FoodListAdapter(getActivity().getBaseContext(), consumedList, 2);
//Add the adapter to the listView.
listView.setAdapter(listAdapter);
}
and here is my adapter class.
public class FoodListAdapter extends ArrayAdapter<FoodListItem>
{
//private
private int type;
public FoodListAdapter(Context context, ArrayList<FoodListItem> _objects) {
super(context, 0, _objects);
type = 0;
}
public FoodListAdapter(Context context, ArrayList<FoodListItem> _objects, int _type) {
super(context, 0, _objects);
type = _type;
}
#Override
public View getView(int position, View reusableView, ViewGroup parent)
{
//Cast the reusable view to a listAdpaterItemView
FoodListItemView listItemView = (FoodListItemView) reusableView;
//Check if the listAdapterItem is null
if(listItemView == null)
{
//If it is null, then create a view.
listItemView = FoodListItemView.inflate(parent, type);
}
if (type == 2)
{
Button deleteButton = (Button) listItemView.findViewById(R.id.listItemViewDeleteBTN);
deleteButton.setTag(new Integer(position));
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer tag = (Integer) view.getTag();
deleteItem(tag.intValue());
}
});
}
//Now we need to set the view to display the data.
listItemView.setData(getItem(position));
return listItemView;
}
private void deleteItem(int position)
{
FoodListItem item = getItem(position);
Global global = (Global) getContext().getApplicationContext();
DietSQLiteHelper database = global.getDatabase();
database.removeConsumed(item.getID());
remove(getItem(position));
}
}
I'm trying to customize a Spinner to be multi-selectable. I have successfully made it multi-selectable, but when an item is reselected, the view inside of the spinner does not get updated.
The Problem
When an item is reselected in the Spinner, the getView() of my SpinnerAdapter gets called, produces the View that I want it to, but that View somehow does not get displayed. I have stepped through everything in the debugger and I cannot find any differences between when a different item is selected or the same item is reselected. I have been through all of the code in Spinner AbsSpinner and AdapterView and I cannot find what may be causing this.
For Example
The spinner is populated with 6 items: Choice 1-6. The spinner originates with all items unselected. Choice 1, 2, and 4 are selected. The view inside the spinner displays "Choice 1, Choice 2, Choice 4" correctly.
Choice 4 is unselected (reselection as far as the Spinner is concerned). Choice 4 is correctly unchecked from the list, but the view inside the spinner does not update. It still displays "Choice 1, Choice 2, Choice 4". Stepping through the debugger, getView() on my SpinnerAdapter gets called and everything. For some reason, the View doesn't actually get displayed.
Choice 2 is unselected (this is not a "re-selection" to the Spinner). Here everything functions as expected. Both the list items and the view inside the Spinner are updated.
Some Code
The multi-selectable Spinner
public class MultiSelectSpinner extends Spinner {
private OnItemSelectedListener listener;
public MultiSelectSpinner(Context context) {
super( context );
}
public MultiSelectSpinner(Context context, AttributeSet attrs) {
super( context, attrs );
}
public MultiSelectSpinner(Context context, AttributeSet attrs, int defStyle) {
super( context, attrs, defStyle );
}
#Override
public void setSelection( int position ) {
super.setSelection( position );
if ( listener != null ) {
listener.onItemSelected( this, getSelectedView(), position, getAdapter().getItemId( position ) );
}
}
public void setOnItemSelectedEvenIfUnchangedListener( OnItemSelectedListener listener ) {
this.listener = listener;
}
}
Spinner Adapter
Only posting the important pieces here. Leaving out getters/setters, etc.
public class FormChoiceSpinnerAdapter implements SpinnerAdapter, OnItemSelectedListener {
private Choice[] choices;
private String title;
private final DataSetObservable dataSetObservable = new DataSetObservable();
public FormChoiceSpinnerAdapter(String[] choices, String title) {
setChoices( new Choice[choices.length] );
for (int i = 0; i < choices.length; i++) {
getChoices()[i] = new Choice( choices[i] );
}
}
#Override
public View getView( int position, View convertView, ViewGroup parent ) {
Context context = parent.getContext();
if ( convertView == null ) {
convertView = LayoutInflater.from( context ).inflate( android.R.layout.simple_spinner_item, parent, false );
}
String displayString = "";
for (int i = 0; i < getChoices().length; i++) {
Choice choice = getChoices()[i];
if ( choice.isSelected() ) {
displayString += choice.getLabel() + ", ";
}
}
if ( displayString.length() > 0 ) {
displayString = displayString.trim().substring( 0, displayString.length() - 2 );
}
else {
displayString = getTitle() + "...";
}
( (TextView) convertView ).setText( displayString );
return convertView;
}
#Override
public View getDropDownView( int position, View convertView, ViewGroup parent ) {
Context context = parent.getContext();
if ( convertView == null ) {
convertView = LayoutInflater.from( context ).inflate( R.layout.simple_dropdown_item, parent, false );
}
Choice choice = getChoices()[position];
TextView text = (TextView) convertView.findViewById( android.R.id.text1 );
text.setText( choice.getLabel() );
ImageView selectedImage = (ImageView) convertView.findViewById( R.id.image_selected );
int visibility = choice.isSelected() ? View.VISIBLE : View.GONE;
selectedImage.setVisibility( visibility );
return convertView;
}
#Override
public void onItemSelected( AdapterView<?> parent, View view, int position, long id ) {
Choice choice = getChoices()[position];
choice.setSelected( !choice.isSelected() );
ImageView imageView = (ImageView) view.findViewById( R.id.image_selected );
if ( imageView != null ) {
imageView.setVisibility( choice.isSelected() ? View.VISIBLE : View.GONE );
}
}
#Override
public void onNothingSelected( AdapterView<?> parent ) {
//No op
}
public static class Choice {
private boolean selected;
private String label;
public Choice(String label) {
this.label = label;
selected = false;
}
}
}
I know this is very late, but I found a solution for a similar problem I was having. Try adding adapter.notifyDataSetChanged() to your onItemSelected(AdapterView<?> parent, View view, int pos, long id) Override method.
My spinner is supposed to show a default message until the user actually selects a value from it. It was working fine unless to user selected the first item in the dropdown, then the spinner wouldn't update itself. Adding adapter.notifyDataSetChanged() worked.
I also looked through some of the source code and I couldn't find exactly what was happening, but I have an idea. Since the Spinner doesn't call the OnItemSelectedListener when selecting the value that is already selected (unless you extend Spinner, which I did with the help of this SO answer), my guess is that if you try to deselect your most recently selected value, Spinner doesn't recognize that as a deselection, and therefore won't redraw the view. For you, selecting 1, 2, 4 in that order, then trying to deselect 4 wouldn't trigger the redraw response, but then deselecting 2 would cause the redraw.
I could be wrong, but I know that our problems were similar and this worked for me.
You need to update the View from the UI thread. See this answer from another question that is similar to yours which details updating the UI thread using an AsyncTask:
https://stackoverflow.com/a/4370785/793150
I had the same issue with a view not updating with fragments, and I believe that this solution is applicable to this issue as well.
I hope this resolves your issue!
I have an Activity that hosts multiple fragments using the actionbar's tab functionality. One of those fragments contains a ListView. Upon this tab being selected, I'd like to select a certain item.
To do this programmatically, I use the following code (where calls is the ListView)
private void selectItem(int position)
{
long itemId = calls.GetItemIdAtPosition(position);
calls.PerformItemClick(calls, position, itemId);
}
If this ListView has been rendered, and I'm calling this, no problem. However, if I call it from onResume, then the code executes but nothing is selected in the end. I figure this is because at the point where I'm calling selectItem, not all items of the ListView have been rendered yet. If however I start off a background thread, sleep for a couple hundred milliseconds, then run the same code (in the ui thread of course), everything is fine, but this is an ugly hack.
Now you might be wondering, "why isn't he using calls.setSelection"? The thing is, I'm using a custom layout that performs expansion - so I need to actually click on the item I want selected (which in turn triggers the layout expansion for the item selected). However, I can call the code that is performed on PerformItemClick directly, the results will be the same (the layout expansion isn't performed).
Isn't there any way for me to catch the "Listview has finished rendering all viewable items" point in time, and then execute my selectItem call at that point? In ASP.NET, I have an event on every UI item telling me when it is done rendering, so I do item selection at that point but I haven't found anything.
Regards
Stephan
Here's the Adapter I'm using
public class ActiveCallsAdapter: ObservableAdapter<Call>
{
public ActiveCallsAdapter(Activity activity, ObservableCollection<Call> calls)
: base(activity, calls)
{
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout;
//View view = convertView;
//if (view == null) // no view to re-use, create new
// view = context.LayoutInflater.Inflate(Resource.Layout.Call, null);
SetTextView(view, Resource.Id.CallerName, item.CallerName);
SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber);
SetTextView(view, Resource.Id.CallStatus, item.State.ToString());
SetTextView(view, Resource.Id.CallDuration, item.Duration);
return view;
}
public void Update(LinearLayout view, Call item)
{
SetTextView(view, Resource.Id.CallerName, item.CallerName);
SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber);
string identifier = "callState_" + item.State.ToString();
int resourceId = Application.Context.Resources.GetIdentifier(identifier, "string", Application.Context.PackageName);
string callStateString = item.State.ToString();
if (resourceId != 0)
{
try
{
callStateString = Application.Context.Resources.GetString(resourceId);
}
catch (Exception e)
{
AndroidLogModel.Model.AddLogMessage("ActiveCallsAdapter", "Unable to find call state string with resource id " + resourceId + " state string: " + identifier, 3);
}
}
SetTextView(view, Resource.Id.CallStatus, callStateString);
//SetTextView(view, Resource.Id.CallDuration, item.Duration);
}
public void UpdateDuration(LinearLayout view, Call item)
{
SetTextView(view, Resource.Id.CallDuration, item.Duration);
}
}
And the base class of that adapter
public class ObservableAdapter<T>: BaseAdapter<T>
{
protected readonly Activity context;
protected readonly ObservableCollection<T> items;
public ObservableAdapter(Activity context, ObservableCollection<T> collection)
{
this.context = context;
this.items = collection;
//this.collection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collection_CollectionChanged);
this.items.CollectionChanged += (sender, e) => NotifyDataSetChanged();
}
void collection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyDataSetChanged();
}
public override T this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout;
// configure view here
return view;
}
protected void SetTextView(LinearLayout view, int id, string text)
{
var textView = view.FindViewById<TextView>(id);
if (textView != null)
textView.SetText(text, TextView.BufferType.Normal);
}
}
My Mono skills are limited so I don't know if I fully understood your adapter, anyway I've adapted some old code and made an adapter that expands a single item when click, also it will move the ListView in onResume to a desired position:
private static class CustomAdapter extends BaseAdapter {
// the data
private ArrayList<String> mData;
// an int pointing to a position that has an expanded layout,
// for simplicity I assume that you expand only one item(otherwise use
// an array or list)
private int mExpandedPosition = -1; // -1 meaning no expanded item
private LayoutInflater mInflater;
public CustomAdapter(Context context, ArrayList<String> items) {
mInflater = LayoutInflater.from(context);
mData = items;
}
public void setExpandedPosition(int position) {
// if the position equals mExpandedPosition then we have a click on
// the same row so simply toggle the row to be gone again
if (position == mExpandedPosition) {
mExpandedPosition = -1;
} else {
// else change position of the row that was expanded
mExpandedPosition = position;
}
// notify the adapter
notifyDataSetChanged();
}
#Override
public int getCount() {
return mData.size();
}
#Override
public String getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.ad_expandedelement,
parent, false);
}
((TextView) convertView.findViewById(R.id.textView1))
.setText(getItem(position));
// see if there is an expanded position and if we are at that
// position
if (mExpandedPosition != -1 && mExpandedPosition == position) {
// if yes simply expand the layout
convertView.findViewById(R.id.button1).setVisibility(
View.VISIBLE);
} else {
// this is required, we must revert any possible changes
// otherwise the recycling mechanism will hurt us
convertView.findViewById(R.id.button1).setVisibility(View.GONE);
}
return convertView;
}
}
The onListItemClick will simply be:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// set the expanded(or collapsed if it's a click on the same row that
// was previously expanded) row in the adapter
((CustomAdapter) getListView().getAdapter())
.setExpandedPosition(position);
}
and in onResume will have:
#Override
protected void onResume() {
super.onResume();
// set the position to the desired element
((CustomAdapter) getListView().getAdapter()).setExpandedPosition(15);
// set the selection to that element so we can actually see it
// this isn't required but has the advantage that it will move the
// ListView to the desired
// position if not visible
getListView().setSelection(15);
}
The R.layout.ad_expandedelement is a simple vertical LinearLayout with a TextView and an initially hidden(visibility set to gone) Button. For this Button I change the visibility to simulate expanding/collapsing a row in the ListView. You should be able to understand my code, if you want I can post on github the full sample.
While I'm not sure of the exact equivalent in C#/Mono, the Android framework provides a callback on Activity called onWindowFocusChanged() that indicates the period when the Window associated with a given Activity is visible to the user. You may have better luck waiting to call your selection method until that time, as the ListView should be measured and laid out by that point. In Java, it would be something like this:
#Override
public void onWindowFocusChanged (boolean hasFocus) {
if (hasFocus) {
selectItem(position);
}
}
You may need to have a bit more logic in there, this callback is directly associated with window focus and isn't a true lifecycle method. I can get called multiple times if you are displaying Dialogs or doing other similar operations.