findViewById fails in onViewCreated of a PreferenceFragment, Android - android

I have two fragments :
- HomeFragment that extends Fragment
- SettingsFragment that extends PreferenceFragment
Each one contains a TextView that I wish to access and modify its content in the Java code. I make this modification inside the onViewCreated by calling findViewById and then, using setText on the TextView.
This works fine for HomeFragment, but it does not in SettingsFragment.
For information, the TextView of SettingsFragment is defined in the layout associated to the Preference. Strangely, it seems that the views hierarchy of PreferenceFragment type is not fully ready in the onViewCreated ! To go further, I tried to put the code that modifies the TextView after some delay (using postDelay), and that worked !
Below, I put all the code necessary to test the issue I have just raised.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final HomeFragment homeFragment = new HomeFragment();
final SettingsFragment settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.add(R.id.fragmentContainer, homeFragment).commit();
// Just a simple button to switch between the two fragments
Button button = findViewById(R.id.activityButton);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(homeFragment.isAdded()) {
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, settingsFragment).commit();
} else {
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, homeFragment).commit();
}
}
});
}
}
HomeFragment.java
public class HomeFragment extends Fragment {
public HomeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView textView = view.findViewById(R.id.homeText);
if(textView != null) {
textView.setText("Home fragment text (modified)");
}
}
}
SettingsFragment.java
public class SettingsFragment extends PreferenceFragment {
public SettingsFragment() {
// Required empty public constructor
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fragment_settings);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void onViewCreated(final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// ---- BLOCK 1 -----
TextView textView = view.findViewById(R.id.settingsText);
if (textView != null) {
textView.setText("Preference 1 (modified)");
}
// ---- BLOCK 2 -----
// view.post(new Runnable() {
// #Override
// public void run() {
// TextView textView = view.findViewById(R.id.settingsText);
// if (textView != null) {
// textView.setText("Preference 1 (modified)");
// }
// }
// });
// ---- BLOCK 3 -----
// view.postDelayed(new Runnable() {
// #Override
// public void run() {
// TextView textView = view.findViewById(R.id.settingsText);
// if (textView != null) {
// textView.setText("Preference 1 (modified)");
// }
// }
// }, 100);
}
}
activity_main.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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/activityText"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:gravity="center_horizontal"
android:text="MainActivity" />
<Button
android:id="#+id/activityButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/activityText"
android:text="Change fragment"/>
<FrameLayout
android:id="#+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/activityButton"
app:layout_constraintBottom_toBottomOf="parent">
</FrameLayout>
</android.support.constraint.ConstraintLayout>
fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".HomeFragment">
<TextView
android:id="#+id/homeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="Home fragment text" />
</android.support.constraint.ConstraintLayout>
fragment_settings.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:layout="#layout/preference_layout_category">
<Preference
android:enabled="true"
android:layout="#layout/preference_layout" />
</PreferenceCategory>
</PreferenceScreen>
preference_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="#+id/settingsText"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textAllCaps="true"
android:text="Preference 1"/>
</android.support.constraint.ConstraintLayout>
preference_layout_category.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="#+id/settingsTextContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textAllCaps="true"
android:background="#android:color/background_dark"
android:textColor="#android:color/background_light"
android:text="Category 1"/>
</android.support.constraint.ConstraintLayout>

I had something similar the other day.
Use findViewById() in the on onCreateView of the fragment instead of the onViewCreated method.
Good luck.

Bind your views in onCreateView() instead like this
View view= inflater.inflate(R.layout.your_fragment, container, false);
TextView textView = view.findViewById(R.id.settingsText);
return view;

Related

how to resolve Null Pointer Exception in android? [duplicate]

This question already has answers here:
Avoiding NullPointerException in Java
(66 answers)
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 7 months ago.
i'm working with fragments. I have created a List fragment and a Detail fragment.
when i click on an item in the list the app crashes and it gives the following null pointer exception.
Attempt to invoke virtual method 'void
android.widget.TextView.setText(java.lang.CharSequence)' on a null
object reference
I tried solutions of similar question but nothing works for me.
here is my Main Activity:
public class MainActivity extends AppCompatActivity implements ListFrag.ItemSelected {
TextView tvDescription;
ArrayList<String> descriptions;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvDescription = (TextView) findViewById(R.id.tvDescription);
descriptions = new ArrayList<>();
descriptions.add("Description for item 1");
descriptions.add("Description for item 2");
descriptions.add("Description for item 3");
}
#Override
public void onItemSelected(int index) {
tvDescription.setText(descriptions.get(index));
}
}
List Fragment:
public class ListFrag extends ListFragment {
ItemSelected activity;
public interface ItemSelected
{
void onItemSelected(int index);
}
public ListFrag() {
// Required empty public constructor
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
activity = (ItemSelected) context;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ArrayList<String> data = new ArrayList<>();
data.add("1.This is item 1");
data.add("2.This is item 2");
data.add("3.This is item 3");
setListAdapter(new ArrayAdapter<>( getActivity() , android.R.layout.simple_list_item_1,data));
}
#Override
public void onListItemClick(#NonNull ListView l, #NonNull View v, int position, long id) {
activity.onItemSelected(position);
} }
Detail Fragment:
public class DetailFrag extends Fragment {
public DetailFrag() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_detail, container, false);
}
}
activity_main layout:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/listFrag"
android:name="com.example.fragmentspart1.ListFrag"
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
tools:layout="#layout/fragment_list" />
<androidx.fragment.app.FragmentContainerView
android:id="#+id/detailFrag"
android:name="com.example.fragmentspart1.DetailFrag"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="2"
tools:layout="#layout/fragment_detail" />
</LinearLayout>
fragment_list layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFFFFF"
tools:context=".ListFrag">
<ListView
android:id="#+id/lvList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
fragment_detail layout :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#color/design_default_color_secondary"
tools:context=".DetailFrag">
<TextView
android:id="#+id/tvDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:text="#string/textview"
android:textColor="#FFFFFF"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
(TextView) findViewById(R.id.tvDescription);
returns null in MainActivity because your TextView tvDescription is in fragment_detail layout.
You must move this line to onViewCreated() of your DetailFragment and in onItemSelected() you must create a new instance of DetailFragmet and pass data as arguments to the DetailFragment instance.
After you start the DetailFragment you can do following in DetailFragment:
override onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
tvDescription = (TextView) findViewById(R.id.tvDescription);
// get your list item data from arguments
tvDescription.setText(descriptions.get(index));
}

Shared element back transition not working with recyclerview and cardviews in fragments

I'm trying to create a transition between a recycler view with cardviews fragment to a fragment that only contains 1 card. The problem is that the back transition is not working, while the enter transition is. If I remove setReorderingAllowed(true); then the back transition is working, but the enter transition stops working.
This is what I have.
Fragment with recyclerview with cardviews
public class OrdersFragment extends Fragment implements OrderAdapter.OnOrderListener {
private OrderAdapter mAdapter;
private TextView bonnenTextView;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
postponeEnterTransition();
}
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_bonnen, container, false);
bonnenTextView = view.findViewById(R.id.bonnen_text_view);
RecyclerView orderRecyclerView = view.findViewById(R.id.order_recycler_view);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
orderRecyclerView.setLayoutManager(layoutManager);
if (mAdapter == null) {
mAdapter = new OrderAdapter(this);
fetchOrders();
}
orderRecyclerView.setAdapter(mAdapter);
return view;
}
private void fetchOrders() {
new OrderFetcher().fetch(new Callback() {
#Override
public void onComplete(Result result) {
if (result instanceof Result.Success) {
mAdapter.setOrders((Order[]) ((Result.Success<?>)result).data);
mAdapter.notifyDataSetChanged();
} else {
Toast.makeText(getContext(), "Could not load orders", Toast.LENGTH_SHORT).show();
}
startPostponedEnterTransition();
}
});
}
#Override
public void onOrderClick(int position, View view, Order order) {
Log.i("OrderClick", "Transition name " + view.getTransitionName());
View carrierTextView = view.findViewById(R.id.carrier_text_view);
View numberTextView = view.findViewById(R.id.id_text_view);
View pickerTextView = view.findViewById(R.id.picker_text_view);
View locationTextView = view.findViewById(R.id.location_text_view);
FragmentTransaction transaction = getParentFragmentManager().beginTransaction();
transaction.addSharedElement(view, view.getTransitionName());
transaction.addSharedElement(carrierTextView, carrierTextView.getTransitionName());
transaction.addSharedElement(numberTextView, numberTextView.getTransitionName());
transaction.addSharedElement(pickerTextView, pickerTextView.getTransitionName());
transaction.addSharedElement(locationTextView, locationTextView.getTransitionName());
transaction.addSharedElement(bonnenTextView, bonnenTextView.getTransitionName());
transaction.replace(R.id.nav_host_fragment, BonFragment.newInstance(view.getTransitionName(), order));
transaction.addToBackStack(null);
transaction.setReorderingAllowed(true);
transaction.commit();
}
}
xml that goes with fragment above
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.orders.OrdersFragment">
<TextView
android:id="#+id/bonnen_text_view"
android:transitionName="bonnen_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:text="#string/orders"
android:textColor="#color/secondary"
android:textSize="40sp"
android:textStyle="bold"
android:typeface="normal" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/order_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"/>
</LinearLayout>
Fragment with single cardview
public static BonFragment newInstance(String cardTransitionName, Order order) {
BonFragment bonFragment = new BonFragment();
Bundle args = new Bundle();
args.putParcelable("orderParcel", order);
args.putString("cardTransitionName", cardTransitionName);
bonFragment.setArguments(args);
return bonFragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View root = inflater.inflate(R.layout.fragment_bon, container, false);
CardView orderCard = root.findViewById(R.id.order_card);
TextView carrierTextView = root.findViewById(R.id.carrier_text_view);
TextView numberTextView = root.findViewById(R.id.id_text_view);
TextView pickerTextView = root.findViewById(R.id.picker_text_view);
TextView locationTextView = root.findViewById(R.id.location_text_view);
if (getArguments() != null && getArguments().getParcelable("orderParcel") != null) {
Order order = getArguments().getParcelable("orderParcel");
orderCard.setTransitionName(getArguments().getString("cardTransitionName"));
if (order != null) {
carrierTextView.setTransitionName("carrier" + order.getIndex());
carrierTextView.setText(order.getCarrier());
numberTextView.setTransitionName("number" + order.getIndex());
numberTextView.setText(String.valueOf(order.getNumber()));
pickerTextView.setTransitionName("picker" + order.getIndex());
pickerTextView.setText(order.getPicker());
locationTextView.setTransitionName("location" + order.getIndex());
locationTextView.setText(order.getPosition());
carrierTextView.setText("Lorem Ipsum");
numberTextView.setText("Dolor sit amet");
pickerTextView.setText("consectetur adipiscing elit");
locationTextView.setText("Mauris semper");
}
}
orderCard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getParentFragmentManager().popBackStack();
}
});
Transition transition = TransitionInflater.from(getContext()).inflateTransition(R.transition.card_transition);
setSharedElementEnterTransition(transition);
setSharedElementReturnTransition(transition);
return root;
}
xml that goes with fragment above
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.orders.OrdersFragment">
<TextView
android:transitionName="bonnen_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:text="#string/orders"
android:textColor="#color/secondary"
android:textSize="40sp"
android:textStyle="bold"
android:typeface="normal" />
<androidx.cardview.widget.CardView
android:id="#+id/order_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardBackgroundColor="#color/primaryLight"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:layout_margin="25dp">
<LinearLayout
android:transitionName="order_card_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="10dp">
<TextView
android:id="#+id/carrier_text_view"
android:transitionName="carrier_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/secondary"
android:textSize="20sp"
android:textStyle="bold"/>
<TextView
android:id="#+id/id_text_view"
android:transitionName="id_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/secondary" />
<TextView
android:id="#+id/picker_text_view"
android:transitionName="picker_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/secondary" />
<TextView
android:id="#+id/location_text_view"
android:transitionName="location_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/secondary" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
This is what it looks like with setReorderingAllowed(true);
This is what it looks like without setReorderingAllowed(true);
EDIT
After applying the answer given by ianhanniballake I got it working.
Here is what I did for future reference.
The only changes I made is in the OrdersFragment class.
I removed the override of the onCreate() method.
I removed the startPostponedEnterTransition(); from my fetchOrders() method and I added
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
postponeEnterTransition();
final ViewGroup parentView = (ViewGroup) view.getParent();
parentView.getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
parentView.getViewTreeObserver().removeOnPreDrawListener(this);
startPostponedEnterTransition();
return true;
}
});
super.onViewCreated(view, savedInstanceState);
}
That's it
As per the Use shared element transitions with a RecyclerView guide, you're calling startPostponedEnterTransition() too early - after setting your data (and calling notifyDataSetChanged() to inform the adapter), you need to wait until the RecyclerView is actually measured and laid out. This requires that you add an OnPreDrawListener and only call startPostponedEnterTransition() once that fires.
In Kotlin you need to start the transition in doOnPreDraw callback of the recyclerView like below:
recyclerView.doOnPreDraw {
startPostponedEnterTransition()
}

Android floatingactionbutton causing memory leak

I have seen several memory leak posts here and I think this one is a bit different. I have a floatingactionbutton in my fragment, whenever I navigate to another fragment using the Navigation graph the fab is reported to be leaking by leakcanary 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:layout_width="match_parent"
android:id="#+id/layoutCategories"
android:layout_height="wrap_content"
android:inflatedId="#+id/panel_layoutCategories"
android:layout="#layout/store_layout_categories" />
<ViewStub
android:id="#+id/layoutDeals"
android:layout_height="wrap_content"
android:inflatedId="#+id/panel_layoutDeals"
android:layout_width="match_parent"
android:layout="#layout/store_layout_deals" />
<ViewStub
android:id="#+id/layoutCollections"
android:layout_height="wrap_content"
android:inflatedId="#+id/panel_layoutCollections"
android:layout_width="match_parent"
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" />
The btnGoToCart is the problematic fab
And my fragment looks like
public class StoreFragment extends Fragment implements View.OnClickListener {
#BindView(R.id.btnGoToCart)
ExtendedFloatingActionButton btnGoToCart;
private StoreFragmentViewModel storeFragmentViewModel;
public StoreFragment() {
// Required empty public constructor
}
#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);
loadData();
}
private void loadData() {
disposables.add(SharedPreferencesUtils
.getInstance(getContext())
.isLoggedIn()
.subscribeWith(new DisposableSingleObserver<Boolean>() {
#Override
public void onSuccess(Boolean aBoolean) {
if (aBoolean)
storeFragmentViewModel.fetchProductCategories();
}
#Override
public void onError(Throwable e) {
}
}));
}
#Override
public void onStart() {
super.onStart();
}
#Override
public void onStop() {
super.onStop();
//I tried this hoping it would work nothing
btnGoToCart.clearAnimation();
((ViewGroup) btnGoToCart.getParent()).removeView(btnGoToCart);
btnGoToCart = null;
}
#Override
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
}
}
I tried doing this in the onStop
//I tried this hoping it would work nothing
btnGoToCart.clearAnimation();
((ViewGroup) btnGoToCart.getParent()).removeView(btnGoToCart);
btnGoToCart = null;
But it did not work
Below are the snippets from Leak canary
Can you share the ExtendedFloatingActionButton implementation? Maybe your doing something wrong there.
Also 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.

Passing data between two fragments in two different activities

I am trying to do a very basic example with Fragments.
Structure : Fragment1 -> Fragment1Activity, Fragment2 -> Fragment2Activity.
Both activities have a STATIC Fragment in it.
Here are the XMLs:
activity_for_fragment_1.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.xx.fragmentstutorial1.Fragment1"
tools:context="com.xx.fragmentstutorial1.Fragment1Activity"/>
fragment1.xml
<?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" >
<TextView
android:id="#+id/textview_fragment_1"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="0.60"
android:text="This is Fragment 1"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="#+id/edittext_fragment_1_text"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="0.20"
android:text="Type your message here..." >
<requestFocus />
</EditText>
<Button
android:id="#+id/button_fragment_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Click Me" />
</LinearLayout>
activity_for_fragment_2.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.xx.fragmentstutorial1.Fragment2"
tools:context="com.xx.fragmentstutorial1.Fragment2Activity"/>
fragment_2.xml
<?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" >
<TextView
android:id="#+id/textview_fragment_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="This is Fragment 2"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/textview_fragment_2_result"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="I will display text from \nFragment 1"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
Now, I have an Edittext and button in the fragment_1. When I click on the button, I want to get the text entered in EditText and set it to the textview(textview_fragment_2_result) in fragment_2
I was able to achieve this, but, I am not very convinced, that the approach I took was good enough. Please look at the java code below..
Fragment1Activity.java
public class Fragment1Activity extends SherlockFragmentActivity implements Fragment1.ButtonClickListener{
Fragment1 fragment1;
#Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_for_fragment_1);
System.out.println("onCreate Fragment1Activity");
fragment1 = (Fragment1) getSupportFragmentManager().findFragmentById(R.id.fragment_1);
}
#Override
public void onButtonClick(String message) {
System.out.println("onButtonClick Fragment1Activity");
startActivity(new Intent(this, Fragment2Activity.class).putExtra("message", message));
}
}
Fragment1.java
public class Fragment1 extends SherlockFragment {
EditText message;
Button clickme;
ButtonClickListener listener;
public interface ButtonClickListener{
public void onButtonClick(String message);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof ButtonClickListener)
listener = (ButtonClickListener) activity;
else {
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
System.out.println("onCreateView Fragment1");
View view = inflater.inflate(R.layout.fragment_1, container, false);
message = (EditText) view.findViewById(R.id.edittext_fragment_1_text);
clickme = (Button) view.findViewById(R.id.button_fragment_1);
clickme.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (!message.getText().toString().equalsIgnoreCase("")) {
System.out.println("Message in Fragment1 = "+message.getText().toString());
listener.onButtonClick(message.getText().toString());
} else {
Toast.makeText(getActivity(),
"Please enter some message...", Toast.LENGTH_LONG)
.show();
}
}
});
return view;
}
}
Fragment2Activity.java
public class Fragment2Activity extends SherlockFragmentActivity {
Fragment2 fragment2;
#Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_for_fragment_2);
fragment2 = (Fragment2) getSupportFragmentManager().findFragmentById(R.id.fragment_2);
fragment2.setMessage(getIntent().getExtras().getString("message").toString());
}
}
Fragment2.java
public class Fragment2 extends SherlockFragment {
String msg;
TextView textview;
public Fragment2() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_2, container, false);
textview = (TextView) view
.findViewById(R.id.textview_fragment_2_result);
textview.setText(msg);
return view;
}
public void setMessage(String message) {
msg = message;
textview.setText(msg);
}
}
I have set the text for textview in setMessage() of Fragment2.java, which I think is not a good approach. If i comment that out, I don't see any text in the textview of Fragment2.
Can someone help me out, on how to pass values between two static fragments correctly.
You should go with interface
Follow the Diagram Here :
Communication Between Two Fragment is Done Via Activity using Interface
Fragment A-------------------------->Activity-------------------->Fragment B
(defines Interface) (Implement interface) (pass it other Fragment B)
Hope this could help
Check This Out
What you have is mostly good. The part of transferring the data from Fragment1 to Activity1 is good. Once the data gets to Activity2, you are setting it in Fragment2. I would change this part a little.
In Activity2:
#Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_for_fragment_2);
fragment2 = (Fragment2) getSupportFragmentManager().findFragmentById(R.id.fragment_2);
fragment2.setArguments(getIntent().getExtras());
}
and in Fragment2.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Bundle bundle = getArguments();
String message = bundle.getString("message");
View view = inflater.inflate(R.layout.fragment_2, container, false);
textview = (TextView) view
.findViewById(R.id.textview_fragment_2_result);
textview.setText(message);
return view;
}
Your approach seems correct. The connection between your Fragment1 and Activity1 is just like in the fragments guideline. Your fragment1 shouldn't know about fragment2 therefore it's OK to pass the responsibility to the fragment's container/parent - activity1 - that knows what to do with the data. This time again your approach is correct - activity1 starts it's slave/child etc. and is responsible to start it with correct data (passed through the intent extras). And then again you have a correct approach - when your activity2 is created you populate it's views with the appropriate content/data (the message) that was meant to be displayed in this activity and given to it by the "parent" activity.

Android: how to get the views of a Fragment

In a class i am adding a Fragment programatically.
This Fragment contains a Button, which i want to have an onClick implementation.
Here is the class i have:
public class ShareProduct extends SherlockFragmentActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.share_product);
FragmentManager fragmentManager = getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
ProductFragment1 fragment = new ProductFragment1();
fragmentTransaction.add(R.id.share_product_parent, fragment);
fragmentTransaction.commit();
Button done_button = (Button) fragment.getView().findViewById(
R.id.done_product_1);
done_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
ProductFragment2 fragment = new ProductFragment2();
fragmentTransaction
.replace(R.id.share_product_parent, fragment);
fragmentTransaction.commit();
}
});
}
}
and my ProductFragment1 class is:
public class ProductFragment1 extends SherlockFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater
.inflate(R.layout.share_product_1, container, false);
return view;
}
}
and the layout my main class uses is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/share_product_parent"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="right|center_vertical"
android:orientation="horizontal" >
</RelativeLayout>
and the layout my ProductFragment1 uses is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/tv1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="25dp" />
<EditText
android:id="#+id/et1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/tv1"
android:textSize="25dp" />
<Button
android:id="#+id/done_product_1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/et1"
android:text="Done" />
</RelativeLayout>
The problem is:
I am getting a NullPointerException at this line in my main class:
Button done_button = (Button) fragment.getView().findViewById(R.id.done_product_1);
done_button.setOnClickListener ....
when i searched for what getView() on a fragment returns. I found out that it returns the view from onCreateView();
I checked it doesn't return null.
Thank You
You are on the wrong track here. You get the NPE because the Fragments views is not created yet. Fragments also have a lifecycle just like Activities.
What you should be doing is assigning the OnClickListener inside your Fragments onViewCreated() method. And from there... you should create a callback and notify your hosting Activity of the event.

Categories

Resources