I made a Fragment
, containing a WebView
and I wanted to define an onKeyDown()
to get back from one web page to the previous one. I did it, but the weirdest part for me was to share a WebView
variable from my Fragment
class to my Activity
class because I can't define onKeyDown()
in the Fragment
. So I just defined a get method and made it static. But I wonder if it was a real mistake and my app can seriously crash in some circumstances.
My Fragment
code:
public class BrowserFragment extends Fragment {
private static WebView webView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.fragment_activity, parent, false);
getActivity().setTitle(R.string.title_rus);
webView = (WebView) v.findViewById(R.id.webView);
webView.setWebViewClient(new SwingBrowserClient());
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
Uri data = Uri.parse("http://www.swinginmoscow.ru/m/");
webView.loadUrl(data.toString());
return v;
}
public static WebView getWebView() {
return webView;
}
}
And my Activity
code:
public class MainBrowserActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new BrowserFragment();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && BrowserFragment.getWebView().canGoBack()) {
BrowserFragment.getWebView().goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
It might work, but it is not a good idea. It could very well cause a crash if you don't handle the Fragment
correctly or are somewhere in your code a little careless regarding its lifecycle. But there is an easy way around this. Instead of using a static method, save the instance and call methods on the instance itself. This way you can check if the instance is null and if not the Fragment
can handle calls to goBack()
or canGoBack()
itself:
public class MainBrowserActivity extends SingleFragmentActivity {
BrowserFragment browserFragment = null;
@Override
protected Fragment createFragment() {
this.browserFragment = BrowserFragment.newInstance();
return this.browserFragment;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && this.browserFragment != null && this.browserFragment.canGoBack()) {
this.browserFragment.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
As you can see the BrowserFragment
instance is saved and then methods like goBack()
or canGoBack()
are called on the BrowserFragment
itself. Of course you have to implement these methods in the BrowserFragment
but that should not be a problem:
public class BrowserFragment extends Fragment {
public static BrowserFragment newInstance() {
BrowserFragment fragment = new BrowserFragment();
return fragment;
}
private WebView webView;
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.fragment_activity, parent, false);
getActivity().setTitle(R.string.title_rus);
webView = (WebView) v.findViewById(R.id.webView);
webView.setWebViewClient(new SwingBrowserClient());
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
Uri data = Uri.parse("http://www.swinginmoscow.ru/m/");
webView.loadUrl(data.toString());
return v;
}
public boolean canGoBack() {
return this.webView != null && this.webView.canGoBack();
}
public void goBack() {
if(this.webView != null) {
this.webView.goBack();
}
}
}
I made a few extra improvements to your code. First of all I added null checks to prevent any possible NullPointerExceptions
and secondly it is recommend to always use a static factory method to create new instances of Fragments
. That is what the static newInstance()
method is that I added to the BrowserFragment
. The advantage of that is that you can implement a method which takes care of setting up the BrowserFragment
for you regardless of where you use it. You can add parameters to newInstance()
method to pass some value to the BrowserFragment
or to add some required listener etc but since you don't pass any values to the BrowserFragment
the newInstance()
method remains pretty empty. Nevertheless it is best practice to always use such factory methods even if they only call new BrowserFragment()
.
Generally this approach is much better. Especially from a architecture perspective because you don't directly interact with the WebView
in the Activity
. The WebView
doesn't have anything to do with the Activity
, it is part of the implementation of the BrowserFragment
and as such the Activity
should not know that there even is a WebView
. How calls to goBack()
or canGoBack()
are implemented or what they exactly do is of no interest to the Activity
. The Activity
just tells the BrowserFragment
"I want to go back" and the BrowserFragment
does the work. This separates the responsibilities better and makes the code more readable, more clear and more maintainable.
EDIT:
Also I don't know of a SingleFragmentActivity
but generally any Activity
implements onBackPressed()
method. You don't have to override onKeyDown()
to catch a back key event. You can just do something like this:
@Override
public void onBackPressed() {
if (this.browserFragment != null && this.browserFragment.canGoBack()) {
this.browserFragment.goBack();
} else {
// The back key event only counts if we execute super.onBackPressed();
super.onBackPressed();
}
}
If you have any other questions please feel free to ask!
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