onBackPressed путаница при работе с фрагментами

Я просмотрел похожие вопросы, касающиеся аналогичной проблемы, но мне еще предстоит найти решение моей проблемы.

У меня есть приложение, использующее навигационный ящик. У меня есть MainActivity, различные FragmentActivities и различные макеты фрагментов. Мой код 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[]{"[email protected]"});
        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;
}
}

Ниже приведена активность для веб-просмотра:

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[]{"[email protected]"});
                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;
    }
}

У меня есть макет фрагмента с веб-просмотром и относительной активностью HomeFragment, которая раздувает этот макет веб-просмотра. Я разместил метод onBackPressed в нижней части MainActivity, чтобы иметь возможность вернуться в этот веб-просмотр с помощью кнопки «Назад». Все работает так, как я хотел бы, взаимодействуя внутри веб-просмотра. Все ссылки открываются в веб-просмотре, и нажатие кнопки «Назад» возвращает меня к предыдущей истории веб-просмотра по мере необходимости. Когда в веб-просмотре больше нет истории и нажата кнопка «Назад», ничего не происходит, что снова идеально, поскольку я не хочу, чтобы приложение закрывалось. Затем от пользователя требуется коснуться значка приложения, чтобы открыть панель навигации и выбрать другой элемент в меню.

Вот где я застрял. Когда я выбираю другой элемент (фрагмент) из панели навигации, он отображается, но когда я нажимаю кнопку «Назад», приложение закрывается с сообщением «К сожалению, мое приложение остановлено. Я пропустил какой-то код или мне нужно добавить код onBackPressed для каждого фрагмента? Что еще более важно, я не понимаю, куда должен идти метод onBackPressed. Должен ли он идти только в MainActivity или должен идти в каждом FragmentActivity? В настоящее время единственный метод onBackPressed, который у меня есть, находится в MainActivity для веб-просмотра, который работает, но все же может быть неправильной практикой. Какой код мне нужен для других фрагментов, чтобы при нажатии кнопки «Назад» приложение не закрывалось с упомянутым выше сообщением, и куда оно должно идти?

Любая помощь высоко ценится!

Вот Логкат:

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)

person Loui    schedule 17.12.2013    source источник
comment
У вас есть логарифм, чтобы показать? След, скорее всего, укажет на проблему.   -  person Nathaniel D. Waggoner    schedule 18.12.2013
comment
я думаю, что onBackPressed() должен быть внутри фрагментов, потому что материал wbview находится внутри фрагментов.   -  person ImGeorge    schedule 18.12.2013
comment
Я добавил Logcat для тех, кто может в этом разобраться :-)   -  person Loui    schedule 18.12.2013


Ответы (1)


Судя по всему, проблема в основном в этих строках:

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

Когда вы изменяете свой фрагмент, ваша иерархия представлений также меняется, и метод findViewById() возвращает значение null. Затем вы пытаетесь получить доступ к нулевой переменной, вызывая ошибку NullPointerException. Вы можете исправить это, добавив нулевую проверку: if (mWebView != null && mWebView.canGoBack())

ИЗМЕНИТЬ

Чтобы ответить на ваш второй вопрос: место, где вы поместите свою логику обработки событий onBackPressed, будет зависеть от того, чего вы хотите достичь. Если вам нужно общее поведение для всех фрагментов внутри действия, вы должны поместить его в свое действие. Но если вам нужно определенное поведение для каждого фрагмента, вы должны поместить логику в каждый фрагмент. Здесь вы можете найти несколько подходов к решению этой проблемы.

person diegocarloslima    schedule 17.12.2013
comment
Ценю ваш ответ. Я использовал findViewById() только для фрагмента веб-просмотра, который работает для onBackPressed. Но у меня нет этого метода для других фрагментов. Должен ли я применить аналогичный метод или сделать ссылку и на другие фрагменты, чтобы избежать фатального исключения? - person Loui; 18.12.2013
comment
Размышляя об этом, я думаю, что вы, вероятно, могли бы иметь интерфейс, определенный для ваших фрагментов, который расширяет некоторый базовый класс onBackPressedListener, который реализует ваше поведение, и, поскольку вы поддерживаете ссылку на свои фрагменты, после определения вашего веб-просмотра является нулевым, просто позвоните в фрагмент под рукой и прижать его сзади - person Nathaniel D. Waggoner; 18.12.2013
comment
Это имеет смысл, Диего. Мне нужно конкретное поведение для каждого фрагмента, поэтому мне нужно обрабатывать логику в каждом фрагменте. Я просматривал эту ссылку несколько раз, но только сейчас мне пришло в голову, что, возможно, мне следует разместить аналогичный обработчик событий во фрагментах, а не в MainActivity, поскольку каждый фрагмент имеет различное поведение. Я продолжу это 2moro, пока у меня не случился нервный срыв :-). С меня хватит на сегодня. Большое спасибо за ответы! - person Loui; 18.12.2013