ViewPager2 does not support direct child views
I'm trying to transition between fragments using the following code but I get the above error when using viewpager2.
Call in fragment 1 to transition to fragment 2:
getFragmentManager().beginTransaction().replace(R.id.viewPager2, new q2_fragment()).addToBackStack(null).commit();
Viewpager2 XML in Main Layout:
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewPager2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="10"
android:orientation="horizontal"
android:scaleType="fitXY" />
Instantiation in Main:
final ViewPager2 viewPager2 = findViewById(R.id.viewPager2);
viewPager2.setAdapter(new QuestionsActivity.ScreenSlidePagerAdapter(this));
viewPager2.setUserInputEnabled(false);
How do I avoid this error with viewpager2?
I got the same error when I put the TabLayout before ViewPager 's closing tag
That is:
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.viewpager2.widget.ViewPager2>
Which is not Allowed!
Just Removing the ending tag and separating TabLayout will work
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.google.android.material.tabs.TabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Would be happy to help, please elaborate on your requirement. What exactly you want to do.
If you want to go to fragment 2 from fragment 1, at a particular point then you should use interface between fragment and activity, to tell the activity to move the viewpager to the item which has fragment 2.
Interface Pattern for Fragment to Activity
Interface
public interface FragmentCallback{
public void goTo(int pos);
}
Activity
public class MyActivity extends AppCompatActivity implements MyStringListener{
#Override
public void goTo(int pos){
yourviewpagerAdapter.setCurrentItem(pos);
}
}
public class Fragment1 {
private FragmentCallback callBack;
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
callBack = (FragmentCallback) context;
} catch (ClassCastException castException) {
/** The activity does not implement the listener. */
}
}
public void someEvent() {
if(callBack!=null) {
callBack.goTo(1);
}
}
}
As the statement says
ViewPager2 does not support direct child views
So never try to add the fragment to viewPager2 directly
i.e the following lines of code will not work with viewPager2.
getFragmentManager().beginTransaction().replace(R.id.viewPager2, new q2_fragment()).addToBackStack(null).commit();
The exception is produced if you try to add fragment directly to viewPager2 with the help of fragmentManager.
So simply remove the above lines of code to get rid of this exception.
Lets Say From Fragment_1 to Fragment_2:
In side the button click in Fragment_1
Bundle result = new Bundle(); result.putString("bundleKey", "result"); getParentFragmentManager().setFragmentResult("requestKey", result);
In side the Fragment_2 onCreate(Bundle savedInstanceState) method
getParentFragmentManager().setFragmentResultListener("requestKey", this, new FragmentResultListener() {
#Override public void onFragmentResult(#NonNull String requestKey, #NonNull Bundle bundle)
{
supported String result = bundle.getString("bundleKey"); System.out.println("----------------------------"+result);
}
});
https://developer.android.com/guide/fragments/communicate#pass-between-fragments
Use mPager.setNestedScrollingEnabled(true);
Related
I'm making a recycler view with a linear layout horizontal, it looks like this:
HorizontalRecyclerView
As you can see under it there are two text view that I want to fill with the item info when the user press an item. I've do two Adapters to show a menu before, this way when you make a long click the item change its layout, but it doesn´t change the information below. The way of manage a selected item usually is using the adapter like this:
if (holder instanceof AdaptadorProyectosViewHolder) {
((AdaptadorProyectosViewHolder) holder).tv_nombreProyecto.setText(nombreProyecto.toString());
((AdaptadorProyectosViewHolder) holder).tv_nombreClienteProyecto.setText("UN CLIENTE");
//When it's a long click the menu is shown in the place of the item
((AdaptadorProyectosViewHolder) holder).itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
mostrarMenu(position); //showMenu(position)
return true;
}
});
What this do is that the layout of an item change (displaying more information, options,etc), but i want the info to be below the Recycler View, not inside it into an item. There's any way to do it?
Here's the xml of the fragment that contains the Recycler View:
<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"
tools:context=".Vistas.Proyectos">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_proyectos"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="#+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
</LinearLayout>
Relying on little details from your question, here's a quick solution.
Add an onClickListener to your adapter:
public interface OnItemClickListener {
void onItemClick(int position);
}
Declare the listener's variable and add it to the adapter's constructor:
private final OnItemClickListener listener;
public MyAdapter ( List<Item> items , Context context , OnItemClickListener listener ) {
mItems = items;
mContext = context;
this.listener = listener;
}
Inside ViewHolder, add OnClickListener:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
private TextView txt;
public ViewHolder(#NonNull View itemView) {
super(itemView);
itemView.setOnClickListener(this);
txt = itemView.findViewById(R.id.tvItemTitle);
}
#Override
public void onClick(View v)
{
listener.onItemClick(getAdapterPosition());
}
}
After adding these to the adapter, now move to the activity or fragment hosting the recyclerview and implement the onClickListener you just created. Here, I used a fragment:
public class LibraryFragment extends MainActivityFragment implements MyAdapter.OnItemClickListener
Add the following to the newly implemented method:
#Override
public void onItemClick(int position)
{
mtext.setText(mItems.get(position).getName());
}
All done.
Notice that mItems is your list of items, and mtext is the TextView hosting the results!
Also, note the getName() method is a method I created. Replace mItems, mtext and getName with your own codes!
Extra note: you need to add the listener to the adapter in the fragment:
adapter = new MyAdapter( mItems , getActivity() , this );
Bouncing off this question:
I have made a custom webview called FlingView that incorporates gesture events. The strategy in the linked question above works fine for me; my activity layout xml is fine:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
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/drawer_layout_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:openDrawer="start">
<android.support.design.widget.CoordinatorLayout
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/coordinator_layout_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.brianclements.android.WebViewActivity">
<net.brianclements.android.FlingView
android:id="#+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:keepScreenOn="true" />
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView
android:id="#+id/nav_view_webview_left"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_webview_drawer_left" />
<android.support.design.widget.NavigationView
android:id="#+id/nav_view_webview_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_webview_drawer_right" />
</android.support.v4.widget.DrawerLayout>
onCreate() does this:
setContentView(R.layout.activity_web_view);
and I've used this method to successfully load it:
mWebview = (FlingView) findViewById(R.id.webview);
Now, the problem is when I try to override functions inside of it:
mWebview = new FlingView(this, null) {
#Override
public void onLeftFling() {
Log.d(TAG, ACTIVITY_TAG + "onFling left: ");
someDynamicObject.thatCantBeUsedStatically();
}
#Override
public void onRightFling() {
Log.d(TAG, ACTIVITY_TAG + "onFling right: ");
anotherDynamicObject.thatCantBeUsedStatically();
}
};
So how do I now insert mWebview back into the layout? The first casting method made it so simple. But from my research, I believe it now involves some combination of inflators, findViewById(), addView(), removeView() but I can't seem to figure it out. Any ideas here?
Views defined in your layout are instantiated in the setContentView() call, and you can't override class methods after instantiation. Swapping out the layout-defined View for your anonymous instance with the necessary overrides would work, but it's essentially discarding the instance created from the layout, and isn't really the cleanest way to this.
I would suggest another approach. Create an interface in your FlingView class that can be used to communicate those actions back to your Activity; very similar to an OnClickListener.
For example:
public class FlingView extends WebView {
public interface FlingListener {
void onLeftFling();
void onRightFling();
}
private FlingListener mListener;
public void setFlingListener(FlingListener listener) {
mListener = listener;
}
public void onLeftFling() {
if (mListener != null) {
mListener.onLeftFling();
}
}
public void onRightFling() {
if (mListener != null) {
mListener.onRightFling();
}
}
...
}
In your Activity, after finding your FlingView instance from the inflated layout, simply set an instance of the listener on it.
setContentView(R.layout.activity_web_view);
...
mWebview = (FlingView) findViewById(R.id.webview);
mWebview.setFlingListener(new FlingView.FlingListener() {
#Override
public void onLeftFling() {
Log.d(TAG, ACTIVITY_TAG + "onFling left: ");
someDynamicObject.thatCantBeUsedStatically();
}
#Override
public void onRightFling() {
Log.d(TAG, ACTIVITY_TAG + "onFling right: ");
anotherDynamicObject.thatCantBeUsedStatically();
}
}
);
If you want to replace the current FlingView with the one you just created you need to do the following:
CoordinatorLayout cl = (CoordinatorLayout) findViewById(R.id.coordinator_layout_webview);
cl.removeViewAt(0); // removing previous flingview
cl.addView(mWebView, 0); // adding the new inflated one
I'm new to android development and having some issues with replacing a fragment with another fragment.
In my MainFragment.java, the first thing I do in onCreate is check for internet connectivity, and if there is not, I replace that fragment with the InternetCheckFragment. However, in InternetCheckFragment.java, when I try to inflate my check_internet.xml, the app closes, but there is no error.
activity_main.xml:
<fragment 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/main_browse_fragment"
android:name="com.ui.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ui.MainActivity"
tools:deviceIds="tv"
tools:ignore="MergeRootFrame" />
MainActivity.java:
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
MainFragment.java:
public class MainFragment extends VerticalGridFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (isConnectedToInternet() == true)
{
// DO stuff
}
else
{
showInternetError();
}
}
public void showInternetError()
{
getFragmentManager().beginTransaction().replace(R.id.show_internet_error , new InternetCheckFragment())
.commit();
}
....
}
InternetCheckFragment.java:
public class InternetCheckFragment extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
// Defines the xml file for the fragment
return inflater.inflate(R.layout.check_internet, parent, false);
}
}
check_internet.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/show_internet_error"
android:background="#color/black_transparent">
<Button android:id="#+id/btn_retry"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="RETRY"
android:layout_centerInParent="true"/>
</RelativeLayout>
However, I'm getting the error No view found for id.
I'm sure I'm not setting something correctly? Can someone point me in the right direction?
Thanks!
You are inflating a Fragment in a Fragment using the Fragment's FragmentManager.
This FragmentManager cannot handle this.
Try using the SupportFragmentManager with AppCompatActivity.getSupportFragmentManager() and the support Fragments from android.support.v4.app.Fragment
Ugh!..it was a stupid mistake, I replaced R.id.show_internet_error within getFragmentManager().beginTransaction().replace(R.id.show_internet_error , new InternetCheckFragment()).commit(); with R.id.main_browse_fragment and now it works.
for this project, I have tabs that have to display something under them and i'm using view pager but for some reason, the fragment layout isn't showing up on screen. I get a blank screen but there is no error or anything. I have been googling and searching for hours now and even started a new project just to make sure it was not a build error but still no luck in finding a solution to my problem.
I have done examples like this many times and used the exact same steps but for some reason, it just isn't working this time. Any help would be greatly appreciated
Here is my mainActivity class
public class MainActivity extends ActionBarActivity {
Toolbar toolbar;
ViewPager myViewPager;
SlidingTabLayout mySlidingTabs;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
myViewPager = (ViewPager) findViewById(R.id.viewPager);
myViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), this));
mySlidingTabs = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
mySlidingTabs.setViewPager(myViewPager);
}
}
Here is my custom adapter class
public class MyPagerAdapter extends FragmentPagerAdapter {
private Context context;
private String[] tabNames = {"Beverages", "Deli & Bakery", "Fresh Produce", "Health & Beauty", "Meat & Seafood", "Natural Foods", "Pet Care"};
public MyPagerAdapter(FragmentManager fm, Context context) {
super(fm);
this.context = context;
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new Beverages();
}
else if (position == 1) {
return new DeliBakery();
}
return null;
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
return tabNames[position];
}
}
Here is my main activty.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar_layout"
android:layout_height="wrap_content"
android:layout_width="match_parent" />
<!-- The Sliding tab -->
<bello.myapplication.SlidingTabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/sliding_tabs"/>
<!-- The pager that allows us to swipe between fragments -->
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
android:background="#android:color/white" />
</LinearLayout>
and here is one of my tab's xml file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab fragment 1"
android:textSize="50dp"
android:padding="5dp" />
</RelativeLayout>
Here is one of my fragment classes
public class Beverages extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.beverages_tab, container, false);
return view;
}
}
When something strange like this happens, try to use Log.i("title", "message") to track if every class/methods are being called.
In your case, it seens that your viewpager isn't in the screen.
If I understood your code, your <include> tag is including the second xml code you posted right?
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab fragment 1"
android:textSize="50dp"
android:padding="5dp" />
</RelativeLayout>
It looks like your layout_height="match_parent" is filling all the space on your screen, giving no space to your ViewPager being inflated by the system, try to change it to layout_height="wrap_content and you should be done.
If not, try to change the background of your ViewPager to check if it is on the screen and is not being hided by something else.
Figured out the problem. The viewpager background color and the background color of the tabs where the same so it made it look like there was a black activity. So for anyone else out there with the same problem, try changing either the text color of your tabs.xml or change the background of your viewpager
I have implemented a ViewPager which connects multiple Fragments together to give off that 'scrolling view' experience as you might already know. Anyhow I have a button on my first fragment that needs to scroll the user to the second fragment. How do I get this done folks? Here's my code:
ViewPager XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
</LinearLayout>
Fragment 1:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3498db" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_gravity="left"
android:layout_marginLeft="15dp"
android:layout_marginTop="25dp"
android:text="Step 1"
android:textColor="#ffffff"
android:textSize="50dp" />
<Button
android:id="#+id/step1Btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:layout_marginBottom="5sp"
android:background="#drawable/buttonshape"
android:shadowColor="#A8A8A8"
android:shadowDx="0"
android:shadowDy="0"
android:shadowRadius="5"
android:text="Next Step"
android:textColor="#FFFFFF"
android:textSize="30sp" />
</LinearLayout>
</RelativeLayout>
In order to make this post short, let's say the second fragment has the same XML.
So we have TWO fragments. Here's the fragment java class:
public class RegisterPageOne extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.registration_page1, container, false);
Button nextStep = (Button) v.findViewById(R.id.step1Btn);
nextStep.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
//Where I want to call the SECOND FRAG
}
});
return v;
}
public static RegisterPageOne newInstance(String text) {
RegisterPageOne f = new RegisterPageOne();
Bundle b = new Bundle();
b.putString("msg", text);
f.setArguments(b);
return f;
}
}
help me out please :)
You can use the setCurrentItem(int item,boolean smoothScroll) method of your viewPager object to achieve this. Here is the documentation.
Saw your followup question about how to refer to the ViewPager, from your Fragment. It's pretty convoluted, for a relative newbie like me, but, I think I can get all of it... I'll give you what I did in my code. Set up a listener in the ViewPager:
public class PageViewActivity extends FragmentActivity
implements PageDisplayPortFrag.OnLinkExpectedListener {
...
}
This gets referenced in your Fragment:
private OnLinkExpectedListener mListenerLink;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListenerLink = (OnLinkExpectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnLinkExpectedListener");
}
}
#Override
public void onDetach() {
mListenerLink = null;
super.onDetach();
}
// THIS IS THE METHOD CALLED BY THE BUTTON CLICK !!!
public void linkExpected(String ticker) {
if (mListenerLink != null) {
mListenerLink.onLinkActivity(ticker);
}
}
public interface OnLinkExpectedListener {
public void onLinkActivity(String ticker);
}
And, then, a method gets called in the ViewPager:
#Override
public void onLinkActivity(String ticker) {
// receive ticker from port click; user wants to view page in webview, for this security
//Toast.makeText(this, "Ticker item clicked: " + ticker, Toast.LENGTH_SHORT).show();
// NEHARA, YOU MAY OR MAY NOT NEED TO DO STUFF HERE
// THIS IS WHAT I NEEDED TO DO; LEAVING IT FOR THE
// getFragmentTag() EXAMPLE -- WHICH CAME FROM Stack Overflow POSTERS
// get all-important fragment tag
String frag_Tag = getFragmentTag(pager.getId(),1);
// use tag to get the webview fragment
PageXYZWebviewFrag xyzWeb = (PageXYZWebviewFrag)
getSupportFragmentManager().findFragmentByTag(frag_Tag);
// send request to the webview fragment
xyzWeb.loadPageForSelectedSecurity(ticker);
// SET THE DESIRED FRAGMENT, assumes I know the fragment number
// from when I set up the ViewPager
// switch to the webview fragment
pager.setCurrentItem(1);
}
// fragment tag getter -- very important, yet has been elusive
private String getFragmentTag(int viewPagerId, int fragmentPosition) {
return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}