FAB与NestedScrollView

时间:2017-05-01 12:20:04

标签: android scrollview floating-action-button nestedscrollview

我有一个"详细信息"片段有很多textview,relativelayouts等。这些片段包含在NestedScrollView:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?android:attr/windowBackground"
    android:orientation="vertical"
    android:paddingBottom="10dp">

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/movie_details_scroll"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        // Here are the textviews, relativelayouts etc...
    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/edit_movies_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@drawable/ic_edit_white_24dp"
    />
</android.support.design.widget.CoordinatorLayout>

现在我想在屏幕底部添加一个FAB(不在nestedscrollview 的底部),当我滚动浏览nestedscrollview时,它也会向下滚动。但是使用我的代码,FAB始终位于nestedscrollview的底部。所以当我一直向下滚动时,FAB出现了。我希望FAB始终可以在右下角看到......

修改

我忘了提到我使用了淡化操作栏(https://github.com/ManuelPeinado/FadingActionBar)但有点编辑。

相关代码:

m_FadingActionBarHelper.createView(getContext()); // this will create the view with header content etc.

createView:

public final View createView(LayoutInflater inflater) {
  //
  // Prepare everything

  mInflater = inflater;
  if (mContentView == null) {
    mContentView = inflater.inflate(mContentLayoutResId, null); // this will load my view which i already posted.
  }
  if (mHeaderView == null) {
    mHeaderView = inflater.inflate(mHeaderLayoutResId, null, false);
  }

  // See if we are in a ListView, WebView or ScrollView scenario

  ListView listView = (ListView) mContentView.findViewById(android.R.id.list);
  View root;
  if (listView != null) {
    root = createListView(listView);
  } else if (mContentView instanceof CDMObservableWebViewWithHeader){
    root = createWebView();
  } else {
    root = createScrollView(); // this will be called in my example
  }

  if (mHeaderOverlayView == null && mHeaderOverlayLayoutResId != 0) {
    mHeaderOverlayView = inflater.inflate(mHeaderOverlayLayoutResId, mMarginView, false);
  }
  if (mHeaderOverlayView != null) {
    mMarginView.addView(mHeaderOverlayView);
  }

  // Use measured height here as an estimate of the header height, later on after the layout is complete
  // we'll use the actual height
  int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
  int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
  mHeaderView.measure(widthMeasureSpec, heightMeasureSpec);
  updateHeaderHeight(mHeaderView.getMeasuredHeight());

  root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
      int headerHeight = mHeaderContainer.getHeight();
      if ((!mFirstGlobalLayoutPerformed || (headerHeight != mLastHeaderHeight)) && headerHeight != 0) {
        updateHeaderHeight(headerHeight);
        mFirstGlobalLayoutPerformed = true;
      }
    }
  });
  return root;
}

createScrollView:

private View createScrollView() {
  ViewGroup scrollViewContainer = (ViewGroup) mInflater.inflate(R.layout.fab__scrollview_container, null);

  CDMObservableScrollView scrollView = (CDMObservableScrollView) scrollViewContainer.findViewById(R.id.fab__scroll_view);
  scrollView.setOnScrollChangedCallback(mOnScrollChangedListener);

  ViewGroup contentContainer = (ViewGroup) scrollViewContainer.findViewById(R.id.fab__container);
  LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
          LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
  mContentView.setLayoutParams(layoutParams);
  contentContainer.addView(mContentView);
  mHeaderContainer = (FrameLayout) scrollViewContainer.findViewById(R.id.fab__header_container);
  initializeGradient(mHeaderContainer);
  mHeaderContainer.addView(mHeaderView, 0);
  mMarginView = (FrameLayout) contentContainer.findViewById(R.id.fab__content_top_margin);

  return scrollViewContainer;
}

将加载的xml:

<denis.de.meperdia.fadingactionbar.CDMRootLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/fab__header_container"/>

    <denis.de.meperdia.fadingactionbar.CDMObservableScrollView
        android:id="@+id/fab__scroll_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/fab__container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <FrameLayout
                android:id="@+id/fab__content_top_margin"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/transparent"/>
        </LinearLayout>
    </denis.de.meperdia.fadingactionbar.CDMObservableScrollView>

</denis.de.meperdia.fadingactionbar.CDMRootLayout>

课程CDMRootLayout:

public class CDMRootLayout extends CoordinatorLayout {

  private View mHeaderContainer;
  private View mListViewBackground;
  private boolean mInitialized = false;

  public CDMRootLayout(Context context) {
    super(context);
  }

  public CDMRootLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public CDMRootLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }

  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    //at first find headerViewContainer and listViewBackground
    if(mHeaderContainer == null)
      mHeaderContainer = findViewById(R.id.fab__header_container);
    if(mListViewBackground == null)
      mListViewBackground = findViewById(R.id.fab__listview_background);

    //if there's no headerViewContainer then fallback to standard FrameLayout
    if(mHeaderContainer == null) {
      super.onLayout(changed, left, top, right, bottom);
      return;
    }

    if(!mInitialized) {
      super.onLayout(changed, left, top, right, bottom);
      //if mListViewBackground not exists or mListViewBackground exists
      //and its top is at headercontainer height then view is initialized
      if(mListViewBackground == null || mListViewBackground.getTop() == mHeaderContainer.getHeight())
        mInitialized = true;
      return;
    }

    //get last header and listViewBackground position
    int headerTopPrevious = mHeaderContainer.getTop();
    int listViewBackgroundTopPrevious = mListViewBackground != null ? mListViewBackground.getTop() : 0;

    //relayout
    super.onLayout(changed, left, top, right, bottom);

    //revert header top position
    int headerTopCurrent = mHeaderContainer.getTop();
    if(headerTopCurrent != headerTopPrevious) {
      mHeaderContainer.offsetTopAndBottom(headerTopPrevious - headerTopCurrent);
    }
    //revert listViewBackground top position
    int listViewBackgroundTopCurrent = mListViewBackground != null ? mListViewBackground.getTop() : 0;
    if(listViewBackgroundTopCurrent != listViewBackgroundTopPrevious) {
      mListViewBackground.offsetTopAndBottom(listViewBackgroundTopPrevious - listViewBackgroundTopCurrent);
    }
  }
}

类CDMObservableScrollView:

public class CDMObservableScrollView extends ScrollView implements CDMObservableScrollable {
    // Edge-effects don't mix well with the translucent action bar in Android 2.X
    private boolean mDisableEdgeEffects = true;

    private CDMOnScrollChangedCallback mOnScrollChangedListener;

    public CDMObservableScrollView(Context context) {
      super(context);
    }

    public CDMObservableScrollView(Context context, AttributeSet attrs) {
      super(context, attrs);
    }

    public CDMObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
      super(context, attrs, defStyle);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
      super.onScrollChanged(l, t, oldl, oldt);
      if (mOnScrollChangedListener != null) {
        mOnScrollChangedListener.onScroll(l, t);
      }
    }

    @Override
    protected float getTopFadingEdgeStrength() {
      // http://stackoverflow.com/a/6894270/244576
      if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        return 0.0f;
      }
      return super.getTopFadingEdgeStrength();
    }

    @Override
    protected float getBottomFadingEdgeStrength() {
      // http://stackoverflow.com/a/6894270/244576
      if (mDisableEdgeEffects && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        return 0.0f;
      }
      return super.getBottomFadingEdgeStrength();
    }

    @Override
    public void setOnScrollChangedCallback(CDMOnScrollChangedCallback callback) {
      mOnScrollChangedListener = callback;
    }
}

编辑2

我现在可以解决问题:

如果我这些线路FAB按我的意愿工作:

    <denis.de.meperdia.fadingactionbar.CDMObservableScrollView
        android:id="@+id/fab__scroll_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

但是我的褪色动作条的同步被摧毁......

对于那么多代码感到抱歉,但如果没有这个代码就很难理解。

6 个答案:

答案 0 :(得分:3)

导入app命名空间,用NestedScrollView围绕RelativeLayout,然后将RelativeLayout设置为anchor FAB

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"     //Import app namespace
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?android:attr/windowBackground"
    android:orientation="vertical"
    android:paddingBottom="10dp">

    <RelativeLayout
        android:id="@+id/mainview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v4.widget.NestedScrollView
            android:id="@+id/movie_details_scroll"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            // Here are the textviews, relativelayouts etc...
        </android.support.v4.widget.NestedScrollView>

    </RelativeLayout>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/edit_movies_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:src="@drawable/ic_edit_white_24dp"
        app:layout_anchor="@id/mainview"           //attributes of app namespace
        app:layout_anchorGravity="bottom|end"
    />
</android.support.design.widget.CoordinatorLayout>

答案 1 :(得分:0)

替换android:layout_gravity="bottom|end"

使用此android:layout_gravity="bottom|right"

答案 2 :(得分:0)

在NestedScrollView中添加以下行。

app:layout_behavior="@string/appbar_scrolling_view_behavior"

答案 3 :(得分:0)

您可以使用“坐标布局”锚点属性轻松执行此操作,以将fab连接到任何视图节点

CoordinatorLayout并使用layout_anchor和layout_anchorGravity属性。

      <ListView
          android:id="@+id/lvToDoList"
          android:layout_width="match_parent"
          android:layout_height="match_parent"></ListView>
<android.support.design.widget.FloatingActionButton
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_gravity="bottom|right"
          android:layout_margin="16dp"
          android:src="@drawable/ic_done"
          app:layout_anchor="@id/lvToDoList"
          app:layout_anchorGravity="bottom|right|end" />

这将使FAB能够在底部列出视图。您可以对任何其他布局/视图执行相同操作。

答案 4 :(得分:-1)

试试这个例子:

activity_main.xml中

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.internet.Main2Activity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include
        layout="@layout/content_main2"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

content_main2.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.internet.Main2Activity"
    tools:showIn="@layout/activity_main2">

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="asdds" />
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

</LinearLayout>

答案 5 :(得分:-1)

最后,我通过将FloatingActionButton从内容布局移到外部来解决了这个问题。

我的容器布局如下所示:

<?xml version="1.0" encoding="utf-8"?>
<denis.de.meperdia.fadingactionbar.CDMRootLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/fab__header_container"/>

    <denis.de.meperdia.fadingactionbar.CDMObservableScrollView
        android:id="@+id/fab__scroll_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/fab__container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <FrameLayout
                android:id="@+id/fab__content_top_margin"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/transparent"/>
        </LinearLayout>
    </denis.de.meperdia.fadingactionbar.CDMObservableScrollView>

    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/fab__floating_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.design.widget.CoordinatorLayout>

</denis.de.meperdia.fadingactionbar.CDMRootLayout>

我为FloatingActionButton添加了一个容器,我通过从另一个文件加载它来动态填充。现在解决了FloatingActionButton的移动问题。还有一个小问题,但我为此提出了一个新问题。

修改

更改了我的解决方案。我有一个问题,如果我想要显示一个快餐栏,FloatingActionButton没有正确滚动。我现在以编程方式将FloatingActionButton添加到根视图中。现在它正常工作。