I have Base Activity extends My all other activities. The Base Activity has features like toolbar, progress bar, and other logic methods that are similar across the activity
After Migrating from ButterKnife to ViewBinding The child activity that is extended from the parent base is not able to access the methods in it And the app gets crashed.
Below is Base Activity Code
pubic class BaseActivity extends AppCompatActivity {
private ActivityBaseBinding activityBaseBinding;
#Override
public void setContentView(int layoutResID) {
activityBaseBinding = ActivityBaseBinding.inflate(getLayoutInflater());
View view = getLayoutInflater().inflate(layoutResID, activityBaseBinding.container, false);
if (layoutResID == R.layout.activity_home) {
activityBaseBinding.toolbarTitle.setVisibility(View.GONE);
} else if (layoutResID == R.layout.activity_my_account) {
activityBaseBinding.toolbarTitle.setVisibility(View.VISIBLE);
} else {
activityBaseBinding.toolbarTitle.setVisibility(View.GONE);
}
activityBaseBinding.container.addView(view);
setContentView(activityBaseBinding.getRoot());
activityBaseBinding.imgBackArrow.setOnClickListener(v -> onBackPressed());
}
If I try to access the toolbar in the base from the child activity this is the error I get
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.iowave.scheduler/com.example.myaccount.views.MyAccount}:
java.lang.NullPointerException: Attempt to read from field
'com.google.android.material.appbar.MaterialToolbar
com.example.databinding.ActivityBaseBinding.toolbarTitle' on
a null object reference
This is the code of my child activity
public class MyAccount extends BaseActivity implements MyAccountImpl {
private NavController navController;
private ActivityMyAccountBinding activityMyAccountBinding;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityMyAccountBinding = ActivityMyAccountBinding.inflate(getLayoutInflater());
View view = activityMyAccountBinding.getRoot();
setContentView(view);
}
}
From what I understand the view binding is only binding the child view and removing all other bindings from the background.
Use this approach for base activity patterns.
abstract class BaseActivity1<Binding extends ViewBinding> extends AppCompatActivity {
Binding binding;
abstract Binding getBinding();
void initBinding(){
binding=getBinding();
}
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.initBinding();
setContentView(binding.getRoot());
}
}
then
class MainActivity extends BaseActivity1<ActivityMainBinding>{
#Override
ActivityMainBinding getBinding(){
return ActivityMainBinding.inflate(getLayoutInflater());
}
}
This could be solve your problem:
I just migrated the project from Butterknife to ViewBinding and face problems related implementing encapsulated activities view into base activity' container, too. Before implementing ViewBinding, I was using getLayoutInflater().inflate(layoutResID, activityContainer, true); where layoutResID was id of encapsulated activities' xml layout (R.layout.activity_main) in BaseActivity so I could inflate encapsulated activities' xml inside activityContainer which is corresponding to FrameLayout inside BaseActivity's xml. But then I figure out that, with ViewBinding, there is no ViewGroup id (xml) but View. So I couldn't use getLayoutInflater().inflate(layoutResID, activityContainer, true); because layoutResID is a ViewGroup id, not View. Here is how I solved.:
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
private var _binding: VB? = null
val childActivityBinding: VB
get() = _binding as VB
abstract fun inflateLayout(
parent: FrameLayout,
inflater: LayoutInflater
): VB
lateinit var baseActivityBinding: ActivityBaseBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
baseActivityBinding = ActivityBaseBinding.inflate(layoutInflater)
_binding = inflateLayout(baseActivityBinding.root.layout_container, layoutInflater)
val coordinatorLayout = baseActivityBinding.root
.
.// some base activity processes like setting Snackbar etc.
val snack = coordinatorLayout.snack
.
super.setContentView(coordinatorLayout)
}
}
Here is Main Activity: [ in Java :) ]
public class MainActivity extends BaseActivity<ActivityMainBinding> {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#NonNull
#Override
public ActivityMainBinding inflateLayout(#NonNull FrameLayout parent, #NonNull LayoutInflater inflater) {
return ActivityMainBinding.inflate(inflater, parent, true);
}
}
With this approach, I was able to inflate the encapsulated activities view inside base activity like a subview.
Related
I am working on an application where I am using bottom navigation on Home where i have three fragments on the second fragment called Post Ad I have a button called enter fragment zone through that i enter into another fragment now when I enter inside another fragment now I don't want there to show the bottom navigation so to hide it I am using a method inside my main activity called "setBottomNavigationVisibility" where I am writing code to set the visibility of bottom nav. but the problem is that it is throwing the null pointer exception in the mainactivty's method saying that
"void com.google.android.material.bottomnavigation.BottomNavigationView.setVisibility(int)' on a null object reference" on the method's line where i am setting the visibility
code of MainActivity
public class MainActivity extends AppCompatActivity {
NavController navController;
BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navController = Navigation.findNavController(this, R.id.fragmentContainerView);
bottomNavigationView = findViewById(R.id.activity_main_bottom_navigation_view);
NavigationUI.setupWithNavController(bottomNavigationView, navController);
}
public void setBottomNavigationVisibility(int visibility) {
bottomNavigationView.setVisibility(visibility);
}}
On the above method when i am trying to setting the visibility on the line bottomNavigationView.setVisibility(visibility); that's where it is thrwoing the exception
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentSecondBinding.inflate(inflater, container, false);
// Inflate the layout for this fragment
View view = binding.getRoot();
viewModel = new ViewModelProvider(requireActivity()).get(PageViewModel.class);
((MainActivity) requireActivity()).setBottomNavigationVisibility(View.GONE);
binding.toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.action_secondFragment2_to_postad);
}
});
No Please guide me how can i solve this error.
do something like this, define interface
interface DashboardActivityDelegate {
void hideBottomBar();
void showBottomBar();
}
your activity must implement this interface
and in your fragment in which you want to hide bottom nav view do this:
declare global variable
private DashboardActivityDelegate dashboardActivityDelegate;
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof DashboardActivityDelegate) {
dashboardActivityDelegate = (DashboardActivityDelegate)context;
}
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (dashboardActivityDelegate != null) {
dashboardActivityDelegate.hideBottomBar();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (dashboardActivityDelegate != null) {
dashboardActivityDelegate.showBottomBar();
}
}
in fragment you want hide bottom bar, just call this line in onViewCreated
getActivity().findViewById(R.id.activity_main_bottom_navigation_view).setVisibility(View.GONE);
and when you leave fragment, call this line in onDestroy or onStop to visible bottom nav for another fragment:
getActivity().findViewById(R.id.activity_main_bottom_navigation_view).setVisibility(View.VISIBLE)
I want to set the text of textview in activity from a fragment. This is how I do it.
MainActivity.java
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
public TextView textViewNotification;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
textViewNotification = (TextView) MenuItemCompat.getActionView(navigationView.getMenu().findItem(R.id.nav_notification));
}
}
HomeFragment.java
public class HomeFragment extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
((MainActivity)getActivity()).getSupportActionBar().setTitle("PIT IAI & FIP Regional");
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_home, container, false);
MainActivity activity = (MainActivity) getActivity();
activity.notification.setText("This is a test"); // => got error here.
return root;
}
}
But it didin't work. This is the error that I got:
android.content.res.Resources$NotFoundException: String resource ID #0x1
at android.content.res.Resources.getText(Resources.java:348)
at android.widget.TextView.setText(TextView.java:5846)
How is it exactly to get and set public attributes of activity from fragments? is it not possible? Please help.
Supposed you want to send the text to the activity based on some action.
You can use an interface, first create a public interface in your fragment and add one method inside it which takes one string parameter
public interface CommunicateWithActivity{
void onCommunicate(String s)
}
, declare a global variable mListener of type CommunicateWithActivity,
private CommunicateWithActivity mListener;
then override onAttach and inside try/catch block
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
mListener = (CommunicateWithActivity) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "CommunicateWithActivity implementation in Activity is required");
}
}
then in the activity implement the interface an override "onComunicate(String s)" method in the activity and you will get your string inside the methd.
#Override
public void onCommunicate(String s) {
//do whatever you want
}
Henry Gunawan your MainActivity does not have any public field called notification you actually named it textViewNotification.
Moreover i am seeing some bad practices in your code
Avoid declaring public fields instead use getters and setters to access them
Your fragment assuming that it's host is always a MainActivity instance and it has a field called notification , which is not a good practice , fragments are meant to be a standalone unit, fragments should not depends on specifics of their host activity as they may be host by any activity hence this is a misuse of fragments, use should instead use callbacks if you want your host activity to do something for you as explained by Amr Sakr.
I am trying to invoke a method named "change_color()" in my one fragment "A" from another fragment "B" using an interface, implemented by the parent activity. When I try to cast my parent activity to the instance of my interface, I get this ClassCastException.
Here's the snippet of fragment "B",
Public class B extends Fragment implements View.onClickListener{
public attendance_to_history var;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_attendance_take,
container, false);
return rootView;
}
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
var = (attendance_to_history) getActivity();
}
Here's the code of interface
interface attendance_to_history{
public void invoke();}
Here's code from my parent activity:
public class tabbed_activity extends AppCompatActivity implements attendance_to_history{
#Override
public void invoke() {
fragment_A frag = new fragment_A();
frag.change_color();
}
}
attendance_to_history connector;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
connector=(attendance_to_history) activity;
}
replace instead for onActivityCreated
here is the solution:
1- in the activity that holds the fragment B, make it implements attendance_to_history and override the method invoke inside the activity.
then your code will work fine.
I'm still trying to grasp the Dagger mindset of Dependency Injection, and am running into some trouble. I have a MyNavBar which is a View in a fragment. I can't figure out how to inject my app's MainWrapper class into it. Here's how my code is laid out:
I have a MainActivity in my app's Android module:
public class MainActivity extends Activity {
private MainWrapper mainWrapper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Create the basic interface which will have parts swapped out with
// fragments in MainWrapper:
this.setContentView(R.layout.main);
ObjectGraph graph = ObjectGraph.create(new MyDaggerModule(this));
this.mainWrapper = new MainWrapper(graph);
}
Then, MainWrapper exists in the library which contains almost all of my app's code:
public class MainWrapper {
#Inject
protected MenuFragment menu;
#Inject
protected Activity activity;
private ObjectGraph objGraph;
public MainWrapper(ObjectGraph objGraph) {
this.objGraph = objGraph;
this.objGraph.inject(this); //populated this.menu and this.activity
Fragment mainFragment = new MainFragment();
this.objGraph.inject(mainFragment);
//Transaction to swap out blank fragment for mainFragment
// ...
}
}
MainFragment is a pretty standard fragment:
public class MainFragment extends Fragment {
protected MyNavBar navBar; //LinearLayout
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View mainContentView = inflater.inflate(R.layout.main_content, container, false);
this.navBar = (MyNavBar) mainContentView.findViewById(R.id.main_nav_bar);
return mainContentView;
}
}
Lastly, here's the MyNavBar fragment:
public class MyNavBar extends UILinearLayout {
#Inject
protected MainWrapper wrapper;
protected View menuButton;
//Constructors that call init() at the end
// ...
protected void init() {
this.setContentView(R.layout.navbar);
this.menuButton = this.findViewById(R.id.menuBtn);
this.menuButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//I want to call methods on MainWrapper here.
}
});
}
}
How can I get MainWrapper to inject wrapper into that MyNavBar class using DI? I realize I could inject MainWrapper into MainFragment, then create a setter on MyNavBar, but that seems to defeat the purpose of DI in the first place.
I guess ultimately what I'm asking is this: If you have your main Activity, which creates a fragment, and that fragment has views, how can you inject into those fragment's views while keeping the ObjectGraph only in the main activity? At some point, won't you have to call objectGraph.inject() on the fragment's Views?
i am creating two java file 1st main activity.java file 2nd fragment.java file create button on fragment.java how to click listener written on activity.java help me
fragment.java
public class fragment extends fragment{
Button btn;
// some code
btn = (Button)layout.findviewbyid(R.id.btn1);
}
}
activity.java
public class activity extends Activity
{
// how to access the click action btn here
btn.setOnclicklistner(new View.OnClickLisitner(){
public OnClick(){
}
To use the button in activity from the fragment, you have to use getActivity()
In your fragment,
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item_select, container, false);
btn = (Button) getActivity().findViewById(R.id.btn);
}
btn is the button in activity
getActivity() in a Fragment returns the Activity the Fragment is currently associated with. (see http://developer.android.com/reference/android/app/Fragment.html#getActivity()).
You can define custom clickListener class and create it's instance in fragment and set listener instance there. Now you can write code in that class. Hope it will help you.
public class MyCustomListener implements OnClickListener{
#override
public void onClick(View v){
// you stuff
}
}
then in your fragment call this
MyCustomListener listener=new MyCustomListener();
btn.setOnClickListener(listener);
Here is my take on the issue, both in Java and Kotlin.
Java:
public final class YourActivity extends AppCompatActivity {
/***/
public final void yourMethod() {
printIn("Printing from yourMethod!")
}
}
public final class YourFragment extends Fragment {
/***/
#Override
public void onViewCreated(View v, Bundle savedInstanceState) {
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
YourActivity yourActivity = (YourActivity) getActivity();
yourActivity.yourMethod();
}
}));
}
}
Kotlin:
class YourActivity : AppCompatActivity() {
/***/
fun yourMethod() {
print("Printing from yourMethod!")
}
}
class YourFragment: Fragment() {
/***/
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
button.setOnClickListener {
val yourActivity = activity as YourActivity
yourActivity.yourMethod()
}
}
}
I hope it helps someone out there =)
If I understand your problem correctly, you want to delegate a button click inside a fragment back to its parent activity.
Keep a reference to the parent activity inside your fragment. Then set the listener to your button in your fragment like this:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
parentActivity.doStuff();
}
};
In your parent Activity, define method doStuff():
public void doStuff() {
// handle button click event here
}