Android的片段处理返回按钮按
我的活动中有一些片段
[1], [2], [3], [4], [5], [6]
并且在返回按钮按下时,如果当前活动片段是[2],则必须从[2]返回到[1],否则不做任何事情。
最佳做法是什么?
编辑 :应用程序不能从[3] … [6]返回到[2]
在片段之间转换时, addToBackStack()
作为FragmentTransaction
一部分进行调用:
FragmentTransaction tx = fragmentManager.beginTransation(); tx.replace( R.id.fragment, new MyFragment() ).addToBackStack( "tag" ).commit();
如果你需要更详细的控制(例如当一些碎片可见时,你想禁止后退键),你可以在你的碎片的父视图上设置一个OnKeyListener
:
//You need to add the following line for this solution to work; thanks skayred fragment.getView().setFocusableInTouchMode(true); fragment.getView().requestFocus(); fragment.getView().setOnKeyListener( new OnKeyListener() { @Override public boolean onKey( View v, int keyCode, KeyEvent event ) { if( keyCode == KeyEvent.KEYCODE_BACK ) { return true; } return false; } } );
我宁愿做这样的事情:
private final static String TAG_FRAGMENT = "TAG_FRAGMENT"; private void showFragment() { final Myfragment fragment = new MyFragment(); final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.fragment, fragment, TAG_FRAGMENT); transaction.addToBackStack(null); transaction.commit(); } @Override public void onBackPressed() { final Myfragment fragment = (Myfragment) getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT); if (fragment.allowBackPressed()) { // and then you define a method allowBackPressed with the logic to allow back pressed or not super.onBackPressed(); } }
如果你覆盖片段视图的onKey方法,你将需要:
view.setFocusableInTouchMode(true); view.requestFocus(); view.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { Log.i(tag, "keyCode: " + keyCode); if( keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { Log.i(tag, "onKey Back listener is working!!!"); getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); return true; } return false; } });
使用addToBackStack方法替换另一个片段时:
getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment).addToBackStack("my_fragment").commit();
然后在你的活动中,使用下面的代码从一个片段返回到另一个(前一个)。
@Override public void onBackPressed() { if (getFragmentManager().getBackStackEntryCount() > 0) { getFragmentManager().popBackStack(); } else { super.onBackPressed(); } }
如果你想处理硬件后退键事件,则必须在Fragment的onActivityCreated()方法中执行以下代码。
您还需要检查Action_Down或Action_UP事件。 如果你不检查那么onKey()方法将会调用2次。
另外,如果你的rootview(getView())不包含焦点,那么它将无法工作。 如果你已经点击了任何控件,那么你需要使用getView()。focusFocus(); 之后只有在Keydown()会打电话。
getView().setFocusableInTouchMode(true); getView().requestFocus(); getView().setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { if (keyCode == KeyEvent.KEYCODE_BACK) { Toast.makeText(getActivity(), "Back Pressed", Toast.LENGTH_SHORT).show(); return true; } } return false; } });
为我工作得很好。
在这里找到最理想的方式: 片段:当按下按钮并定制它时调用哪个回调
public class MyActivity extends Activity { //... //Defined in Activity class, so override @Override public void onBackPressed() { super.onBackPressed(); myFragment.onBackPressed(); } } public class MyFragment extends Fragment { //Your created method public static void onBackPressed() { //Pop Fragments off backstack and do your other checks } }
创建界面:
BackButtonHandlerInterface
public interface BackButtonHandlerInterface { void addBackClickListener (OnBackClickListener onBackClickListener); void removeBackClickListener (OnBackClickListener onBackClickListener); }
OnBackClickListener
public interface OnBackClickListener { boolean onBackClick(); }
在活动中:
public class MainActivity extends AppCompatActivity implements BackButtonHandlerInterface { private ArrayList<WeakReference<OnBackClickListener>> backClickListenersList = new ArrayList<>(); @Override public void addBackClickListener(OnBackClickListener onBackClickListener) { backClickListenersList.add(new WeakReference<>(onBackClickListener)); } @Override public void removeBackClickListener(OnBackClickListener onBackClickListener) { for (Iterator<WeakReference<OnBackClickListener>> iterator = backClickListenersList.iterator(); iterator.hasNext();){ WeakReference<OnBackClickListener> weakRef = iterator.next(); if (weakRef.get() == onBackClickListener){ iterator.remove(); } } } @Override public void onBackPressed() { if(!fragmentsBackKeyIntercept()){ super.onBackPressed(); } } private boolean fragmentsBackKeyIntercept() { boolean isIntercept = false; for (WeakReference<OnBackClickListener> weakRef : backClickListenersList) { OnBackClickListener onBackClickListener = weakRef.get(); if (onBackClickListener != null) { boolean isFragmIntercept = onBackClickListener.onBackClick(); if (!isIntercept) isIntercept = isFragmIntercept; } } return isIntercept; } }
在片段 :
public class MyFragment extends Fragment implements OnBackClickListener{ private BackButtonHandlerInterface backButtonHandler; @Override public void onAttach(Activity activity) { super.onAttach(activity); backButtonHandler = (BackButtonHandlerInterface) activity; backButtonHandler.addBackClickListener(this); } @Override public void onDetach() { super.onDetach(); backButtonHandler.removeBackClickListener(this); backButtonHandler = null; } @Override public boolean onBackClick() { //This method handle onBackPressed()! return true or false return false; } }
@Override public void onResume() { super.onResume(); getView().setFocusableInTouchMode(true); getView().requestFocus(); getView().setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){ if (mDrawerLayout.isDrawerOpen(GravityCompat.START)){ mDrawerLayout.closeDrawer(GravityCompat.START); } return true; } return false; } }); }
或者你可以使用getSupportFragmentManager().getBackStackEntryCount()
来检查要做什么:
@Override public void onBackPressed() { logger.d("@@@@@@ back stack entry count : " + getSupportFragmentManager().getBackStackEntryCount()); if (getSupportFragmentManager().getBackStackEntryCount() != 0) { // only show dialog while there's back stack entry dialog.show(getSupportFragmentManager(), "ConfirmDialogFragment"); } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) { // or just go back to main activity super.onBackPressed(); } }
如果你管理添加到每个事务的堆栈流,那么你可以做这样的事情,以便当用户按下后退按钮(你也可以映射主页按钮)时显示以前的片段。
@Override public void onBackPressed() { if (getFragmentManager().getBackStackEntryCount() > 0) getFragmentManager().popBackStack(); else super.onBackPressed(); }
我正在与SlidingMenu和Fragment合作,在这里介绍我的案例,希望能帮助别人。
按下[返回]键时的逻辑:
- 当SlidingMenu显示,关闭它,没有更多的事情要做。
- 或者当第二个(或更多)片段显示时,回退到前一个片段,而没有更多的事情要做。
-
SlidingMenu不显示,当前的片段是#0,原来的[Back]键是否做。
public class Main extends SherlockFragmentActivity { private SlidingMenu menu=null; Constants.VP=new ViewPager(this); //Some stuff... @Override public void onBackPressed() { if(menu.isMenuShowing()) { menu.showContent(true); //Close SlidingMenu when menu showing return; } else { int page=Constants.VP.getCurrentItem(); if(page>0) { Constants.VP.setCurrentItem(page-1, true); //Show previous fragment until Fragment#0 return; } else {super.onBackPressed();} //If SlidingMenu is not showing and current Fragment is #0, do the original [Back] key does. In my case is exit from APP } } }
这是一个非常好的和可靠的解决方案: http : //vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/
这个人做了一个抽象的片段来处理backPress行为,并使用策略模式在活动片段之间切换。
对于你们中的一些人来说,在抽象类中可能有一点缺点…
不久之后,链接的解决方案如下所示:
// Abstract Fragment handling the back presses public abstract class BackHandledFragment extends Fragment { protected BackHandlerInterface backHandlerInterface; public abstract String getTagText(); public abstract boolean onBackPressed(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(!(getActivity() instanceof BackHandlerInterface)) { throw new ClassCastException("Hosting activity must implement BackHandlerInterface"); } else { backHandlerInterface = (BackHandlerInterface) getActivity(); } } @Override public void onStart() { super.onStart(); // Mark this fragment as the selected Fragment. backHandlerInterface.setSelectedFragment(this); } public interface BackHandlerInterface { public void setSelectedFragment(BackHandledFragment backHandledFragment); } }
并在活动中使用:
// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS // IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment public class TheActivity extends FragmentActivity implements BackHandlerInterface { private BackHandledFragment selectedFragment; @Override public void onBackPressed() { if(selectedFragment == null || !selectedFragment.onBackPressed()) { // Selected fragment did not consume the back press event. super.onBackPressed(); } } @Override public void setSelectedFragment(BackHandledFragment selectedFragment) { this.selectedFragment = selectedFragment; } }
工作代码:
package com.example.keralapolice; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentManager.OnBackStackChangedListener; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; public class ChiefFragment extends Fragment { View view; // public OnBackPressedListener onBackPressedListener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle args) { view = inflater.inflate(R.layout.activity_chief, container, false); getActivity().getActionBar().hide(); view.setFocusableInTouchMode(true); view.requestFocus(); view.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { Log.i(getTag(), "keyCode: " + keyCode); if (keyCode == KeyEvent.KEYCODE_BACK) { getActivity().getActionBar().show(); Log.i(getTag(), "onKey Back listener is working!!!"); getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); // String cameback="CameBack"; Intent i = new Intent(getActivity(), home.class); // i.putExtra("Comingback", cameback); startActivity(i); return true; } else { return false; } } }); return view; } }
我认为最简单的方法是创建一个接口,并在Activity中检查片段是否为接口类型,如果是,则调用其方法来处理弹出窗口。 这里是在片段中实现的接口。
public interface BackPressedFragment { // Note for this to work, name AND tag must be set anytime the fragment is added to back stack, eg // getActivity().getSupportFragmentManager().beginTransaction() // .replace(R.id.fragment_container, MyFragment.newInstance(), "MY_FRAG_TAG") // .addToBackStack("MY_FRAG_TAG") // .commit(); // This is really an override. Should call popBackStack itself. void onPopBackStack(); }
以下是如何实现它。
public class MyFragment extends Fragment implements BackPressedFragment @Override public void onPopBackStack() { /* Your code goes here, do anything you want. */ getActivity().getSupportFragmentManager().popBackStack(); }
而在你的Activity中,当你处理pop(很可能在onBackPressed和onOptionsItemSelected中),使用这个方法弹出backstack:
public void popBackStack() { FragmentManager fm = getSupportFragmentManager(); // Call current fragment's onPopBackStack if it has one. String fragmentTag = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1).getName(); Fragment currentFragment = getSupportFragmentManager().findFragmentByTag(fragmentTag); if (currentFragment instanceof BackPressedFragment) ((BackPressedFragment)currentFragment).onPopBackStack(); else fm.popBackStack(); }
我们创建了一个小型库,用于处理多个片段和/或活动中的背压。 用法与在您的gradle文件中添加依赖关系一样简单:
compile 'net.skoumal.fragmentback:fragment-back:0.1.0'
让你的片段实现BackFragment
接口:
public abstract class MyFragment extends Fragment implements BackFragment { public boolean onBackPressed() { // -- your code -- // return true if you want to consume back-pressed event return false; } public int getBackPriority() { return NORMAL_BACK_PRIORITY; } }
通知你关于后退的片段:
public class MainActivity extends AppCompatActivity { @Override public void onBackPressed() { // first ask your fragments to handle back-pressed event if(!BackFragmentHelper.fireOnBackPressedEvent(this)) { // lets do the default back action if fragments don't consume it super.onBackPressed(); } } }
有关更多详细信息和其他用例,请访问GitHub页面:
你可以使用getActionBar().setDisplayHomeAsUpEnabled()
:
@Override public void onBackStackChanged() { int backStackEntryCount = getFragmentManager().getBackStackEntryCount(); if(backStackEntryCount > 0){ getActionBar().setDisplayHomeAsUpEnabled(true); }else{ getActionBar().setDisplayHomeAsUpEnabled(false); } }
rootView.setFocusableInTouchMode(true); rootView.requestFocus(); rootView.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { Fragment NameofFragment = new NameofFragment; FragmentTransaction transaction=getFragmentManager().beginTransaction(); transaction.replace(R.id.frame_container,NameofFragment); transaction.commit(); return true; } return false; } }); return rootView;
将addToBackStack()添加到片段事务中,然后使用以下代码实现片段的后退导航
getSupportFragmentManager().addOnBackStackChangedListener( new FragmentManager.OnBackStackChangedListener() { public void onBackStackChanged() { // Update your UI here. } });
如果你正在使用FragmentActivity。 然后这样做
首先调用这个在你的Fragment里面。
public void callParentMethod(){ getActivity().onBackPressed(); }
然后在你的父FragmentActivity
类中调用onBackPressed
方法。
@Override public void onBackPressed() { //super.onBackPressed(); //create a dialog to ask yes no question whether or not the user wants to exit ... }
将此代码添加到您的活动中
@覆盖
public void onBackPressed() { if (getFragmentManager().getBackStackEntryCount() == 0) { super.onBackPressed(); } else { getFragmentManager().popBackStack(); } }
在commit()之前在你的Fragment中加入这一行
ft.addToBackStack(“任何名称”);
对于那些使用静态片段的人
在一个情况下,如果你有一个静态片段,那么这将是可取的。 制作一个片段的实例对象
private static MyFragment instance=null;
在MyFragment的onCreate()中初始化那个实例
instance=this;
也做一个函数来获取实例
public static MyFragment getInstance(){ return instance; }
也做功能
public boolean allowBackPressed(){ if(allowBack==true){ return true; } return false; } //allowBack is a boolean variable that will be set to true at the action //where you want that your backButton should not close activity. In my case I open //Navigation Drawer then I set it to true. so when I press backbutton my //drawer should be get closed public void performSomeAction(){ //.. Your code ///Here I have closed my drawer }
在你的活动中你可以做
@Override public void onBackPressed() { if (MyFragment.getInstance().allowBackPressed()) { MyFragment.getInstance().performSomeAction(); } else{ super.onBackPressed(); } }
在片段类中把这个代码放回事件:
rootView.setFocusableInTouchMode(true); rootView.requestFocus(); rootView.setOnKeyListener( new OnKeyListener() { @Override public boolean onKey( View v, int keyCode, KeyEvent event ) { if( keyCode == KeyEvent.KEYCODE_BACK ) { FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.frame_container, new Book_service_provider()).commit(); return true; } return false; } } );
看完所有的解决方案后,我意识到有一个更简单的解决方案。
在托管所有片段的活动的onBackPressed()中,找到要防止后压的片段。 那么如果找到了,就回来。 那么popBackStack将永远不会发生这个片段。
@Override public void onBackPressed() { Fragment1 fragment1 = (Fragment1) getFragmentManager().findFragmentByTag(“Fragment1”); if (fragment1 != null) return; if (getFragmentManager().getBackStackEntryCount() > 0){ getFragmentManager().popBackStack(); } }
检查后台工作完美
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (getFragmentManager().getBackStackEntryCount() == 1) { // DO something here since there is only one fragment left // Popping a dialog asking to quit the application return false; } } return super.onKeyDown(keyCode, event); }
在你的oncreateView()方法中,你需要编写这个代码,在KEYCODE_BACk条件下,你可以写任何你想要的功能
View v = inflater.inflate(R.layout.xyz, container, false); //Back pressed Logic for fragment v.setFocusableInTouchMode(true); v.requestFocus(); v.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { if (keyCode == KeyEvent.KEYCODE_BACK) { getActivity().finish(); Intent intent = new Intent(getActivity(), MainActivity.class); startActivity(intent); return true; } } return false; } });