× {{alert.msg}} Never ask again
Get notified about new tutorials RECEIVE NEW TUTORIALS

OnKeyDown inside WebView fragment

Xaver Kapeller
Mar 13, 2015
<p>It might work, but it is not a good idea. It could very well cause a crash if you don't handle the <code>Fragment</code> 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 <code>Fragment</code> can handle calls to <code>goBack()</code> or <code>canGoBack()</code> itself:</p> <pre><code>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 &amp;&amp; this.browserFragment != null &amp;&amp; this.browserFragment.canGoBack()) { this.browserFragment.goBack(); return true; } return super.onKeyDown(keyCode, event); } } </code></pre> <p>As you can see the <code>BrowserFragment</code> instance is saved and then methods like <code>goBack()</code> or <code>canGoBack()</code> are called on the <code>BrowserFragment</code> itself. Of course you have to implement these methods in the <code>BrowserFragment</code> but that should not be a problem:</p> <pre><code>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 &amp;&amp; this.webView.canGoBack(); } public void goBack() { if(this.webView != null) { this.webView.goBack(); } } } </code></pre> <p>I made a few extra improvements to your code. First of all I added null checks to prevent any possible <code>NullPointerExceptions</code> and secondly it is recommend to always use a static factory method to create new instances of <code>Fragments</code>. That is what the static <code>newInstance()</code> method is that I added to the <code>BrowserFragment</code>. The advantage of that is that you can implement a method which takes care of setting up the <code>BrowserFragment</code> for you regardless of where you use it. You can add parameters to <code>newInstance()</code> method to pass some value to the <code>BrowserFragment</code> or to add some required listener etc but since you don't pass any values to the <code>BrowserFragment</code> the <code>newInstance()</code> method remains pretty empty. Nevertheless it is best practice to always use such factory methods even if they only call <code>new BrowserFragment()</code>.</p> <p>Generally this approach is much better. Especially from a architecture perspective because you don't directly interact with the <code>WebView</code> in the <code>Activity</code>. The <code>WebView</code> doesn't have anything to do with the <code>Activity</code>, it is part of the implementation of the <code>BrowserFragment</code> and as such the <code>Activity</code> should not know that there even is a <code>WebView</code>. How calls to <code>goBack()</code> or <code>canGoBack()</code> are implemented or what they exactly do is of no interest to the <code>Activity</code>. The <code>Activity</code> just tells the <code>BrowserFragment</code> "I want to go back" and the <code>BrowserFragment</code> does the work. This separates the responsibilities better and makes the code more readable, more clear and more maintainable.</p> <p><strong>EDIT:</strong></p> <p>Also I don't know of a <code>SingleFragmentActivity</code> but generally any <code>Activity</code> implements <code>onBackPressed()</code> method. You don't have to override <code>onKeyDown()</code> to catch a back key event. You can just do something like this:</p> <pre><code>@Override public void onBackPressed() { if (this.browserFragment != null &amp;&amp; this.browserFragment.canGoBack()) { this.browserFragment.goBack(); } else { // The back key event only counts if we execute super.onBackPressed(); super.onBackPressed(); } } </code></pre> <p>If you have any other questions please feel free to ask!</p> <p>This tip was originally posted on <a href="http://stackoverflow.com/questions/23231218/OnKeyDown%20inside%20WebView%20fragment/23231631">Stack Overflow</a>.</p>
comments powered by Disqus