Android floatingactionbutton causing memory leak - android

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.

Related

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()
}

Why is onActivityCreated() not called in second fragment?

I am creating an app in which in portrait mode I have an activity that has a fragment which contains a listview and on clicking on that listview, Second activity is called to show the data using second fragment but the problem is that when second fragment, i.e. Frag2 is called its onActivityCreated() method is not getting called. Why so?
MainActivity.xml
portrait mode
<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"
tools:context="com.example.ankit.fragprac2.MainActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/frag1"
android:name="com.example.ankit.fragprac2.Frag1"
/>
</LinearLayout>
landscape mode
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/frag1"
android:name="com.example.ankit.fragprac2.Frag1"
android:layout_weight="1"/>
<fragment
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/frag2"
android:name="com.example.ankit.fragprac2.Frag2"
android:layout_weight="1"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements Frag1.Comm {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void respond(int i)
{
FragmentManager fm=getFragmentManager();
Frag2 fg2= (Frag2) fm.findFragmentById(R.id.frag2);
if(fg2!=null && fg2.isVisible())
{
fg2.setData(i);
}
else
{
Intent in=new Intent(this,SecondActivity.class);
in.putExtra("position",i);
startActivity(in);
}
}
}
Fragment1.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/list"/>
</RelativeLayout>
Fragment1.java
public class Frag1 extends Fragment {
ListView lv;
Comm c1;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.activity_frag1,container,false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String[] s=getResources().getStringArray(R.array.topics);
c1 = (Comm) getActivity();
lv = getActivity().findViewById(R.id.list);
lv.setAdapter(new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1,s));
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
c1.respond(i);
}
});
}
interface Comm
{
void respond(int i);
}
}
Fragment2.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/text1"
android:layout_centerHorizontal="true"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="#000"/>
</RelativeLayout>
Fragment2.java
public class Frag2 extends Fragment {
TextView t;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.activity_frag2,container,false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Toast.makeText(getActivity(),"OAC",Toast.LENGTH_SHORT).show();
t=getActivity().findViewById(R.id.text1);
// s1=new String[getResources().getStringArray(R.array.data).length];
// Toast.makeText(getActivity(),""+s1.length,Toast.LENGTH_SHORT).show();
if(savedInstanceState!=null)
{
String s=savedInstanceState.getString("data",null);
t.setText(s);
}
}
public void setData(int i)
{ String[] s1 =getResources().getStringArray(R.array.data);
t.setText(s1[i]);
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("data",t.getText().toString());
}
}
SecondActivity.xml
<RelativeLayout 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="com.example.ankit.fragprac2.SecondActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/frag21"
android:name="com.example.ankit.fragprac2.Frag2"/>
</RelativeLayout>
SecondActivity.java
public class SecondActivity extends AppCompatActivity {
int i;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Bundle b= getIntent().getExtras();
if(b!=null)
{
i= b.getInt("position",0);
}
FragmentManager fm=getFragmentManager();
Frag2 fga2= (Frag2) fm.findFragmentById(R.id.frag21);
fga2.setData(i);
}
}
In the SecondActivity.onCreate(), move this code:
FragmentManager fm=getFragmentManager();
Frag2 fga2= (Frag2) fm.findFragmentById(R.id.frag21);
fga2.setData(i);
To SecondActivit.onStart().
When using fga2.setData(i) in the onCreate, the Activity is not created yet; it is being created. Hence the onActivityCreated() method of the fragment has not been called yet.
Other notes:
Activity should extent FragmentActivity instead of AppCompatActivity.
you can use getSupportFragmentManager().findFragmentById to get the fragment.
Also see the Activity Life Cycle and Fragment LifeCycle.
Hope this helps.

Adding scrolllistener to a recyclerview and hide a view on another layout

I have some fragments let's say A, B, and C. I get the corresponding layouts for these fragments using inflate method.
In A and B, I have a Recycler View which I tend to add a Scroll Listener. Also, in fragment C, I have a view (let's say a tablayout) which I want to hide/show it by scrolling the recycler views (which are in A and B).
I am not sure how can I achieve that. Since the tablayout is in the layout of fragment C (and not in A and B).
I was trying to inflate the layout of C in A and B, but it didn't work.
This is the scrolllistener on recycler view (fragments A and B):
View view = inflater.inflate(R.layout.fragment_A, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.myChannelsRecyclerView);
tabLayout = (SmartTabLayout) view.findViewById(R.id.tabs);
recyclerView.addOnScrollListener(new HideShowScrollListener() {
#Override
public void onHide() {
//tabLayout.animate().translationY(250);
//tabLayout return null
}
#Override
public void onShow() {
//tabLayout.animate().translationY(0);
//tabLayout return null again
}
});
I should note that HideShowScrollListener() is a helper method which is written by me.
fragment_A.xml:
<RelativeLayout 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.support.v7.widget.RecyclerView
android:id="#+id/myChannelsRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible" />
</RelativeLayout>
main_fragment.xml (fragment C)
<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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<come.example.CustomViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<smartertablayout.SmartTabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:stl_dividerThickness="1dp"
app:stl_defaultTabTextSize="9sp"
app:stl_distributeEvenly="true"
app:stl_indicatorThickness="0dp"/>
</LinearLayout>
Main fragment (fragment C in example) is responsible to change and replace the other fragments (A and B) using a pager. It somehow is the parent of this fragments
//something like this from my suggestion above
class Fragment_A extends Fragemnt{
YourScollListener animateCallback;
RecyclerView recyclerView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_A, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.myChannelsRecyclerView);
recyclerView.addOnScrollListener(new HideShowScrollListener() {
#Override
public void onHide() {
animateCallback.animate();
}
#Override
public void onShow() {
}
});
return view;
}
public void onAttach(Context context) {
super.onAttach(context);
animateCallback = (YourScrollListener) getParentFragment();
}
public interface YourScrollListener{
void animate();
}
}
//main frag
class Main_Fragment extends Fragemnt implements YourScrollListener{
animate(){
//do stuff
}
}

ZXing barcode scanner in custom layout in fragment 2

Using ZXing Android Embedded
Example taken from here and there is the answer
My fragment class:
public class ExciseBarcodeScanFragment extends AbstractFragment {
private Button button;
private CompoundBarcodeView barcodeView;
#Override
public int getTitleRes() {
return R.string.validation_of_excise_stamps_on_alcohol_products;
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_excise_barcode_scan, container, false);
init(rootView);
return rootView;
}
private void init(View rootView) {
// ...
barcodeView = (CompoundBarcodeView) rootView.findViewById(R.id.barcode_scanner);
barcodeView.decodeContinuous(callback);
}
private BarcodeCallback callback = new BarcodeCallback() {
#Override
public void barcodeResult(BarcodeResult result) {
if (result.getText() != null) {
barcodeView.setStatusText(result.getText());
}
//Do something with code result
}
#Override
public void possibleResultPoints(List<ResultPoint> resultPoints) {
}
};
#Override
public void onResume() {
barcodeView.resume();
super.onResume();
}
#Override
public void onPause() {
barcodeView.pause();
super.onPause();
}
}
Fragment xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingBottom="20dp">
<com.journeyapps.barcodescanner.CompoundBarcodeView
android:id="#+id/barcode_scanner"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="40"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="30dp">
<Button
android:id="#+id/manuallyButton"
style="#style/SearchField.Button"
android:text="#string/search"
android:layout_width="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
Screen
I know that comes as a duplicate, but it does not work.
I'm new, I'm sorry if the horrible formatting.
Please help.
Everything is working. It turns on the emulator as shown does not seem to work, and work on your phone.

Fragment communicating between fragment using interface failed, nullpointerException

Trying to Communicating FragmentA to FragmentB, doing is taking numeric value from fragmentA adding them and displaying result in fragmentB using interface, but I got nullpointerException, I have look many times in the code but seems everything is fine but getting nullpointerException showing line 34 in FragmentA
listener.setResult(result+" ");
Here's My FragmentA
public class FragmentA extends Fragment{
MyFragmentInterface listener;
Button buton;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view =inflater.inflate(R.layout.fragment_a,container,false);
final EditText editText= (EditText) view.findViewById(R.id.edit_text1);
final EditText editText2= (EditText) view.findViewById(R.id.edit_text2);
buton= (Button) view.findViewById(R.id.button);
buton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int num1=Integer.parseInt(editText.getText().toString());
int num2=Integer.parseInt(editText2.getText().toString());
int result=num1+num2;
listener.setResult(result+" ");
}
});
return view;
}
#Override
public void onAttach(Context context) {
listener= (MyFragmentInterface) context;
super.onAttach(context);
}
}
Here's my fragment_a.xml
<?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">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/edit_text1"
android:inputType="number"
android:textSize="30sp"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:id="#+id/edit_text2"
android:textSize="30sp"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Calculate"
android:id="#+id/button"
android:background="#null"
android:textAllCaps="false"/>
</LinearLayout>
FragmentB.java
public class FragmentB extends Fragment {
TextView textView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
textView= (TextView) getActivity().findViewById(R.id.textview);
return inflater.inflate(R.layout.fragment_b,container,false);
}
public void putresult(String r){
textView.setText(r);
}
}
fragment_b.xml
<?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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:id="#+id/textview"
android:text="Text will appear here"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements MyFragmentInterface {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentA fragmentA=new FragmentA();
FragmentManager fragmentManager=getFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.addToBackStack(null);
fragmentTransaction.replace(R.id.linear1,fragmentA);
fragmentTransaction.commit();
FragmentB fragmentB=new FragmentB();
FragmentManager fragmentManager1=getFragmentManager();
FragmentTransaction fragmentTransaction1=fragmentManager1.beginTransaction();
fragmentTransaction1.addToBackStack(null);
fragmentTransaction1.replace(R.id.linear2,fragmentB);
fragmentTransaction1.commit();
}
#Override
public void setResult(String result) {
FragmentB fragmentnew=new FragmentB();
fragmentnew= (FragmentB) getFragmentManager().findFragmentById(R.id.linear2);
fragmentnew.putresult(result);
}
}
MyFragmentInterface.java
public interface MyFragmentInterface {
public void setResult(String result);
}
activity_main.xml
<?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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="queendevelopers.com.communicate.MainActivity"
android:weightSum="2"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="#+id/linear1"
android:orientation="vertical" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="#+id/linear2"
android:orientation="vertical" />
</LinearLayout>
error log:
08-16 16:11:42.307 7630-7630/queendevelopers.com.communicate E/AndroidRuntime: FATAL EXCEPTION: main
Process: queendevelopers.com.communicate, PID: 7630
java.lang.NullPointerException: Attempt to invoke interface method 'void queendevelopers.com.communicate.MyFragmentInterface.setResult(java.lang.String)' on a null object reference
at queendevelopers.com.communicate.FragmentA$1.onClick(FragmentA.java:34)
at android.view.View.performClick(View.java:4832)
at android.view.View$PerformClick.run(View.java:19844)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5319)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1016)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811)
To expand on the comment I made that solved the problem:
The original issue was not to do with your interface callback (this was not the root cause of the NullPointerException).
When the callback method setResult(String result) was invoked this line :
fragmentnew.putresult(result); was causing the null pointer because, in the method putresult in your fragment there is a null reference to your textView (textView.setText(r);).
For every Activity you can have an associated view heirarchy (your XML file), this is set by setContentView() in onCreate(). Now if you try and reference any #+id widgets from the XML layout you can cast relevant object type : TextView, EditText, ListView. Fragments (which are attached to a hosting Activity) can also have their own layouts, this is inflated in onCreateView(). Your final code should look something like this:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_b,container,false);
textView = (TextView) view.findViewById(R.id.textview);
return view;
}
Your view object now contains your view hierarchy for the inflated XML Layout, from which you can access all widgets.
The line of code that was incorrect was:
textView = (TextView) getActivity().findViewById(R.id.textview);
The reason being you are using the convenience method getActivity() this will return the hosting Activity reference - thus you now will have access to the hosting Activity - used a lot for gaining a Context of other purposes, however your view id reference is invalid because it is in the view hierarchy of your inflated layout in your fragment.
Try this method inside your Fragment
#Override
public void onAttach(Context context) {
super.onAttach(context);
listener = (MyFragmentInterface) getActivity();
}
try this
(((MyFragmentInterface)getActivity()).setResult(result+" ");
Change in Fragment A:
from:
buton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int num1=Integer.parseInt(editText.getText().toString());
int num2=Integer.parseInt(editText2.getText().toString());
int result=num1+num2;
listener.setResult(result+" ");
}
});
To
buton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int num1=Integer.parseInt(editText.getText().toString());
int num2=Integer.parseInt(editText2.getText().toString());
int result=num1+num2;
((MainActivity) getActivity()).setResult(result+" ");
}
});
Because I think your listener object is not initialised before being used so try this answer please.

Categories

Resources