如何使NavigationView标头粘滞

时间:2015-07-21 19:31:22

标签: android androiddesignsupport

是否可以在设计支持库NavigationView中使标题粘滞?

<android.support.design.widget.NavigationView
    android:id="@+id/nav_drawer"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:headerLayout="@layout/nav_header"
    app:menu="@menu/nav_drawer"
    style="@style/navigation_view_drawer"
    />

编辑:

到目前为止,我的尝试导致了这个

覆盖NavigationView小部件并添加新方法:

public class CustomNavigationView extends NavigationView {

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


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


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


// Inflates header as a child of NavigationView, on top of the normal menu
public void createHeader(int res) {
    LayoutInflater inflater = LayoutInflater.from(getContext());;
    View view = inflater.inflate(res, this, false);
    addView(view);
}

}

然后将此添加到活动的onCreate:

CustomNavigationView navigationView = (CustomNavigationView) findViewById(R.id.your_navigation_view);
navigationView.createHeader(R.layout.your_header);

这可以达到预期的效果(如果有点不好意思)但是当菜单项位于标题下方时你仍然可以点击它们,有什么想法可以解决这个问题吗?

3 个答案:

答案 0 :(得分:2)

经过大量的实验后,我得到了一些有用的东西......非常hacky,我很想听到你需要改进的建议!

覆盖的NavigationView

public class CustomNavigationView extends NavigationView {

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


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


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


// Consumes touch in the NavigationView so it doesn't propagate to views below
public boolean onTouchEvent (MotionEvent me) {
    return true;
}


// Inflates header as a child of NavigationView
public void createHeader(int res) {
    LayoutInflater inflater = LayoutInflater.from(getContext());
    View view = inflater.inflate(res, this, false);

    // Consumes touch in the header so it doesn't propagate to menu items below
    view.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event){
            return true;
        }
    });

    addView(view);
}


// Positions and sizes the menu view
public void sizeMenu(View view) {
    // Height of header
    int header_height = (int) getResources().getDimension(R.dimen.nav_header_height);

    // Gets required display metrics
    DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
    float screen_height = displayMetrics.heightPixels;

    // Height of menu
    int menu_height = (int) (screen_height - header_height);

    // Layout params for menu
    LayoutParams params = new LayoutParams(
            LayoutParams.MATCH_PARENT,
            LayoutParams.WRAP_CONTENT);
    params.gravity = Gravity.BOTTOM;
    params.height = menu_height;
    view.setLayoutParams(params);
}

}

这是在主要活动的onCreate

// Inflates the nav drawer
    CustomNavigationView navigationView = (CustomNavigationView) findViewById(R.id.your_nav_view);
    navigationView.createHeader(R.layout.your_nav_header);

    // sizes nav drawer menu so it appears under header
    ViewGroup parent = (ViewGroup) navigationView;
    View view = parent.getChildAt(0);
    navigationView.sizeMenu(view);

答案 1 :(得分:1)

我知道这个问题很老,但也许我可以帮助其他人。

<android.support.design.widget.NavigationView
    android:id="@+id/nav_drawer"
    style="@style/navigation_view_drawer"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:headerLayout="@layout/nav_header"
    app:menu="@menu/nav_drawer">

    <include layout="@layout/nav_header" />
</android.support.design.widget.NavigationView>

app:headerLayout 是多余的,但是必需的。

答案 2 :(得分:0)

根据enyciaa的答案编辑 这是简化,更稳定,并支持Android数据绑定 此外,添加了一个API来获取粘性标题视图(在数据绑定到标题布局的情况下)

覆盖的NavigationView

// Add this annotation if databinding is enabled
@BindingMethods({
    @BindingMethod(type = StickyHeaderNavigationView.class, attribute = "app:stickyHeader", method = "setStickyHeader")
})
public class StickyHeaderNavigationView extends NavigationView {

@Nullable
View mStickyHeaderView;

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

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

public StickyHeaderNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

// Consumes touch in the NavigationView so it doesn't propagate to views below
@Override
public boolean onTouchEvent(MotionEvent me) {
    return true;
}

@Nullable
public View getStickyHeaderView() {
    return mStickyHeaderView;
}

// Set a header layout as a child of NavigationView
public void setStickyHeader(@LayoutRes int res) {

    if (mStickyHeaderView != null) {
        removeView(mStickyHeaderView);
    }

    LayoutInflater inflater = LayoutInflater.from(getContext());
    View view = mStickyHeaderView = inflater.inflate(res, this, false);

    // Consumes touch in the header so it doesn't propagate to menu items below
    view.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }
    });

    addView(view);

    // Listen to layout update. When ready, we can do #sizeMenu
    getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener(this, view));
}

// Positions and sizes the menu view
private void sizeMenu(@NonNull View view, View headerView) { 

    // Height of header
    int headerHeight = headerView.getHeight();

    // Gets required display metrics
    DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
    float screenHeight = displayMetrics.heightPixels;

    // Height of menu
    int menuHeight = (int) (screenHeight - headerHeight);

    // Layout params for menu
    LayoutParams params = new LayoutParams(
            LayoutParams.MATCH_PARENT,
            LayoutParams.WRAP_CONTENT);
    params.gravity = Gravity.BOTTOM;
    params.height = menuHeight;
    view.setLayoutParams(params);
}


// To listen for layout update, that means header height was calculated
private static class OnGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {

    final WeakReference<StickyHeaderNavigationView> mViewRef;
    final WeakReference<View> mHeaderView;

    OnGlobalLayoutListener(@NonNull StickyHeaderNavigationView view,
                           @NonNull View headerView) {
        mViewRef = new WeakReference<>(view);
        mHeaderView = new WeakReference<>(headerView);
    }

    @Override
    public void onGlobalLayout() {

        StickyHeaderNavigationView view = mViewRef.get();
        if (view == null) {
            return;
        }

        // Update once only as the header should be fixed size
        view.getViewTreeObserver().removeOnGlobalLayoutListener(this);

        View headerView = mHeaderView.get();
        if (headerView == null) {
            return;
        }

        // childAt(0) is the navigation menu
        view.sizeMenu(view.getChildAt(0), headerView);
    }
}
}

启用数据绑定
将其应用于布局xml

<StickyHeaderNavigationView
    ...
    android:stickyHeader="@{@layout/your_nav_header}" />

未启用数据绑定
将其应用于主要活动的onCreate中

// Inflates the nav drawer
CustomNavigationView navigationView = (CustomNavigationView) findViewById(R.id.your_nav_view);
navigationView.setStickyHeader(R.layout.your_nav_header);