I'm trying to add a RecyclerView underneath a Fragment that I add programmatically, but it doesn't show up. I would like the RecyclerView to dynamically use the remaining space of the screen (since the Fragment above it can vary in size) so I put constraints at the edges and set the height to 0dp. The RecyclerView populates just fine, but it displays somewhere below the screen (I have to put the height of the RecyclerView to ~1300 dp to make it show up).
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary">
<ImageView
android:id="#+id/Toolbar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#color/colorPrimary"
android:contentDescription="#null"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<RelativeLayout
android:id="#+id/FragmentContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/RecyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/FragmentContainer"/>
</androidx.constraintlayout.widget.ConstraintLayout>
I then use this code to programmatically add the Fragment to my FragmentContainer:
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Remove current fragment(s)
for (fragment in supportFragmentManager.fragments) {
supportFragmentManager.beginTransaction().remove(fragment!!).commit()
}
val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
var fragment: Fragment
if (someBoolean)
fragment = FirstFragment()
else
fragment = SecondFragment()
// Add correct fragment to container
transaction.add(R.id.FragmentContainer, fragment)
transaction.commit()
}
I have tried putting the RecyclerView into various layouts, but the results weren't any better. Any ideas are appreciated!
Related
I have a layout that contains a view pager and a fab.
The view pager has fragments as pages.
What I want is to access the FAB from inside a fragment.
fragment_user_anime_list(parent)
<layout 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">
<data>
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.destructo.sushi.ui.user.animeList.MyAnimeListFragment">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
style="#style/ToolBarWithNavigation"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="#style/PopupMenu"
app:title="#string/my_anime_list"
app:titleTextAppearance="#style/TextAppearance.Sushi.H1" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/my_anime_list_tablayout"
style="#style/TabLayoutStyle"
android:layout_width="fill_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:tabIndicator="#drawable/custom_tab_layout_indicator"
app:tabMode="scrollable" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/my_anime_list_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<ProgressBar
android:id="#+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_gravity="center"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/random_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="#dimen/margin_normal"
android:layout_marginBottom="#dimen/margin_normal"
android:layout_gravity="bottom|right"
android:src="#drawable/ic_question_line"
app:layout_behavior="#string/hide_bottom_view_on_scroll_behavior"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
fragment_my_anime_list(child viewpager page)
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/userAnimeRecycler"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="#+id/user_anime_list_pagination_progressbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="#+id/user_anime_list_progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/userAnimeRecycler" />
<ProgressBar
android:id="#+id/user_anime_list_pagination_progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
in the child fragment
private lateinit var binding: FragmentUserAnimeListBinding
private lateinit var randomAnimeFab: FloatingActionButton
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentUserAnimeListBinding
.inflate(inflater, container, false).apply {
lifecycleOwner = viewLifecycleOwner
}
randomAnimeFab = binding.root.random_fab
return binding.root
}
This throws Null pointer exception. view cannot be null
How do I get a reference to the fab using viewBinding?
I found a solution without using view binding. Don't know if it's the best one.
You get the parentfragment and reference it's views directly.
val fab = requireParentFragment().random_fab
and then you can check if it's null and do whatever you want
fab?.let{
//Do something
}
If anyone knows how to do it using viewBinding let me know.
How do I get a reference to the fab using viewBinding?
Now you want to access the FAB from one of ViewPager pages/fragments through data binding.
Normally views should be accessed within its lifecycle owner (i.e. its activity/fragment), so views of the parent fragment (which hosts the FAB & the ViewPager), should be touched by this fragment, not by a page fragment.
You did a milestone by using requireParentFragment().
val fab = requireParentFragment().random_fab
But instead of referencing the FAB directly from the page, you can create a method in the parent fragment instead and access this method from the page. And do the changes on FAB on this method.
And through this method, you can pass data between a page and the parentFragment without having to touch views of a fragment from another fragment.
So you could have a method in the parent fragment:
fun changeMyFab() {
binding.random_fab ...
}
And call requireParentFragment().changeMyFab() from the page, and you can pass some data as method parameters to change the FAB behavior.
my activity_main.xml has two framelayouts that acts as containers for two fragments .When i run each fragment individually ,they run good .when i try to initialize and display both fragments ,one of the fragments called recents fragment is taking the full screen view .
when i run this fragment individually ,it takes full screen view instead of running in its own container.
and when i try to initialize my second fragment after initializing recents fragment i get an errorNo view found for id 0x7f070044 (com.example.diary:id/allnotesfragment) for fragment BlankFragment{7eb6bf4 (bc0fee9d-4a46-4e2b-ab31-d060c0ec14ae) id=0x7f070044}
activity code where i initialize both the fragments are is given below
package com.example.diary;
public class MainActivity extends AppCompatActivity {
int j;
List<NotesEntry> entries;
RecyclerAdapter adapter;
NotesDatabase db;
int numofitems;
String notesdata = "new_notes";
String defaultnotes = "old_notes";
Context c = this;
GridLayoutManager gridLayoutManager;
RecentsFragment recentsFragment;
ActivityMainBinding binding;
String textfortest;
int id;
FragmentManager fragmentManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecentsFragment recentsFragment=new RecentsFragment();
BlankFragment blankFragment=new BlankFragment();
fragmentManager =getSupportFragmentManager();
fragmentManager.beginTransaction().add(R.id.recentsfragment,recentsFragment).commit();
fragmentManager.beginTransaction().add(R.id.allnotesfragment,blankFragment).commit();
}
}
its layout xml is
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/recentsfragment"
android:layout_width="395dp"
android:layout_height="325dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.618"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/view"></FrameLayout>
<view
android:id="#+id/view"
class="android.widget.Button"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="8dp"
android:background="#android:color/darker_gray"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="-51dp" />
<FrameLayout
android:id="#+id/allnotesfragment"
android:layout_width="395dp"
android:layout_height="327dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toTopOf="#+id/view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"></FrameLayout>
i tried to implement it on other layouts too ,but the result is same.
thanks for your time
Your layout is most likely incorrect. Try something like this.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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_constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/fragment_container1"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/some_view"/>
<view
android:id="#+id/some_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/fragment_container1"
app:layout_constraintBottom_toTopOf="#id/fragment_container2"/>
<FrameLayout
android:id="#+id/fragment_container2"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/some_view"
app:layout_constraintBottom_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
After spending a lot of time on it, I found that the problem is not with the layout but with the fragment itself.
In the oncreateView method of a fragment, I used databinding to refer to view instead of findViewById() and I have implemented a wrong way of databinding.
so by implementing databinding in the following way, I solved my problem.
fragmentBlankBinding = FragmentBlankBinding.inflate(inflater,container,false);
fragmentBlankBinding.recyclerView.setAdapter(adapter);
fragmentBlankBinding.recyclerView.setLayoutManager(layoutManager);
view=fragmentBlankBinding.getRoot();
return view;
Note: you need not refer to any layouts using this method as the layout is automatically fetched from databinding layout file of your fragment.
Hope it helps someone
I am building an activity (MainActivity.kt) with a "android.support.design.widget.BottomNavigationView" which has multiple tabs. When a user taps a tab then "BottomNavigationView.OnNavigationItemSelectedListener" should present a corresponding fragment.
Question:
How to set at runtime in onCreateView method a different fragment than the defined in layout file by "android:name" attribute.
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
Layout/ActivityMain.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.developer.myapplication.Locator"
android:id="#+id/fragment"
/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/navigation"
app:layout_constraintTop_toBottomOf="#+id/fragment"
app:layout_constraintStart_toStartOf="parent"/>
</android.support.constraint.ConstraintLayout>
If you want to change a fragment at runtime you should be using a FrameLayout as a container for your fragment instead of using the fragment tag. Once you use a FrameLayout, it is very easy to swap fragments during runtime; you can just use getFragmentManager().beginTransaction().replace(R.id.FrameLayoutId, new MyFragment()).commit(). Furthermore, you normally use the fragment tag for fragments that are there to stay (meaning you are not planning to replace them or modify them during runtime). For more information you can take a look at how master-detail view is implemented with fragments (Android: when / why should I use FrameLayout instead of Fragment?)
Hope this helps! :)
I want to add footer with empty layout to the bottom of the recyclerView which will show up only if certain View is visible on screen(In my case FabButton). FabButton is overlapping my last RecyclerView item, so I want to add empty Footer to the bottom of the recyclerview if this FabButton is visible.
ViewHolder inside Adapter for Footer:
inner class FooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
Inside onBindViewHolder:
... ViewHolder above is for items.
else if(holder is FooterViewHolder){
if (parentFragment.isFabButtonVisible()){
holder.itemView.visibility = View.VISIBLE
} else {
holder.itemView.visibility = View.GONE
}
}
This part of the code is working(also else branch is executed correctly), but my footer view is permanently visible on screen. Even if i add only View.GONE it is still visible.
XML for Parent Activity:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#color/splash_white"
android:layout_weight="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
>
<com.app.TabLayoutEntity.CustomViewPager
android:id="#+id/fragmentViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="0dp"
android:background="#color/splash_white"
tools:layout_editor_absoluteY="-2dp" />
<ImageButton
android:id="#+id/fabButton"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:background="#drawable/fab_gradient"
android:clickable="true"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:padding="8dp"
android:elevation="8dp"
android:shape="oval"
android:src="#drawable/ico_fab" />
</android.support.design.widget.CoordinatorLayout>
Fragment XML(Inside ViewPager):
<?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:clipToPadding="true"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/itemRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/recyclerview_corners"
android:clipToPadding="false"
android:scrollbars="vertical"
android:paddingBottom="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
If You just wanna avoid overlapping of last item and Fab then you should do this below simple change in xml on recyclerview and parent layout
<android.support.v7.widget.RecyclerView
android:clipToPadding="false"
android:paddingBottom="paddingindp"/>// for example You can add 100dp and test
and on parent layout add this below line
android:clipToPadding="true"
this will make your recyclerview scroll over that Fab and its doesnt require to add Footer view
Hope this work for your requirement.
You can add your condition in getItemCount() method since you need to increase your list size if you add footer
Like,
override fun getItemCount(): Int {
if (parentFragment.isFabButtonVisible()){
return mModels.size + 1
} else {
return mModels.size
}
}
I made an app with onboarding flow.
I have a recyclerview in my MainActivity class and I want to update it when onResume hits(Right after the onboarding flow).
So this how the recycler xml looks:
<android.support.v7.widget.RecyclerView
android:id="#+id/notifyRecycler"
android:layout_width="0dp"
android:layout_height="230dp"
android:clipToPadding="false"
android:orientation="horizontal"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
and this it the cell layout:
<android.support.constraint.ConstraintLayout 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/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="#+id/cardDetails"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
</...>
I swap the data within the adapter with clear and add and everything works great when I change the cell layout view height from 0dp to match parent.
It also works great for the first run from oncreate, but when I try to update from onResume it just not notify the data set changed.
Here's the code of the adapter called from onResume function:
public void setNewData(ArrayList<Model> models) {
this.notifications.clear();
this.notifications.addAll(models);
notifyDataSetChanged();
}
in onCreate:
adapter = new NotificationsAdapter(new ArrayList<>(), this);
notifyRecycler.setAdapter(adapter);
in onResume,I made boolan var to make it update only when resuming the app:
ArrayList<NotificationModel> notificationsList = new ArrayList<>();
notificationsList.add(...);
notificationsList.add(...);
adapter.setNewData(notificationsList);
Wierd solution, Solved by changing the parent ViewGroup from ConstraintsLayout to RelativeLayout.