Here when I landed to first fragment all views are loaded and scrolling but when I come from third fragment i.e from some other activity I need to land in third fragment,and when navigate to first fragment views are not loading which makes non scrollable.
Firstfragment xml contains recyclerview and Group elements.
MyActivity class:
MyAdapter adapter = new MyAdapter(this, getSupportFragmentManager(), fragments);
viewPager.setOffscreenPageLimit(3);
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
//MyAdapter
public class MyAdapter extends FragmentPagerAdapter {
private Context mContext;
private List<Fragment> fragmentList ;
public MyAdapter(Context context, FragmentManager fm,List<Fragment> fragmentList) {
super(fm);
mContext = context;
this.fragmentList = fragmentList;
}
#Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return mContext.getString(R.string.firstfragmentname);
case 1:
return mContext.getString(R.string.seconsfrag);
case 2:
return mContext.getString(R.string.thirdfrag);
default:
return null;
}
}
}
//XML has custom viewpager
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/headerHeight"
android:layout_marginBottom="#dimen/footerHeight"
android:fadingEdgeLength="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/dp_30_60"
android:layout_marginTop="10dp"
android:layout_marginEnd="#dimen/dp_30_60"
android:orientation="vertical">
<TextView
android:id="#+id/sp_header"
style="#style/pageTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="#string/my_bill" />
<com.view.Tab
android:id="#+id/tab_custom"
android:layout_width="match_parent"
android:layout_height="#dimen/tab_height" />
<com.custom.CustomViewPager
android:id="#+id/sp_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="#dimen/dp_10_53"
android:layout_marginTop="4dp"
android:layout_marginEnd="#dimen/dp_10_53"
android:layout_marginBottom="4dp" />
</LinearLayout>
</ScrollView>
//CustomViepager
public class CustomViepager extends ViewPager {
public CustomViepager(Context context) {
super(context);
initPageChangeListener();
}
public CustomViepager(Context context, AttributeSet attrs) {
super(context, attrs);
initPageChangeListener();
}
private void initPageChangeListener() {
addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
requestLayout();
}
});
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
View child = getChildAt(getCurrentItem());
if (child != null) {
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
public void setCurrentItem(int item) {
super.setCurrentItem(item, false);
}
#Override
public void setCurrentItem(int item, boolean smoothScroll) {
super.setCurrentItem(item, false);
}
}
Use a simple view pager and Try to use it like this
CustomPagerAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
class MyPagerAdapter(fm: FragmentManager, behaviour: Int) : FragmentStatePagerAdapter(fm, behaviour) {
private val mFragmentList = ArrayList<Fragment>()
override fun getItem(position: Int): Fragment {
return mFragmentList[position]
}
override fun getCount(): Int {
return mFragmentList.size
}
fun addFragment(fragment: Fragment) {
mFragmentList.add(fragment)
}
}
In Activity/Fragment
private var mAdapter: MyPagerAdapter? = null
in onCreate
mAdapter = MyPagerAdapter(getChildFragmentManager(), FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
mAdapter?.addFragment(YOUR_FIRST_FRAGMENT)
mAdapter?.addFragment(YOUR_SECOND_FRAGMENT)
mAdapter?.addFragment(YOUR_THIRD_FRAGMENT)
sp_viewpager.offscreenPageLimit = 3
sp_viewPAger.adapter = mAdapter
When you want to land on the desired position, after the above code write
sp_viewpager.setCurrentItem(1/2/3,true/false)
#LakwinderSingh answer to this question is perfect.
Just want to highlight one thing is that, the main trick or real problem is with the offscreen page limit it's the number of fragments the pager creates left or right to the current fragment, by default this value is one.
That's why when we scroll to the third Fragment it doesn't load anything cause loading the third fragment will require loading 2 fragments at the same time to the left, by changing the offscreen page limit value we can determine the number of fragments that load up at a specific time in this case which would be 2.
If we did above then we also have to override PagerAdapter's default function destroyItem() to an empty function without super so that there aren't too many fragments destroyed and created at the same time cause that would make sliding choppy or make fragments blank.
Related
I want to design my tabitems from a tablayout, but the tabs are not made in my .xml file. I don't know exactly where my tabs get generated, but it looks like it happens in a adapter
Normally you would make a drawable to design views like buttons, but can I do this also for tabs in a tabLayout?
My MainActivity doesn't create tabItems on startup, and I'm only gathering the data for the fragments here
#Override
protected void onCreate(Bundle savedInstance)
{
super.onCreate(savedInstance);
recentCardItems = new ArrayList<>();
followingCardItems = new ArrayList<>();
//This method sets the views in the abstract class DrawerLayoutActivity
setMenuLayoutElements(R.layout.activity_main, R.id.toolbar_main, R.id.drawer_layout_main);
setCardAdapterToReviewList();
configureFireStoreToLoadNewReviewsIntoList();
}
My adapter class, with the tabs "All" and "Following"
final int PAGE_COUNT = 2;
private String tabTitles[] = new String[] { "All", "Following" };
private Context context;
private List<CardItem> cardItemsRecent;
private List<CardItem> cardItemsFollowing;
private RecyclerViewFragment fragmentRecent;
private RecyclerViewFragment fragmentFollowing;
public RecyclerFragmentAdapter(FragmentManager fm, Context context, List<CardItem> cardItemsRecent, List<CardItem> cardItemsFollowing) {
super(fm);
this.context = context;
this.cardItemsRecent = cardItemsRecent;
this.cardItemsFollowing = cardItemsFollowing;
this.fragmentRecent = RecyclerViewFragment.newInstance(cardItemsRecent, R.layout.fragement_recyclerview_home, R.id.recyclerView);
this.fragmentFollowing = RecyclerViewFragment.newInstance(cardItemsFollowing, R.layout.fragment_recyclerview_following, R.id.reyclerViewFollowing);
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public Fragment getItem(int position) {
Fragment fragmentToReturn;
switch (position)
{
case 0:
fragmentToReturn = fragmentRecent;
break;
case 1:
fragmentToReturn = fragmentFollowing;
break;
default:
fragmentToReturn = null;
}
return fragmentToReturn;
}
//this getPageTitle method seems like it generates the titles of my tabs
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return tabTitles[position];
}
public RecyclerViewFragment getFragmentRecent() {
return fragmentRecent;
}
public RecyclerViewFragment getFragmentFollowing() {
return fragmentFollowing;
}
As shown here my xml.file doesn't have tabitems
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed" />
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
Also here in this image you can see it
I'm expecting a tabitem which I can give an #drawable shape, so it has a custom made layout, like a white background or something.
This is what my MainActivity shows at the moment
you can directly add tabs to tablayout using addTab method.
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.addTab(new TabLayout.newTab().setText("All"));
tabLayout.addTab(new TabLayout.newTab().setText("Following"));
so i have the classic viewpager in a tablayout that looks something like this:
My viewPagerAdapter class looks like this:
public class HomePagerAdapter extends FragmentPagerAdapter {
int mNumOfTabs;
public HomePagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
TabHomeFragment tab1 = new TabHomeFragment();
return tab1;
case 1:
TabShopFragment tab2 = new TabShopFragment();
return tab2;
case 2:
TabMeFragment tab3 = new TabMeFragment();
return tab3;
default:
return null;
}
}
#Override
public int getCount() {
return mNumOfTabs;
}
}
So now on one of the tabs i need to add a fragment on top of it. so its a search fragment really. i do a search in the tab bar and i want the back end search results to appear in only ONE of the tabs (the first one). but i want the search results displayed in a fragment called searchFragmentResults and i want it to be laid out on top of the first tab. Then when user hits the back button it will just go back to the original content in the first tab. Is this possible with view pager ?
so visually when i hit the search icon on the first tabs tabBar it should bring up a searchView and when i search for the users query it should bring up a another fragment with the results but only in the tab that it started from. Here is an example:
i cant call fragtransaction.replace(somecontentview, somefragment); (or its add method) because i did not add them to a contentview. i let the viewpager do it for me. So how is this achieved ?
i figured out how to do this. The tab should be a fragment who's purpose is to only contain other fragments. the idea is based off of this SO. But i had a need to do it for a viewPager. Lets go through the steps. first the viewpager adapter:
public class HomePagerAdapter extends FragmentStatePagerAdapter {
//integer to count number of tabs
int tabCount;
private Fragment mCurrentFragment;
private String[] tabTitles = new String[]{"tab0", "tab1", "tab2", "tab3"};
//Constructor to the class
public HomePagerAdapter(FragmentManager fm, int tabCount) {
super(fm);
this.tabCount = tabCount;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
TabHomeContainerFragment tab1 = new Tab0ContainerFragment();
return tab1;
case 1:
TabShopFragment tab2 = new Tab1ContainerFragment();
return tab2;
case 2:
TabMeFragment tab3 = new Tab2ContainerFragment();
return tab3;
case 3:
TabBagFragment tab4 = new Tab3ContainerFragment();
return tab4;
default:
return null;
}
}
//Overriden method getCount to get the number of tabs
#Override
public int getCount() {
return tabCount;
}
public Fragment getCurrentFragment() {
return mCurrentFragment;
}
//* this is key to get the current tab to pop the fragments afterwards**/
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (getCurrentFragment() != object) {
mCurrentFragment = ((Fragment) object);
}
super.setPrimaryItem(container, position, object);
}
#Override
public CharSequence getPageTitle(int position) {
return tabTitles[position];
}
Lets go into a container to see how it would look:
add this to your xml for all the containers (along with anything else you want visually but they ALL must have the same id of container_framelayout:
<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">
<FrameLayout android:id="#+id/container_framelayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android" />
</android.support.design.widget.CoordinatorLayout>
you dont have to put it in a coordinatorLayout i just find it fixes some bugs and works well.
In your fragments base class i copied almost the same code from the SO i mentioned above but slight modification if you want to add tag or not:
public void replaceFragment(Fragment fragment, boolean addToBackStack,String tag) {
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.replace(R.id.container_framelayout, fragment,tag);
transaction.commit();
getChildFragmentManager().executePendingTransactions();
}
public boolean popFragment() {
Log.e("test", "pop fragment: " + getChildFragmentManager().getBackStackEntryCount());
boolean isPop = false;
if (getChildFragmentManager().getBackStackEntryCount() > 0) {
isPop = true;
getChildFragmentManager().popBackStack();
}
return isPop;
}
Now lets look at the activities layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#color/white"/>
<android.support.design.widget.TabLayout
android:id="#+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill"
app:tabTextColor="#color/black"
app:tabSelectedTextColor="#android:color/darker_gray"
/>
Now i'll show you how to set up the tablayout in the activity hosting the tablayout:
its standard:
public class HomePageActivity implements TabLayout.OnTabSelectedListener {
private final int NUM_OF_TABS = 4;
#BindView(R.id.pager)
public ViewPager viewPager;
public HomePagerAdapter adapter;
#BindView(R.id.tabLayout)
TabLayout tabLayout;
#NonNull
#Override
public HomePagePresenter createPresenter() {
return new HomePagePresenter();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_homepage);
ButterKnife.bind(this);
initView();
}
private void initView() {
for (int i = 0; i < NUM_OF_TABS; i++)
tabLayout.addTab(tabLayout.newTab());
adapter = new HomePagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
//Adding adapter to pager
viewPager.setAdapter(adapter);
tabLayout.addOnTabSelectedListener(this);
tabLayout.setupWithViewPager(viewPager);
// configure tab icons
int[] imageTabResId = {
R.drawable.welcome1,
R.drawable.welcome2,
R.drawable.welcome3,
R.drawable.welcome1};
for (int i = 0; i < imageTabResId.length; i++) {
tabLayout.getTabAt(i).setIcon(imageTabResId[i]);
}
}
/** this is key. we get the current fragment showing and pop it **/
#Override
public void onBackPressed() {
boolean isPopFragment = false;
isPopFragment = ((BaseFragment) adapter.getCurrentFragment()).popFragment();
if (!isPopFragment) {
finish();
}
}
#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) {
}
}
I guess the major part is onBackPress getting the current fragment.
I have bottom navigation bar on click of item in navigation bar i am replacing fragments. I have 3 fragments A,B,C so on click of b item B fragment is loaded and in B i am calling 3-4 APIs. So now if i go to C and then again come to B a new instance of B Fragment is created and again those APIs are called how can i save the fragment instance state and not call APIs again while changing fragments. This is my code.
mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
Fragment currentLoaded = fgMan.findFragmentById(R.id.container_body);
switch (id) {
case R.id.nearby_fragment:
if (!(currentLoaded instanceof SpotFeedMapFragment)) {
removeScroll();
mNearByFragment = fgMan.findFragmentByTag(NEARBY_FRAGMENT_TAG) != null ? fgMan.findFragmentByTag(NEARBY_FRAGMENT_TAG) : mNearByFragment;
fgMan.beginTransaction().setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out);
fgMan.beginTransaction().replace(R.id.container_body, mNearByFragment, NEARBY_FRAGMENT_TAG).commit();
fgMan.executePendingTransactions();
getSupportActionBar().setTitle(getString(R.string.nearby_fragment));
}
break;
case R.id.route_fragment:
if (!(currentLoaded instanceof BusLocationsFragment)) {
if (!inParent) {
mRl.removeView(fixLayout);
p.addRule(RelativeLayout.BELOW, toolbar.getId());
scrollView.setLayoutParams(p);
scrollView.addView(fixLayout);
mRl.addView(scrollView);
inParent = true;
}
//mFragment = new BusLocationsFragment();
mBusLocFragment = fgMan.findFragmentByTag(BUS_LOC_FRAGMENT_TAG) != null ? fgMan.findFragmentByTag(BUS_LOC_FRAGMENT_TAG) : mBusLocFragment;
fgMan.beginTransaction().setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out);
fgMan.beginTransaction().replace(R.id.container_body, mBusLocFragment, BUS_LOC_FRAGMENT_TAG).commit();
fgMan.executePendingTransactions();
getSupportActionBar().setTitle(getString(R.string.app_name));
}
break;
case R.id.newsfeed_activity:
if (!(currentLoaded instanceof NewsFeedActivity)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
removeScroll();
}
mNewsFeedFragment = fgMan.findFragmentByTag(NEWSFEED_FRAGMENT_TAG) != null ? fgMan.findFragmentByTag(NEWSFEED_FRAGMENT_TAG) : mNewsFeedFragment;
fgMan.beginTransaction().setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out);
fgMan.beginTransaction().replace(R.id.container_body, mNewsFeedFragment, NEWSFEED_FRAGMENT_TAG).commit();
fgMan.executePendingTransactions();
getSupportActionBar().setTitle(getString(R.string.news));
}
break;
}
return true;
}
});
I have already initialized fragments member variables above in onCreateof MainActivity
You should use a FragmentPagerAdapter to initiate the fragments so when you want to switch in between them, the state of the fragments will be saved.
CutomViewPager viewPager = (CustomViewPager) findViewById(R.id.viewpager1);
ViewPagerAdapter adapter = new ViewPagerAdapter (MainActivity.this.getSupportFragmentManager());
adapter.addFragment(new SpotFeedMapFragment(), "title");
adapter.addFragment(new BusLocationsFragment(), "title");
adapter.addFragment(new NewsFeedActivity(), "title");
viewPager.setAdapter(adapter);
then in the bottom navigation selected you can set fragment by simple command
viewPager.setCurrentItem(n);
my viewpager class is as follows:
public class CustomViewPager extends ViewPager {
private boolean isPagingEnabled;
public CustomViewPager(Context context) {
super(context);
this.isPagingEnabled = true;
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
this.isPagingEnabled = true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return this.isPagingEnabled && super.onTouchEvent(event);
}
//for samsung phones to prevent tab switching keys to show on keyboard
#Override
public boolean executeKeyEvent(KeyEvent event) {
return isPagingEnabled && super.executeKeyEvent(event);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return this.isPagingEnabled && super.onInterceptTouchEvent(event);
}
public void setPagingEnabled(boolean enabled) {
this.isPagingEnabled = enabled;
}
}
in the xml instead of a empty layout for fragemnt u need:
<com.package.util.CustomViewPager
android:id="#+id/viewpager1"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Code for custom FragmentPagerAdapter:
private class ViewPagerAdapter extends FragmentPagerAdapter {
private final SparseArray<WeakReference<Fragment>> instantiatedFragments = new SparseArray<>();
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
final Fragment fragment = (Fragment) super.instantiateItem(container, position);
instantiatedFragments.put(position, new WeakReference<>(fragment));
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
instantiatedFragments.remove(position);
super.destroyItem(container, position, object);
}
#Nullable
Fragment getFragment(final int position) {
final WeakReference<Fragment> wr = instantiatedFragments.get(position);
if (wr != null) {
return wr.get();
} else {
return null;
}
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
To restore/retain a fragment's state you should use ViewPager2 as it is the updated version of ViewPager.
You will get the code on my GitHub repository with three menu items in the Bottom Navigation Bar with more functionality. I am also providing a simple description here with two menu items in the Bottom Navigation Bar.
Step by step guide (to restore/retain an EditText's state as an example):
Step 1:
Add dependencies in your build.gradle (app module) file:
dependencies {
def nav_version = "2.3.0"
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
implementation 'androidx.viewpager2:viewpager2:1.0.0'
}
Step 2:
Add menu_bottom_navigation.xml to res/menu: (You may also add icons to menu items)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/menu_first"
android:checked="true"
android:title="First"
app:showAsAction="always" />
<item
android:id="#+id/menu_second"
android:checked="false"
android:title="Second"
app:showAsAction="always" />
</menu>
Step 3:
Add activity_main.xml to res/layout: (adding menu to BottomNavigationView and placing ViewPager2)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:id="#+id/activityRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
android:orientation="vertical"
android:animateLayoutChanges="true"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewpager2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/bottom_navigation"
android:layout_alignParentTop="true"
android:layout_weight="1"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
android:fitsSystemWindows="true"
app:itemIconSize="20dp"
android:background="#A8DD44"
app:menu="#menu/menu_bottom_navigation" />
</LinearLayout>
Step 4:
Add fragment_first.xml to res/layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="20dp"
tools:context="com.example.rough.Fragment.FirstFragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="First Fragment"
android:layout_centerInParent="true"
android:textSize="30sp" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Write something & it will stay"
android:ems="13"/>
</LinearLayout>
Step 5:
Add fragment_second.xml to res/layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:orientation="vertical"
tools:context="com.example.rough.Fragment.SecondFragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Second Fragment"
android:layout_centerInParent="true"
android:textSize="30sp" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Write something & it will stay"
android:ems="13"/>
</LinearLayout>
Step 6:
ViewPagerAdapter.java:
public class ViewPagerAdapter extends FragmentStateAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
public ViewPagerAdapter(#NonNull FragmentManager fragmentManager, Lifecycle b ) {
super(fragmentManager,b);
}
public void addFragment(Fragment fragment) {
mFragmentList.add(fragment);
}
#NonNull
#Override
public Fragment createFragment(int position) {
return mFragmentList.get(position);
}
#Override
public int getItemCount() {
return mFragmentList.size();
}
}
Step 7:
FirstFragment.java:
public class FirstFragment extends Fragment {
public FirstFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false);
}
}
Step 8:
SecondFragment.java:
public class SecondFragment extends Fragment {
public SecondFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false);
}
}
Step 9:
MainActivity.java:
public class MainActivity extends AppCompatActivity {
BottomNavigationView bottomNavigationView;
private ViewPager2 viewPager2;
FirstFragment firstFragment;
SecondFragment secondFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager2 = findViewById(R.id.viewpager2);
bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_first:
viewPager2.setCurrentItem(0,false);
break;
case R.id.menu_second:
viewPager2.setCurrentItem(1,false);
break;
}
return false;
}
});
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
switch (position) {
case 0:
bottomNavigationView.getMenu().findItem(R.id.menu_first).setChecked(true);
break;
case 1:
bottomNavigationView.getMenu().findItem(R.id.menu_second).setChecked(true);
break;
}
}
});
setupViewPager(viewPager2);
}
private void setupViewPager(ViewPager2 viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager(), getLifecycle());
firstFragment =new FirstFragment();
secondFragment =new SecondFragment();
adapter.addFragment(firstFragment);
adapter.addFragment(secondFragment);
viewPager.setAdapter(adapter);
}
}
I used bottom navigation bar and I did it by customizing viewpager and I disable the swipe navigation. Each time user clicks bottom item, set relevant fragment in viewpager. Viewpager control state of fragment, so no need control state.
Custom ViewPager
public class BottomNavigationViewPager extends ViewPager {
private boolean enabled;
public BottomNavigationViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
this.enabled = false;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onTouchEvent(event);
}
return false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onInterceptTouchEvent(event);
}
return false;
}
/**
* Enable or disable the swipe navigation
* #param enabled
*/
public void setPagingEnabled(boolean enabled) {
this.enabled = enabled;
}
}
If you still want to control state of fragment, you can see my answer in this link
How to save fragment state in android?
Solution Using Navigation Component
Use these versions, Multi stack support is only available from these versions
versions.fragment = "1.4.0-alpha01"
versions.navigation = "2.4.0-alpha01"
First, you need to create nav graphs for each fragment in the bottom navigation
Filename: first.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="#+id/first"
app:startDestination="#id/homeFragment"
tools:ignore="UnusedNavigation">
<fragment
android:id="#+id/homeFragment"
android:label="#string/fragment_A_title"
android:name="com.app.company.HomeFragment"
>
</fragment>
</navigation>
create nav graph for all tabs and setup bottom navigation menu using these ids
<item android:title="#string/title_one"
android:id="#+id/first"
android:icon="#drawable/ic_icon_24"/>
<item android:title="#string/title_two"
android:id="#+id/second"
android:icon="#drawable/ic_icon_24"/>
.
.
.
include these nav graphs inside the main nav graph and use this main graph in FragmentContainerView
<include app:graph="#navigation/first"/>
<include app:graph="#navigation/second"/>
<include app:graph="#navigation/third"/>
Setup the bottom navigation
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment_host) as NavHostFragment
navController = navHostFragment.navController
val controller = binding.bottonNavigation.setupWithNavController(navController)
appBarConfig = AppBarConfiguration(
setOf(
R.id.homeFragment,
R.id.secondFragment,
R.id.thirdFragment
)
)
setupActionBarWithNavController(navController, appBarConfig)
Refer to the following link for more info
Navigation multiple back stacks
The simplest solution for that is to override "OnCreate()" method in your Fragment B and call you APIs in "OnCreate()" method instead of "OnCreateView()".
Hope it will work for you!
You shouldn't have to bring ViewPager in to accomplish this. I have one activity with a BottomNavigationView and a fragment container. When a user clicks a navigation tab, I do the following:
final Fragment existing = getSupportFragmentManager().findFragmentByTag(fragmentName);
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
getSupportFragmentManager().getFragments().forEach(transaction::hide);
if (existing != null) {
transaction.show(existing);
} else {
transaction.add(R.id.new_main_fragment_frame, instantiate, fragmentName);
}
transaction.commit();
I'm handling my own back press at the activity level by overriding onBackPressed and this is working well. The fragments lazy load in and then are re-used if the user is switching between tabs.
First: Sorry for the wall of text/code, but I think most of it is needed to understand the problem.
I am creating an app using Fragments in a ViewPager and a TabHost. The ViewPager has a custom FragmentPagerAdapter that will feed the various pages in the ViewPager. I have ran into a problem where the custom FragmentPagerAdapter starts adding the various Fragments to the BackStack, but it fails at a point where it checks that the container ID (in this case the ID of the ViewPager) against the ID of the Fragments to add. These are different, thus the program fails. I am fairly new to using Fragments, so I am not sure if my code follows best practice. What could be the error in the following?
The Activity, which inflates the main XML layout.
public class MyActivity extends FragmentActivity implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener{
private MyViewPager mViewPager;
private FragmentTabHost mFragmentTabHost;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
setupViewPager();
setupFragmentTabHost();
}
private void setupViewPager()
{
mViewPager = (MyViewPager) findViewById(R.id.my_pager);
}
private void setupFragmentTabHost()
{
mFragmentTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
mFragmentTabHost.setOnTabChangedListener(this);
mFragmentTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);
mFragmentTabHost.addTab(mFragmentTabHost.newTabSpec("tab1").setIndicator("Tab 1", null), TabFragment.class, null);
mFragmentTabHost.addTab(mFragmentTabHost.newTabSpec("tab2").setIndicator("Tab 2", null), TabFragment.class, null);
mFragmentTabHost.addTab(mFragmentTabHost.newTabSpec("tab3").setIndicator("Tab 3", null), TabFragment.class, null);
}
#Override
protected void onDestroy()
{
}
public MyViewPager getMyPager()
{
return mViewPager;
}
#Override
public void onTabChanged(String tabId) {
int position = mFragmentTabHost.getCurrentTab();
mViewPager.setCurrentItem(position);
}
#Override
public void onPageSelected(int position)
{
mFragmentTabHost.setCurrentTab(position);
}
#Override
public void onPageScrollStateChanged(int arg0) {}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
}
The main XML file, my_activity.xml, containing the ViewPager, the TabHost and the Fragments for the ViewPager:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.app.FragmentTabHost
android:id="#android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0" />
<com.mycompany.myapp.gui.mypager.MyViewPager
android:id="#+id/my_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
<TabWidget
android:id="#android:id/tabs"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.app.FragmentTabHost>
<fragment
android:name="com.mycompany.myapp.gui.mypager.FilteredRecipesFragment"
android:id="#+id/filtered_recipes_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
<fragment
android:name="com.mycompany.myapp.gui.mypager.SelectedRecipesFragment"
android:id="#+id/selected_recipes_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
<fragment
android:name="com.mycompany.myapp.gui.mypager.ShoppingListFragment"
android:id="#+id/shopping_list_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
</LinearLayout>
Note that onCreateView is called for each custom Fragment, they are inflated and the root View of each of them are returned. Here is one example, for FilteredRecipesFragment. The other custom Fragments are similar.
public class FilteredRecipesFragment extends Fragment {
private FilteredRecipesListFragment mFilteredRecipesListFragment;
private Button showRecipeFilterButton;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.filtered_recipes_fragment, container, false);
mFilteredRecipesListFragment = (FilteredRecipesListFragment)
getFragmentManager().findFragmentById(R.id.filtered_recipes_list_fragment);
showRecipeFilterButton = (Button) rootView.findViewById(R.id.show_recipe_filter_dialog_button);
showRecipeFilterButton.setOnClickListener(new RecipeFilterButtonListener());
return rootView;
}
}
Finally, the custom ViewPager and its custom FragmentPagerAdapter, where the program fails.
public class MyViewPager extends ViewPager{
private MyActivity mMyActivity;
private MyPagerAdapter mMyPagerAdapter;
public MyViewPager(Context context, AttributeSet attrs)
{
super(context, attrs);
mMyActivity = (MyActivity) context;
mMyPagerAdapter = new MyPagerAdapter(mMyActivity.getSupportFragmentManager(), mMyActivity);
this.setAdapter(mMyPagerAdapter);
this.setOnPageChangeListener(mMyActivity);
this.setCurrentItem(PagerConstants.PAGE_SHOPPING_LIST); // Page 0
}
}
MyPagerAdapter.java:
public class MyPagerAdapter extends FragmentPagerAdapter{
private MyActivity mMyActivity;
public MyPagerAdapter(FragmentManager fragmentManager, MyActivity myActivity)
{
super(fragmentManager);
mMyActivity = myActivity;
}
#Override
public Fragment getItem(int position)
{
switch (position) {
case PagerConstants.PAGE_FILTER_RECIPES: // 0
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.filtered_recipes_fragment);
case PagerConstants.PAGE_SELECTED_RECIPES: // 1
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.selected_recipes_fragment);
case PagerConstants.PAGE_SHOPPING_LIST: // 2
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.shopping_list_fragment);
default:
return null;
}
}
#Override
public int getCount()
{
return PagerConstants.NUMBER_OF_PAGES; // 3
}
#Override
public CharSequence getPageTitle(int position)
{
return PagerConstants.PAGE_TITLES(position);
}
}
Everything seems to be working ok, but after every custom Fragment is inflated, the custom ViewPager starts to add them too. Here is the stack output from Eclipse:
06-07 15:37:49.815: E/AndroidRuntime(793): java.lang.IllegalStateException: Can't change container ID of fragment FilteredRecipesFragment{4605b0e0 #0 id=0x7f09003f android:switcher:2131296318:0}: was 2131296319 now 2131296318
BackStackRecord.doAddOp(int, Fragment, String, int) line: 407
BackStackRecord.add(int, Fragment, String) line: 389
MyPagerAdapter(FragmentPagerAdapter).instantiateItem(ViewGroup, int) line: 99
MyViewPager(ViewPager).addNewItem(int, int) line: 832
MyViewPager(ViewPager).populate(int) line: 982
MyViewPager(ViewPager).populate() line: 914
MyViewPager(ViewPager).onMeasure(int, int) line: 1436
MyViewPager(View).measure(int, int) line: 8171
LinearLayout(ViewGroup).measureChildWithMargins(View, int, int, int, int) line: 3132
... More calls <snipped>
In BackStackRecord.doAppOp it fails because the container ID (i.e. the ID of the MyViewPager is different from the Fragment ID. Here is the code for that method:
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
fragment.mFragmentManager = mManager;
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("Can't change tag of fragment "
+ fragment + ": was " + fragment.mTag
+ " now " + tag);
}
fragment.mTag = tag;
}
if (containerViewId != 0) {
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
// IT FAILS HERE!
throw new IllegalStateException("Can't change container ID of fragment "
+ fragment + ": was " + fragment.mFragmentId
+ " now " + containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
}
I know that the container ID is the ID of the custom ViewPager because it is its ID that is passed through in the instantiateItem(ViewGroup, int) call. In my case, the ID of the MyViewPager instance is 2131296319 and the ID of the Fragment is 2131296318, hence it fails.
Where am I taking the wrong turn here? What am I misunderstanding in the whole ViewPager/FragmentPagerAdapter/Fragment concept?
The problem is here:
#Override
public Fragment getItem(int position)
{
switch (position) {
case PagerConstants.PAGE_FILTER_RECIPES: // 0
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.filtered_recipes_fragment);
case PagerConstants.PAGE_SELECTED_RECIPES: // 1
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.selected_recipes_fragment);
case PagerConstants.PAGE_SHOPPING_LIST: // 2
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.shopping_list_fragment);
default:
return null;
}
you have to return a new instance of your Fragments and not an existing one, which has already a parent and, hence, a container assinged. Remove the Fragments you declared in your layout, and change your getItem like
#Override
public Fragment getItem(int position)
{
switch (position) {
case PagerConstants.PAGE_FILTER_RECIPES: // 0
return new FilteredRecipesFragment();
case PagerConstants.PAGE_SELECTED_RECIPES: // 1
return new SelectedRecipesFragment();
case PagerConstants.PAGE_SHOPPING_LIST: // 2
return new ShoppingListFragment()
default:
return null;
}
One of the possible reason for the issue is when you're trying to add the same fragment twice. Illustrated below
public void populateFragments() {
Fragment fragment = new Fragment();
//fragment is added for the first time
addFragment(fragment);
// fragment is added for the second time
// This call will be responsible for the issue. The
addFragment(fragment);
}
public void addFragment(Fragment fragment) {
FrameLayout frameLayout = AppView.createFrameLayout(context);
view.addView(frameLayout);
getSupportFragmentManager().beginTransaction().add(frameLayout.getId(), fragment).commit();
}
I'm new to Android and therefore faced such problem.
How can I change layout from:
To:
XML fragment_main:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
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"
tools:context="com.example.snbgearassistant.MainActivity$PlaceholderFragment" >
<TextView
android:id="#+id/section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
So I need these tabs having grid layout with different content.
You must use a GridView inside the ViewPager. So, in your MainActivity, you would have this layout.
Create the activity_main.xml layout
This is the main layout. Everything will live inside of it, including your fragments and tabs.
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.myapp.gridview.MainActivity" />
Create your MainActivity.java class
public class MainActivity extends ActionBarActivity implements ActionBar.TabListener
{
SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {#link ViewPager} that will host the section contents.
*/
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Here we load the xml layout we created above
setContentView(R.layout.activity_main);
// Set up the action bar.
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
// When swiping between different sections, select the corresponding
// tab. We can also use ActionBar.Tab#select() to do this if we have
// a reference to the Tab.
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener()
{
#Override
public void onPageSelected(int position)
{
actionBar.setSelectedNavigationItem(position);
}
});
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++)
{
// Create a tab with text corresponding to the page title defined by
// the adapter. Also specify this Activity object, which implements
// the TabListener interface, as the callback (listener) for when
// this tab is selected.
actionBar.addTab(
actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction)
{
// When the given tab is selected, switch to the corresponding page in
// the ViewPager.
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction)
{
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction)
{
}
/**
* A {#link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter
{
public SectionsPagerAdapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int position)
{
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return new PlaceholderFragment();
}
#Override
public int getCount()
{
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position)
{
Locale l = Locale.getDefault();
switch (position)
{
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
}
return null;
}
}
}
Don't forget to create your strings for these R.string.title_section1, ... strings on your code, or you will have an error.
Now we must create a layout for the fragment (the page that will be displayed inside the tab), and it must contain a GridView.
Create a fragment_main.xml layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<GridView
android:id="#+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp"
android:stretchMode="columnWidth"
android:numColumns="2" />
</FrameLayout>
Now let's define the fragment class that will take care of inflating this layout and handling the views.
Create a fragment to inflate the GridView layout: PlaceHolderFragment.java
/**
* A placeholder fragment containing a the gridview
*/
public class PlaceholderFragment extends Fragment
{
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
// Here we inflate the layout we created above
GridView gridView = (GridView) rootView.findViewById(R.id.gridview);
gridView.setAdapter(new MyAdapter(MainActivity.this.getApplicationContext()));
return rootView;
}
}
Now we must create an adapter class to handle each item of the GridView, this way you can manage the behavior of each one.
Create the Adapter to support the GridView items: MyAdapter.java
As you can see here, we are adding some items to the GridView by adding them to an ArrayList of the type Item defined in the end of the adapter class.
private class MyAdapter extends BaseAdapter
{
private List<Item> items = new ArrayList<Item>();
private LayoutInflater inflater;
public MyAdapter(Context context)
{
inflater = LayoutInflater.from(context);
items.add(new Item("Image 1", Color.GREEN));
items.add(new Item("Image 2", Color.RED));
items.add(new Item("Image 3", Color.BLUE));
items.add(new Item("Image 4", Color.GRAY));
items.add(new Item("Image 5", Color.YELLOW));
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int i)
{
return items.get(i);
}
#Override
public long getItemId(int i)
{
return items.get(i).colorId;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup)
{
View v = view;
ImageView picture;
TextView name;
if(v == null)
{
v = inflater.inflate(R.layout.gridview_item, viewGroup, false);
v.setTag(R.id.picture, v.findViewById(R.id.picture));
v.setTag(R.id.text, v.findViewById(R.id.text));
}
picture = (ImageView)v.getTag(R.id.picture);
name = (TextView)v.getTag(R.id.text);
Item item = (Item)getItem(i);
picture.setBackgroundColor(item.colorId);
name.setText(item.name);
return v;
}
private class Item
{
final String name;
final int colorId;
Item(String name, int drawableId)
{
this.name = name;
this.colorId = drawableId;
}
}
}
Now to make the GridView items keep with the correct width, aligned side by side, we use a custom class to define the measured dimension.
Why this needs to be done? According to #kcoppock's answer:
Basically, in Android's ImageView class, there's no way to simply specify "hey, keep a square aspect ratio (width / height) for this view" unless you hard code width and height. You could do some manual adjustment of LayoutParams in the adapter's getView, but frankly, it's much simpler to let ImageView handle all the measurements, and just override the results to say "Whatever the width ends up being, make my height stay the same". You never have to think about it, it's always square, and it just works as expected. Basically this is the easiest way to keep the view square.
Create a class SquareImageView.java
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
public class SquareImageView extends ImageView
{
public SquareImageView(Context context)
{
super(context);
}
public SquareImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public SquareImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()); //Snap to width
}
}
Now we must define the XML layout for the GridView items.
Create a XML layout gridview_item.xml
As you can see, here we add two items to the layout. One is a element of the type SquareImageView (the class we created above) and the TextView which is a label for each image.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.myapp.gridview.SquareImageView
android:id="#+id/picture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
/>
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp"
android:paddingBottom="15dp"
android:layout_gravity="bottom"
android:textColor="#android:color/white"
android:background="#55000000"
/>
</FrameLayout>
And here it is, I tested the code and this is the final result. Of course you would change those colors for your images, but this is the approach you should follow.
Note: To set images instead of colors to the GridView item, in your getView() method of the MyAdapter class use setImageResource(int) instead of setBackgroundColor(int).
Answer Given By rogcg is very good and nice. But the Images for each fragment is same. I like to add some codes in the mainactivity which has viewpager.I think we can use fragment instead of activity, Here is the code.The same code as the Main Activity given by rogcg. Add these codes too.
In Layout for mainfragment add ActionBarlayout,toolbar and slidingtablayout
In Mainfragment,add
private List<Fragment> mFragments = new Vector<Fragment>();
in oncreate(), create three fragments ,
mFragments.add(new HomeFragment());
mFragments.add(new Title1());
mFragments.add(new Title2());
mFragments.add(new Title3());
in onCreateView(),add
mSectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager());
mViewPager.setAdapter(mSectionsPagerAdapter);
tabLayout = (SlidingTabLayout) v.findViewById(R.id.tabanim_tabs);
tabLayout.setViewPager(mViewPager);
in SectionPageAdapter class,add
#Override
public Fragment getItem(int position) {
return mFragments.get(position+1);
}
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position)
{
case 0:
return getString(R.string.English).toUpperCase(l);
case 1:
return getString(R.string.Tamil).toUpperCase(l);
case 2:
return getString(R.string.Hindi).toUpperCase(l);
}
return null;
}
Now add any view in Title1() fragment as you usage and add any things in it I think this message was useful. please vote for me. Thank you.