36

UPDATE: Solved! Problem was related to my Viewpager not WebView.

I'm trying to add a "Go Back" function to my WebView which is inside a Fragment. But I can't figure out how to:

public final class TestFragment extends Fragment {

    static WebView mWeb;
    private View mContentView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState)
    {   
        mContentView = inflater.inflate(R.layout.webview, null);
        mWeb = (WebView)mContentView.findViewById(R.id.webview);

        WebSettings settings = mWeb.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setSupportZoom(false);
        mWeb.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
        mWeb.getSettings().setBuiltInZoomControls(false);
        mWeb.loadUrl("myurl...");
        mWeb.setOnKeyListener(new OnKeyListener(){
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if ((keyCode == KeyEvent.KEYCODE_BACK) && mWeb.canGoBack()) {
                    mWeb.goBack();
                    return true;
                }
                return false;
            }
        });
    }   
}

I also tried something like:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KeyEvent.KEYCODE_BACK) && mWeb.canGoBack()) {
        mWeb.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

Another solution but same problem:

@Override
public void onBackPressed()
{
    if(webView.canGoBack())
        webView.goBack();
    else
        super.onBackPressed();
}

Any ideas how to get this working?

Andrew T.
  • 4,701
  • 8
  • 43
  • 62
Markus Rubey
  • 5,153
  • 2
  • 21
  • 17
  • What happens? the third option that implements onBackPressed() works for me, make sure that you actually have something to go back to. – Ian Warwick May 17 '12 at 07:20
  • and the issue was? I have a very similar situation :) – desgraci Sep 08 '15 at 20:51
  • [How to go back to previous page if back button is pressed in WebView?](https://stackoverflow.com/q/6077141/6521116) – LF00 Jun 23 '17 at 04:20

14 Answers14

55

Perhaps its android restriction. Try to do this using handler.

public final class TestFragment extends Fragment {


    static WebView mWeb;
    private View mContentView;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case 1:{
                    webViewGoBack();
                }break;
            }
        }
    };

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {   

        mContentView = inflater.inflate(R.layout.webview, null);
        mWeb = (WebView)mContentView.findViewById(R.id.webview);

        WebSettings settings = mWeb.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setSupportZoom(false);
        mWeb.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
        mWeb.getSettings().setBuiltInZoomControls(false);
        mWeb.loadUrl("myurl...");
        mWeb.setOnKeyListener(new OnKeyListener(){

            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_BACK 
                        && event.getAction() == MotionEvent.ACTION_UP 
                        && mWeb.canGoBack()) {
                    handler.sendEmptyMessage(1);
                    return true;
                }

                return false;
            }

        });

    }   

    private void webViewGoBack(){
        mWeb.goBack();
    }
}
Roman Black
  • 3,501
  • 1
  • 22
  • 31
  • 1
    Worked for me too, This should be selected as answer! – Deepak S. Gavkar Sep 14 '15 at 07:03
  • 1
    this works, however there is one flaw. You're only detecting the keyCode, not the KeyEvent. So when you hit the back button, you're onKey will get called twice, on for the downpress and one for the up. So it should be: if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == MotionEvent.ACTION_UP) { – Marty Miller Aug 18 '16 at 02:12
  • can you tell me why use handler.without handler it wont be possible – Deepak Borade Jul 20 '18 at 11:42
35

You can check this code :

    webView.canGoBack();
    webView.setOnKeyListener(new View.OnKeyListener() {

        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK
                    && event.getAction() == MotionEvent.ACTION_UP
                    && webView.canGoBack()) {
                webView.goBack();
                return true;
            }
            return false;
        }
    });
Nawrez
  • 3,314
  • 8
  • 28
  • 42
XZandr
  • 351
  • 3
  • 2
23

Actually you can not do directly inside the fragment. The onBackPressed can be overridden in the FragmentActivity. What you can do is:

  1. Override the onBackPressed inside the activity.
  2. When the onBackPressed is called, check if the instance of the current fragment is the instance showing the webview.
  3. If it is, ask the fragment if the webview can go back.
  4. if it is not, call the super or whatever you need

Edit:

 @Override
 public void onBackPressed() {
       Fragment webview = getSupportFragmentManager().findFragmentByTag("webview");
       if (webview instanceof MyWebViewFragment) {
              boolean goback = ((MyWebViewFragment)webview).canGoBack();
              if (!goback)
                super.onBackPressed();
       }
 }
Kyle Falconer
  • 8,302
  • 6
  • 48
  • 68
Blackbelt
  • 156,034
  • 29
  • 297
  • 305
  • 2
    Thanks for this code, really helped me provide a quick solution to the issues i was having with a navigation drawer and fragment webview. – Jaison Brooks Nov 27 '13 at 08:46
8

I've created a simple interface:

public interface IOnBackPressed {
    boolean onBackPressed();
}

in the Activity:

public class MyActivity extends Activity {
    @Override public void onBackPressed() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
       if (!(fragment instanceof IOnBackPressed) || !((IOnBackPressed) fragment).onBackPressed()) {
          super.onBackPressed();
       }
    }
}

in the Fragment:

public class MyFragment extends Fragment implements IOnBackPressed {
   @Override
    public boolean onBackPressed() {
        if (webview.canGoBack()) {
            webview.goBack();
            // backpress is not considered in the Activity
            return true;
        } else {
            // activity will act normal
            return false;
        }
    }
}
Manuel Schmitzberger
  • 5,162
  • 3
  • 36
  • 45
7

In WebViewActivity.java, I added 1 method:

@Override
public void onBackPressed() {

    WebViewFragment fragment = (WebViewFragment)
            getSupportFragmentManager().findFragmentById(R.id.fragmentContainer);
    if (fragment.canGoBack()) {
        fragment.goBack();
    } else {
        super.onBackPressed();
    }
}

In WebViewFragment.java, I added 2 methods:

public boolean canGoBack() {
    return mWebView.canGoBack();
}

public void goBack() {
    mWebView.goBack();
}
Ram Patra
  • 16,266
  • 13
  • 66
  • 81
7

@RomanBlack's answer gave me the right idea, but since we use kotlin I had to adapt the answer a little bit.

webView.setOnKeyListener { _, _, keyEvent ->
        if (keyEvent.keyCode == KeyEvent.KEYCODE_BACK && !webView.canGoBack()) {
            false
        } else if (keyEvent.keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == MotionEvent.ACTION_UP) {
            webView.goBack()
            true
        } else true
    }

if you want to do it with returns you have to add something like:

return@setOnKeyListener true
Mike T
  • 1,194
  • 14
  • 25
5

There is a simple way with BackPressedDispatcher

Fragment:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val callback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            if(webView.canGoBack()){
               webView.goBack()
            } else {
                isEnabled = false
                requireActivity().onBackPressed()
            }
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(this,callback)
}

Activity :

override fun onBackPressed() {
    val fragment = supportFragmentManager.findFragmentByTag("WebViewFragment")
    if (WebViewFragment::class.java.isInstance(fragment)) {
        if (onBackPressedDispatcher.hasEnabledCallbacks()) {
            onBackPressedDispatcher.onBackPressed()
            return
        }
        super.onBackPressed()
    }
}

Maybe these codes can be improved, but it works very well for me. For more information visit here.

Burak Dizlek
  • 4,805
  • 2
  • 23
  • 19
3

my solution was in fragment I added to public methods

public static boolean canGoBack(){
        return mWebView.canGoBack();
    }

    public static void goBack(){
        mWebView.goBack();
    }

then from activity I call

@Override
public void onBackPressed() {
    if(webFragment.canGoBack()){
        webFragment.goBack();
    }else{
        super.onBackPressed();
    }

}

note The mwebview is static

Omid Aminiva
  • 667
  • 5
  • 14
2

you can do this by :

  • in the Activity put :

    // Set WebView
    
    public void setWebView(WebView web) {
    
        this.web = web;
    }
    
  • in the web fragment after ActivityCreated() put:

    ((Your_Activity) getActivity()).setWebView(webView);

  • Don't forget to set webView from the onCreateView() like these:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        webView = (WebView) inflater.inflate(R.layout.your_web_fragment, container,
                false);
        return web;
    }
    
biegleux
  • 13,179
  • 11
  • 45
  • 52
AndroMan
  • 91
  • 5
1

@OmidAmnivia answer is correct your app the solution to the crash is

@Override
public void onBackPressed() {
if(webFragment.isInitialized && webFragment.canGoBack()){
    webFragment.goBack();
}else{
    super.onBackPressed();
}
}

You have to check whether your class has been initialised or not.

h_code
  • 53
  • 5
1

This is how I did in my app. I consume back press event till web view can go back. Once web view cant go back I show hint to user that if he keep on pressing back then app will exit.

It will give user a chance to stay in your app when webview cant go back. I felt it more user friendly:

          //time passed between two back presses.
          private val TIME_INTERVAL = 200 
           // variable to keep track of last back press
          private var mBackPressed: Long = 0


        webView!!.requestFocus()
        webView.setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
            if (keyCode == KeyEvent.KEYCODE_BACK
                    && event.action == MotionEvent.ACTION_UP
                  ) {
                if(webView.canGoBack()) {
                    //go back in previous page
                    webView.goBack()
                    return@OnKeyListener true
                }
                else
                {
                    if (mBackPressed + TIME_INTERVAL > System.currentTimeMillis())
                    {   // dont consume back press and pass to super
                        return@OnKeyListener false
                    }
                    else {
                        // show hint for double back press
                        Toast.makeText(context, " Double Tap back button to exit the demo", Toast.LENGTH_SHORT).show();
                        mBackPressed = System.currentTimeMillis();
                        return@OnKeyListener true
                    }
                }
            }
            return@OnKeyListener false

        })
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
0

First of all get back pressed in fragment

 mContentView.setFocusableInTouchMode(true);
 mContentView.requestFocus();
 mContentView.setOnKeyListener( new OnKeyListener()
 {
   @Override
   public boolean onKey( View v, int keyCode, KeyEvent event )
  {
      if( keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack())
      {                                                    
            mWebView.goBack();
            return true;
      }
      return false;
  }
 } );

hope it will works.

Dalvinder Singh
  • 1,073
  • 1
  • 12
  • 19
0

This worked in my case

public class FantasyFragment extends Fragment  implements SwipeRefreshLayout.OnRefreshListener {

WebView webview;
SwipeRefreshLayout swipeLayout;
String currentUrl="https://www.stackoverflow.com/";

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View root = inflater.inflate(R.layout.fragment_stadium, container, false);


    swipeLayout = (SwipeRefreshLayout) root.findViewById(R.id.swipescreen);
    swipeLayout.setOnRefreshListener(this);

    return root;
}

@Override
public void onStart() {
    super.onStart();
    LoadWeb();
}

public void LoadWeb() {



    webview = (WebView) getActivity().findViewById(R.id.webview786);
    swipeLayout.setRefreshing(true);
    webview.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
    webview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
    webview.getSettings().setAppCacheEnabled(true);

    WebSettings webSettings = webview.getSettings();
    webSettings.setJavaScriptEnabled(true);

    webSettings.setDatabaseEnabled(true);
    webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
    webSettings.setUseWideViewPort(true);
    webSettings.setSavePassword(true);
    webSettings.setSaveFormData(true);
    webSettings.setEnableSmoothTransition(true);

    webview.loadUrl(currentUrl);

    webview.setWebViewClient(new WebViewClient() {
        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            Snackbar.make(view, "Connection Error", Snackbar.LENGTH_LONG)
                    .setAction("Retry", new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            LoadWeb();
                        }
                    }).show();
        }
        @Override
        public void onPageFinished(WebView view, String url) {
            swipeLayout.setRefreshing(false);
            currentUrl = url;
            super.onPageFinished(view, url);
        }
    });

    webview.canGoBack();
    webview.setOnKeyListener(new View.OnKeyListener() {

        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK
                    && event.getAction() == MotionEvent.ACTION_UP
                    && webview.canGoBack()) {
                webview.goBack();
                return true;
            }
            return false;
        }
    });
}
@Override
public void onRefresh() {
    LoadWeb();
}
}
Tariq Mahmood
  • 147
  • 15
0

The simplest answer is jut to setOnKeyListeneron into webView itself:

webView.setOnKeyListener(new View.OnKeyListener() {
    @Override
    public boolean onKey( View v, int keyCode, KeyEvent event ) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            if( keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()){
                webView.goBack();
                return true;
            }
        }
        return false;
    }
});
R3ason
  • 86
  • 1
  • 2
  • 8