安卓Phonegap和Fragments

4

我正在尝试将Phonegap CordovaWebView实现到我的Fragment中,但是它不起作用。

我的布局如下(cordovawebview.xml):

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent">

<org.apache.cordova.CordovaWebView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id = "@+id/mainView"/>

</FrameLayout>

在我的Fragment的onCreateView()方法中,我尝试进行布局填充:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {

    View v = inflater.inflate(R.layout.cordovawebview, container); // <--- the error occurs here!
    // CordovaWebView webView = (CordovaWebView)v.findViewById(R.id.mainView);

    return v;
}

也许有人能提供一些修复它的提示。我总是会得到这个错误:
10-25 15:52:02.839: ERROR/AndroidRuntime(2878): FATAL EXCEPTION: main
    android.view.InflateException: Binary XML file line #21: Error inflating class org.apache.cordova.CordovaWebView
    at android.view.LayoutInflater.createView(LayoutInflater.java:613)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:687)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
    at com.advantageframework.tabs.fragments.SampleFragmentA.onCreateView(SampleFragmentA.java:81)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:871)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1083)
    at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:635)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1431)
    at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:420)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4745)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    at dalvik.system.NativeStart.main(Native Method)
    Caused by: java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Constructor.constructNative(Native Method)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    at android.view.LayoutInflater.createView(LayoutInflater.java:587)
    ... 19 more
    Caused by: java.lang.NullPointerException
    at org.apache.cordova.CordovaWebView.loadConfiguration(CordovaWebView.java:643)
    at org.apache.cordova.CordovaWebView.<init>(CordovaWebView.java:131)
    ... 22 more

你解决问题了吗?我也遇到了同样的问题,正在调查中。 - Patrick Boos
非常遗憾,我现在正在使用CordovaInterface和活动(Activities)。如果你需要为此设置入口点,只需尝试 https://github.com/infil00p/CordovaActionView。在MainActivity中,您可以将“MainActivity extends ActionBarAvtivity”更改为“MainActivity extends FragmentActivity”或SherlockFragmentActivity(如果您需要它)。 - Bins Ich
3个回答

7
我找到了答案。如果你查看logcat,你会发现它试图将Context转换为CordovaInterface。而这个Context是Activity,所以失败了。你可能让Fragment成为了CordovaInterface。这就是我做的事情。你需要让Activity成为CordovaInterface,并将事件(onMessage等)从Activity转发到Fragment。
以下是我的Activity(为了简化,删除了一些内容)。我有一个非常接近这个的东西,它工作得很好。之后你仍然会遇到一些较小的问题,但这些问题很容易解决。
public class MyCordovaActivity extends SherlockFragmentActivity implements
        CordovaInterface {
    private final ExecutorService mThreadPool = Executors.newCachedThreadPool();
    private CordovaPlugin mActivityResultCallback;

    private CordovaFragment mFragment;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.nbu_web_activity);
        mFragment = new CordovaFragment();
        getSupportFragmentManager().beginTransaction()
                .add(R.id.fragment, mFragment)
                .commit();

        // TODO this value you could pass to the activity with a intent extra
        // or allow to do this through a seperate function, ...
        String url = "http://....";
        mFragment.loadUrl(url);
    }

    @Override
    protected void onNewIntent(final Intent intent) {
        super.onNewIntent(intent);
        NBUGapFragment fragment = getCordovaFragment();
        if (fragment != null && fragment.appView != null) {
            fragment.appView.onNewIntent(intent);
        }
    }

    @Override
    public void onBackPressed() {
        NBUGapFragment fragment = getCordovaFragment();
        if (fragment == null || !fragment.onBackPressed()) {
            super.onBackPressed();
        }
    }

    @Override
    public void cancelLoadUrl() {
        getCordovaFragment().cancelLoadUrl();
    }

    @Override
    public Activity getActivity() {
        return this;
    }

    @Override
    public Context getContext() {
        return this;
    }

    @Override
    public ExecutorService getThreadPool() {
        return mThreadPool;
    }

    @Override
    public Object onMessage(final String id, final Object data) {
        return getCordovaFragment().onMessage(id, data);
    }

    @Override
    public void setActivityResultCallback(final CordovaPlugin plugin) {
        mActivityResultCallback = plugin;
    }

    @Override
    public void startActivityForResult(final CordovaPlugin plugin, final Intent intent,
            final int requestCode) {
        mActivityResultCallback = plugin;
        startActivityForResult(intent, requestCode);
    }

    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
        if (mActivityResultCallback != null) {
            mActivityResultCallback.onActivityResult(requestCode, resultCode, intent);
        } else {
            super.onActivityResult(requestCode, resultCode, intent);
        }
    }

    private CordovaFragment getCordovaFragment() {
        // the CordovaFragment is the one implementing CordovaInterface
        return mFragment;
    }
}

你能提供一个裸代码结构吗?为了确保 - 那将是太棒了! - Bins Ich
@PatrickBoos 我知道这个类,当我想在我的活动中使用CordovaWebView时,我也会使用它。我基本上要问的是,这个DroidGap类是从Activity扩展的,而不是Fragment。如果您想在片段中实现CordovaInterface,则需要实现getActivity()方法,这也是FragmentActivity的超类。 - osayilgan
据我所见,您在上面的代码中有一个名为CordovaFragment的类,您是否也在此Fragment中实现了CordovaInterface?如果您实现了它,能否在这里分享一下?因为我尝试做类似的事情,但每当我完成FragmentActivity时都会出现错误,它说我需要取消注册接收器。但是我在Fragment或FragmentActivity中没有任何接收器。 - osayilgan
5
顺便说一下,我越是使用带有片段的Cordova,就越开始不喜欢Cordova了。它完全是为在活动中使用而构建的,而不是在片段中使用。你最终会面临大量的问题。这个问题现在可能不是你最后遇到的问题;但是它是可以解决的,需要投入很多工作。 - Patrick Boos
我在我的一个项目中必须使用CordovaInterface,正如你所说的,似乎FragmentActivity出了一些问题。因为它与Activity完美地配合使用,但与FragmentActivity不兼容。 - osayilgan
显示剩余3条评论

2

CordovaWebView假定Inflater的上下文(在此情况下为主Activity)实现了CordovaInterface。没有接口通过inflater传递分离对象(上下文和CordovaInterface对象),您应该创建包含两个对象的代理上下文,并将方法调用委托给每个对象。

private class CordovaContext extends ContextWrapper implements CordovaInterface
{
    CordovaInterface ci;

    // Hold two objects. Method call to Context will be proxied by ContextWrapper, so only delegate CordovaInterface.
    // You should add/modify when method in CordovaInterface changed
    public CordovaContext(Context base, CordovaInterface ci) {
        super(base);
        this.ci = ci;
    }
    public void startActivityForResult(CordovaPlugin command,
            Intent intent, int requestCode) {
        ci.startActivityForResult(command, intent, requestCode);
    }
    public void setActivityResultCallback(CordovaPlugin plugin) {
        ci.setActivityResultCallback(plugin);
    }
    public Activity getActivity() {
        return ci.getActivity();
    }
    public Object onMessage(String id, Object data) {
        return ci.onMessage(id, data);
    }
    public ExecutorService getThreadPool() {
        return ci.getThreadPool();
    }

}

在Fragment中,创建一个使用此Context对象的inflater;
LayoutInflater localInflater = inflater.cloneInContext(new CordovaContext(getActivity(), this));
View v = localInflater.inflate(R.layout.cordovawebview, container, false);

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

    View v = inflater.inflate(R.layout.cordovawebview, container, false); // <--- add false parameter
    CordovaWebView webView = (CordovaWebView)v.findViewById(R.id.mainView);

    return v;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接