web-dev-qa-db-ja.com

ツールバー-アクティビティが1つしかないドロワーから戻るボタンに切り替える

引き出しの開閉アイコン(ハンバーガーから矢印へ)から単純な戻る矢印への変更方法について、しばらくの間探していました。現時点のアプリケーションには、複数のフラグメントを切り替えるアクティビティが1つしかありません。ある時点で、メインフラグメントの1つ(つまり、ドロワーのフラグメントの1つ)から、前のフラグメントの下に階層的にあるフラグメント(つまり、「新規追加」フラグメント)に移行したいと思います。この新しいフラグメントでは、引き出しボタンの代わりに戻るボタンを表示するツールバーが必要です。

私はかなり長い間、さまざまなソリューションを探して試してきました。最も注目すべきものは次のとおりです。

現時点では、非表示および表示(およびネイティブの引き出しアイコンを非表示/表示)するカスタムアイコンを作成する、長くて骨の折れる方法を考えています。しかし、引き出しと戻るボタンを切り替えるより良い方法はありますか?

関連する質問ですが、マテリアルデザインのドキュメントを見てきました。いくつかの例では、左上隅にXがあります。ドロワーの実装とバックアップ/バックアップボタンの実装とでは、実装の違いは何ですか?

ありがとう〜

編集:

アイコンを置き換える方法はわかりますが、クリックイベントを取得するにはどうすればよいですか?

これまでのところ、これが私の最高のリードでした。

私が今試したこと:

  • 必要に応じてDrawerToggleを無効にしました(つまり、mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);
  • NavigationDrawerFragment、Activity、および現在テストしているDialogFragmentにonOptionsItemSelectedのログを追加しました。item.getItemId() == Android.R.id.homeがtrueの場合に実行されます。これらのログステートメントはどれもオフになりません

より良いコンテキストのために、メニューに「保存」ボタンを追加し、引き出しアイコンを「X」に変更するフルスクリーンフラグメントを作成しました。フラグメントはメニュー保存イベントを取得できますが、Xがタップされたときにアクティビティとドロワーも取得できません。

編集2:

要求に応じて、ここにいくつかのコードがあります。これはすべて this Github repo からのものであることに注意してください。これは私が積極的に取り組んでいます(あちこちでいくつかの役に立たない機能があることに注意してください)。

ActivityMain

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Add the toolbar
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    if (mToolbar != null) {
        setSupportActionBar(mToolbar);
    }

    // Initialize the drawer
    mNavigationDrawerFragment = (NavigationDrawerFragment)
            getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);

    // Set up the drawer
    mNavigationDrawerFragment.setUp(
            R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout),
            mToolbar);

    // TODO: Check if this helps to catch the main toolbar button click
    getSupportActionBar().setDisplayShowHomeEnabled(true);

    // Get the titles for the Toolbar
    mTitles = getResources().getStringArray(R.array.drawer_items);

    mDrawerPosition = -1;
    if (savedInstanceState == null) {
        // If there was no saved position, then the default, starting position should be used
        forceChangeItemSelected(0);
    }
    else {
        // Otherwise, get the saved position from the bundle
        int position = savedInstanceState.getInt(KEY_DRAWERPOS);
        mNavigationDrawerFragment.setSelectedItem(position);
        // Title needs to be re-set
        getSupportActionBar().setTitle(mTitles[position]);
    }

    // If I include the below bit, then the DrawerToggle doesn't function
        // I don't know how to switch it back and forth
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(LOG_TAG, "Navigation was clicked");

        }
    });
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    Log.d(LOG_TAG, "Activity responding to menu click...");
    if(item.getItemId() == Android.R.id.home) Log.d(LOG_TAG, "Activity got it....");

    // If the fragment is supposed to handle things, then let it
    if(mIsFragmentHandlingMenus) return false;

    int id = item.getItemId();
    if(id == R.id.save) {
        // This isn't implemented! If chosen, then there's a bug!
        Log.e(LOG_TAG, "onOptionsItemSelected: Save was selected!");
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void fragmentHandlingMenus(boolean isFragmentHandlingMenus) {
    // Simply store the setting
    mIsFragmentHandlingMenus = isFragmentHandlingMenus;

    // Toggle the drawer as necessary
    mNavigationDrawerFragment.toggleDrawerUse(!isFragmentHandlingMenus);
}

NavigationDrawerFragment

public void toggleDrawerUse(boolean useDrawer) {
    // Enable/Disable the icon being used by the drawer
    mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);

    // TODO: Enable/Disable the drawer even being able to open/close
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Log.d(LOGTAG, "Drawer responding to menu click...");
    if(item.getItemId() == Android.R.id.home) Log.d(LOGTAG, "Drawer got it....");
    if (mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

GoalAdderFragment

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    // Allow this fragment to handle toolbar menu items
    setHasOptionsMenu(true);

    // Set up the toolbar
    ((ActionBarActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    ((ActionBarActivity) getActivity()).getSupportActionBar().setHomeAsUpIndicator(Android.R.drawable.ic_menu_close_clear_cancel);
    ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(getResources().getString(R.string.title_addgoal));
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    // Cache the Activity as the frag handler if necessary
    if(mFragHandler == null)
        mFragHandler = (TransactionHandler.FragmentTransactionHandler) getActivity();
    // Tell the Activity to let fragments handle the menu events
    mFragHandler.fragmentHandlingMenus(true);
}

@Override
public void onDetach() {
    super.onDetach();

    // Tell the Activity that it can now handle menu events once again
    mFragHandler.fragmentHandlingMenus(false);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.save_menu, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Log.d(LOGTAG, "Item id: " + item.getItemId() + " | Save id: " + R.id.save);
    Toast.makeText(getActivity(), "Fragment activated!", Toast.LENGTH_SHORT).show();

    switch (item.getItemId()) {
        case R.id.save:
            return true;
        case Android.R.id.home:
            return true;
        default:
            break;
    }

    return false;
}

解決:

これが最終的な解決策であり、以下の受け入れられた答えの助けを借りて:

NavigationDrawerFragment

private View.OnClickListener mOriginalListener;

public void setUp(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
     /* Rest of setting up code */

     // Save the default listener after setting everything else up
     mOriginalListener = mDrawerToggle.getToolbarNavigationClickListener();
}

// Tells the toolbar+drawer to switch to the up button or switch back to the normal drawer
public void toggleDrawerUse(boolean useDrawer) {
    // Enable/Disable the icon being used by the drawer
    mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);

    // Switch between the listeners as necessary
    if(useDrawer)
        mDrawerToggle.setToolbarNavigationClickListener(mOriginalListener);
    else
        mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(), "Custom listener", Toast.LENGTH_SHORT).show();
            }
        });
}
40
Jawad

それはおそらくあなたが聞きたいことではありませんが、概念的な観点からでも、断片ではなく新しい活動に行きます。

メインアクティビティはドロワーに厳密にリンクされているため、ドロワーにアクセスせずに新しいフラグメントをロードしても意味がありません(ただし、そう思う場合は他の回答をお待ちください)。新しいアクティビティは両方の問題を解決します。それは引き出しがなく、メインの子になる可能性があるためです。

あなたの副質問もスポットに見えます。 「新規追加」アクティビティは、ガイドラインの「フルスクリーンダイアログ」視覚パターンにうまく適合します。見る:

http://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs

このパターンには、右上に「保存」、肯定ボタン、およびXがあります。概念的には、Xボタンは、バックスタックをナビゲートするのではなく、プロセスをキャンセル/中止することです。これは、アクションを起こさずに何かを却下していることを意味します。これは、あなたがやりたいことにぴったりです。

設計の観点から見ると、新しいActivityで簡単に作成できます。また、フラグメントのポイントが基本的にタブレットと大画面で一度に2つ以上を表すことができる場合-再び-左の古いフラグメントと右の「新しい追加」フラグメントにあまり満足しません。

むしろ-タブレット上で-ガイドラインで提案されているように、フローティングダイアログを使用します。

http://www.google.com/design/spec/components/dialogs.html#dialogs-confirmation-dialogs

そのため、携帯電話にはXボタンを、タブレットにはフローティングダイアログ(下部にボタンがある)を使用したフルスクリーンアクティビティがあります。これは、私にとって、最もガイドラインに準拠したアプローチです。


リンク全体を読むことをお勧めします。 <-とXの違いについては、

Xは、ビューの状態が常に保存されている場合、またはアプリに下書きまたは自動保存機能がある場合に使用される上矢印とは異なります。たとえば、すべての変更がすぐにコミットされるため、設定で上矢印が使用されます。

そしてまた

この設定例 でXをタッチすると、すべての変更が破棄されます。変更は、[保存]をタッチしたときにのみ保存されます。

12
natario

このコードをActivityonCreate()に入れます。私にとってはうまくいきます。 compileSdk 23以上を使用しても。

    drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    if(toolbar != null) {
        toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        toggle.syncState();
        drawer.setDrawerListener(toggle);
        getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(true); // show back button
                    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            onBackPressed();
                        }
                    });
                } else {
                    //show hamburger
                    getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                    toggle.syncState();
                    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            drawer.openDrawer(GravityCompat.START);
                        }
                    });
                }
            }
        });
44
matusalem

最新のAPI 24でも動作するはずです。

アクティビティでonCreate()これを行います:

final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
final DrawerLayout drawer = (DrawerLayout) view.findViewById(R.id.drawer_layout);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, 
    R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();

final View.OnClickListener originalToolbarListener = toggle.getToolbarNavigationClickListener();

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            toggle.setDrawerIndicatorEnabled(false);
            toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    getSupportFragmentManager().popBackStack();
                }
            });
        } else {
            toggle.setDrawerIndicatorEnabled(true);
            toggle.setToolbarNavigationClickListener(originalToolbarListener);
        }
    }
});
27
mixel

@matusalemからの答えは素晴らしいです。画面に左側からスワイプしてドロワーを開くこともできるので、注意してください。一部の人にとってはこれが望ましいかもしれませんが、私にとっては、メインフラグメント以外のフラグメントでは意味をなさないため、ドロワーを無効にしていました。スワイプはここで簡単に無効になります- ナビゲーションドロワー-スワイプを無効にします

これはおそらく答えへのコメントに属しますが、十分な評判はありません。謝罪いたします。

4
tpankake

フラグメントを変更するときに、同じアクティビティ内でハンバーガーメニューと戻る矢印を切り替えるときに同じ問題が発生しました。これが私の実用的なソリューションです。誰かに役立つことを願っています。

アクティビティ内のリスナー:

private View.OnClickListener toolbarMenuListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //will be called only if toggle.setDrawerIndicatorEnabled(false); !
            Log.v(tag,"toggle onClick:"+v.getId()+" Android.R.id.home:"+Android.R.id.home);
            onBackPressed();
        }
    };

OnCreate()のようなコード:

...
...
setSupportActionBar(toolbar);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();

//set listener so you know when back on arrow is pressed
toggle.setToolbarNavigationClickListener(toolbarMenuListener);
...
...

コメントで興味のある部分(返されるクラスは私のクラスの一部であり、無効に設定できます):

/**
     * Method to set up action bar drawer.
     * @param enableBackDrawerIcon set true if want to show drawer back arrow,
     *                             false to show hamburger menu.
     * @param title shown next to drawer icon
     */
    public BaseMenusActivity drawerSetupToggle(boolean enableBackDrawerIcon, String title) {
        //NOTE: order of methods call is important!
        // If you change order order of setDrawerIndicatorEnabled and setDisplayHomeAsUpEnabled
        // method calls it won't work, weird bugs will happen (like no icon at all)
        if(enableBackDrawerIcon){
            Log.v(tag,"show drawer back icon");
            //hides hamburger menu and enables View.OnClickListener to be called
            toggle.setDrawerIndicatorEnabled(false);
            //show back arrow
            if(getSupportActionBar()!=null)
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        } else {
            Log.v(tag,"show hamburger menu");
            //hide back arrow
            if(getSupportActionBar()!=null)
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            //shows hamburger menu and prevents View.OnClickListener to be called
            toggle.setDrawerIndicatorEnabled(true);
        }

        setTitle(title);
        return this;
    }

[〜#〜] note [〜#〜]:呼び出されたメソッドの順序は重要です!このように2行で書くことができればより良いでしょうが、WO N'T WORK(少なくとも私にとって):

toggle.setDrawerIndicatorEnabled(!enableBackDrawerIcon);
     getSupportActionBar().setDisplayHomeAsUpEnabled(enableBackDrawerIcon);

メソッドの順序が混乱する理由に興味がある場合は、それらのメソッドの実装を調べてください。

0
user1540907
//This if block makes the menu back button to respond to clicks
    //The onOptionsItemSelected fun for whatever reason was not capturing back menu clicks
    if (toolbar != null) {
       /* toggle = ActionBarDrawerToggle(
                this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
        toggle.syncState()
        drawer_layout.setDrawerListener(toggle)*/
        supportFragmentManager.addOnBackStackChangedListener(object : FragmentManager.OnBackStackChangedListener {
            override fun onBackStackChanged() {
                if (supportFragmentManager.backStackEntryCount > 0) {
                    supportActionBar?.setDisplayHomeAsUpEnabled(true) // show back button
                    toolbar.setNavigationOnClickListener(object : View.OnClickListener {
                        override fun onClick(v: View) {
                            onBackPressed()
                        }
                    })
                } else {
                    //show hamburger
                    supportActionBar?.setDisplayHomeAsUpEnabled(false)
                    toggle.syncState()
                    toolbar.setNavigationOnClickListener(object : View.OnClickListener {
                        override fun onClick(v: View) {
                            drawer_layout.openDrawer(GravityCompat.START)
                        }
                    })
                }
            }
        })

    }

使用している場合は、「toggle = ActionBarDrawerToggle(this、drawer_layout、toolbar、R.string.navigation_drawer_open、R.string.navigation_drawer_close)toggle.syncState()ドロワー_layout.setDrawerListener(toggle)」(4-7行)をコメントアウトする必要がありますAndroid Studioで自動生成されたナビゲーションレイアウト。そうでない場合、戻るメニューボタンの動作が不安定になります。それが私がしたことであり、私にとって完璧に機能しました。

0
defemz