I'm using Android fragments to load the code in my application. To create a simple loader I have LoadingFragment extends Fragment
and then my fragment classes extend that, for example: MyFragment extends LoadingFragment
.
The LoadingFragment
has hideLoader
and showLoader
which theoretically my subclass Fragment should be able to call in onCreateView
and onPostExecute
to show and hide a progress bar in between loads.
Layout wise I have a main_layout.xml
which has a framelayout for the dynamic fragments and a static relativelayout that houses the progress bar.
At the moment my fragments load and replace from within one another onclick of elements but I have removed that code.
The Problem
The issue is the setVisibilty
in LoadingFragment
seems to have zero effect when calling it from the subclasses, for example MyFragment
, why is this?
I have given LoadingFragment
it's own View
variable of viewMaster
which I believe should reference main_layout
but still the visibility changes seem to have no effect?
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyFragment myFragment = MyFragment.newInstance();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_holder, myFragment).commit();
}
}
LoadingFragment
public class LoadingFragment extends Fragment {
View viewMaster;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
viewMaster = inflater.inflate(R.layout.main_layout, container, false);
return viewMaster;
}
public void showLoader() {
viewMaster.findViewById(R.id.fragment_holder).setVisibility(View.INVISIBLE);
viewMaster.findViewById(R.id.loading).setVisibility(View.VISIBLE);
}
public void hideLoader() {
viewMaster.findViewById(R.id.fragment_holder).setVisibility(View.VISIBLE);
viewMaster.findViewById(R.id.loading).setVisibility(View.INVISIBLE);
}
}
MyFragment
public class MyFragment extends LoadingFragment {
View view;
public MyFragment() {}
public static MyFragment newInstance() {
MyFragment fragment = new MyFragment();
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
super.showLoader();
view = inflater.inflate(R.layout.fragment_lans, container, false);
MyFragment.ApiCallJob apicalljob = new MyFragment.ApiCallJob();
apicalljob.execute("a string");
return view;
}
public class ApiCallJob extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String[] params) {
// do things
}
@Override
protected void onPostExecute(String data) {
// do things
// tried both to be sure but they should do the same?
hideLoader();
MyFragment.super.hideLoader();
}
}
}
main_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="app.stats.MainActivity"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/fragment_holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
<RelativeLayout
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/progress_bar"/>
</RelativeLayout>
</RelativeLayout>
The issue is the
setVisibility
inLoadingFragment
seems to have zero effect when calling it from the subclasses, for exampleMyFragment
, why is this?
Let's look at what happens when MyFragment onCreateView
is called:
You call super
onCreateView
which is in LoadingFragment
. This inflates main_layout
and returns the inflated view. Note that this view is not referenced in MyFragment
(i.e. View mainLayout = super.onCreateView(inflater, container, savedInstanceState);
)
You just inflated the view that has loading
view group, then threw it away. However, you did manage to save a reference to that view in the LoadingFragment
superclass.
Next you inflate R.layout.fragment_lans
and (after starting your load) you return that view as the view to be used in the fragment.
So the thing to observe here is that LoadingFragment
now has a reference to a view that is nowhere in the fragment's active view hierarchy.
Given that, it's no wonder that setVisibility
doesn't do anything, because the fragment isn't displaying that view.
I wouldn't use inheritance to do this, but if you must use it, here's how to fix your problem.
Let's just use the ProgressBar
without the view group wrapper. Since it's in a RelativeLayout
, let's center it. Then make it android:visibility="gone"
so it's effectively out of the fragment layout:
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"/>
You're going to put this progress bar in the layout for every LoadingFragment
subclass, including fragment_lans.xml
. You can use the <include>
tag to make life easier here.
Change the LoadingFragment
so that a) it doesn't interfere with the inflation of its subclass' layouts and b) it uses the progress bar in the subclass' active view hierarchy:
public class LoadingFragment extends Fragment {
public void showLoader() {
if (getView() != null) {
View progressBar = getView().findViewById(R.id.progress_bar);
if (progressBar != null) {
progressBar.setVisibility(View.VISIBLE);
}
}
}
public void hideLoader() {
if (getView() != null) {
View progressBar = getView().findViewById(R.id.progress_bar);
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
}
}
}
Now you should see what you expect.
EDIT
If you really want to use the layout scheme you started with, here is my recommendation:
Your activity is inflating main_layout
that has the progress bar, so I would give some of the responsibility to the activity.
Now, you could just write some code for the fragment that would trawl around the view hierarchy and look for the progress bar. That wouldn't be my preference.
Here's another approach:
Create an interface
interface ProgressDisplay {
public void showProgress();
public void hideProgress();
}
Make the activity implement it
public class MainActivity extends AppCompatActivity implements ProgressDisplay {
...
public void showProgress() {
findViewById(R.id.loading).setVisibility(View.VISIBLE);
}
public void hideProgress() {
findViewById(R.id.loading).setVisibility(View.GONE);
}
Then add the methods to LoadingFragment
that call the activity:
protected void showProgress() {
if (getActivity() instanceof ProgressDisplay) {
((ProgressDisplay) getActivity()).showProgress();
}
}
protected void hideProgress() {
if (getActivity() instanceof ProgressDisplay) {
((ProgressDisplay) getActivity()).hideProgress();
}
}
This way you can take any activity that inflates a layout with your progress view and have it implement ProgressDisplay
so you can use the LoadingFragment
class with it.
You could also make a LoadingActivity
class and subclass that, too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With