Hi I want to add pull to refresh on my apps using this component PullToResharp, The project is great but im having problem adding custom listview in fragment, does anyone here done this before help me resolve it, thanks.
here is the code
fragment
namespace ListViewPullToRefresh.Fragments
{
public class SampleListFragment : SupportListFragment
{
private IPullToRefresharpView _ptrView;
private List<TableItem> _itemHeading = new List<TableItem>();
public SampleListFragment() : base()
{
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.nav, null, false);
}
public override void OnViewStateRestored(Bundle savedInstanceState)
{
base.OnViewStateRestored(savedInstanceState);
_itemHeading.Add(new TableItem(){Heading = "Ivan", Subheading = "Guinto"});
_itemHeading.Add(new TableItem(){Heading = "Jonathan", Subheading = "Guinto"});
_itemHeading.Add(new TableItem(){Heading = "Keneth", Subheading = "Guinto"});
if (_ptrView == null && ListView is IPullToRefresharpView)
{
_ptrView = (IPullToRefresharpView)ListView;
_ptrView.RefreshActivated += ptr_view_RefreshActivated;
}
ListView.Adapter = new HomeScreenAdapter(this, _itemHeading);
}
private void ptr_view_RefreshActivated(object sender, EventArgs e)
{
View.PostDelayed(() =>
{
if (_ptrView != null)
{
_ptrView.OnRefreshCompleted();
}
}, 2000);
}
public override void OnDestroyView()
{
if (_ptrView != null)
{
_ptrView.RefreshActivated -= ptr_view_RefreshActivated;
_ptrView = null;
}
base.OnDestroyView();
}
public override void OnResume()
{
base.OnResume();
ListView.ItemClick += ListView_ItemClick;
}
public override void OnPause()
{
base.OnPause();
ListView.ItemClick -= ListView_ItemClick;
}
void ListView_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
Toast.MakeText(Activity,e.Position+ " Clicked",ToastLength.Short).Show();
}
}
HomeScreenAdapter.cs
public class HomeScreenAdapter :BaseAdapter<TableItem>
{
public HomeScreenAdapter(Activity content, List<TableItem> items)
{
_content = content;
_items = items;
}
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;
if (view == null)
view = _content.LayoutInflater.Inflate(Resource.Layout.CustomView, null);
view.FindViewById<TextView>(Resource.Id.Text1).Text = item.Heading;
view.FindViewById<TextView>(Resource.Id.Text2).Text = item.Subheading;
return view;
}
public override int Count
{
get { return _items.Count; }
}
public override TableItem this[int position]
{
get { return _items[position]; }
}
}
}
im getting this error
Error 1 The best overloaded method match for 'ListViewPullToRefresh.HomeScreenAdapter.HomeScreenAdapter(Android.App.Activity, System.Collections.Generic.List)' has some invalid arguments C:\Users******\Documents\Visual Studio 2013\Projects\ListViewPullToRefresh\ListViewPullToRefresh\Fragments\SampleListFragment.cs 43 32 ListViewPullToRefresh
Error 2 Argument 1: cannot convert from 'ListViewPullToRefresh.Fragments.SampleListFragment' to 'Android.App.Activity' C:\Users****\Documents\Visual Studio 2013\Projects\ListViewPullToRefresh\ListViewPullToRefresh\Fragments\SampleListFragment.cs 43 54 ListViewPullToRefresh
when i putting my custom adapter to the fragment, i get the error here ListView.Adapter = new HomeScreenAdapter(this, _itemHeading); and here public HomeScreenAdapter(Activity content, List<TableItem> items)
{
_content = content;
_items = items;
}
Related
I have a custom listview in my C# Android app, each row contains a textview, ImageView and a switch. When a Listview item is clicked, I want to turn the row's item switch on.
MainActivity:
List<TableList> list = = new List<TableList>();
list.Add(new TableList("Germany"));
list.Add(new TableList("France"));
list.Add(new TableList("Finland"));
listView.ItemClick += delegate (object sender, AdapterView.ItemClickEventArgs e)
{
string selected = t.Name;
if (selected == "France")
{
// Turn the proper switch for France row ON
}
};
ListAdapter and ListClass for the Listview:
public class ListAdapter : BaseAdapter<TableList>
{
List<TableList> items;
Activity context;
public ListAdapter(Activity context, List<TableList> items)
: base()
{
this.context = context;
this.items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override TableList this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
View view = convertView;
if (view == null) // no view to re-use, create new
view = context.LayoutInflater.Inflate(Resource.Layout.CoinList, null);
view.FindViewById<TextView>(Resource.Id.CoinName).Text = item.Name;
view.FindViewById<ImageView>(Resource.Id.imageView1).SetImageResource(Resource.Drawable.n);
If item is clicked set it on
{
view.FindViewById<Switch>(Resource.Id.switch).SetOn
}
else
{
view.FindViewById<Switch>(Resource.Id.switch).SetOf
}
return view;
}
}
public class TableList
{
public string Name;
public TableList(string Name)
{
this.Name = Name;
}
}
I don't know where I should set the Switch ON (in the listView.ItemClick event or in the ListAdapter) and I don't know how to set it to ON. Please help me to do so.
Here is my demo.
You can choose one to achieve your goal. I will show you how to do this by ItemClick event:
When a Listview item is clicked, I want to turn the row's item switch on.
Because, Switch will grab the focus from ViewGroup. So, I remove the focus from Switch in the MyAdapter:
holder.ms.Focusable = false;//ms is Switch
Now, this is my ItemClick event( turn switch on while click the item):
private void MListView_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
var ll = e.View as LinearLayout;
var sw = ll.GetChildAt(1) as Switch;
if (sw.Checked)
{
sw.Checked = false;
adapter.changeState((int)sw.Tag,false);
}
else
{
sw.Checked = true;
adapter.changeState((int)sw.Tag, true);
}
}
As we all know, ListView has reuse problem, so, I add a bool property to control the Switch's state:
public class MyData:Java.Lang.Object {
public MyData(string p,bool b) {
this.position = p;
this.isCheck = b;
}
public string position { get; set; }
public bool isCheck { get; set; }
}
Below is changeState method:
internal void changeState(int position, bool v)
{
mitems[position].isCheck = v;
this.NotifyDataSetChanged();
}
And this is CheckedChange event:
private void Ms_CheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e)
{
var sm = sender as Switch;
Log.Error("Ms_CheckedChange", (int)sm.Tag+"");
if (e.IsChecked&&!mitems[(int)sm.Tag].isCheck)
{
mitems[(int)sm.Tag].isCheck = true;
this.NotifyDataSetChanged();
}
else if(!e.IsChecked&& mitems[(int)sm.Tag].isCheck)
{
mitems[(int)sm.Tag].isCheck = false;
this.NotifyDataSetChanged();
}
}
I'm trying to implement this code here in my project:
The differences are that instead of a ScrollView I'm using a RecyclerView and that instead of having my RecyclerView directly in the Activity I have inside a fragment that sets the listener I need.
The problem is that while the OnScrollChanged(int l, int t, int oldl, int oldt) gets actually called everytime I scroll, the parameters it obtains are always 0 and I don't understand why, so ScrollChangedTarget doesn't work as intended.
This is the custom RecyclerView:
public class NotifyingScrollRecyclerView : RecyclerView
{
private Activity activity;
private View headerView;
private View footerView;
public delegate void ScrollChangedHandler(int l, int t, int oldl, int oldt, EventArgs e);
public event ScrollChangedHandler scrollChanged;
public EventArgs e = null;
//Enabling all required constructors
public NotifyingScrollRecyclerView(Context context) : base(context)
{
}
public NotifyingScrollRecyclerView(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public NotifyingScrollRecyclerView(IntPtr ptr, JniHandleOwnership ownership) : base(ptr, ownership)
{
}
public NotifyingScrollRecyclerView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
//This method attribute allows us to register the inbuilt OnScrollChanged event that fires when scrolling a ScrollView
[Android.Runtime.Register("onScrollChanged", "(IIII)V", "GetOnScrollChanged_IIIIHandler")]
protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
{
base.OnScrollChanged(l, t, oldl, oldt);
scrollChanged(l, t, oldl, oldt, e);
}
}
//Set an event listener
public class ScrollViewChangedListener
{
private Activity activity;
private NotifyingScrollRecyclerView scrollView;
private Android.Support.V7.App.ActionBar actionBar;
private Drawable actionBarDrawable;
be changed
public ScrollViewChangedListener(Activity a, NotifyingScrollRecyclerView n)
{
n.scrollChanged += ScrollChangedTarget;
this.activity = a;
this.scrollView = n;
this.actionBar = ((UserPageActivity)a).SupportActionBar;
this.actionBarDrawable = a.GetDrawable(Resource.Drawable.actionbar_background);
this.actionBarDrawable.SetAlpha(0);
}
//Handle the changing of the scroll
public void ScrollChangedTarget(int l, int t, int oldl, int oldt, EventArgs e)
{
//You set the View you want to be your header as a header height, and then get it's height
int headerHeight = activity.FindViewById<ImageView>(Resource.Id.profilebanner).Height -
this.actionBar.Height;
float ratio = (float)Math.Min(Math.Max(t, 0), headerHeight) / headerHeight;
int newAlpha = (int)(ratio * 255);
this.actionBarDrawable.SetAlpha(newAlpha);
this.actionBar.SetBackgroundDrawable(this.actionBarDrawable);
}
}
This is the Activity which calls the fragment (it doesn't do anything in particular aside calling the fragment in this case):
[Activity(Label = "UserPageActivity")]
public class UserPageActivity : BaseActivity
{
protected override int LayoutResource => Resource.Layout.activity_user_page;
UserViewModel viewModel;
TextView username;
TextView usernameToolbar;
Button followButton;
ViewPager pager;
UserTabsAdapter adapter;
bool IsLoggedUser;
protected override void OnCreate(Bundle savedInstanceState)
{
IsLoggedUser = Intent.GetStringExtra("userId").Equals(LoginController.GetInstance().CurrentUser.Email);
base.OnCreate(savedInstanceState);
viewModel = new UserViewModel();
viewModel.UserLoaded += new UserViewModel.UserLoadedHandler(OnUserLoaded);
viewModel.LoadUserCommand.Execute(Intent.GetStringExtra("userId"));
username = FindViewById<TextView>(Resource.Id.profilename);
usernameToolbar = FindViewById<TextView>(Resource.Id.usernamePage);
followButton = FindViewById<Button>(Resource.Id.followButton);
username.Text = Intent.GetStringExtra("username");
usernameToolbar.Text = Intent.GetStringExtra("username");
adapter = new UserTabsAdapter(this, SupportFragmentManager);
pager = FindViewById<ViewPager>(Resource.Id.user_viewpager);
var tabs = FindViewById<TabLayout>(Resource.Id.tabs);
pager.Adapter = adapter;
tabs.SetupWithViewPager(pager);
pager.OffscreenPageLimit = 5;
pager.PageSelected += (sender, args) =>
{
var fragment = adapter.InstantiateItem(pager, args.Position) as IFragmentVisible;
fragment?.BecameVisible();
};
}
private void OnUserLoaded(bool loaded)
{
}
protected override void OnStart()
{
base.OnStart();
if (IsLoggedUser)
{
followButton.Visibility = ViewStates.Gone;
}
else
{
bool following;
if (LoginController.GetInstance().CurrentUser.FollowsUsers.ContainsKey(Intent.GetStringExtra("userId")))
{
followButton.Text = "Unfollow";
following = true;
}
else
{
followButton.Text = "Follow";
following = false;
}
followButton.Click += (object sender, EventArgs e) =>
{
followButton.Enabled = false;
if (following)
{
UserService service = ServiceLocator.Instance.Get<UserService>();
service.SetUser(LoginController.GetInstance().CurrentUser);
service.RemoveFollowsUserCommand.Execute(viewModel.LoadedUser.Email);
service.SetUser(viewModel.LoadedUser);
service.RemoveFollowedByUserCommand.Execute(LoginController.GetInstance().CurrentUser.Email);
followButton.Text = "Follow";
following = false;
}
else
{
UserService service = ServiceLocator.Instance.Get<UserService>();
service.SetUser(LoginController.GetInstance().CurrentUser);
service.AddFollowsUserCommand.Execute(viewModel.LoadedUser);
service.SetUser(viewModel.LoadedUser);
service.AddFollowedByUserCommand.Execute(LoginController.GetInstance().CurrentUser);
followButton.Text = "Unfollow";
following = true;
}
followButton.Enabled = true;
};
}
}
}
class UserTabsAdapter : FragmentStatePagerAdapter
{
string[] titles;
public override int Count => titles.Length;
public UserTabsAdapter(Context context, Android.Support.V4.App.FragmentManager fm) : base(fm)
{
titles = context.Resources.GetTextArray(Resource.Array.user_sections);
}
public override Java.Lang.ICharSequence GetPageTitleFormatted(int position) =>
new Java.Lang.String(titles[position]);
public override Android.Support.V4.App.Fragment GetItem(int position)
{
switch (position)
{
case 0: return UserContestsFragment.NewInstance();
case 1: return UserPartecipationsFragment.NewInstance();
case 2: return GlobalContestFragment.NewInstance();
case 3: return MessagesFragment.NewInstance();
}
return null;
}
public override int GetItemPosition(Java.Lang.Object frag) => PositionNone;
}
This is the fragment which setups the listener for the recyclerview:
public class UserContestsFragment : AbstractRefresherFadingToolbarFragment<Contest>
{
public static UserContestsFragment NewInstance() =>
new UserContestsFragment { Arguments = new Bundle() };
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ContestViewModel viewModel = new ContestViewModel();
base.ViewModel = viewModel;
base.LoadItemsCommand = viewModel.LoadAllByCreatorUserCommand;
base.param = Activity.Intent.GetStringExtra("userId");
base.adapter = new ContestsAdapter(Activity, viewModel);
var view = base.OnCreateView(inflater, container, savedInstanceState);
ScrollViewChangedListener listener = new ScrollViewChangedListener(Activity, recyclerView);
return view;
}
And this is the abstract fragment needed by that fragment which is in charge of setting up the layout:
public abstract class AbstractRefresherFadingToolbarFragment<T> : Android.Support.V4.App.Fragment, IFragmentVisible
{
public ICollectionViewModel<T> ViewModel;
public ICommand LoadItemsCommand;
public object param; //parametro per il LoadItemsCommand
public ItemsAdapter<T> adapter;
public SwipeRefreshLayout refresher;
public ProgressBar progress;
public NotifyingScrollRecyclerView recyclerView;
//LruCache cache = new LruCache((int)(Runtime.GetRuntime().MaxMemory() / 4));
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.Inflate(Resource.Layout.fragment_fading_toolbar, container, false);
recyclerView = view.FindViewById<NotifyingScrollRecyclerView>(Resource.Id.recyclerView);
//ScrollViewChangedListener listener = new ScrollViewChangedListener((UserPageActivity)Activity, recyclerView);
//adapter.cache = cache;
recyclerView.HasFixedSize = true;
recyclerView.SetAdapter(adapter);
recyclerView.SetItemViewCacheSize(4);
//recyclerView.ChildViewAttachedToWindow += new EventHandler<RecyclerView.ChildViewAttachedToWindowEventArgs>(OnChildViewAttached);
//recyclerView.ChildViewDetachedFromWindow += new EventHandler<RecyclerView.ChildViewDetachedFromWindowEventArgs>(OnChildViewDetached);
refresher = view.FindViewById<SwipeRefreshLayout>(Resource.Id.refresher);
refresher.SetColorSchemeColors(Resource.Color.accent);
progress = view.FindViewById<ProgressBar>(Resource.Id.progressbar_loading);
progress.Visibility = ViewStates.Gone;
return view;
}
}
I didn't read your entire code but I looked at the site you linked in your question. The functionality of the hidden action bar you want to use is handled by CollapsingToolbarLayout in the support library. To know how to use it, go to Cheesesquare. It is a complete example of the support library widgets and can be built and run without any changes.
Edit:
RecyclerView has a method named AddOnScrollListener. Use it instead of the OnScrollChanged. If you use an inherited class from RecyclerView, you can call it in all of the constructors of that class.
I have my fragments inside view page but I would like to reload the data on the tab selected.I tried returning PositionNone in the GetItemPosition method in my FragmentPagerAdapter but it does not work.
I tried adding notifyDataSetChanged(); on tab selected but it throws nullpointer exception.
I even tried setting the viewPager.setOffscreenPageLimit
I managed to find the link :
Replace Fragment inside a ViewPager
but it is on java.
I have managed to convert most of the code to c# but gets stuck on the commented code. I am not sure how to call the listener and instantiate the class at the same time? There is a lot of similar question but unfortunately not one in Xamarin Android.
I need to do this in xamarin android. Any help will be greatly appreciated.
This is my FragmentAdapter
public class TabsFragmentPagerAdapter : FragmentPagerAdapter
{
private readonly Android.Support.V4.App.Fragment[] fragments;
static readonly int NUM_ITEMS = 2;
private readonly Android.Support.V4.App.FragmentManager FragmentManager;
private Android.Support.V4.App.Fragment mFragmentAtPos0;
private readonly ICharSequence[] titles;
public TabsFragmentPagerAdapter(Android.Support.V4.App.FragmentManager fm, Android.Support.V4.App.Fragment[] fragments, ICharSequence[] titles) : base(fm)
{
this.fragments = fragments;
this.titles = titles;
}
public override int Count
{
get
{
return fragments.Length;
}
}
public override int GetItemPosition(Java.Lang.Object objectValue)
{
if (objectValue.GetType() == typeof(startJobFrag) && mFragmentAtPos0.GetType() == typeof(jobCaptureFrag))
{
return PositionNone;
}
return PositionUnchanged;
}
public override ICharSequence GetPageTitleFormatted(int position)
{
return titles[position];
}
public override Android.Support.V4.App.Fragment GetItem(int position)
{
if (position == 0)
{
if (mFragmentAtPos0 == null)
{
//not working
}
return mFragmentAtPos0;
}
else
{
return new jobCaptureFrag();
}
}
This is my Actvity
void FnInitTabLayout()
{
var fragments = new Android.Support.V4.App.Fragment[]
{
new frag 1(),
new frag 2(),
new frag 3(),
};
//Tab title array
var titles = CharSequence.ArrayFromStringArray(new[] {
"Page 1",
"Page 2",
"Page 3",
});
var viewPager = FindViewById<ViewPager>(Resource.Id.viewpager);
//viewpager holding fragment array and tab title text
//viewPager.
viewPager.Adapter = new TabsFragmentPagerAdapter(SupportFragmentManager, fragments, titles);
// Give the TabLayout the ViewPager
tabLayout.SetupWithViewPager(viewPager);
viewPager.PageSelected += (object sender, ViewPager.PageSelectedEventArgs e) =>
{
//FnInitTabLayout();
if(e.Position == 0)
{
//handle tab click/selected
}
};
}
public interface FirstPageFragmentListener
{
void onSwitchToNextFragment();
}
Fragment
public class Fragment1: Android.Support.V4.App.Fragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
View view = inflater.Inflate(Resource.Layout.myview, container, false);
}
}
I managed to solve it on my own.
In My activity I called NotifyDataSetChanged() on Pageselected event
Activity
ViewPager.PageSelected += (object sender, ViewPager.PageSelectedEventArgs e) =>
{
if (e.Position == 1)
{
_pager.Adapter.NotifyDataSetChanged();
}
};
Then I changed my Adapter from FragmentPagerAdapter to FragmentStatePagerAdapter and override the GetItemPosition.
Adapter
public class TabsFragmentPagerAdapter : FragmentStatePagerAdapter
{
public override int GetItemPosition(Java.Lang.Object objectValue)
{
return PositionNone;
}
}
Refresh a specific tab - inside the Adapter
public override Android.Support.V4.App.Fragment GetItem(int position)
{
if(position == 1) //second tab selected
{
NotifyDataSetChanged();
}
return fragments[position];
}
I'm sure there's a better way to do it but this works.
I just Created ViewPager With PagerAdapter and I put inside it 3 items but when i run the program ViewPager.ChildCount give me 2 ?
and when i scroll to second View the adapter add the third item and give me 3 count but when I scroll to the final View (3) it give me 2 in the count , but at this time the last 2 items !!
in my code I'm Trying to display the position in the textView inside the layout
it give my right numbers in the first scroll then it start mix position randomly
I DON'T KNOW WHY ?? PLEASE HELP
I Am Using Xamarin
and there is my code
public class SlidingTabsFragment : Fragment
{
private SlidingTabScrollView mSlidingTabScrollView;
private ViewPager mViewPager;
protected static View CurrentView;
protected static int CurrentViewPosition;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.fragment_sample, container, false);
}
public override void OnViewCreated(View view, Bundle savedInstanceState)
{
mSlidingTabScrollView = view.FindViewById<SlidingTabScrollView>(Resource.Id.sliding_tabs);
mViewPager = view.FindViewById<ViewPager>(Resource.Id.viewpager);
mViewPager.Adapter = new SamplePagerAdapter();
mViewPager.PageSelected += MViewPager_PageSelected;
mViewPager.PageScrolled += MViewPager_PageScrolled;
mSlidingTabScrollView.ViewPager = mViewPager;
}
private void MViewPager_PageScrolled(object sender, ViewPager.PageScrolledEventArgs e)
{
if (e.Position < mViewPager.ChildCount && e.Position>=0)
{
SamplePagerAdapter ad = (SamplePagerAdapter)mViewPager.Adapter;
TextView txtTitle = mViewPager.GetChildAt(e.Position).FindViewById<TextView>(Resource.Id.item_title);
int pos = ad.GetItemPosition(CurrentView);
txtTitle.Text = e.Position.ToString();
}
}
private void MViewPager_PageSelected(object sender, ViewPager.PageSelectedEventArgs e)
{
if (e.Position < mViewPager.ChildCount && e.Position >= 0)
{
SamplePagerAdapter ad = (SamplePagerAdapter)mViewPager.Adapter;
TextView txtTitle = mViewPager.GetChildAt(e.Position).FindViewById<TextView>(Resource.Id.item_title);
txtTitle.Text = e.Position.ToString();
}
}
public class SamplePagerAdapter : PagerAdapter
{
List<string> items = new List<string>();
public override void SetPrimaryItem(ViewGroup container, int position, Java.Lang.Object obj)
{
CurrentView = (View)obj;
CurrentViewPosition = position;
}
public SamplePagerAdapter() : base()
{
items.Add("Home");
items.Add("Sell");
items.Add("Rent");
}
public override int Count
{
get { return items.Count; }
}
public override bool IsViewFromObject(View view, Java.Lang.Object obj)
{
return view == obj;
}
public override Java.Lang.Object InstantiateItem(ViewGroup container, int position)
{
View view = LayoutInflater.From(container.Context).Inflate(Resource.Layout.pager_item, container, false);
container.AddView(view);
return view;
}
public string GetHeaderTitle (int position)
{
return items[position];
}
public override void DestroyItem(ViewGroup container, int position, Java.Lang.Object obj)
{
container.RemoveView((View)obj);
}
}
I solve it , It was because I have to Set OffScreenLimit property
I get a problem when I am trying to show an AlertDialog in my adapter. It's showed many time when I want to delete an article (The first article for each category) or when I try to delete a category (Exactly when I remove a second category )
This is my code
ArticlesConfigurations.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using SymaMobile.Core.Models;
using Vici.CoolStorage;
using SymaMobile.Droid.Adapters;
using Android.Database;
namespace SymaMobile.Droid
{
public class ArticlesConfiguration : Fragment
{
private ListView listViewCatgArticles;
private ListCategorieArticlesConfigAdapter adapterCatConfig;
private ListArticleConfigAdapter adapterArticles;
private List<CategoriesArticlesConfig> listCatgArticles;
private List<ArticlesConfig> listArticles;
private Button ajouterArticle;
private GridView gridArticles;
private ArticlesConfig art;
private TextView codeBarre;
private TextView libelle;
private CategoriesArticlesConfig categorieActuelle;
private Articles articleActuelle;
private CSList<CategoriesArticles, int> catglist;
private Spinner categorie;
private Spinner articles;
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.Inflate(Resource.Layout.fragment_configuration_articles, container, false);
listViewCatgArticles = v.FindViewById<ListView>(Resource.Id.lv_articles_fragment_configuration_articles);
ajouterArticle = v.FindViewById<Button>(Resource.Id.bt_ajouter_fragment_configuration_articles);
gridArticles = v.FindViewById<GridView>(Resource.Id.gv_articles_fragment_articles_configuration);
listCatgArticles = CategoriesArticlesConfig.List().ToList<CategoriesArticlesConfig>();
adapterCatConfig = new ListCategorieArticlesConfigAdapter(Activity, listCatgArticles);
listViewCatgArticles.Adapter = adapterCatConfig;
ajouterArticle.Click += ajouterArticle_Click;
listViewCatgArticles.ItemClick += listViewCatgArticles_ItemClick;
gridArticles.ItemClick += gridArticles_ItemClick;
return v;
}
void gridArticles_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
if (e.Position == gridArticles.Count-1)
{
Android.Util.Log.Error("Position grid", ""+e.Position);
Android.Util.Log.Error("grid Count", "" + gridArticles.Count);
AlertDialog.Builder builder = new AlertDialog.Builder(Activity);
builder.SetTitle(Resource.String.ajouter_categorie);
LayoutInflater inflater = Activity.LayoutInflater;
View v = inflater.Inflate(Resource.Layout.alertdialog_ajouter_article_configuration, null);
codeBarre = v.FindViewById<TextView>(Resource.Id.ed_code_barre_alert_dialog_ajouter_article_configuration);
libelle = v.FindViewById<TextView>(Resource.Id.tv_nom_article_alert_dialog_ajouter_article_configuration);
categorie = v.FindViewById<Spinner>(Resource.Id.sp_categories_alert_dialog_ajouter_article_configuration);
articles = v.FindViewById<Spinner>(Resource.Id.sp_articles_alert_dialog_ajouter_article_configuration);
var adapter = new ArrayAdapter<string>(Activity, Android.Resource.Layout.SimpleSpinnerDropDownItem);
catglist = CategoriesArticles.List();
for (int i = 0; i < catglist.Count; i++)
{
adapter.Add(catglist[i].Nom);
}
categorie.ItemSelected += categorie_ItemSelected;
categorie.Adapter = adapter;
codeBarre.TextChanged+=codeBarre_TextChanged;
builder.SetPositiveButton(Resource.String.ajouter, delegate
{
if (articleActuelle != null && categorieActuelle != null)
{
ArticlesConfig a = ArticlesConfig.New();
a.Article = articleActuelle;
a.CategorieArticles = categorieActuelle;
a.Save();
listArticles.Add(a);
adapterArticles.NotifyDataSetChanged();
}
});
builder.SetNegativeButton(Resource.String.annuler, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.SetView(v);
builder.Show();
}
}
void categorie_ItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
List<Articles> a = catglist[e.Position].Articles.ToList<Articles>();
var adapter = new ArrayAdapter<string>(Activity, Android.Resource.Layout.SimpleSpinnerDropDownItem);
for (int i = 0; i < a.Count; i++)
{
adapter.Add(a[i].Libelle);
}
articles.Adapter = adapter;
}
private void codeBarre_TextChanged(object sender, Android.Text.TextChangedEventArgs e)
{
if (codeBarre.Text.Length > 2)
{
articleActuelle = Articles.ReadFirst("CodeBarre ='" + codeBarre.Text + "'");
if (articleActuelle != null)
{
libelle.Text = articleActuelle.Libelle;
}
else
{
libelle.Text = "";
}
}
}
void listViewCatgArticles_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
categorieActuelle = CategoriesArticlesConfig.Read((int)adapterCatConfig.GetItemId(e.Position));
listArticles = categorieActuelle.ArticlesConfig.ToList();
adapterArticles = new ListArticleConfigAdapter(Activity, listArticles);
gridArticles.Adapter = adapterArticles;
}
void ajouterArticle_Click(object sender, EventArgs e)
{
AlertDialog.Builder builder = new AlertDialog.Builder(Activity);
builder.SetTitle(Resource.String.ajouter_categorie);
LayoutInflater inflater = Activity.LayoutInflater;
View v = inflater.Inflate(Resource.Layout.alertdialog_ajouter_categorie_article_configuration, null);
TextView nom = v.FindViewById<TextView>(Resource.Id.ed_nom_ajouter_categorie_fragment_article_configuration);
builder.SetPositiveButton(Resource.String.ajouter, delegate
{
if (nom.Text.Length > 0)
{
CategoriesArticlesConfig c = CategoriesArticlesConfig.New();
c.Nom = nom.Text;
c.Save();
c = CategoriesArticlesConfig.ReadFirst("CategorieArticlesConfigID=" + c.CategorieArticlesConfigID);
listCatgArticles.Add(c);
adapterCatConfig.NotifyDataSetChanged();
}
});
builder.SetNegativeButton(Resource.String.annuler, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.SetView(v);
builder.Show();
}
}
}
ListCategorieArticlesConfigAdapter.cs
namespace SymaMobile.Droid.Adapters
{
class ListCategorieArticlesConfigAdapter : BaseAdapter
{
private List<CategoriesArticlesConfig> list;
private int indice;
private Activity Activity;
public ListCategorieArticlesConfigAdapter(Android.App.Activity Activity, List<CategoriesArticlesConfig> list)
: base()
{
this.Activity = Activity;
this.list = list;
}
public override int Count
{
get { return list.Count; }
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return list[position].CategorieArticlesConfigID;
}
public override Android.Views.View GetView(int position, Android.Views.View convertView, Android.Views.ViewGroup parent)
{
var view = (convertView ??
Activity.LayoutInflater.Inflate(
Resource.Layout.list_item_categories_articles_configuration,
parent,
false)) as LinearLayout;
var nom = view.FindViewById(Resource.Id.tv_nom_list_item_categories_articles_configuration) as TextView;
var modify = view.FindViewById(Resource.Id.bt_modify_list_categories_articles_configuration) as Button;
var delete = view.FindViewById(Resource.Id.bt_delete_list_categories_articles_configuration) as Button;
modify.Click += modify_Click;
indice = position;
delete.Click +=delete_Click;
nom.Text = list[position].Nom;
return view;
}
void delete_Click(object sender, EventArgs e)
{
Android.App.AlertDialog.Builder builder = new Android.App.AlertDialog.Builder(Activity);
builder.SetMessage(Resource.String.msg_supprimer);
builder.SetPositiveButton(Resource.String.oui, delegate
{
CSDatabase.ExecuteNonQuery("DELETE FROM CategoriesArticlesConfig WHERE CategorieArticlesConfigID=" + list[indice].CategorieArticlesConfigID);
list.RemoveAt(indice);
NotifyDataSetChanged();
});
builder.SetNegativeButton(Resource.String.non, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.Show();
}
void modify_Click(object sender, EventArgs e)
{
AlertDialog.Builder builder = new AlertDialog.Builder(Activity);
builder.SetTitle(Resource.String.modifier_categorie);
LayoutInflater inflater = Activity.LayoutInflater;
View v = inflater.Inflate(Resource.Layout.alertdialog_ajouter_categorie_article_configuration, null);
TextView _nom = v.FindViewById<TextView>(Resource.Id.ed_nom_ajouter_categorie_fragment_article_configuration);
_nom.Text = list[indice].Nom;
builder.SetNegativeButton(Resource.String.annuler, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.SetPositiveButton(Resource.String.modifier, delegate {
CategoriesArticlesConfig c = CategoriesArticlesConfig.ReadFirst("CategorieArticlesConfigID=" + list[indice].CategorieArticlesConfigID);
if (c != null)
{
c.Nom = _nom.Text;
c.Save();
}
list[indice].Nom = _nom.Text;
NotifyDataSetChanged();
});
builder.SetView(v);
builder.Show();
}
}
}
ListArticleConfigAdapter
namespace SymaMobile.Droid.Adapters
{
class ListArticleConfigAdapter : BaseAdapter
{
Activity context;
private List<ArticlesConfig> liste;
private int indice;
public ListArticleConfigAdapter(Activity context, List<ArticlesConfig> liste)
: base()
{
this.context = context;
this.liste = liste;
}
public override int Count
{
get { return liste.Count+1; }
}
public override long GetItemId(int position)
{
return position;
}
public override Android.Views.View GetView(int position, View convertView, ViewGroup parent)
{
var view = (convertView ??
context.LayoutInflater.Inflate(
Resource.Layout.list_item_article_configuration,
parent,
false)) as LinearLayout;
var image = view.FindViewById(Resource.Id.iv_list_item_article_configuration) as ImageView;
var nom = view.FindViewById(Resource.Id.tv_nom_article_list_item_article_configuration) as TextView;
var supprimer = view.FindViewById(Resource.Id.bt_delete_list_item_article_configuration) as Button;
Android.Util.Log.Error("Position : ", ""+position+" List Count : "+liste.Count);
if (position < liste.Count)
{
nom.Text = liste[position].Article.Libelle;
image.SetImageBitmap(Tools.ImageTools.StringToBitMap(liste[position].Article.Image));
}
else
{
nom.Text = context.Resources.GetString(Resource.String.ajouter);
image.SetImageResource(Resource.Drawable.add128x128);
supprimer.Visibility = ViewStates.Invisible;
}
indice = position;
supprimer.Click += supprimer_Click;
return view;
}
void supprimer_Click(object sender, EventArgs e)
{
Android.App.AlertDialog.Builder builder = new Android.App.AlertDialog.Builder(context);
builder.SetMessage(Resource.String.msg_supprimer);
builder.SetPositiveButton(Resource.String.oui, delegate
{
CSDatabase.ExecuteNonQuery("DELETE FROM ArticlesConfig WHERE ArticlesConfigID=" + liste[indice].ArticlesConfigID);
liste.RemoveAt(indice);
NotifyDataSetChanged();
});
builder.SetNegativeButton(Resource.String.non, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.Show();
}
public override Java.Lang.Object GetItem(int position)
{
return position;
}
}
}
thank you in advance
u have need to do this code on next activity onbackpress or
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
progressDialog.dismiss();
}
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
System.exit(0);
}
In ListCategorieArticlesConfigAdapter.GetView (), you're adding new event handlers for modify and delete, even when the view has been recycled, which means it will already have those handlers. Convert your convertView ?? check into an if/else check and do not reinitialise the handlers when convertView != null.
Modified your adapter to use view holder pattern. This should solve your issues.
class ListCategorieArticlesConfigAdapter : BaseAdapter
{
private List<CategoriesArticlesConfig> list;
private Activity Activity;
public ListCategorieArticlesConfigAdapter(Android.App.Activity Activity, List<CategoriesArticlesConfig> list)
: base()
{
this.Activity = Activity;
this.list = list;
}
public override int Count
{
get { return list.Count; }
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return list[position].CategorieArticlesConfigID;
}
public override Android.Views.View GetView(int position, Android.Views.View convertView,
Android.Views.ViewGroup parent)
{
ViewHolder vh;
var view = convertView;
if (view == null)
{
view = Activity.LayoutInflater.Inflate(Resource.Layout.list_item_categories_articles_configuration,
false);
vh = new ViewHolder(list);
vh.Initialize(view);
view.Tag = vh;
}
vh = view.Tag as ViewHolder;
vh.Bind(position);
return view;
}
private class ViewHolder : Java.Lang.Object
{
private TextView _nom;
private Button _modify;
private Button _delete;
private List<CategoriesArticlesConfig> _list;
public ViewHolder(List<CategoriesArticlesConfig> list)
{
_list = list;
}
public void Initialize(View view)
{
_nom = view.FindViewById<TextView>(Resource.Id.tv_nom_list_item_categories_articles_configuration);
_modify = view.FindViewById<Button>(Resource.Id.bt_modify_list_categories_articles_configuration);
_delete = view.FindViewById<Button>(Resource.Id.bt_delete_list_categories_articles_configuration);
}
public void Bind(int position)
{
_modify.Tag = position;
_modify.Click += modify_Click;
_delete.Click += delete_Click;
_nom.Text = _list[position].Nom;
}
void delete_Click(object sender, EventArgs e)
{
var indice = (int)(((View)sender).Tag);
Android.App.AlertDialog.Builder builder = new Android.App.AlertDialog.Builder(Activity);
builder.SetMessage(Resource.String.msg_supprimer);
builder.SetPositiveButton(Resource.String.oui, delegate
{
CSDatabase.ExecuteNonQuery("DELETE FROM CategoriesArticlesConfig WHERE CategorieArticlesConfigID=" + list[indice].CategorieArticlesConfigID);
list.RemoveAt(indice);
NotifyDataSetChanged();
});
builder.SetNegativeButton(Resource.String.non, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.Show();
}
void modify_Click(object sender, EventArgs e)
{
var indice = (int)(((View)sender).Tag);
AlertDialog.Builder builder = new AlertDialog.Builder(Activity);
builder.SetTitle(Resource.String.modifier_categorie);
LayoutInflater inflater = Activity.LayoutInflater;
View v = inflater.Inflate(Resource.Layout.alertdialog_ajouter_categorie_article_configuration, null);
TextView _nom = v.FindViewById<TextView>(Resource.Id.ed_nom_ajouter_categorie_fragment_article_configuration);
_nom.Text = _list[indice].Nom;
builder.SetNegativeButton(Resource.String.annuler, (Android.Content.IDialogInterfaceOnClickListener)null);
builder.SetPositiveButton(Resource.String.modifier, delegate
{
CategoriesArticlesConfig c = CategoriesArticlesConfig.ReadFirst("CategorieArticlesConfigID=" + list[indice].CategorieArticlesConfigID);
if (c != null)
{
c.Nom = _nom.Text;
c.Save();
}
_list[indice].Nom = _nom.Text;
NotifyDataSetChanged();
});
builder.SetView(v);
builder.Show();
}
}
}
I find a solution. Is not the best but it work fine.
Firstly, I declare a boolean variable :
bool entree;
and in GetView method :
if ((position == 0 && !entree) || (position != 0 && entree))
{
...
entree = true;
}
that's all.