I'm currently trying to build an android application which goal is to display 3 tabs (with a fragment per tab). Each tab display a listview with its proper information. The problem is that when i'm trying to refresh the listview after, for example, after deleting an element, I have trouble refreshing the good listview. Infact, I noticed that when I first load my application, I can freely delete an element on my first listview (in the first tab) and it'll refresh normally. But, if I slide to the next tab (to the right) and try to delete an item, it won't delete the item on this listview, but in the 1st one. Same thing, if I slide to the right once again, and try to delete an element, it will delete an item on the middle fragment (the 2nd one). From this time, wether I delete items on the 1st, 2nd or third tab won't matter because it's only the 2nd one which will be updated.
I think there's an underlying cycle of life mechanism which I don't understand explaining all this but I don't know what to change to my code.
In term of organization I have a MainActivity with 3 ListFragments (TabImages, TabMusiques, TabVideos). I'm using a FragmentStatePagerAdapter to instanciate my fragments depending on the position currently viewed by the user.
/**
* Adapter used to instanciate the right Fragment depending on the current tab selected in the
* MainActivity.
*/
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
// This will Store the Titles of the Tabs which are Going to be passed when ViewPagerAdapter is created
CharSequence Titles[];
// Store the number of tabs, this will also be passed when the ViewPagerAdapter is created
int NumbOfTabs;
//Build a Constructor and assign the passed Values to appropriate values in the class
public ViewPagerAdapter(FragmentManager fm,CharSequence mTitles[], int mNumbOfTabsumb) {
super(fm);
this.Titles = mTitles;
this.NumbOfTabs = mNumbOfTabsumb;
}
/**
* This method returns the fragment for a given position in the tabs
* @param position Integer describing the position in the tabs of the activity
*/
@Override
public Fragment getItem(int position) {
if(position == 0) // if the position is 0 we are returning the First tab
{
TabImages tabImages = new TabImages();
return tabImages;
}
else if(position == 1) //if the position is 1 we are returning the Second type of tab
{
TabMusiques tabMusiques = new TabMusiques();
return tabMusiques;
}
else //else we return the last type of tab
{
TabVideos tabVideos = new TabVideos();
return tabVideos;
}
}
/**
* This method returns the titles for the Tabs in the Tab Strip
* @param position position of the tab asked
* return the title of the tab
*/
@Override
public CharSequence getPageTitle(int position) {
return Titles[position];
}
/**
* This method returns the Number of tabs for the tabs Strip
*/
@Override
public int getCount() {
return NumbOfTabs;
}
}
Fragment :
public class TabImages extends ListFragment {
//Views
private View rootView;
//Vars
private Dossier dossier;
private StableArrayAdapterImage adapterImages;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/**
* Method called on the creation of the Fragment (initiated by the MainActivity)
*/
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//Inflate the layout of the music tab
rootView = inflater.inflate(R.layout.tab, container, false);
dossier = new Dossier("Dossier1", "02/06/2015", EnumTypeElement.IMAGE);
//Creating the object to add to the Dossier
Element d1 = new Dossier("sous-dossier1", "03/12/2008", EnumTypeElement.IMAGE);
Element d2 = new Dossier("sous-dossier2", "23/03/2002", EnumTypeElement.IMAGE);
Element i1 = new Image("image1", "12/12/2012");
Element i2 = new Image("image2", "13/03/2007");
Element i3 = new Image("image3", "00/00/0000");
Element i4 = new Image("image4", "15/15/1515");
Element i5 = new Image("image5", "21/12/2345");
Element i6 = new Image("image6", "22/11/1994");
Element i7 = new Image("image7", "12/08/1995");
Element i8 = new Image("image8", "01/12/1985");
try {
dossier.ajouterElement(d1);
dossier.ajouterElement(d2);
dossier.ajouterElement(i1);
dossier.ajouterElement(i2);
dossier.ajouterElement(i3);
dossier.ajouterElement(i4);
dossier.ajouterElement(i5);
dossier.ajouterElement(i6);
dossier.ajouterElement(i7);
dossier.ajouterElement(i8);
}
catch (WrongTypeElementException e){
e.getMessage();
}
adapterImages = new StableArrayAdapterImage(getActivity(), android.R.layout.simple_list_item_1, dossier.getListeElement());
setListAdapter(adapterImages);
return rootView;
}
public void updateFragmentImages(){
if(adapterImages != null){
adapterImages.notifyDataSetChanged();
}
}
/**
* Method called once the Fragment is created
* Allows us to register the Context menu and make it available for the event listeners
* @param savedInstanceState If the fragment is being re-created from a previous state, this is the state
*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
registerForContextMenu(getListView());
}
/**
* Method called on click of a ListView item
* @param l the ListView on which the call occured
* @param v the View containing the ListView
* @param position the position of the item clicked
* @param id the id of the item clicked
*/
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Element element = dossier.getListeElement().get(position);
if(element instanceof Dossier) //Si l'element est un dossier
{
Toast.makeText(getActivity(), "Impossible de lire un fichier actuellement", Toast.LENGTH_SHORT).show();
}
else
{
//Clicking on a element result in the launching of the lecture activity
Intent myIntent = new Intent(v.getContext(), ImageLectureActivity.class);
myIntent.putExtra("dossier", dossier);
myIntent.putExtra("position", position);
startActivityForResult(myIntent, 0);
}
}
/**
* Listener of the LongClick on an item of the list
* Results in the layout inflation of the menu
* @param menu The context menu being created
* @param v The view for which the context menu is being built
* @param menuInfo additional information on the menu (depends mainly on the view v)
*/
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = this.getActivity().getMenuInflater();
inflater.inflate(R.menu.context_menu_images, menu);
menu.setHeaderTitle(R.string.menuImageTitle);
}
/**
* Listener of the click on an item of the contextual menu
* Handles the items one by one
* @param item selected item of the menu.
* @return true to override the behaviour of the click on the item, false otherwise
*/
@Override
public boolean onContextItemSelected(MenuItem item) {
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case R.id.menu1:
return true;
case R.id.menu2:
return true;
case R.id.menu3:
//Handles the click on the "Supprimer" button of the context menu
new AlertDialog.Builder(getActivity())
.setTitle("Supprimer")
.setMessage("Confirmer la suppression")
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
try{
dossier.supprimerElement(info.position);
updateFragmentImages();
}
catch(ElementNotFoundException e) {
e.getMessage();
}
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// do nothing
}
})
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
return true;
default:
return super.onContextItemSelected(item);
}
}
}
Adapter used to populate the listview (there's an adapter per tab because the 3 listviews won't take the same objects :
package com.antoine.polytelecommande.views.Adapters;
public class StableArrayAdapterImage extends ArrayAdapter<Element> {
private HashMap<String, Integer> mIdMap = new HashMap<>();
/**
* Constructor of the adapter.
* Iterates on the element of the list to populate the HashMap mIdMap.
* @param context Application context passed to the adapter.
* @param textViewResourceId layout used for the listview.
* @param objects List of Images used to populate the listview.
*/
public StableArrayAdapterImage(Context context, int textViewResourceId, List<Element> objects) {
super(context, textViewResourceId, objects);
for (int i = 0; i < objects.size(); ++i) {
mIdMap.put(objects.get(i).toString(), i);
}
}
/**
* Getter of an item in the listview given its position.
* @param position position in the listview.
* @return the id of the item.
*/
@Override
public long getItemId(int position) {
String item = getItem(position).toString();
return mIdMap.get(item);
}
@Override
public boolean hasStableIds() {
return true;
}
}