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");
}
});
}
Related
I am creating a time prompter app for a sport activity. Not able to move back from Settings Fragment to Launcher Fragment. The code is similar to code that moves back from Action Fragment to Launcher Fragment successfully. I am getting error here that findNavController in NavHostFragment cannot be applied to SettingsFragment. It also says SettingsFragment cannot be converted to Fragment.
public void onClick(View view) {
NavHostFragment.findNavController(SettingsFragment.this)
.navigate(R.id.action_SettingsFragment_to_LauncherFragment);
public class SettingsFragment extends Fragment {
private FragmentSettingsBinding binding;
#Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {
binding = FragmentSettingsBinding.inflate(inflater, container, false);
return binding.getRoot();
}
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.goBackButtonSettings.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
NavHostFragment.findNavController(SettingsFragment.this)
.navigate(R.id.action_SettingsFragment_to_LauncherFragment);
}
});
}
}
SettingsFragment xml is below
<?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:contentDescription="#string/start_action"
android:foregroundTint="#color/teal_700"
android:textAlignment="center"
tools:context=".SettingsFragment">
<Button
android:id="#+id/goBackButtonSettings"
android:layout_width="65dp"
android:layout_height="54dp"
android:layout_marginBottom="24dp"
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" />
</androidx.constraintlayout.widget.ConstraintLayout>
Not able to locate where exactly the code is wrong and fix it. Need help.
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.
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)}"
Problem
As shown in the figure below, I have two tabs called "température" which are created in the same way (see mainFragment). In this two tabs, I have a viewpager to get previous graphics ( 05 august, 04 august,...).
The first tab works well but in the second, nothing appears. When debugging, I can see that the fragment is created in both cases. It's only on the second tab where the problem is. Even more, I can see the viewpager working when sliding to the left or right end.
I'm seeking since hours now and I don't understand where the problem is. Why the fragments don't appear ?
I'm using android.support.v13.app.FragmentStatePagerAdapter for the adapter.
Here is a short movie to show the problem :
https://www.dropbox.com/s/vf8qlrd75mdwijp/device-2017-08-06-214510.mp4?dl=0
Code
MainFragment
public class MainFragment extends Fragment implements BTListener {
// the different ID for the views.
private final static int ID_TEMPERATURE = 1;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.main,container,false);
final ViewGroup mainContainer = (ViewGroup) rootView.findViewById(R.id.main_container);
createTemperatureTab(mainContainer);
createTemperatureTab(mainContainer);
return rootView;
}
private void createTemperatureTab(ViewGroup mainContainer){
//inflate the view
ViewGroup view = (ViewGroup) LayoutInflater.from(getActivity()).inflate(R.layout.main_list,mainContainer,false);
((TextView)view.findViewById(R.id.title_main_list)).setText("Température");
((TextView)view.findViewById(R.id.information_main_list)).setText("°C");
final ExpandableLayout expandableLayout = (ExpandableLayout) view.findViewById(R.id.expandable_layout);
CustomPager viewPager = (CustomPager) view.findViewById(R.id.viewpager);
LinearGraphAdapter linearGraphAdapter = new LinearGraphAdapter(getChildFragmentManager());
viewPager.setAdapter(linearGraphAdapter);
viewPager.setCurrentItem(linearGraphAdapter.getCount());
expandableLayout.setExpanded(false);
expandableLayout.setDuration(500);
expandableLayout.setInterpolator(new DecelerateInterpolator());
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(expandableLayout.isExpanded()) {
expandableLayout.collapse();
}
else {
expandableLayout.expand();
}
}
});
mainContainer.addView(view);
}
}
main_list
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:src="#mipmap/ic_launcher" />
<TextView
android:id="#+id/title_main_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:textSize="20sp" />
<TextView
android:id="#+id/information_main_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="80dp"
android:textSize="20sp" />
</LinearLayout>
<net.cachapa.expandablelayout.ExpandableLayout
android:id="#+id/expandable_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.example.renaud.aquariumslinding.adapter.CustomPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.example.renaud.aquariumslinding.adapter.CustomPager>
</net.cachapa.expandablelayout.ExpandableLayout>
</LinearLayout>
LineGraphFragment
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.linear_graph_layout,container,false);
lineChart = (LineChart) rootView.findViewById(R.id.linear_graph);
initiateChart(lineChart); // not important for this case
drawChart(lineChart,dayOfYear); // not important for this case
return rootView;
}
linear_graph_layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/linear_graph_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:elevation="4dp"
android:textColor="#fff" />
<com.github.mikephil.charting.charts.LineChart
android:id="#+id/linear_graph"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
LinearGraphAdapter
public class LinearGraphAdapter extends FragmentStatePagerAdapter {
public static int MAXIMUM_COUNT_PAGER = 7;
private int mCurrentPosition = -1;
public LinearGraphAdapter(FragmentManager fragmentManager){
super(fragmentManager);
}
#Override
public Fragment getItem(int position) {
Bundle bundle = new Bundle();
bundle.putInt(Constant.CUSTOM_PAGER_NUMBER,MAXIMUM_COUNT_PAGER-position);
Fragment fragment = new LinearGraphFragment();
fragment.setArguments(bundle);
return fragment;
}
#Override
public int getCount() {
return MAXIMUM_COUNT_PAGER;
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
if (position != mCurrentPosition) {
Fragment fragment = (Fragment) object;
CustomPager pager = (CustomPager) container;
if (fragment != null && fragment.getView() != null) {
mCurrentPosition = position;
pager.measureCurrentView(fragment.getView());
}
}
}
}
Ok, after a night of research and reading the API I figured it out. I don't understand everything but it seems the v13 and v4 library have a conflict.
To override it, firstly :
Make sure that you called getChildFragmentManager() instead of getFragmentManager()
Create a different ID for all the different viewpageryou have. Those IDs must have at least 4 digits. Of course, it is better to have those in R.String
Set your ID in every viewpager by calling setID()
If it doesn't work, please clean and rebuild your project.
And finally, the reward :
I hope that it will help.