【问题标题】:OnKeyDown inside WebView fragmentWebView 片段内的 OnKeyDown
【发布时间】:2014-04-22 22:14:42
【问题描述】:

我创建了一个Fragment,其中包含一个WebView,我想定义一个onKeyDown() 以从一个网页返回到前一个网页。我做到了,但对我来说最奇怪的部分是将WebView 变量从我的Fragment 类共享到我的Activity 类,因为我无法在Fragment 中定义onKeyDown()。所以我只是定义了一个 get 方法并将其设为静态。但我想知道这是否是一个真正的错误,我的应用在某些情况下可能会严重崩溃。

我的Fragment 代码:

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;
    }
}

还有我的Activity 代码:

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

【问题讨论】:

    标签: android android-fragments webview


    【解决方案1】:

    它可能会起作用,但这不是一个好主意。如果您没有正确处理Fragment,或者在代码中的某个地方对其生命周期有些粗心,它很可能会导致崩溃。但是有一个简单的方法可以解决这个问题。不要使用静态方法,而是保存实例并在实例本身上调用方法。这样你可以检查实例是否为空,如果不是,Fragment 可以处理对goBack()canGoBack() 本身的调用:

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

    如您所见,BrowserFragment 实例已保存,然后在 BrowserFragment 本身上调用 goBack()canGoBack() 等方法。当然,您必须在BrowserFragment 中实现这些方法,但这应该不是问题:

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

    我对您的代码做了一些额外的改进。首先,我添加了空检查以防止任何可能的NullPointerExceptions,其次,建议始终使用静态工厂方法来创建Fragments 的新实例。这就是我添加到BrowserFragment 的静态newInstance() 方法。这样做的好处是,您可以实现一个方法来为您设置BrowserFragment,无论您在哪里使用它。您可以向newInstance() 方法添加参数以将一些值传递给BrowserFragment 或添加一些所需的侦听器等,但由于您没有向BrowserFragment 传递任何值,newInstance() 方法仍然很空。尽管如此,最好的做法是始终使用此类工厂方法,即使它们只调用 new BrowserFragment()

    通常这种方法要好得多。尤其是从架构的角度来看,因为您不直接与Activity 中的WebView 交互。 WebViewActivity 没有任何关系,它是BrowserFragment 实现的一部分,因此Activity 不应该知道甚至有WebView。对goBack()canGoBack() 的调用是如何实现的,或者它们究竟做了什么,Activity 并不感兴趣。 Activity 只是告诉BrowserFragment“我想回去”,BrowserFragment 完成工作。这更好地分离了职责,使代码更易读、更清晰、更易于维护。

    编辑:

    我也不知道SingleFragmentActivity,但通常任何Activity 都实现onBackPressed() 方法。您不必重写 onKeyDown() 来捕获后键事件。你可以这样做:

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

    如果您有任何其他问题,请随时提出!

    【讨论】:

    • 哦!非常感谢您的辛勤工作和解释!这正是我想要实现的,但不知道如何实现(尤其是如何让 Fragment 完成这项工作)。我的 SIngleFragment Activity 只包含创建片段、开始事务和添加到容器的代码。谢谢!现在我会去思考你的整个实现 =)
    • 顺便说一句,也许你知道,我将 WebView 添加到片段中,这样活动生命周期就不会影响页面表示。例如,我从纵向模式更改为横向模式,我不希望我的应用程序再次下载起始页面。我应该在 saveInstanceState 中保存一些东西,但我应该保存什么?
    • 谢谢,+1 清晰的代码和很好的解释。它非常适合我!我只有一个问题,当我在 android 中有一个在我的 webview 中打开链接的菜单时,我该如何处理?当我单击 webview 内的链接时效果很好,但是当我单击菜单“配置文件”然后单击另一个菜单并按回时,它不起作用...知道如何修复它吗? :)
    猜你喜欢
    • 2019-06-11
    • 1970-01-01
    • 1970-01-01
    • 2012-08-26
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多