【问题标题】:onBackPressed confusion when working with fragments处理片段时的 onBackPressed 混乱
【发布时间】:2013-12-18 07:05:29
【问题描述】:

我查看过关于类似问题的类似问题,但我还没有找到解决问题的方法。

我有一个使用抽屉式导航的应用程序。我有一个 MainActivity、各种 FragmentActivity 和各种片段布局。我的 MainActivity 代码如下:

public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;

// nav drawer title
private CharSequence mDrawerTitle;

// used to store app title
private CharSequence mTitle;

// slide menu items
private String[] navMenuTitles;
private TypedArray navMenuIcons;

private ArrayList<NavDrawerItem> navDrawerItems;
private NavDrawerListAdapter adapter;

MediaPlayer ourSong;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    ourSong = MediaPlayer.create(MainActivity.this, R.raw.audirs6);

    SharedPreferences getPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
    boolean music = getPrefs.getBoolean("checkbox", true);
    if (music == true)
    ourSong.start();

    mTitle = mDrawerTitle = getTitle();

    // load slide menu items
    navMenuTitles = getResources().getStringArray(R.array.nav_drawer_items);

    // nav drawer icons from resources
    navMenuIcons = getResources().obtainTypedArray(R.array.nav_drawer_icons);

    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    mDrawerList = (ListView) findViewById(R.id.list_slidermenu);

    navDrawerItems = new ArrayList<NavDrawerItem>();

    // adding nav drawer items to array
    // AutoMart Home
    navDrawerItems.add(new NavDrawerItem(navMenuTitles[0], navMenuIcons.getResourceId(0, -1)));
    // Search For A Car
    navDrawerItems.add(new NavDrawerItem(navMenuTitles[1], navMenuIcons.getResourceId(1, -1)));

    navDrawerItems.add(new NavDrawerItem(navMenuTitles[2], navMenuIcons.getResourceId(2, -1)));

    navDrawerItems.add(new NavDrawerItem(navMenuTitles[3], navMenuIcons.getResourceId(3, -1)));

    // Recycle the typed array
    navMenuIcons.recycle();

    mDrawerList.setOnItemClickListener(new SlideMenuClickListener());

    // setting the nav drawer list adapter
    adapter = new NavDrawerListAdapter(getApplicationContext(),
            navDrawerItems);
    mDrawerList.setAdapter(adapter);

    // enabling action bar app icon and behaving it as toggle button
    getActionBar().setDisplayHomeAsUpEnabled(true);
    getActionBar().setHomeButtonEnabled(true);

    if (savedInstanceState == null) {
        // on first time display view for first nav item
        displayView(0);
    }   

    mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
            R.drawable.ic_drawer, //nav menu toggle icon
            R.string.app_name, // nav drawer open - description for accessibility
            R.string.app_name // nav drawer close - description for accessibility
    ) {
        public void onDrawerClosed(View view) {
            getActionBar().setTitle(mTitle);
            // calling onPrepareOptionsMenu() to show action bar icons
            invalidateOptionsMenu();
        }


        public void onDrawerOpened(View drawerView) {
            getActionBar().setTitle(mDrawerTitle);
            // calling onPrepareOptionsMenu() to hide action bar icons
            invalidateOptionsMenu();
        }
    };
    mDrawerLayout.setDrawerListener(mDrawerToggle);

}

/**
 * Slide menu item click listener
 * */
private class SlideMenuClickListener implements
        ListView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        // display view for selected nav drawer item
        displayView(position);
    }
}

@Override
protected void onStop() {
    // TODO Auto-generated method stub
    super.onStop();
    ourSong.release();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // toggle nav drawer on selecting action bar app icon/title
    if (mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action bar actions click
    switch (item.getItemId()) {
    case R.id.action_settings:
        Intent settings  = new Intent("com.loui.automartdirectcars.SETTINGS");
        startActivity(settings);
        break;
    case R.id.aboutUs:
        Intent about = new Intent("com.loui.automartdirectcars.ABOUT");
        startActivity(about);
        break;
    case (R.id.tellFriend):
        Intent share = new Intent(Intent.ACTION_SEND);
        share.setType("application/octet-stream");
        share.putExtra(Intent.EXTRA_SUBJECT, "AutoMart Direct Cars Android App");
        share.putExtra(Intent.EXTRA_TEXT, "Hey check out AutoMart Direct Cars Android App at https://play.google.com/store/apps/details?id=com.loui.automartdirectcars");
        startActivity(share);
        break;
    case R.id.contactDeveloper:
        String body = "Enter your Question, Enquiry or Feedback below:\n\n";
        Intent contactDev = new Intent(Intent.ACTION_SEND);
        contactDev.setType("application/octet-stream");
        contactDev.putExtra(Intent.EXTRA_EMAIL, new String[]{"louigi7386@gmail.com"});
        contactDev.putExtra(Intent.EXTRA_SUBJECT, "AutoMart Direct Cars Android App");
        contactDev.putExtra(Intent.EXTRA_TEXT, body);
        startActivity(contactDev);
        break;
    case R.id.exit:
        finish();
        break;
    }
        return super.onOptionsItemSelected(item);
}

/* *
 * Called when invalidateOptionsMenu() is triggered
 */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    // if nav drawer is opened, hide the action items
    boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
    menu.findItem(R.id.action_settings).setVisible(!drawerOpen);
    menu.findItem(R.id.aboutUs).setVisible(!drawerOpen);
    menu.findItem(R.id.tellFriend).setVisible(!drawerOpen);
    menu.findItem(R.id.contactDeveloper).setVisible(!drawerOpen);
    menu.findItem(R.id.exit).setVisible(!drawerOpen);

    return super.onPrepareOptionsMenu(menu);
}

/**
 * Diplaying fragment view for selected nav drawer list item
 * @param <FragmentTransaction>
 * */
private <FragmentTransaction> void displayView(int position) {
    // update the main content by replacing fragments
    Fragment fragment = null;
    switch (position) {
    case 0:
        fragment = new HomeFragment();
        break;
    case 1:
        fragment = new SearchCarFragment();
        break;
    case 2:
        fragment = new LogoFlipperFragment();
        break;
    case 3:
        fragment = new CommunityFragment();
        break;
    default:
        break;
    }


        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
                .replace(R.id.frame_container, fragment).addToBackStack(null).commit();

        // update selected item and title, then close the drawer
        mDrawerList.setItemChecked(position, true);
        mDrawerList.setSelection(position);
        setTitle(navMenuTitles[position]);
        mDrawerLayout.closeDrawer(mDrawerList);

}



@Override
public void setTitle(CharSequence title) {
    mTitle = title;
    getActionBar().setTitle(mTitle);
}

/**
 * When using the ActionBarDrawerToggle, you must call it during
 * onPostCreate() and onConfigurationChanged()...
 */

@Override
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    // Sync the toggle state after onRestoreInstanceState has occurred.
    mDrawerToggle.syncState();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    // Pass any configuration change to the drawer toggls
    mDrawerToggle.onConfigurationChanged(newConfig);
    }
public void onBackPressed() {
    WebView mWebView = (WebView) findViewById(R.id.webView);
    if (mWebView.canGoBack())
        mWebView.goBack(); 
    return;
}
}

下面是 webview 的 Activity:

public class HomeFragment extends Fragment {

public HomeFragment(){}


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

    View rootView = inflater.inflate(R.layout.fragment_home, container, false);
    WebView mWebView = (WebView) rootView.findViewById(R.id.webView);


  //Initializing and loading url in webview       
    mWebView = (WebView) rootView.findViewById(R.id.webView);
    mWebView.getSettings().setJavaScriptEnabled(true);
    mWebView.loadUrl("http://www.automartdirect.co.uk");
    mWebView.setWebViewClient(new WebViewClient() {


        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if( url.startsWith("http:") || url.startsWith("https:") ) {
                return false;
            }

            // Otherwise allow the OS to handle it
            else if (url.startsWith("tel:")) { 
                Intent tel = new Intent(Intent.ACTION_DIAL, Uri.parse(url)); 
                startActivity(tel);
                return true;
            }
            else if (url.startsWith("mailto:")) {
                String body = "Enter your Question, Enquiry or Feedback below:\n\n";
                Intent mail = new Intent(Intent.ACTION_SEND);
                mail.setType("application/octet-stream");
                mail.putExtra(Intent.EXTRA_EMAIL, new String[]{"info@automartdirect.co.uk"});
                mail.putExtra(Intent.EXTRA_SUBJECT, "Enquiry From AutoMart Direct Cars Android App");
                mail.putExtra(Intent.EXTRA_TEXT, body);
                startActivity(mail);
                return true;
                }
            return true;
        }
    });
    return rootView;
    }
}

我有一个带有 webview 和相关 HomeFragment Activity 的片段布局,它使这个 webview 布局膨胀。我在 MainActivity 的底部放置了 onBackPressed 方法,以便能够使用后退按钮返回此 webview 中。在 webview 中进行交互时,一切都按我的意愿工作。所有链接在 webview 中打开并按返回按钮将我带回到以前的 webview 历史记录。当 webview 中没有更多历史记录并按下后退按钮时,什么也不会发生,这又是理想的,因为我不希望退出应用程序。然后,这需要用户点击应用图标以打开导航抽屉并在菜单中选择另一个项目。

这是我卡住的地方。当我从导航抽屉中选择另一个项目(片段)时,它会显示出来,但是当我按下后退按钮时,应用程序关闭并显示消息“不幸的是,myapp 已停止。我错过了一些代码还是必须为每个片段添加 onBackPressed 代码?更重要的是,我对 onBackPressed 方法应该去哪里感到困惑。它应该只进入 MainActivity 还是应该进入每个 FragmentActivity?就目前而言,我拥有的唯一 onBackPressed 方法是在 MainActivity for webview 中有效,但可能仍然是错误的做法。我需要什么代码来处理其他片段,当按下后退按钮时,它不会使用我提到的上述消息关闭应用程序,它应该去哪里?

非常感谢任何帮助!

这里是 Logcat:

12-17 22:34:03.957: D/AndroidRuntime(8477): Shutting down VM
12-17 22:34:03.957: W/dalvikvm(8477): threadid=1: thread exiting with uncaught exception (group=0x41288930)
12-17 22:34:03.987: E/AndroidRuntime(8477): FATAL EXCEPTION: main
12-17 22:34:03.987: E/AndroidRuntime(8477): java.lang.NullPointerException
12-17 22:34:03.987: E/AndroidRuntime(8477):     at com.loui.automartdirectcars.MainActivity.onBackPressed(MainActivity.java:273)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at android.app.Activity.onKeyUp(Activity.java:2145)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at android.view.KeyEvent.dispatch(KeyEvent.java:2633)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at android.app.Activity.dispatchKeyEvent(Activity.java:2375)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1865)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at android.view.ViewRootImpl.deliverKeyEventPostIme(ViewRootImpl.java:3701)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at android.view.ViewRootImpl.handleImeFinishedEvent(ViewRootImpl.java:3651)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:2818)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at android.os.Handler.dispatchMessage(Handler.java:99)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at android.os.Looper.loop(Looper.java:137)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at android.app.ActivityThread.main(ActivityThread.java:5195)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at java.lang.reflect.Method.invokeNative(Native Method)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at java.lang.reflect.Method.invoke(Method.java:511)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:795)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:562)
12-17 22:34:03.987: E/AndroidRuntime(8477):     at dalvik.system.NativeStart.main(Native Method)

【问题讨论】:

  • 你有 logcat 显示吗?跟踪可能会指出问题所在。
  • 我认为 onBackPressed() 应该在 Fragments 内,因为 wbview 的东西在 Fragments 内。
  • 我已经为任何能理解它的人添加了 Logcat :-)

标签: android android-fragments webview navigation-drawer


【解决方案1】:

显然,问题基本上出在以下几行:

WebView mWebView = (WebView) findViewById(R.id.webView);
if (mWebView.canGoBack())

当您更改片段时,您的视图层次结构也会更改,并且方法 findViewById() 返回 null。然后你尝试访问一个空变量,导致NullPointerException。你可以通过添加一个空检查来解决这个问题:if (mWebView != null &amp;&amp; mWebView.canGoBack())

编辑

回答您的第二个问题:您放置 onBackPressed 事件处理逻辑的位置将取决于您想要实现的目标。如果你想要一个活动中所有片段的一般行为,你应该把它放在你的活动中。但是,如果您想要每个片段的特定行为,您应该将逻辑放在每个片段中。 Here 你可以找到一些处理方法。

【讨论】:

  • 感谢您的回复。我仅将 findViewById() 用于适用于 onBackPressed 的 webview 片段。但是对于其他片段,我没有这种方法。我应该应用类似的方法还是参考其他片段以避免致命异常?
  • 对此,我认为您可能为您的 Fragments 定义了一个接口,该接口扩展了一些实现您的行为的基本“onBackPressedListener”类,并且由于您维护对您的片段的引用,之后确定你的 webview 为空,只需调用手头的片段并让它 perofmr 按下后
  • 这是有道理的迭戈。我想要每个片段的特定行为,因此我需要处理每个片段中的逻辑。我多次查看该链接,但我只是突然想到我可能应该将类似的事件处理程序放在片段中而不是 MainActivity 中,因为每个片段都有不同的行为。在我精神崩溃之前将继续进行 2moro :-)。我今天受够了。非常感谢您的回复!
猜你喜欢
  • 2016-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-20
相关资源
最近更新 更多