如何使 NavigationView header 具有粘性
How to make NavigationView header sticky
是否可以在设计支持库 NavigationView 中设置 header 粘性?
<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);
}
}
然后将其添加到 activity 的 onCreate:
CustomNavigationView navigationView = (CustomNavigationView) findViewById(R.id.your_navigation_view);
navigationView.createHeader(R.layout.your_header);
这达到了预期的效果(如果有点笨拙)但是当菜单项低于 header 你仍然可以点击它们,有什么解决这个问题的想法吗?
经过大量试验后,我找到了一些有用的东西...它很老套,我很想听听您的任何改进建议!
覆盖的 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);
}
}
这在 main 的 onCreate 中 activity
// 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);
我知道这个问题很老了,但也许我可以帮助别人。
<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 是多余的,但是是必需的。
根据 enyciaa 的回答编辑。
这是简化的,更稳定并且支持 Android 数据绑定。
此外,添加了一个 API 以获得粘性 header 视图(在数据绑定到 header 布局的情况下)
覆盖的 NavigationView
// Add this annotation if databinding is enabled
@BindingMethods({
@BindingMethod(type = StickyHeaderNavigationView.class, attribute = "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
...
app:stickyHeader="@{@layout/your_nav_header}" />
未启用数据绑定
在 main activity
的 onCreate 中应用它
// Inflates the nav drawer
CustomNavigationView navigationView = (CustomNavigationView) findViewById(R.id.your_nav_view);
navigationView.setStickyHeader(R.layout.your_nav_header);
来自
回答
xml
<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>
Java
/* Access to a nav_header view */
View view = navigationView.getRootView().findViewById(R.id.view_id);
是否可以在设计支持库 NavigationView 中设置 header 粘性?
<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);
}
}
然后将其添加到 activity 的 onCreate:
CustomNavigationView navigationView = (CustomNavigationView) findViewById(R.id.your_navigation_view);
navigationView.createHeader(R.layout.your_header);
这达到了预期的效果(如果有点笨拙)但是当菜单项低于 header 你仍然可以点击它们,有什么解决这个问题的想法吗?
经过大量试验后,我找到了一些有用的东西...它很老套,我很想听听您的任何改进建议!
覆盖的 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);
}
}
这在 main 的 onCreate 中 activity
// 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);
我知道这个问题很老了,但也许我可以帮助别人。
<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 是多余的,但是是必需的。
根据 enyciaa 的回答编辑。
这是简化的,更稳定并且支持 Android 数据绑定。
此外,添加了一个 API 以获得粘性 header 视图(在数据绑定到 header 布局的情况下)
覆盖的 NavigationView
// Add this annotation if databinding is enabled
@BindingMethods({
@BindingMethod(type = StickyHeaderNavigationView.class, attribute = "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
...
app:stickyHeader="@{@layout/your_nav_header}" />
未启用数据绑定
在 main activity
// Inflates the nav drawer
CustomNavigationView navigationView = (CustomNavigationView) findViewById(R.id.your_nav_view);
navigationView.setStickyHeader(R.layout.your_nav_header);
来自
xml
<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>
Java
/* Access to a nav_header view */
View view = navigationView.getRootView().findViewById(R.id.view_id);