I'm currently working on an app that will hopefully act like an "eBook" player. Having read through a number of tutorials related to fragments, I decided to take the 'Fragment/ Viewpager' approach.
Moving into a little more detail, I decided to follow a great tutorial on nested fragments by Linden Darlinghttps://plus.google.com/100467024733771542884/posts/24HcFW5hEiV.
I created a 'webview' fragment for each individual book page, with it's own layout. So if my book consists of 6 pages, then I'll have BookPage1Fragment, BookPage2Fragment, BookPage3Fragment, BookPage4Fragment, BookPage5Fragment, BookPage6Fragment etc. I the managed to get this working within the viewpager, so all swiping through fragments etc working fine. The problem is, only one fragment at a time is visible on screen. I'd like to have two fragments (multi-pane), when on running the app on large devices and landscape orientation and single-pane (one fragment at a time) when running on smaller devices or portrait view. I want to display a two pane layout with one fragment left and one fragment righT.
See code below:
public class ParentFragment extends Fragment {
public static final String TAG = ParentFragment.class.getSimpleName();
public static ParentFragment newInstance() {
return new ParentFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_parent, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ViewPager mViewPager = (ViewPager) view.findViewById(R.id.viewPager);
mViewPager.setAdapter(new MyAdapter(getChildFragmentManager()));
}
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 6;
}
/*
#Override
public Fragment getItem(int position) {
Bundle args = new Bundle();
args.putInt(TextViewFragment.POSITION_KEY, position);
return TextViewFragment.newInstance(args);
}
*/
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new BookPage1Fragment();
case 1:
return new BookPage2Fragment();
case 2:
return new BookPage3Fragment();
case 3:
return new BookPage4Fragment();
case 4:
return new BookPage5Fragment();
case 5:
return new BookPage6Fragment();
default:
return getItem(0);
}
}
#Override
public CharSequence getPageTitle(int position) {
return "Fragment # " + position;
}
}
}
Basically two fragments appearing on landscape mode and one fragment appearing at a time when in portrait mode, just as the example describes on developer.blogspot http://android-developers.blogspot.in/2011/02/android-30-fragments-api.html, but embedded within a viewpager.
Here is my main.layout.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout> xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
Here is what my webview fragment
public class WebViewFragment extends Fragment {
#SuppressLint("SetJavaScriptEnabled")
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View mainView = (View) inflater.inflate(R.layout.fragment_web, container, false);
WebView myWebView = (WebView) mainView.findViewById(R.id.webview);
myWebView.loadUrl("file:///android_asset/Ebook-001.html");
myWebView.setWebViewClient(new MyWebViewClient());
myWebView.getSettings().setBuiltInZoomControls(true);
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.getSettings().setSupportZoom(true);
myWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
myWebView.getSettings().setAllowFileAccess(true);
myWebView.getSettings().setDomStorageEnabled(true);
myWebView.getSettings().setUseWideViewPort(true);
myWebView.getSettings().setLoadWithOverviewMode(true);
return mainView;
}
}
here is my webview fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<WebView
android:id="#+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
So in conclusion I'd like to:
1: Display two fragments within viewpager in landscape mode, and single in portrait mode.
2: Use one activity regardless of the device size, and decide at runtime whether to combine fragments in the layout (to create a multiple-pane design) or swap fragments (to create a single-pane design)
Hope this all makes sense! Thank you in advance for any suggestions...
You have to create a layout file for the landscape mode.You have to create a file in res folder with layout-land name.In that folder create a layout with same name which is placed in layout file.Android automatically access it when screen is change to landscape mode.
Related
I have 3 fragments (totally different from each other) and one activity (MainActivity). What I would like to do is to be able to swipe between them (with finger, not with buttons) with a transition like a TabLayout.
According to what I saw, I can do it using ViewPager. But the problem is that ViewPager uses TabLayout.
There is a way to swipe betweens fragments, using Viewpager, without TabLayout ?
This code will help you
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Set up the child Fragments you would like to display. I made 3 child Fragments, calling them ChildFragment1, ChildFragment2, and ChildFragment3. Remember to have them extend support.v4.app.fragment. Now make layouts for all three of the Fragments. I call them child_fragment_1_layout, child_fragment_2_layout, and child_fragment_3_layout.
public class ChildFragment1 extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(R.layout.child_fragment_1_layout, container, false);
Button buttonInFragment1 = rootView.findViewById(R.id.button_1);
buttonInFragment1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getContext(), "button in fragment 1", Toast.LENGTH_SHORT).show();
}
});
return rootView;
}
}
Make an adapter for the ViewPager. You will have to extend either theFragmentPagerAdapter or the FragmentStatePagerAdapter. For this tutorial we will use the FragmentPagerAdapter . The difference between the two can be found here. After extending the FragmentPagerAdapter, you will need to call super(FragmentManager) in your constructor and implement the methods getItem(position) and getCount(). The getItem(position)method is used to return the fragment at the corresponding position, ordered from left to right. So ChildFragment1 would be at position 0, ChildFragment2 would be at position 1, and ChildFragment3 would be at position 2. The getCount() method is to count how many Fragments there are to display, and in this case, there are 3.
public class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position)
{
case 0:
return new ChildFragment1(); //ChildFragment1 at position 0
case 1:
return new ChildFragment2(); //ChildFragment2 at position 1
case 2:
return new ChildFragment3(); //ChildFragment3 at position 2
}
return null; //does not happen
}
#Override
public int getCount() {
return 3; //three fragments
}
}
Now find your ViewPager in MainActivity and call the setAdapter() method and pass in your custom adapter. If you are doing this in another Fragment (Nested Fragments), you will have to pass in getChildFragmentManager() in the argument of your adapter instead. Now your ViewPager is all set
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
}
}
First off: I am aware there are a number of similar questions, however, none of the suggestions worked.
I have a ViewPager that I would like to use with fragments. The ViewPager works, and I can see in the log that I can scroll left and right. I can see the animation when I try to scroll left and there's no previous item (or right when there's no following item). However, the screen remains blank. The fragment itself is functional, however, I've tried it in a different context.
I am using the androidx namespaced packages.
Things I've tried (<--> means I've tried both):
FragmentPagerAdapter <--> FragmentStatePagerAdapter
getFragmentManager() <--> getChildFragmentManager()as the FragmentManager for the adapter
setSaveFromParentEnabled to false.
setOffscreenPageLimit to the number of fragments.
Relevant code (I have taken out sections of code that are irrelevant to this question):
The Fragment that contains the ViewPager:
public class AddListingMediaFragment extends Fragment {
private ViewPager viewPagerTop;
private AddMediaAdapter addMediaAdapter;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_add_listing_media, container, false);
setupViewPagerTop(view);
return view;
}
private void setupViewPagerTop(View view) {
viewPagerTop = view.findViewById(R.id.view_pager_top);
addMediaAdapter = new AddMediaAdapter(getChildFragmentManager());
viewPagerTop.setAdapter(addMediaAdapter);
viewPagerTop.setSaveFromParentEnabled(false);
viewPagerTop.setOffscreenPageLimit(5);
}
}
The Adapter (please do note, as mentioned, that I've tried both FragmentPagerAdapter and FragmentStatePagerAdapter:
public class AddMediaAdapter extends FragmentPagerAdapter {
private static final String TAG = AddMediaAdapter.class.getSimpleName();
public AddMediaAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 5;
}
#Override
public Fragment getItem(int position) {
Log.i(TAG, "getItem: " + String.valueOf(position));
return new AddListingMediaCameraFragment();
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
The layout file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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">
<androidx.viewpager.widget.ViewPager
android:id="#+id/view_pager_top"
android:layout_width="300dp"
android:layout_height="300dp"
/>
<include
layout="#layout/bottom_sheet_add_listing_media_gallery"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
You do not need to override isViewFromObject in your adapter, FragmentPagerAdapter has already taken care of it. ViewPager calls this method to decide how to render the pages.
Your view == object will return false because the object is always a Fragment not a View. So just drop the override, and it will fix the issue.
I am new to this Fragment theory and I can't seem to understand where we get the R.id.container So I want to start a fragment once a button is clicked. Here is my method
My main Method, I have not added any code to start my Fragment class.
callCenter.setOnClickListener(view -> openCallCenter());
//on clicked open call center which should start a fragment
private void openCallCenter() {
}
My Fragment Class: empty for now;
public class CallCenterFragment extends Fragment {
public static final String TAG = CallCenterFragment.class.getSimpleName();
public CallCenterFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_call_center, container, false);
}
}
My Fragment Layout Empty for now:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:id="#+id/fragment_container"
tools:context=".fragment.CallCenterFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/hello_blank_fragment" />
</FrameLayout>
SO: my biggest question is on my mainActivity class what code should I add to start this fragment and can someone explain to me since I have seen several example where do we get transaction.replace(R.id.container)
Thanks in advance.
So I will refer you here for more details
On the container Purpose of Container in Fragments for Android
And if you want to display your fragment on the Main activity as your question says try this code.
Add the code on the method.
CallCenterFragment fragment = new
CallCenterFragment();
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.container, fragment);
trans.commit();
Note. On your main xml you need to add the container id eg.
<FrameLayout
android:id="#+id/container"
Add width and height />
You will display the text given on your fragment which is string hello blank fragment.
Set the ViewPager in activity xml file where you want show fragment. Create a sub class PageAdapter for super class for super class FragmentPagerAdapter. Complete abstract methods in Subclass.
public class PageAdapter extends FragmentPagerAdapter {
public PageAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getI tem(int position) {
Fragment fragment = null;
switch (position){
case 0:{
fragment = new ContactFragment();
break;
}
case 1:{
fragment = new RecentFragment();
break;
}
}
return fragment;
}
#Override
public int getCount() {
// 2 is no of fragments
return 2;
}
}
Then After
//--write this code in base activity of fragment
contactview_f = (ViewPager) findViewById(R.id.fragment_layout);
pageAdapter = new PageAdapter(getSupportFragmentManager());
contactview_f.setAdapter(pageAdapter);
I basically have everything set up already and I don't why it's not working. The only problem I have is that the Vertical won't work when I set it up together. Look at the code down below.
My Main Acitvity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//HorizontalViewPagerAdapter
View background = findViewById(R.id.am_background_view);
ViewPager viewPager = findViewById(R.id.am_view_pager);
HorizontalViewPagerAdapter adapter = new HorizontalViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(1);
}
My Controller Fragment
public class ControllerFragment extends Fragment {
public static ControllerFragment create() {
return new ControllerFragment();
}
VerticalViewPager verticalViewPager;
VerticalViewPagerAdapter verticalPageAdapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.vertical, null);
verticalViewPager = view.findViewById(R.id.am_scrollView);
verticalPageAdapter = new VerticalViewPagerAdapter(getChildFragmentManager());
verticalViewPager.setAdapter(verticalPageAdapter);
return inflater.inflate(R.layout.vertical, null);
}
public class VerticalViewPagerAdapter extends FragmentPagerAdapter {
public VerticalViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return SettingsFragment.create();
case 1:
return EmptyFragment.create();
case 2:
return ExtrasFragment.create();
}
return null;
}
#Override
public int getCount() {
return 3;
}
}
}
This is my HorizontalAdapter
public class HorizontalViewPagerAdapter extends FragmentPagerAdapter {
public HorizontalViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return ChatFragment.create();
case 1:
return ControllerFragment.create();
case 2:
return StoryFragment.create();
}
return null;
}
#Override
public int getCount() {
return 3;
}
Its like the whole code is completely ignoring the VerticalView Pager because it doesnt show at all in my app and its completely the same as a normal ViewPager
Having understood what you want to achieve, here's how you'd achieve it!
There are 6 fragments in total.
Home
Left
Right
Top
Botton
Controller
Update
The previous method overrides and prevents horizontal gestures from being captured. I've searched a bit and found an alternative.
First of all, you need a different vertical pager library. I tried the one here and it works perfectly.
So what you wanna do is, get that java file on your project and change your controller's XML like this
<?xml version="1.0" encoding="utf-8"?>
<c.kristofer.jax2.VerticalPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/am_scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/top"
class="c.kristofer.jax2.Fragments.SettingsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment
android:id="#+id/home"
class="c.kristofer.jax2.Fragments.HomeFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment
android:id="#+id/bottom"
class="c.kristofer.jax2.Fragments.SettingsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</c.kristofer.jax2.VerticalPager>
This ViewPager does not take an adapter, rather it takes <fragment> tags for its child views. The Controller's java code will also change a bit
VerticalPager verticalViewPager = view.findViewById(R.id.am_scrollView);
verticalViewPager.snapToPage(1);
The result can be seen here. You can play around with the code and see what suits you best.
Previous Method
The activity will have one of the ViewPagers, either the Horizontal one or the Vertical one. The middle fragment of that ViewPager will be the Controller. Controller will be responsible for the second ViewPager. The middle fragment of this ViewPager will be Home.
This way, you will have 2 degrees of freedom from the Activity, and 2 degrees of freedom from the Controller.
In my application i have a tabhost with a tabwidget that have several Tabs.
Now i need a tab that show me inside the tabcontent a schedule grid that allow swipe to right and left to move through the months. But i need that the tab stay fixes and only the schedule swipes.
The navigation type (Fixed tabs + Swipe) allow me that? From what I understand this navigation allow the swipe but the tab don't stay the same.
What i need is possible?
Thanks for your help and attention.
I would say possible, stay with your codes on tabhost and tabwidget.
So I would assume one of those tabs are calling an Activity which probably named Schedule.class, by default tabhost does not allow any swiping to change tab feature, that is fine.
So in your Schedule Activity, you would be using ViewPager, I learnt how to use it from this article: http://thepseudocoder.wordpress.com/2011/10/05/android-page-swiping-using-viewpager/
Which is pretty easy to be understood. You may try to use it, hope I answered your question
Update: Here's a sample
Schedule.class
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.schedule);
ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
List<Fragment> fragments = new ArrayList<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment2.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment3.class.getName()));
MyFragmentAdapter miscFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(), fragments);
viewPager.setAdapter(miscFragmentAdapter);
}
MyFragmentAdapter.class
public class MyFragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public MiscFragmentAdapter(FragmentManager fragmentManager, List<Fragment> fragments) {
super(fragmentManager);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
}
schedule.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Fragment1.class or Fragment2.class or Fragment3.class
public class Fragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (container == null) {
// We have different layouts, and in one of them this
// fragment's containing frame doesn't exist. The fragment
// may still be created from its saved state, but there is
// no reason to try to create its view hierarchy because it
// won't be displayed. Note this is not needed -- we could
// just run the code below, where we would create and return
// the view hierarchy; it would just never be used.
return null;
}
return (LinearLayout) inflater.inflate(R.layout.fragment1, container, false);
}
}
fragment1 is a simple layout with whatever you want inside it.