I need to design the following screen, and I need your advice:
Explanation:
The title is static/fixed and I don't need to do anything with it.
Yellow: this is the interesting part, I need to design a ViewPager like screen that has the capability to scroll left/right for Max 4 screens.
Red: In every screen I need to add a Table/Grid that can be scrollable as well if it's not fits screen size.
Green: The page switching can be done using the green buttons in the bottom of the screen or by scrolling the ViewPager.
The Question Is: Can this behavior be achieve using a ViewPager or should I use Fragments? If Fragment is the way to go, then how would I implement the page switching using the sliding gesture? if it's a ViewPager then how to add the inside scrolling and how to control it using the buttons at the bottom?
Any help would be appreciated, Thanks.
I think this should be tackled with a Non Swipeable ViewPager. There is no way the view pager and the underlying Fragments should respond to the swiping gesture. The methods to override to disable swiping within the ViewPager are:
onTouchEvent() - returns false.
onInterceptTouchEvent()- returns false.
Refer to this SO question for more information on how to achieve this.
Next up you want to be using Fragments within each of your pager holders. So we're building the following layout:
Within the parent activity a FragmentPagerAdapter is instantiated and your tabs added with a tag:
Activity changes
#Override
protected void onCreate(final Bundle saveInstanceState) {
final FragmentPagerAdapter myTabAdapter = new MyFragmentPagerAdapter(
<Your ViewPager View>, <Your activity context, this>);
myTabAdapter.addTab(getActionBar().newTab(), "YOUR TAG", "Your Title");
// etc...
}
So this gives us the frame of the diagram above. A hosting activity, containing a ViewPager and the underlying tabs. Next up is getting the Fragments (containing your tables) into each of the respective tabs. This is handled by the FragmentPagerAdapter implementation:
Fragment adapter (inner class to activity):
private class MyFragmentPagerAdapter extends FragmentPagerAdapter implements
ActionBar.TabListener, ViewPager.OnPageChangeListener {
/**
* Constructs a pager adapter to back a {#link ViewPager}.
*
* #param pager
* The {#link ViewPager} widget.
* #param activityContext
* The context the widget is being added under.
*/
public SpotMenuFragmentPagerAdapter(final ViewPager pager,
final Context activityContext) {
super(getFragmentManager());
pager.setAdapter(this);
this.context = activityContext;
}
/**
* Adds a tab to the hosting activity action bar.
*
* #param newTab
* The tab to add.
* #param tag
* The tab tag for id purposes.
* #param label
* The label of the tab displayed to the user.
*/
public void addTab(final ActionBar.Tab newTab, final String tag,
final String label) {
newTab.setTag(tag);
newTab.setText(label);
newTab.setTabListener(this);
getSupportActionBar().addTab(newTab);
}
/**
* This is where you do the work of building the correct fragment based
* on the tab currently selected.
*
* #see FragmentPagerAdapter#getItem(int)
*/
#Override
public Fragment getItem(final int position) {
final Tab tab = getActionBar().getTabAt(position);
if ("MY TAG".equals(tab.getTag().toString()) {
// instantiate the fragment (table) for "MY TAG"
} else {
// instantiate something else...
}
}
/**
* One fragment per tab.
*
* #see android.support.v4.view.PagerAdapter#getCount()
*/
#Override
public int getCount() {
return getSupportActionBar().getTabCount();
}
/**
* #see ViewPager.OnPageChangeListener#onPageScrollStateChanged(int)
*/
#Override
public void onPageScrollStateChanged(final int arg0) {
// No-op.
}
/**
* #see ViewPager.OnPageChangeListener#onPageScrolled(int, float, int)
*/
#Override
public void onPageScrolled(final int arg0, final float arg1,
final int arg2) {
// No-op.
}
/**
* #see ViewPager.OnPageChangeListener#onPageSelected(int)
*/
#Override
public void onPageSelected(final int position) {
getSupportActionBar().setSelectedNavigationItem(position);
}
/**
* #see TabListener#onTabSelected(app.ActionBar.Tab,
* app.FragmentTransaction)
*/
#Override
public void onTabSelected(final Tab tab, final FragmentTransaction ft) {
viewPager.setCurrentItem(tab.getPosition());
}
/**
* #see TabListener#onTabUnselected(ActionBar.Tab,
* app.FragmentTransaction)
*/
#Override
public void onTabUnselected(final Tab tab, final FragmentTransaction ft) {
// No-op.
}
/**
* #see TabListener#onTabReselected(ActionBar.Tab,app.FragmentTransaction)
*/
#Override
public void onTabReselected(final Tab tab, final FragmentTransaction ft) {
// No-op.
}
}
So hopefully by this point we have an activity hosting a 'non-swipeable' view pager and a mechanism for switching tabs in the form of the tab bar underneath the title (or alongside depending on the screen size). From this point I am sure you could customise to replace the tab bar with some navigational arrows.
Note: A lot of that was written from memory but hopefully I've conveyed the gist of where I would go with this.
Update
In response to the updated question: you can set the tab to be any old view. Set the TabSpec accordingly. Apologies I haven't used this myself.
You have to use HorizontalScrollView which it'll contain a LinearLayout and the Linear is the one that will contain your individual elements
The XML would see something like this
<HorizontalScrollView
android:id="#+id/horizontal_scroll_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="horizontal"
android:scrollbarStyle="outsideInset">
<LinearLayout android:id="#+id/content_scroll"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical" />
</HorizontalScrollView>
Related
I am trying to add dot indicators to my view pager, I tried different types and none work, for some reason it doesn't appear on the fragment. It doesn't crash... Just doesn't appear.
I am trying to use this library
View pager XML file:
<LinearLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.pixelcan.inkpageindicator.InkPageIndicator
android:id="#+id/indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dotDiameter="8dp"
app:dotGap="8dp"
app:animationDuration="320"
app:pageIndicatorColor="#a3a0a0"
app:currentPageIndicatorColor="#000000" />
</android.support.v4.view.ViewPager>
</LinearLayout>
the fragment activity file:
public class HighScoreScreenSlide extends FragmentActivity {
/**
* The number of pages (wizard steps) to show in this demo.
*/
private static final int NUM_PAGES = 3;
/**
* The pager widget, which handles animation and allows swiping horizontally to access previous
* and next wizard steps.
*/
private ViewPager mPager;
private int countDownInd;
Bundle bundle;
/**
* The pager adapter, which provides the pages to the view pager widget.
*/
private PagerAdapter mPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.high_score_view_pager);
countDownInd = getIntent().getIntExtra("gameType", 0);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.setCurrentItem(countDownInd);
InkPageIndicator inkPageIndicator = (InkPageIndicator) findViewById(R.id.indicator);
inkPageIndicator.setViewPager(mPager);
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
/**
* A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
* sequence.
*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter( FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) { //use position
HighScoreFragment fragment = new HighScoreFragment();
bundle=new Bundle();
bundle.putInt("gameType",position);
fragment.setArguments(bundle);
return fragment;
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
Also this library didn't work.
If there is more code needed to understand I'll be happy to provide it
Thanks!
Try to put InkPageIndicator view not inside ViewPager but on the same level with it, like shown in the sample. In this particular case, InkPageIndicator and ViewPager should be the children of LinearLayout. If you want indicator dots to be on top of the view pager, consider replacing LinearLayout with FrameLayout.
I have been trying to implement a ViewPager with different fragments.
And the problem is when i run the app, in the ViewPager, out of all the pages, only one page is visible and that page only gets changed when I slide over to the other pages in the ViewPager.
Take a look at my code,(although I checked it many times referring it with online resources).
This is what each of my fragments look like:
public class fragment1 extends Fragment {
/* Variable to store reference to the ACtivity */
Activity mCurrentActivity;
/* Variable storing reference to the ArrayList */
private ArrayList<Word> mDefaultWords;
/**
* THe empty public Constructor
*/
public fragment1(){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
/** Getting reference to the Activity */
mCurrentActivity = getActivity();
// Populating the ArrayList here
// And later in the onActivityCreated callback I set an adapter on the ArrayList
return inflater.inflate(R.layout.activity_others, container, false);
}
#Override
public void onActivityCreated(Bundle savedStateInstance){
super .onActivityCreated(savedStateInstance);
/**
* Creating {#link ArrayAdapter} to link the {#link String}
* from {#link ArrayList} {#param
*/
MyAdapter adaptItems = new MyAdapter(mCurrentActivity, mDefaultWords);
// Getting the id of the ListView in numberActivity.xml
ListView myList = (ListView) mCurrentActivity.findViewById(R.id.theList);
//Chaning background color
myList.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.holo_purple));
// Setting the adapter with the {#link ListView}
myList.setAdapter(adaptItems);
}
}
}
My Activity setting the adapter class extending FragmentPagerAdapter as a private inner class and setting the adapter on the ViewPager.
public class Main2Activity extends AppCompatActivity {
private ViewPager mViewPager;
private FragmentPagerAdapter mFragmentStatePagerAdapter;
private FragmentManager mFragmentManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
mFragmentManager = getSupportFragmentManager();
mViewPager = (ViewPager) findViewById(R.id.theViewPager);
mFragmentStatePagerAdapter = new MyFragmentStatePagerAdapter(mFragmentManager);
/* Setting the apdapter on the pager */
mViewPager.setAdapter(mFragmentStatePagerAdapter);
}
public class MyFragmentStatePagerAdapter extends FragmentPagerAdapter {
public MyFragmentStatePagerAdapter(FragmentManager fragmentManager){
super(fragmentManager);
}
#Override
public int getCount(){
return 4;
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new fragment1();
} else if (position == 1){
return new fragment2();
} else if (position == 2) {
return new fragment3();
} else {
return new fragment4();
}
}
}
}
And here is the layout with the ViewPager
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/theViewPager"
android:visibility="visible" />
As I said, when I run the app only one page gets displayed, other pages are present in the ViewPager but they are blank and displays the default background color,
And the one page that is displayed is the one that gets changed when I swipe left or right in the ViewPager.
So what's the issue?
dont downvote the question, its a genuine problem.
So, I worked my way around, let me say how.
What happened is, I was working on a cloned project that had a old gradle version and sdktools version was also not updated and was quite old.
and the min API targetted was API 15
And I was testing my application on API 21.
So, what I did is I used a different layouts for each of my fragments.
That is for each fragment I created its own XML layout.
And that worked perfectly.
Odd problem, so I updated the gradle and sdktools, to avoid such weird problems.
I am reusing the same fragment to display an image with an animation. The problem which I facing is the fragment view is loaded with the animation before it is visible to the user while swiping. Below is my PagerAdapter
public class PollsAdapter extends FragmentStatePagerAdapter {
/** Constructor of the class */
int clickPosition = 0;
ArrayList<Polls> pollsList = new ArrayList<Polls>();
public PollsAdapter(FragmentManager fm,ArrayList<Polls> pollsList) {
super(fm);
this.pollsList=pollsList;
}
/** This method will be invoked when a page is requested to create */
#Override
public Fragment getItem(int arg0) {
PollsFragment myFragment = new PollsFragment();
Bundle data = new Bundle();
data.putSerializable("poll", pollsList.get(arg0));
data.putInt("position", arg0);
myFragment.setArguments(data);
return myFragment;
}
/** Returns the number of pages */
#Override
public int getCount() {
return pollsList.size();
}
public int getItemPosition(Object item) {
return POSITION_NONE;
}
}
This is how I set my viewPager
pollsAdapter = new PollsAdapter(fm, pollsList);
viewPager = (ViewPager) _rootView.findViewById(R.id.pager);
viewPager.setAdapter(pollsAdapter);
I tried by using `viewPager.setOffscreenPageLimit(1); but this didn't work. I'm sure I miss something. Please help me to solve this problem.
This is --unfortunately-- not possible. To display the flip animation, ViewPager has to pre-load at least the fragments to the left/right of the currently displayed fragment.
Because of this, the minimum possible value for ViewPager.setOffscreenPageLimit() is 1 as at least the direct neighbours need to be available.
In your case, the problem is rather that your fragment seems to be starting an animation as soon at is created.
In that case, you will need to change the starting logic for your animation. Your fragment can get notified by the ViewPager as soon as it is scrolled to (see ViewPager.OnPageChangeListener) for the mechanism). Note that the OnPageListener is not called when your activity is resumed so you also need to handle this case..
How to determine when Fragment becomes visible in ViewPager has more information on that topic.
I have three pages in my viewpager, each page is a fragment, And I have some EditText's in each page, I third page I have a Button called SAVE, Now in this button click event I have to the values from all EditText's. I have tried many way, but none is worked, Always I am getting NullPinterException. Any help will be highly appreciable.
Thanks,
Guna.
I have a very similar set up in my current app. What I did was create a subclass of Fragment that has the method:
public abstract String[] getForm();
the getForm method essentially returns a String[] containing the string stored in each form. Each Fragment has to implement that correctly. Now once you have that, in your Activity that contains the ViewPager initialize a list of fragments that your activity's ViewPagerAdapter should use to display. That way, now when you are in the final fragment and this button is clicked (and your fragment that contains the button click successfully informs the activity that the button click event occurred), your activity will know to iterate through the whole list of fragments, calling the fragments respective getForm method implementation.
Note that this will only work if you are not using a ViewStatePagerAdapter. The reason for this is because the ViewStatePagerAdapter is not guaranteed to keep all of your fragments in memory.
Here is a code example (In the code example, I have my view pager stored in a fragment but this design would most definitely work if you were keeping your viewpager in an activity). The real work is being done in the submit method. That is where we are collecting the fields from the other fragment (Hence this method should be called in you OnButtonClickListener code):
public class CreateAccountFragment extends RestCallExecutingFragment implements ViewPager.OnPageChangeListener {
private OnAccountCreationListener onAccountCreationListener;
public static final int VARIOUS_FRAG_POS = 2;
public static final int ACCOUNT_INFO_FRAG_POS = 0;
private static final int ADDRESS_FRAG_POS = 1;
public static final int CREATE_ACCOUNT_ID = 0;
public CreateAccountFragment() {
// Required empty public constructor
}
ArrayList<FormFragment> fragmentsToDisplay;
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {#link android.support.v13.app.FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {#link android.support.v13.app.FragmentStatePagerAdapter}.
*/
SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {#link android.support.v4.view.ViewPager} that will host the section contents.
*/
ViewPager mViewPager;
#InjectView(R.id.rb_accountInfo)
RadioButton rb_accountInfo;
#InjectView(R.id.rb_address)
RadioButton rb_address;
#InjectView(R.id.rb_various)
RadioButton rb_various;
#InjectView(R.id.rg_createAccount)
RadioGroup rg_createAccount;
#InjectView(R.id.tv_pageTitle)
TextView tv_pageTitle;
List<RadioButton> radioButtons;
CreateAccountCommand createAccountCommand;
private static ArrayList<FormFragment> getCreateAccountFragments(){
ArrayList<FormFragment> list = new ArrayList<>();
list.add(AccountInfoFragment.newInstance());
list.add(AddressFragment.newInstance());
list.add(VariousFragment.newInstance());
return list;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.frag_create_account, container, false);
ButterKnife.inject(this, view);
fragmentsToDisplay = getCreateAccountFragments();
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager());
//todo make it easier to press the radio button
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) view.findViewById(R.id.pager);
mViewPager.setOnPageChangeListener(this);
mViewPager.setAdapter(mSectionsPagerAdapter);
return view;
}
public void submit() {
//todo move the create account button to this activity's view
AccountSubmissionRDTO createAccountSubmissionDTO;
try {
AccountInfoData accountInfoData = (AccountInfoData) fragmentsToDisplay.get(ACCOUNT_INFO_FRAG_POS).submitForm();
AddressData addressData = (AddressData) fragmentsToDisplay.get(ADDRESS_FRAG_POS).submitForm();
VariousData variousData = (VariousData) fragmentsToDisplay.get(VARIOUS_FRAG_POS).submitForm();
// createAccountSubmissionDTO = new CreateAccountSubmissionRDTO(CREATE_ACCOUNT_ID,0, -1, accountInfoData,addressData,variousData); //todo create actual server and local ids
}
/**
* A {#link android.support.v13.app.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) {
FormFragment selectedFragment = fragmentsToDisplay.get(position);
Assert.assertNotNull("the fragment selected should be within list", selectedFragment);
return selectedFragment;
}
#Override
public int getCount() {
return fragmentsToDisplay.size();
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
String pageTitle = fragmentsToDisplay.get(position).getPageTitle();
return pageTitle.toUpperCase(l);
}
}
}
if you've used the sherlock master detail flow, please help me.
I have added tabs and have removed the data inside the detail fragment/activity but when I try to inflate a button inside the tabs, it doesn't work.
Can you help me?
Here's the list activity that I've modified to display tabs when in the two-pane mode.
package com.example.sample;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockFragmentActivity;
/**
* An activity representing a list of Courses. This activity has different
* presentations for handset and tablet-size devices. On handsets, the activity
* presents a list of items, which when touched, lead to a
* {#link CourseDetailActivity} representing item details. On tablets, the
* activity presents the list of items and item details side-by-side using two
* vertical panes.
* <p>
* The activity makes heavy use of fragments. The list of items is a
* {#link CourseListFragment} and the item details (if present) is a
* {#link CourseDetailFragment}.
* <p>
* This activity also implements the required
* {#link CourseListFragment.Callbacks} interface to listen for item selections.
*/
public class CourseListActivity extends SherlockFragmentActivity implements
CourseListFragment.Callbacks {
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
private boolean once = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_course_list);
if (findViewById(R.id.course_detail_container) != null) {
// The detail container view will be present only in the
// large-screen layouts (res/values-large and
// res/values-sw600dp). If this view is present, then the
// activity should be in two-pane mode.
mTwoPane = true;
// In two-pane mode, list items should be given the
// 'activated' state when touched.
((CourseListFragment) getSupportFragmentManager().findFragmentById(
R.id.course_list)).setActivateOnItemClick(true);
}
// TODO: If exposing deep links into your app, handle intents here.
}
/**
* Callback method from {#link CourseListFragment.Callbacks} indicating that
* the item with the given ID was selected.
*/
#Override
public void onItemSelected(String id) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
CourseDetailFragment fragment = new CourseDetailFragment();
getSupportFragmentManager().beginTransaction()
.replace(R.id.course_detail_container, fragment).commit();
if (once) {
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// initiating both tabs and set text to it.
ActionBar.Tab assignTab = actionBar.newTab().setText("Assignments");
ActionBar.Tab schedTab = actionBar.newTab().setText("Schedule");
ActionBar.Tab contactTab = actionBar.newTab().setText("Contact");
// Create three fragments to display content
Fragment assignFragment = new Assignments();
Fragment schedFragment = new Schedule();
Fragment contactFragment = new Contact();
assignTab.setTabListener(new MyTabsListener(assignFragment));
schedTab.setTabListener(new MyTabsListener(schedFragment));
contactTab.setTabListener(new MyTabsListener(contactFragment));
actionBar.addTab(assignTab);
actionBar.addTab(schedTab);
actionBar.addTab(contactTab);
once = false;
}
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, CourseDetailActivity.class);
startActivity(detailIntent);
}
}
class MyTabsListener implements ActionBar.TabListener {
public Fragment fragment;
public MyTabsListener(Fragment fragment) {
this.fragment = fragment;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.replace(R.id.twopanecontainer, fragment);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
}
}
Here's the fragment course detail layout that holds a textview by default.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/twopanecontainer"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/course_detail"
style="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".CourseDetailFragment" />
</LinearLayout>
What should I modify so I can inflate a different view for each tab?
Thanks
Have a ViewPager in yout activity layout, then implement FragmentPagerAdapter