I have created a ViewPager in my MainActivity that contains 5 tabs. In the first tab I have put a ListView in order to display some items. The problem is that whenever I swipe to a different tab and then return to the first, the items in the list are duplicated (i.e list contains A-B-C and then A-B-C-A-B-C). The odd thing is that this occurs only when I swipe further than the second tab. Here is my code :
MainActivity :
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
private ViewPager viewPager;
private TabsPagerAdapter mAdapter;
private ActionBar actionBar;
//private String[] tabs = { "Mixed", "Videos", "Audio", "Text", "Picture" };
final int[] ICONS = new int[] {
R.drawable.mixed,
R.drawable.video,
R.drawable.audio,
R.drawable.note,
R.drawable.photo
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.pager);
actionBar = getActionBar();
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Adding Tabs
for (int i=0;i<ICONS.length;i++) {
actionBar.addTab(actionBar.newTab().setIcon(MainActivity.this.getResources().getDrawable(ICONS[i]))
.setTabListener(this));
}
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public android.support.v4.app.Fragment getItem(int index) {
switch (index) {
case 0:
return MixedFragment.newInstance();
case 1:
return new VideosFragment();
case 2:
return new AudioFragment();
case 3:
return new TextFragment();
case 4:
return new PictureFragment();
}
return null;
}
@Override
public int getCount() {
// get item count - equal to number of tabs
return 5;
}
}
}
Fragment Class :
public class MixedFragment extends ListFragment implements AdapterView.OnItemClickListener {
ListView mixed;
JSONArray videos =null;
List<NameValuePair> row;
ArrayAdapter<VideoRow> adapter;
private List<VideoRow> listRow = new ArrayList<>();
private List<String> pathList = new ArrayList<>();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.mixed, container, false);
mixed = (ListView) v.findViewById(android.R.id.list);
row = new ArrayList<>();
// mixed.setOnItemClickListener(this);
new populateLists().execute();
return v;
}
public static MixedFragment newInstance(){
MixedFragment mx = new MixedFragment();
return mx;
}
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
String sendPath = pathList.get(position);
Intent start = new Intent(getActivity(), PlayVideo.class);
start.putExtra("Path", sendPath);
startActivity(start);
}
class populateLists extends AsyncTask<String,String,String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
JSONParser jParser = new JSONParser();
JSONObject obj = jParser.makeHttpRequest(Config.URL_Populate,"GET",row);
try {
// Checking for SUCCESS TAG
int success = obj.getInt("success");
if (success == 1) {
// videos found
// Getting Array of videos
videos = obj.getJSONArray("video");
// looping through All videos
for (int i = 0; i < videos.length(); i++) {
JSONObject c = videos.getJSONObject(i);
// Storing each json item in variable
String path = Config.URL_2server+c.getString("path");
String thumbnail = c.getString("thumbnail");
String title = c.getString("title");
String name =c.getString("name");
title = "\""+title+"\"";
byte[] bytes = Base64.decode(thumbnail,Base64.DEFAULT);
Bitmap thumb = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
pathList.add(path);
listRow.add(new VideoRow(path,thumb, name, title));
}
}
adapter = new MyListAdapterFrag(getActivity(), R.layout.enlarged_list_row, listRow,1);
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String file_url) {
mixed.setAdapter(adapter);
}
}
}
ListAdapter :
public class MyListAdapterFrag extends ArrayAdapter<VideoRow> {
List<VideoRow> lRow;
Bitmap myThumb;
int activity;
public MyListAdapterFrag(Context context, int resource, List<VideoRow> listRow, int i) {
super(context, resource, listRow);
lRow=listRow;
activity=i;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
View row = convertView;
if (row == null) {
LayoutInflater inflater = LayoutInflater.from(getContext());
if (activity == 1) {
row = inflater.inflate(R.layout.list_row, parent, false);
} else if (activity == 2) {
row = inflater.inflate(R.layout.enlarged_list_row, parent, false);
}
viewHolder = new ViewHolder();
viewHolder.txtName = (TextView) row.findViewById(R.id.name);
viewHolder.txtTitle = (TextView) row.findViewById(R.id.title);
viewHolder.imageThumb = (ImageView) row.findViewById(R.id.thumbnail);
row.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) row.getTag();
}
VideoRow thisRow = lRow.get(position);
viewHolder.txtName.setText(thisRow.getName());
Bitmap thumb = thisRow.getThumb();
if (activity == 1) {
myThumb = Bitmap.createScaledBitmap(thumb, 40, 40, true);
} else if (activity == 2) {
myThumb = Bitmap.createScaledBitmap(thumb, 60, 60, true);
}
viewHolder.imageThumb.setImageBitmap(myThumb);
viewHolder.txtTitle.setText(thisRow.getTitle());
return row;
}
private static class ViewHolder {
ImageView imageThumb;
TextView txtTitle;
TextView txtName;
}
}
So, do you guys have any idea what causes this bug ?
The problem is caused by the fact that your fragment gets reinitialized when the ViewPager goes past a certain point.
This is called the off screen page limit.
This means, that once your first Fragment is past the limit, when you return to it, the onCreateView function will be called again. You need to check whether the populateLists
AsyncTask really needs to fire or not.
Alternatively, you can increase your off screen page limit, to retain all your Fragments.