I want to use onClick method in fragment but data binding doesn't see method.
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
FragmentTodayBinding fragmentTodayBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_today, container, false);
View view = fragmentTodayBinding.getRoot();
final MainViewModel mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
fragmentTodayBinding.setMainViewModel(mainViewModel);
fragmentTodayBinding.searchButton.setOnClickListener(this::onSearchClick);
});
return view;
}
#Override
public void onSearchClick(View v) {
mainViewModel.getCity();
Toast.makeText(getActivity(), "X", Toast.LENGTH_SHORT).show();
}
Button xml:
<Button
android:id="#+id/search_button"
android:onClick="#{(v)-> todayFragmentInterface.onSearchClick(v)}"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="10dp"
android:layout_marginTop="5dp"
android:background="#drawable/ic_search_white_24dp" />
How to use it properly in fragment?
Follow this step:
1) Add this line in onCreateView
fragmentTodayBinding.fragment = this;
2) Add this line in your layout.xml
<variable name="fragment" type="yourPackage.ClassName" />
3) Edit onClick in xml with
android:onClick="#{(v)-> fragment.onSearchClick(v)}"
You can also remove:
fragmentTodayBinding.searchButton.setOnClickListener(this::onSearchClick);
I saw that you use #Override so needs an Interface, the goal is the same, just use variable interface in the xml variable (and refer to it with package of interface), than use fragmentTodayBinding.myInterface = this;
So:
1) fragmentTodayBinding.myInterface = this;
2) <variable name="myInterface" type="yourPackage.Interface" />
3) android:onClick="#{(v)-> myInterface.onSearchClick(v)}"
Related
I am an Android programming beginner. Appears my problem is simple and silly, still not able to locate the issue.
In the fragment, my objective is to show a dot every second which helps prompting someone to do some physical action, but no way near that. I am moving bit by bit and got struct at the first level itself.
public class ActionFragment extends Fragment {
private FragmentActionBinding binding;
TextView pCycleNum;
int pCycle;
#Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {
View v = inflater.inflate(R.layout.fragment_action, container, false);
pCycleNum = v.findViewById(R.id.pCN);
pCycleNum.setText(String.valueOf(1));
binding = FragmentActionBinding.inflate(inflater, container, false);
return binding.getRoot();
}
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.buttonSecond.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
NavHostFragment.findNavController(ActionFragment.this)
.navigate(R.id.action_ActionFragment_to_LauncherFragment);
}
});
binding.buttonAction.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
pCycleNum.setText("5");
}
});
}
I am setting pCycleNum to 5. But when I run the app on emmulator, it does not show any change. Not able to find out where I am going wrong.
The fragment xml is as follows
<?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/white"
android:foregroundTint="#color/teal_700"
tools:context=".ActionFragment">
<Button
android:id="#+id/button_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/teal_200"
android:backgroundTint="#F57F17"
android:text="#string/goback"
android:textColor="#color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.96"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/textview_second"
app:layout_constraintVertical_bias="0.96" />
<TextView
android:id="#+id/textview_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="#color/teal_700"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/buttonAction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="178dp"
android:layout_marginStart="139dp"
android:layout_marginTop="33dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.13"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.97" />
<TextView
android:id="#+id/pCN"
android:layout_width="174dp"
android:layout_height="112dp"
android:layout_marginStart="80dp"
android:layout_marginTop="130dp"
android:text="1"
android:textColor="#color/teal_200"
android:textSize="96sp"
android:visibility="visible"
app:layout_constraintStart_toEndOf="#+id/imageView13"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Please help to me to locate the error why app not displaying the set value. Also help me to understand why it is doing so. Thanks.
Just remove these lines from onCreateView() method.
View v = inflater.inflate(R.layout.fragment_action, container, false);
pCycleNum = v.findViewById(R.id.pCN);
pCycleNum.setText(String.valueOf(1));
And then use binding.pCN.setText("5");
This happened because you are using both findViewById() and view binding simulataneously.
And you are returning the binding in onCreateView() method. And onViewCreated() method don't know from where to get this pCN text view so it just skips.
you got a textview by id from v layout but then inflated a new layout, gave its reference to binding and returned the binding view not the view you used findViewById on
to avoid this mistake afterwards, just get your views from the binding object, you'll find them all by their id names (without any underscore character "_") like I did in binding.pCN
public class ActionFragment extends Fragment {
private FragmentActionBinding binding;
TextView pCycleNum;
int pCycle;
#Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {
binding = FragmentActionBinding.inflate(inflater, container, false);
pCycleNum =binding.pCN;
return binding.getRoot();
}
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.buttonSecond.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
NavHostFragment.findNavController(ActionFragment.this)
.navigate(R.id.action_ActionFragment_to_LauncherFragment);
}
});
binding.buttonAction.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
pCycleNum.setText("5");
}
});
}
This is my xml file
<LinearLayout
android:orientation="vertical"
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="40dp"
android:id="#+id/MainPersonalIDLayout">
<TextView
android:text="Personal ID"
android:textSize="18dp"
android:textColor="#000"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:id="#+id/PersonalIDTitle" />
<LinearLayout
android:orientation="horizontal"
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:id="#+id/SubPersonalIDLayout">
<TextView
android:text="No ID"
android:textSize="20dp"
android:gravity="center"
android:layout_width="200dp"
android:layout_height="match_parent"
android:id="#+id/PersonalID" />
<Button
android:text="Use this"
android:textAllCaps="false"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:onClick="UsePersonalID"
android:id="#+id/UsePersonalID" />
</LinearLayout>
</LinearLayout>
This is my Dashboard Fragment
public class DashboardTab : Fragment
{
View DashboardView;
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
DashboardView = inflater.Inflate(Resource.Layout.Dashboard, container, false);
return DashboardView;
}
}
And this is my MainActivity.cs, I created an instance of the Dashboard fragment
SetContentView(Resource.Layout.activity_main);
DashboardTab Dashboard = new DashboardTab();
TextView PersonalIDHolder = Dashboard.View.FindViewById<TextView>(Resource.Id.PersonalID);
This is the error that shows when I run
System.NullReferenceException: 'Object reference not set to an instance of an object.'
Is it possible to get the view from an instance of Fragment?
Android get view of fragment in activity
Thank you
Yes, You will get the System.NullReferenceException because you are trying to access the Fragment's View in onCreate Method of activity and till now fragment view is not initialized.
Update your views in OnCreateView method in Fragment
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var DashboardView = inflater.Inflate(Resource.Layout.Dashboard, container, false);
TextView textView = View.FindViewById<TextView>(Resource.Id.PersonalID);
return DashboardView;
}
OR If your changes depend on Activity your fragment is attached to, then you can use OnActivityCreated method of Fragment.
public override void OnActivityCreated(Bundle savedInstanceState)
{
base.OnActivityCreated(savedInstanceState);
TextView textView = View.FindViewById<TextView>(Resource.Id.PersonID);
var act = (MainActivity)Activity;
}
The answer is yes.I don't know how you get instance of your fragment, but there is a way to get a Fragment by FindFragmentByTag.
When you add the Fragment,you could do something like:
SupportFragmentManager
.BeginTransaction()
.Add(Resource.Id.settingsContainer, new DashboardTab() , "DashboardTab")
.Commit();
then get the DashboardTab fragemnt in Activity OnResume() or OnStart() method:
protected override void OnResume()
{
base.OnResume();
DashboardTab Dashboard = SupportFragmentManager.FindFragmentByTag("DashboardTab");
TextView textView = Dashboard.View.FindViewById<TextView>(Resource.Id.PersonalID);
}
I have an app am working on, the app is using leak canary to detect possible memory leaks. The app seems to be fine except for my extended floating action button, according to leak canary the button is leaking, I do not have an idea on how to rectify this.
Below is my XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.store.StoreFragment">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/small_margin"
app:cardElevation="#dimen/normal_elevation"
app:shapeAppearanceOverlay="#style/ShapeAppearanceOverlayLargeCutLeftTopCorner">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="#+id/swipeToRefresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.core.widget.NestedScrollView
android:id="#+id/nestScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadingEdge="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ViewStub
android:id="#+id/layoutCategories"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="#+id/panel_layoutCategories"
android:layout="#layout/store_layout_categories" />
<ViewStub
android:id="#+id/layoutDeals"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="#+id/panel_layoutDeals"
android:layout="#layout/store_layout_deals" />
<ViewStub
android:id="#+id/layoutCollections"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="#+id/panel_layoutCollections"
android:layout="#layout/store_layout_collections" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="#+id/btnGoToCart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:text="#string/cart"
android:textColor="#android:color/white"
android:textStyle="bold"
app:backgroundTint="#color/colorAccent"
app:elevation="#dimen/large_elevation"
app:icon="#drawable/ic_shopping_cart_24px"
app:iconTint="#android:color/white"
app:layout_anchor="#id/nestScrollView"
app:layout_anchorGravity="bottom|end"
app:shapeAppearanceOverlay="#style/ShapeAppearanceOverlayLargeCutLeftTopCorner" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And my Java code
public class StoreFragment extends Fragment implements View.OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_store, container, false);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
btnGoToCart.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
....
case R.id.btnGoToCart:
Navigation.findNavController(v).navigate(R.id.cartFragment);
break;
....
}
}
}
Below is a snippet from leak canary. At first the leak was pointing to the ID of the coordinator layout, I removed it, now it is pointing to the Extended floating action button.
Instead of having the Fragment implement OnClickListener why not just create a new OnClickListner inner class? From what I see you ar passing the fragment as the OnClickListener and the view later holds a reference to your fragment and that's probably the cause of the leak. Just do (new OnClickListener () {}); instead of implementing OnClickListener.
EDIT:
I've just noticed that you are using ButterKnife in a Fragment.
If your using ButterKnife in a Fragment you should use:
private Unbinder unbinder;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
unbinder = ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}
#Override public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
BINDING RESET Fragments have a different view lifecycle than
activities. When binding a fragment in onCreateView, set the views to
null in onDestroyView. Butter Knife returns an Unbinder instance when
you call bind to do this for you. Call its unbind method in the
appropriate lifecycle callback.
Trying to run method in a layout file from my view model but, I keep getting error: cannot find symbol class ViewModels from my layout file. The layout file is a fragment. I tried invalidating the cache and resyncing but, it did not help. Do i need to add anything to the fragment itself? I seen people use data binding to launch the fragment but, I've read its optional.
Update: Took out OnClick method to test and it is still throwing error. I guess the problem is with my deceleration but, idk why. When i am editing the layout the path shows up when I type the view model name in.
Update2: Tried setting type in variable equal to path of activity that launches fragment for testing and it built just fine. There must be a way to add an import that is not my activity.
Layout file
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="viewTest"
type="rangers.socmanv2.ViewModels.BattleRhythmViewModel" />
</data>
<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/battleRhythmMenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Fragments.BattleRhythmFrag">
<Button
android:id="#+id/newBattle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="76dp"
android:layout_marginLeft="76dp"
android:layout_marginTop="88dp"
android:text="New Battle Rhythm"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<Button
android:id="#+id/editBattle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="76dp"
android:layout_marginLeft="76dp"
android:layout_marginTop="50dp"
android:text="Edit Battle Rhythm"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/newBattle" />
<Button
android:id="#+id/deleteBattle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="76dp"
android:layout_marginLeft="76dp"
android:layout_marginTop="50dp"
android:text="Delete Battle Rhythm"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/editBattle" />
</android.support.constraint.ConstraintLayout>
</layout>
Fragment
public class BattleRhythmFrag extends Fragment {
private BattleRhythmViewModel mViewModel;
private View view;
private Button test;
public static BattleRhythmFrag newInstance() {
return new BattleRhythmFrag();
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.battle_rhythm_fragment, container, false);
mViewModel = new BattleRhythmViewModel();
return view;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(BattleRhythmViewModel.class);
// TODO: Use the ViewModel
}
}
View model
public class BattleRhythmViewModel extends ViewModel {
public void launchNewFragment(FragmentManager fragM)
{
Log.d("viewmodel","hitting launchNewFrag");
HomeFrag test = new HomeFrag();
fragM.beginTransaction().replace(R.id.homeContent,test,"launched"+test);
}
public void test()
{Log.d("viewmodel","hitting test");
}
}
Your error is in viewModel constructor.
you must add a default constructor beacuse you don't added any factory ViewModelProvider.Factory but your BattleRhythmViewModel dosen't have any default constructor.
ANSWER 1:
public class BattleRhythmViewModel extends ViewModel {
public void launchNewFragment(){
}
public void test()
{Log.d("viewmodel","hitting test");
}
}
}
ANSWER 2: better than answer 1 beacuse you don't need add default and can use your fragment in model
public class Factory implements ViewModelProvider.Factory {
private FragmentManager fragM;
public Factory(FragmentManager fragM) {
this.fragM= fragM;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
if (modelClass.isAssignableFrom(BattleRhythmViewModel.class)) {
return (T) new BattleRhythmViewModel(fragM);
}
throw new IllegalArgumentException("Unknown ViewModel class");
}
}
change your viewmodel creator line to
Factory factory = new Factory(this)
mViewModel = ViewModelProviders.of(this,factory).get(BattleRhythmViewModel.class);
I am building an Android app using Fragments. In the XML file for one of my Fragments I simply have one ListFragment and one Button, like this (the FilteredRecipesListFragment is extending ListFragment):
<?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" >
<com.mycompany.myapp.gui.FilteredRecipesListFragment
android:id="#+id/filtered_recipes_list_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true" />
<Button
android:id="#+id/show_recipe_filter_dialog_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/show_recipe_filter_dialog_button"
android:onClick="showFilter"
/>
</LinearLayout>
Fairly simple stuff. Furthermore the class FilteredRecipesFragment that is to inflate this XML file looks like this:
public class FilteredRecipesFragment extends Fragment {
private FilteredRecipesListFragment mFilteredRecipesListFragment;
private Button mShowRecipeFilterButton;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.filtered_recipes_fragment, container, false);
mFilteredRecipesListFragment = (FilteredRecipesListFragment) getFragmentManager().findFragmentById(R.id.filtered_recipes_list_fragment);
mShowRecipeFilterButton = (Button) rootView.findViewById(R.id.show_recipe_filter_dialog_button);
showRecipeFilterButton.setOnClickListener(new RecipeFilterButtonListener());
return rootView;
}
}
Should also be straightforward. The problem is at the line inflating FilteredRecipesListFragment. Here I get NoSuchMethodException: FilteredRecipesListFragment(Context, AttributeSet), because I have not implemented that constructor in my FilteredRecipesListFragment class. I am not sure why I would need that, since calling super(Context, AttributeSet) is not an option in Fragments as in Views.
And this is probably where I am heading wrong; am I approaching the whole concept of Fragments wrong in this case? Is it better practice or does it make more sense to use a ListView instead of a custom ListFragment inside another Fragment? If this is ok using the FilteredRecipesListFragment(Context, AttributeSet) method, how should I use this to make the class inflatable?
Here is my FilteredRecipesListFragment for reference:
public class FilteredRecipesListFragment extends ListFragment
{
private FilteredRecipesListAdapter mRecipeAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mRecipeAdapter = new FilteredRecipesListAdapter(getActivity(), null);
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
public void onPause()
{
super.onPause();
}
#Override
public void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
}
}
Try looking at this.
Look at how you should declare a fragment in XML:
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="#+id/headlines_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />