dynamically add and remove view to viewpager
Solution 1:
After figuring out which ViewPager methods are called by ViewPager and which are for other purposes, I came up with a solution. I present it here since I see a lot of people have struggled with this and I didn't see any other relevant answers.
First, here's my adapter; hopefully comments within the code are sufficient:
public class MainPagerAdapter extends PagerAdapter
{
// This holds all the currently displayable views, in order from left to right.
private ArrayList<View> views = new ArrayList<View>();
//-----------------------------------------------------------------------------
// Used by ViewPager. "Object" represents the page; tell the ViewPager where the
// page should be displayed, from left-to-right. If the page no longer exists,
// return POSITION_NONE.
@Override
public int getItemPosition (Object object)
{
int index = views.indexOf (object);
if (index == -1)
return POSITION_NONE;
else
return index;
}
//-----------------------------------------------------------------------------
// Used by ViewPager. Called when ViewPager needs a page to display; it is our job
// to add the page to the container, which is normally the ViewPager itself. Since
// all our pages are persistent, we simply retrieve it from our "views" ArrayList.
@Override
public Object instantiateItem (ViewGroup container, int position)
{
View v = views.get (position);
container.addView (v);
return v;
}
//-----------------------------------------------------------------------------
// Used by ViewPager. Called when ViewPager no longer needs a page to display; it
// is our job to remove the page from the container, which is normally the
// ViewPager itself. Since all our pages are persistent, we do nothing to the
// contents of our "views" ArrayList.
@Override
public void destroyItem (ViewGroup container, int position, Object object)
{
container.removeView (views.get (position));
}
//-----------------------------------------------------------------------------
// Used by ViewPager; can be used by app as well.
// Returns the total number of pages that the ViewPage can display. This must
// never be 0.
@Override
public int getCount ()
{
return views.size();
}
//-----------------------------------------------------------------------------
// Used by ViewPager.
@Override
public boolean isViewFromObject (View view, Object object)
{
return view == object;
}
//-----------------------------------------------------------------------------
// Add "view" to right end of "views".
// Returns the position of the new view.
// The app should call this to add pages; not used by ViewPager.
public int addView (View v)
{
return addView (v, views.size());
}
//-----------------------------------------------------------------------------
// Add "view" at "position" to "views".
// Returns position of new view.
// The app should call this to add pages; not used by ViewPager.
public int addView (View v, int position)
{
views.add (position, v);
return position;
}
//-----------------------------------------------------------------------------
// Removes "view" from "views".
// Retuns position of removed view.
// The app should call this to remove pages; not used by ViewPager.
public int removeView (ViewPager pager, View v)
{
return removeView (pager, views.indexOf (v));
}
//-----------------------------------------------------------------------------
// Removes the "view" at "position" from "views".
// Retuns position of removed view.
// The app should call this to remove pages; not used by ViewPager.
public int removeView (ViewPager pager, int position)
{
// ViewPager doesn't have a delete method; the closest is to set the adapter
// again. When doing so, it deletes all its views. Then we can delete the view
// from from the adapter and finally set the adapter to the pager again. Note
// that we set the adapter to null before removing the view from "views" - that's
// because while ViewPager deletes all its views, it will call destroyItem which
// will in turn cause a null pointer ref.
pager.setAdapter (null);
views.remove (position);
pager.setAdapter (this);
return position;
}
//-----------------------------------------------------------------------------
// Returns the "view" at "position".
// The app should call this to retrieve a view; not used by ViewPager.
public View getView (int position)
{
return views.get (position);
}
// Other relevant methods:
// finishUpdate - called by the ViewPager - we don't care about what pages the
// pager is displaying so we don't use this method.
}
And here's some snips of code showing how to use the adapter.
class MainActivity extends Activity
{
private ViewPager pager = null;
private MainPagerAdapter pagerAdapter = null;
//-----------------------------------------------------------------------------
@Override
public void onCreate (Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView (R.layout.main_activity);
... do other initialization, such as create an ActionBar ...
pagerAdapter = new MainPagerAdapter();
pager = (ViewPager) findViewById (R.id.view_pager);
pager.setAdapter (pagerAdapter);
// Create an initial view to display; must be a subclass of FrameLayout.
LayoutInflater inflater = context.getLayoutInflater();
FrameLayout v0 = (FrameLayout) inflater.inflate (R.layout.one_of_my_page_layouts, null);
pagerAdapter.addView (v0, 0);
pagerAdapter.notifyDataSetChanged();
}
//-----------------------------------------------------------------------------
// Here's what the app should do to add a view to the ViewPager.
public void addView (View newPage)
{
int pageIndex = pagerAdapter.addView (newPage);
// You might want to make "newPage" the currently displayed page:
pager.setCurrentItem (pageIndex, true);
}
//-----------------------------------------------------------------------------
// Here's what the app should do to remove a view from the ViewPager.
public void removeView (View defunctPage)
{
int pageIndex = pagerAdapter.removeView (pager, defunctPage);
// You might want to choose what page to display, if the current page was "defunctPage".
if (pageIndex == pagerAdapter.getCount())
pageIndex--;
pager.setCurrentItem (pageIndex);
}
//-----------------------------------------------------------------------------
// Here's what the app should do to get the currently displayed page.
public View getCurrentPage ()
{
return pagerAdapter.getView (pager.getCurrentItem());
}
//-----------------------------------------------------------------------------
// Here's what the app should do to set the currently displayed page. "pageToShow" must
// currently be in the adapter, or this will crash.
public void setCurrentPage (View pageToShow)
{
pager.setCurrentItem (pagerAdapter.getItemPosition (pageToShow), true);
}
}
Finally, you can use the following for your activity_main.xml
layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
Solution 2:
I was looking for simple solution to remove views from viewpager (no fragments) dynamically. So, if you have some info, that your pages belongs to, you can set it to View as tag. Just like that (adapter code):
@Override
public Object instantiateItem(ViewGroup collection, int position)
{
ImageView iv = new ImageView(mContext);
MediaMessage msg = mMessages.get(position);
...
iv.setTag(media);
return iv;
}
@Override
public int getItemPosition (Object object)
{
View o = (View) object;
int index = mMessages.indexOf(o.getTag());
if (index == -1)
return POSITION_NONE;
else
return index;
}
You just need remove your info from mMessages, and then call notifyDataSetChanged()
for your adapter. Bad news there is no animation in this case.
Solution 3:
There are quite a few discussions around this topic
- ViewPager PagerAdapter not updating the View
- Update ViewPager dynamically?
- Removing fragments from FragmentStatePagerAdapter
Although we see it often, using POSITION_NONE
does not seem to be the way to go as it is very inefficient memory-wise.
Here in this question, we should consider using Alvaro's approach:
... is to
setTag()
method ininstantiateItem()
when instantiating a new view. Then instead of usingnotifyDataSetChanged()
, you can usefindViewWithTag()
to find the view you want to update.
Here is a SO answer with code based on this idea
Solution 4:
I did a similar program. hope this would help you.In its first activity four grid data can be selected. On the next activity, there is a view pager which contains two mandatory pages.And 4 more pages will be there, which will be visible corresponding to the grid data selected.
Following is the mainactivty MainActivity
package com.example.jeffy.viewpagerapp;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.os.Parcel;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.GridView;
import java.lang.reflect.Array;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
SharedPreferences pref;
SharedPreferences.Editor editor;
GridView gridView;
Button button;
private static final String DATABASE_NAME = "dbForTest.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "diary";
private static final String TITLE = "id";
private static final String BODY = "content";
DBHelper dbHelper = new DBHelper(this);
ArrayList<String> frags = new ArrayList<String>();
ArrayList<FragmentArray> fragmentArray = new ArrayList<FragmentArray>();
String[] selectedData;
Boolean port1=false,port2=false,port3=false,port4=false;
int Iport1=1,Iport2=1,Iport3=1,Iport4=1,location;
// This Data show in grid ( Used by adapter )
CustomGridAdapter customGridAdapter = new CustomGridAdapter(MainActivity.this,GRID_DATA);
static final String[ ] GRID_DATA = new String[] {
"1" ,
"2",
"3" ,
"4"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
frags.add("TabFragment3");
frags.add("TabFragment4");
frags.add("TabFragment5");
frags.add("TabFragment6");
dbHelper = new DBHelper(this);
dbHelper.insertContact(1,0);
dbHelper.insertContact(2,0);
dbHelper.insertContact(3,0);
dbHelper.insertContact(4,0);
final Bundle selected = new Bundle();
button = (Button) findViewById(R.id.button);
pref = getApplicationContext().getSharedPreferences("MyPref", MODE_PRIVATE);
editor = pref.edit();
gridView = (GridView) findViewById(R.id.gridView1);
gridView.setAdapter(customGridAdapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//view.findViewById(R.id.grid_item_image).setVisibility(View.VISIBLE);
location = position + 1;
if (position == 0) {
Iport1++;
Iport1 = Iport1 % 2;
if (Iport1 % 2 == 1) {
//dbHelper.updateContact(1,1);
view.findViewById(R.id.grid_item_image).setVisibility(View.VISIBLE);
dbHelper.updateContact(1,1);
} else {
//dbHelper.updateContact(1,0);
view.findViewById(R.id.grid_item_image).setVisibility(View.INVISIBLE);
dbHelper.updateContact(1, 0);
}
}
if (position == 1) {
Iport2++;
Iport1 = Iport1 % 2;
if (Iport2 % 2 == 1) {
//dbHelper.updateContact(2,1);
view.findViewById(R.id.grid_item_image).setVisibility(View.VISIBLE);
dbHelper.updateContact(2, 1);
} else {
//dbHelper.updateContact(2,0);
view.findViewById(R.id.grid_item_image).setVisibility(View.INVISIBLE);
dbHelper.updateContact(2,0);
}
}
if (position == 2) {
Iport3++;
Iport3 = Iport3 % 2;
if (Iport3 % 2 == 1) {
//dbHelper.updateContact(3,1);
view.findViewById(R.id.grid_item_image).setVisibility(View.VISIBLE);
dbHelper.updateContact(3, 1);
} else {
//dbHelper.updateContact(3,0);
view.findViewById(R.id.grid_item_image).setVisibility(View.INVISIBLE);
dbHelper.updateContact(3, 0);
}
}
if (position == 3) {
Iport4++;
Iport4 = Iport4 % 2;
if (Iport4 % 2 == 1) {
//dbHelper.updateContact(4,1);
view.findViewById(R.id.grid_item_image).setVisibility(View.VISIBLE);
dbHelper.updateContact(4, 1);
} else {
//dbHelper.updateContact(4,0);
view.findViewById(R.id.grid_item_image).setVisibility(View.INVISIBLE);
dbHelper.updateContact(4,0);
}
}
}
});
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
editor.putInt("port1", Iport1);
editor.putInt("port2", Iport2);
editor.putInt("port3", Iport3);
editor.putInt("port4", Iport4);
Intent i = new Intent(MainActivity.this,Main2Activity.class);
if(Iport1==1)
i.putExtra("3","TabFragment3");
else
i.putExtra("3", "");
if(Iport2==1)
i.putExtra("4","TabFragment4");
else
i.putExtra("4","");
if(Iport3==1)
i.putExtra("5", "TabFragment5");
else
i.putExtra("5","");
if(Iport4==1)
i.putExtra("6", "TabFragment6");
else
i.putExtra("6","");
dbHelper.updateContact(0, Iport1);
dbHelper.updateContact(1, Iport2);
dbHelper.updateContact(2, Iport3);
dbHelper.updateContact(3, Iport4);
startActivity(i);
}
});
}
}
Here TabFragment1,TabFragment2 etc are fragment to be displayed on the viewpager.And I am not showing the layouts since they are out of scope of this project.
MainActivity will intent to Main2Activity Main2Activity
package com.example.jeffy.viewpagerapp;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.NestedScrollView;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.FrameLayout;
import java.util.ArrayList;
public class Main2Activity extends AppCompatActivity {
private ViewPager pager = null;
private PagerAdapter pagerAdapter = null;
DBHelper dbHelper;
Cursor rs;
int port1,port2,port3,port4;
//-----------------------------------------------------------------------------
@Override
public void onCreate (Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Toolbar toolbar = (Toolbar) findViewById(R.id.MyToolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapse_toolbar);
NestedScrollView scrollView = (NestedScrollView) findViewById (R.id.nested);
scrollView.setFillViewport (true);
ArrayList<String > selectedPort = new ArrayList<String>();
Intent intent = getIntent();
String Tab3 = intent.getStringExtra("3");
String Tab4 = intent.getStringExtra("4");
String Tab5 = intent.getStringExtra("5");
String Tab6 = intent.getStringExtra("6");
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setText("View"));
tabLayout.addTab(tabLayout.newTab().setText("All"));
selectedPort.add("TabFragment1");
selectedPort.add("TabFragment2");
if(Tab3!=null && !TextUtils.isEmpty(Tab3))
selectedPort.add(Tab3);
if(Tab4!=null && !TextUtils.isEmpty(Tab4))
selectedPort.add(Tab4);
if(Tab5!=null && !TextUtils.isEmpty(Tab5))
selectedPort.add(Tab5);
if(Tab6!=null && !TextUtils.isEmpty(Tab6))
selectedPort.add(Tab6);
dbHelper = new DBHelper(this);
// rs=dbHelper.getData(1);
// port1 = rs.getInt(rs.getColumnIndex("id"));
//
// rs=dbHelper.getData(2);
// port2 = rs.getInt(rs.getColumnIndex("id"));
//
// rs=dbHelper.getData(3);
// port3 = rs.getInt(rs.getColumnIndex("id"));
//
// rs=dbHelper.getData(4);
// port4 = rs.getInt(rs.getColumnIndex("id"));
Log.i(">>>>>>>>>>>>>>", "port 1" + port1 + "port 2" + port2 + "port 3" + port3 + "port 4" + port4);
if(Tab3!=null && !TextUtils.isEmpty(Tab3))
tabLayout.addTab(tabLayout.newTab().setText("Tab 0"));
if(Tab3!=null && !TextUtils.isEmpty(Tab4))
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
if(Tab3!=null && !TextUtils.isEmpty(Tab5))
tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
if(Tab3!=null && !TextUtils.isEmpty(Tab6))
tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
final ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
final PagerAdapter adapter = new PagerAdapter
(getSupportFragmentManager(), tabLayout.getTabCount(), selectedPort);
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
// setContentView(R.layout.activity_main2);
// Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
// setSupportActionBar(toolbar);
// TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
// tabLayout.addTab(tabLayout.newTab().setText("View"));
// tabLayout.addTab(tabLayout.newTab().setText("All"));
// tabLayout.addTab(tabLayout.newTab().setText("Tab 0"));
// tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
// tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
// tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
// tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
}
}
ViewPagerAdapter Viewpageradapter.class
package com.example.jeffy.viewpagerapp;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Jeffy on 25-01-2016.
*/
public class PagerAdapter extends FragmentStatePagerAdapter {
int mNumOfTabs;
List<String> values;
public PagerAdapter(FragmentManager fm, int NumOfTabs, List<String> Port) {
super(fm);
this.mNumOfTabs = NumOfTabs;
this.values= Port;
}
@Override
public Fragment getItem(int position) {
String fragmentName = values.get(position);
Class<?> clazz = null;
Object fragment = null;
try {
clazz = Class.forName("com.example.jeffy.viewpagerapp."+fragmentName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
fragment = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return (Fragment) fragment;
}
@Override
public int getCount() {
return values.size();
}
}
Layout for main2activity acticity_main2.xml
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/MyAppbar"
android:layout_width="match_parent"
android:layout_height="256dp"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapse_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:background="@color/material_deep_teal_500"
android:fitsSystemWindows="true">
<ImageView
android:id="@+id/bgheader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
android:background="@drawable/screen"
app:layout_collapseMode="pin" />
<android.support.v7.widget.Toolbar
android:id="@+id/MyToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="parallax" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:id="@+id/nested"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/MyToolbar"
android:background="?attr/colorPrimary"
android:elevation="6dp"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
Mainactivity layout
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.example.jeffy.viewpagerapp.MainActivity"
tools:showIn="@layout/activity_main">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridView1"
android:numColumns="2"
android:gravity="center"
android:columnWidth="100dp"
android:stretchMode="columnWidth"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</GridView>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SAVE"
android:id="@+id/button" />
</LinearLayout>
</RelativeLayout>
Hope this would help someone... Click up button please if this helped you