【问题标题】:Coordinating Floating-Action Button behaviour across Child Fragments/Activities跨子片段/活动协调浮动操作按钮行为
【发布时间】:2015-04-04 10:29:17
【问题描述】:

我在协调 Activity 中的浮动操作按钮与其子项的行为时遇到问题。

在我的应用程序中,我有一个 Activity ,其中包含一个 ViewPager 和一个 xml 定义的 FloatingActionMenu 构造与 following libraryViewPager 包含灵活数量的Fragments,每个Fragment 内部是一个RecyclerView

单击时,FloatingActionMenu 扩展为两个按钮 - 一个向ViewPager 添加一个额外的页面/片段,另一个在用户所在的ViewPager 的页面上向RecyclerView 添加一个额外的行目前开启。此功能有效。

为了隐藏按钮,我在子片段中使用addOnItemTouchListenerRecyclerView。然后,在OnInterceptTouchEvent 中,如果滑动手势足够大,我使用自定义Interface 将事件转发回托管Activity,它会执行动画以隐藏或显示按钮。这是我的一些代码:

public class Categories extends ActionBarActivity implements
        View.OnClickListener,
        FloatingButtonInterface{

//...
// Final parameter is for the custom Interface
    mAdapter = new CategoriesViewPagerAdapter(mFragmentManager, allCategoriesCursor, this); 
    mViewPager = (ViewPager) findViewById(R.id.categories_view_pager_pager);
    mViewPager.setAdapter(mAdapter);

    menuOriginalXPos = mFloatingActionsMenu.getX();
    menuOriginalYPos = mFloatingActionsMenu.getY();

//...
// onTouchEvent is the imaginative name for my custom Interface method.
    @Override
    public void onTouchEvent(int callBackID) {
        if (callBackID == FloatingButtonInterface.MOTION_DOWN){
            if (mFloatingActionsMenu.getVisibility() == View.GONE) {
                Animation translate = new TranslateAnimation(
                        menuOriginalXPos, menuOriginalXPos, mViewPager.getHeight(), menuOriginalYPos);
                translate.setDuration(250);
                translate.setInterpolator(new LinearInterpolator());
                mFloatingActionsMenu.startAnimation(translate);
                mFloatingActionsMenu.setEnabled(true);
                mFloatingActionsMenu.setVisibility(View.VISIBLE);
            }
        } else if (callBackID == FloatingButtonInterface.MOTION_UP) {
            if (mFloatingActionsMenu.getVisibility() == View.VISIBLE) {
                Animation translate = new TranslateAnimation(
                        menuOriginalXPos, menuOriginalXPos, menuOriginalYPos, mViewPager.getHeight());
                translate.setDuration(250);
                translate.setInterpolator(new LinearInterpolator());
                mFloatingActionsMenu.startAnimation(translate);
                mFloatingActionsMenu.setEnabled(false);
                mFloatingActionsMenu.setVisibility(View.GONE);
            }
        }
    }

这是我非常直接的界面:

public interface FloatingButtonInterface {

    public static final int MOTION_UP = 4;
    public static final int MOTION_DOWN = MOTION_UP+1;
    public static final int MOTION_NONE = 0;

    public void onTouchEvent(int callBackID);
}

在我的ViewPager

public class CategoriesViewPagerAdapter extends FragmentStatePagerAdapter {

private static final String LOG_TAG = "CategoriesViewPagerAd";

private Cursor mCursor;
private FloatingButtonInterface hideMenuInterface;
private FragmentManager mFragmentManager;
private boolean refreshItem = false;

public CategoriesViewPagerAdapter(FragmentManager fm, Cursor data, FloatingButtonInterface hideMenuInterface) {
    super(fm);
    mCursor = data;
    mFragmentManager = fm;
    this.hideMenuInterface = hideMenuInterface;
}

@Override
public Fragment getItem(int i) {
    mCursor.moveToPosition(i);
    int categoryFragmentIdentifier =
            mCursor.getInt(mCursor.getColumnIndex(FilesDatabaseHelper.KEY_ID));
    CategoryFragment frag = CategoryFragment.newInstance(categoryFragmentIdentifier);
    frag.setOnTouchEvent(hideMenuInterface);
    return frag;
}

最后,ViewPagerFragments 之一:

public class CategoryFragment extends Fragment implements
        RecyclerView.OnItemTouchListener,
        View.OnClickListener{
//...
private static final int TOUCH_SLOP=200;
//...
static CategoryFragment newInstance(int categoryIdentifier){
        CategoryFragment frag = new CategoryFragment();
        Bundle bundle = new Bundle();
        bundle.putInt(CATEGORY_ID, categoryIdentifier);
        frag.setArguments(bundle);

        return frag;
    }
//...
@Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mRecyclerView.addOnItemTouchListener(this);
//...
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        switch (e.getAction()){
            case MotionEvent.ACTION_DOWN:
                originalFingerPos = e.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (e.getY() - originalFingerPos > TOUCH_SLOP){
                    if (hideMenuInterface != null) {
                        hideMenuInterface.onTouchEvent(FloatingButtonInterface.MOTION_DOWN);
                    } else {
                        Log.e(LOG_TAG, "hideMenuInterface is null");
                    }
                } else if (e.getY() + TOUCH_SLOP < originalFingerPos ){
                    if (hideMenuInterface != null) {
                        hideMenuInterface.onTouchEvent(FloatingButtonInterface.MOTION_UP);
                    } else {
                        Log.e(LOG_TAG, "hideMenuInterface is null");
                    }
                }
        }
        return false;
    }

我遇到的主要问题与我在配置更改后用户在屏幕上向上或向下滑动时用于自动隐藏按钮的代码有关。如果我在测试设备上更改屏幕方向,当我尝试通过滑动隐藏按钮时,一些Fragments 没有响应,当我检查LogCat 时,我可以看到hideMenuInterface 是@987654349 @。我能做些什么来解决这个问题?为什么它只影响少数片段而不是所有片段?如果我在 ViewPager 中滑动到最后一个片段,然后再次返回,问题就会自行解决 - 可能是因为片段已被重新创建。

感谢您提供的任何见解!我只包含了我认为相关的代码,否则会有页面,但请询问您是否认为还有其他帮助。

【问题讨论】:

    标签: android android-viewpager android-recyclerview floating-action-button


    【解决方案1】:

    我在Android Developer docs 中找到了这个问题的答案。按照此页面上的说明操作后,按钮行为更加可靠。


    编辑:

    为了扩展我之前的答案,我通过以下方式应用了文档中的信息:

    我通过添加一个名为FloatingButtonInterface 的内部interface 来更改CategoryFragment(占据每个ViewPager 项目的片段),并在onAttach() 期间为FloatingButtonInterface 变量赋值:

    public class CategoryFragment extends Fragment implements RecyclerView.OnItemTouchListener {
    //...
        private static final int TOUCH_SLOP = 200;
        private FloatingButtonInterface floatingButtonInterface;
    //...
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            floatingButtonInterface = (FloatingButtonInterface) activity;
        }
    //...
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            switch (e.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    originalFingerPos = e.getY();
                    return false;
                case MotionEvent.ACTION_MOVE:
                    if (e.getY() - originalFingerPos > TOUCH_SLOP) {
                        floatingButtonInterface.onTouchEvent(FloatingButtonInterface.MOTION_DOWN);
                        return false;
                    } else if (e.getY() + TOUCH_SLOP < originalFingerPos) {
                        floatingButtonInterace.onTouchEvent(FloatingButtonInterface.MOTION_UP);
                        return false;
                    }
                    return false;
                }
                return false;
            }
    //...
    public interface FloatingButtonInterface {
    
        public static final int MOTION_UP = MOTION_NONE + 4;
        public static final int MOTION_DOWN = MOTION_UP + 1;
        public static final int MOTION_NONE = 0;
    
        public void onTouchEvent(int callBackId);
    }
    

    唯一的其他变化是在ViewPagerAdapter 内部。由于CategoryFragment 负责分配接口,因此适配器中与此相关的任何代码都可以删除,因此适配器现在看起来像:

    public class CategoriesViewPagerAdapter extends FragmentStatePagerAdapter {
    
    private static final String LOG_TAG = "CategoriesViewPagerAd";
    
    private Cursor mCursor;
    private FloatingButtonInterface hideMenuInterface;
    private FragmentManager mFragmentManager;
    private boolean refreshItem = false;
    
    public CategoriesViewPagerAdapter(FragmentManager fm, Cursor data) {
        super(fm);
        mCursor = data;
        mFragmentManager = fm;
    }
    
    @Override
    public Fragment getItem(int i) {
        mCursor.moveToPosition(i);
        int categoryFragmentIdentifier =
                mCursor.getInt(mCursor.getColumnIndex(FilesDatabaseHelper.KEY_ID));
        CategoryFragment frag = CategoryFragment.newInstance(categoryFragmentIdentifier);
        return frag;
    }
    

    【讨论】:

    • 您还可以添加一个解释答案的答案。这将有助于其他人到达这里。
    • @Droidekas 公平点。我已经编辑了答案以添加更多信息。
    猜你喜欢
    • 1970-01-01
    • 2017-06-14
    • 2019-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-11
    • 2015-06-25
    • 1970-01-01
    相关资源
    最近更新 更多