My MainActivity layout includes a bottom navigation bar because I would like to use this as the main navigation components within my application's fragments. However, I have a login page which I do not want the navigation bar to be visible on. Since the fragment is being created from the MainActivity, it inherits the navigation bar and I can't find a way to hide it on the login fragment and show it on subsequent fragments.
For ease:
I have a MainActivity layout with a BottomNavigationBar
All fragments natively inherit the BottomNavigationBar from the MainActivity layout
I want the visibility of the navigation bar to be "GONE" on the user login fragment
I want the visibility of the navigation bar to be "VISIBLE" on the remaining fragments
Is there a way to do this?
The reason I am defining the BottomNavigationBar within the MainActivity is that I initially had a separate navigation bar on each fragment which required separate listeners on each fragment (making the code more extensive than it should be). Additionally, when I implemented the navigation bar as an individual component within each fragment, the item selected animations no longer functioned.
I have tried to use <include layout="#layout/main_activity android:visibility="GONE" android:layout_width="match_parent" android:layout_height="wrap_content"/> from within the login fragment but this has not worked.
I would appreciate any help people have on this matter.
Please feel free to let me know if you would like to see any of my code. I wasn't really sure which parts of my code would be relevant.
I would suggest you to use separate activities for this case - LoginActivity and MainActivity. Since you may at some point add ForgotPasswordFragment and maybe something else. To hide something you don't need in the first place is bad practice IMHO.
In case you don't want to change your approach you can create BaseFragment that will have abstract val showBottomNavBar() and will override it in every fragment.
And then in onViewCreated you'll check that flag and update UI accordingly.
Something like this:
abstract class BaseFragment : Fragment() {
abstract val showBottomNavBar: Boolean
//.. your other stuff
}
class FragmentA : BaseFragment() {
override val showBottomNavBar = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (!showBottomNavBar) {
(requireActivity() as YourAcitivy).hideNavBar() //this is bad
someKindOfBroadcastManager.sendEvent(hideNavBar) // this is somewhat better
}
}
}
But I would still suggest you to decouple login/main logic from single activity
You can access activity from fragment with getActivity() method and cast to your activity. Of course you have to add hideBottomNavigationBar method to your activity and also you have to be sure that your fragments host activity is MyActivity.
MyActivity activity = ((MyActivity)getActivity());
activity.hideBottomNavigationBar();
After that you can define a marker interface that named IFullScreen then in your BaseFragment's onCreateView method you can check if this class is instance of IFullScreen and decide to show or hide bottom navigation bar.
interface IFullScreen { }
class BaseFragment extends Fragment {
#CallSuper
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
MyActivity activity = ((MyActivity)getActivity());
if (this instanceof IFullScreen)
activity.hideBottomNavigationBar();
else
activity.showBottomNavigationBar();
}
}
class FragmentA extends BaseFragment implements IFullScreen {
//Rest of fragment code
}
Related
i need some help. I'm making an app on android studio and I have a Button initialized in a Fragment. I need to call this button in another Activity and set a click listener on it.
The problem is that when I do this I get the .NullPointerExceptionError and it says that the value of the object is null.
Now i tried to initialize the button in both my fragment and Activity but I keep getting that error and the app crashes.
I need some help to call this button from the Fragment. Maybe my question is similar to some other but it's hours that I'm searching a solution to this problem and I found nothing.
Thanks in advance for all your help
Since the button is part of the fragment, it only fits that any interactions/modifications to the button be defined in the Fragment's scope itself.
There are 2 ways to go about it:
Create the click listener in the Fragment itself and navigate to another activity from there.
Create an interface which your activity implements and the fragment has an instance of that interface. On the button click, call the interface's method. That would give you control in the activity which is hosting the fragment.
MyActivity extends AppCompatActivity implements MyClickListener {
void onMyButtonClick() {
// Navigate to another activity here
}
}
MainFragment extends Fragment {
private MyClickListener listener;
void onAttach(Context context) {
listener = (MyClickListener) context;
// Now call listener.onMyButtonClick() from button's on click listener.
}
}
interface MyClickListener {
void onMyButtonClick();
}
I have a BaseActivity that takes care of Fragment transitions by acting as the central controller for it's Fragment's touch listeners. One of the Fragment is a custom Listview that will cause the BaseActivity to display a new Fragment when tapped. This works when the Activity is created from scratch, but I have a problem with it when the activity is destroyed (through the Dev options "Don't Keep Activities", or through memory cleanup).
I have verified through logs that I am setting the Fragment's listener when the activity starts up from being destroyed, but tapping on the Listview doesn't transition to a new Fragment. I also tried to see if it was a focusing issue but putting android:descendantFocusability="blocksDescendants" in the Listview layout xml also doesn't solve the issue.
The listener is set when I create the fragment and it is the first fragment the activity instantiates when it starts up. Any insights would be appreciated!
I'll add a answer to be easier to help you. The first thing is that you listener should be set inside fragment's onAttach:
#Override
public void onAttach(Context context) {
// here, your context is also your activity
super.onAttach(context);
if (context instanceof YourListener) {
this.fragmentListener = (YourListener) context;
}
}
Please change this and check if something changed!
I'm planning to create a floating contextual menu for my app in a fragment after onLongClick and I also have a custom Adapter extending from RecyclerView.Adapter and a custom holder extending from RecyclerView.ViewHolder.
I found out there are 2 options to register :
Option 1 : Registering the listener inside onCreateViewHolder method by holder.itemView.setOnLongClickListener and retrieving any ViewHolder instance variable I want by calling holder.getSomeVariable() inside the event handler and set it to current instance variable so I can retrieve it from Activity/Fragment (because I can get reference of adapter from my fragment).
The advantage I notice is that it allows me to declare the implementation onCreateContextMenu() and onContextItemSelected() method and override them in my Fragment/Activity, which allows the reusing of the existing provided method and get the ViewHolder variables inside the onContextItemSelected().
I notice the downside is that we need to create extra setter and getter method in the adapter (because we cannot reference ViewHolder instance from Activity/Fragment).
Option 2 : Registering the click listener inside ViewHolder. According to this website ,the advantage is it allows more explicit click listener (I also have other listeners registered in the ViewHolder class, so they're group together which makes the code structure looks better).
However, I notice the downside from using this way is we cannot declare onCreateContextMenu() and onContextItemSelected() inside Activity/Fragment anymore because we cannot get the viewholder reference from Activity/Fragment, we can only get adapter reference. As a result, the readability is a bit worse. (I have a contextual_menu.xml, so will need to do the inflation in the viewholder as well instead of in fragment/activity. Is inflation of XML in viewholder even a correct practice?)
From what I understand, in general scenario , it's better to register listener in ViewHolder (Option 2).
But in my context, which method (Option 1 or Option 2) should I apply? Does the general scenario still apply (should I apply Option 2)?
Also are there any more advantages and disadvantages from those 2 listed options?
I use it in Activity or Fragment that way
1- Create interface like that:
public interface OnItemLongClickListener {
void onItemLongClick(int position);
}
EDIT:
2- In your Adapter ViewHolder class create a reference like that:
private OnItemLongClickListener listener;
3- In adapter create setter method to initialize listener which will call another setter method in ViewHolder class like that:
class Adapter extends RecyclerView.Adapter<YourViewHolder>{
.
.
.
.
public void setOnItemLongClickListener(OnItemLongClickListener listener){
viewHolderObject.setonItemLongClickListener(listener);
}
}
public class YourViewHolder extends RecyclerView.ViewHolder{
.
.
.
.
public void setOnItemLongClickListener(OnItemLongClickListener listener){
this.listener = listener;
}
}
4- In your ViewHolder in method onLongClick() do this:
#override
public boolean onLongClick(View v){
listener.onItemLongClick(getAdapterPosition());
}
5- In your Activity Do the contextual menu staff
I hope this help you.!
how can I create a class that instantiates my navigation drawer correctly?
I want to outsource it because it is a lot of code and its always the same.
I already tried to create such a class. The problem is,
there are these two methods:
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
that are overriden in the activity itself. Moreover the title doesnt change if the drawer is opened/closed and at last the arrow that is used as the button to open is not animated!
Thx for help!
First android studio provides a template for creating Drawer Activity. If you are going to create your project for the first time you can use:
or if you have created your project and you want to create a Drawer Activity you can find it like this:
Now if you do not like above approaches you can create an abstract activity class name it for example DrawerActivity and do all initialization in it. Then in every project you can add it and extends it. That is one time work. But you must design it carefully so it must be general enough and also meet your common requirements. For example it can have a protected DrawerLayout field so when you extends it you initialize that field after setContentView of subclass activity and all the stuff like onPostCreate and onConfigurationChanged are done in the DrawerActivity(superclass).
Is it possible to make an onClickListener app-global?
I basically have several fragments that will use the same numpad buttons for input and instead of registering and filtering click events for each button in each fragment I wanted to ask if it was possible to share an onClickListener throughout the entire app.
This is the setting:
public class LoginFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle states) {
this.context = getActivity();
// TODO: Register onClickListener...somehow
context.registerReceiver(receiver, filter); //For the intent listening
view = inflater.inflate(R.layout.layout_login_screen, container, false);
buildUI(null);
return view;
}
(and two different fragments simmilar to this one)
and then the idea was:
public class NumPadListener implements OnClickListener {
#Override
public void onClick(View v) {
System.out.println("Yup...I'm listening?");
// TODO: Do funny intent stuff here
}
}
Is this even possible? And if yes, how? :) If it isn't, do you have any recommendations on how to implement this in the best way? Thanks
Yes, as dymmeh has shown. But the proper way is probably to create your own view component containing all the numpad buttons. You would need to define a layout file, and create a class which would extend some kind of ViewGroup (see compound controls).
This custom component would take care of onClick events of buttons inside it. You could then expose some kind of interface (listener) for activities and fragments to attach to if you need them to react for higher-level events.
Then, you would just include your custom component in any layouts you need instead of copypasting a bunch of buttons and the onClick listener-attaching code.