仿微博、人人的feed详情页面:Listview上下滑动,导航栏view可吸附在顶部的效果。
2017-03-28 21:27
736 查看
首先,谢谢原作者!
![](http://upload-images.jianshu.io/upload_images/2957973-587d00d0d74e31a4.gif?imageMogr2/auto-orient/strip)
效果图.gif
Github: https://github.com/qizhenghao/StickyNavigationBar
欢迎拍砖,拍拍更进步。
没有对比,怎么会有伤害,下面是 微博、人人的Feed详情页:
![](http://upload-images.jianshu.io/upload_images/2957973-7e6171769f4998a6.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
微博、人人Feed详情页.jpeg
实例化两个一样的导航栏view,一个放在页面根布局顶部的view1,另一个放在ListView的headerView中的view2,在OnScrollListener的onScroll方法中,检测view2在屏幕中的位置是不是滑动到了顶部,决定顶部view1的显示与隐藏,以达到看起来只有一个导航栏view显示的效果;
2、
为了保持两个导航栏view的状态同步,使用了观察者模式;
3、
导航栏中的Tab切换,即切换ListView的adapter,并且记录滑动的位置信息。
![](http://upload-images.jianshu.io/upload_images/2957973-604921557834768f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
UML图.png
StickyNavHostSubject:它把所有的自定义导航栏view的引用保存到一个list里。AbstractSubject提供了接口,可以增加和删除观察者对象。
StickyNavHost:自定义的导航栏view,继承自ViewGroup,可以根据具体需求自行更改显示的布局、样式等。
NavListViewScollListener:需要为ListView设置的滑动事件,封装了对吸附导航栏显示、隐藏的逻辑。
MainActivity:用于演示demo,包含了对导航栏view的初始化,以及切换tab的操作等。
NavListViewScollListener的onScroll()方法:
2、
MainActivity中初始化操作:
3、
MainActivity中点击切换导航栏Tab的回调:
4、
StickyNavHostSubject做的事情就很简单了,和常见的观察者模式没区别:
Android 技能树
一、实现效果
上图:![](http://upload-images.jianshu.io/upload_images/2957973-587d00d0d74e31a4.gif?imageMogr2/auto-orient/strip)
效果图.gif
Github: https://github.com/qizhenghao/StickyNavigationBar
欢迎拍砖,拍拍更进步。
没有对比,怎么会有伤害,下面是 微博、人人的Feed详情页:
![](http://upload-images.jianshu.io/upload_images/2957973-7e6171769f4998a6.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
微博、人人Feed详情页.jpeg
二、实现原理
1、实例化两个一样的导航栏view,一个放在页面根布局顶部的view1,另一个放在ListView的headerView中的view2,在OnScrollListener的onScroll方法中,检测view2在屏幕中的位置是不是滑动到了顶部,决定顶部view1的显示与隐藏,以达到看起来只有一个导航栏view显示的效果;
2、
为了保持两个导航栏view的状态同步,使用了观察者模式;
3、
导航栏中的Tab切换,即切换ListView的adapter,并且记录滑动的位置信息。
三、UML图
![](http://upload-images.jianshu.io/upload_images/2957973-604921557834768f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
UML图.png
StickyNavHostSubject:它把所有的自定义导航栏view的引用保存到一个list里。AbstractSubject提供了接口,可以增加和删除观察者对象。
StickyNavHost:自定义的导航栏view,继承自ViewGroup,可以根据具体需求自行更改显示的布局、样式等。
NavListViewScollListener:需要为ListView设置的滑动事件,封装了对吸附导航栏显示、隐藏的逻辑。
MainActivity:用于演示demo,包含了对导航栏view的初始化,以及切换tab的操作等。
三、具体细节
1、NavListViewScollListener的onScroll()方法:
@Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //处理了root导航栏的显示与隐藏, 本质上只是控制root导航栏的显示 //而在listView的headerView中的导航栏不做处理,因为它会随着listView的滑动自行滑出页面 if (NavBean.IS_NEED_ATTACH && rootView != null && nav != null) { rootView.getLocationOnScreen(rootLocation); headView.getLocationOnScreen(headLocation); //根据两者在屏幕中的location位置信息,决定root导航栏的显示与隐藏 if (rootLocation[1] > headLocation[1]) { rootView.setVisibility(View.VISIBLE); } else { rootView.setVisibility(View.INVISIBLE); } //记录当前listView的滑动位置 nav.setFirstVisibleItem(firstVisibleItem); nav.setTopDistance((view.getChildAt(0) == null) ? 0 : view.getChildAt(0).getTop()); } }
2、
MainActivity中初始化操作:
private void initNavsView() { initNavsData(); stickyNavHostRoot.setTabItemClickListener(this);//设置点击回调 stickyNavHostHead.setTabItemClickListener(this);//设置点击回调 stickyNavHostRoot.setShowTopLine(false); stickNavHostSubject = new StickNavHostSubject(); stickNavHostSubject.attachObserver(stickyNavHostRoot);//观察者模式 stickNavHostSubject.attachObserver(stickyNavHostHead); NavBean[] sortedNavs = new NavBean[mNavs.size()];//指定导航栏的排列顺序 sortedNavs[0] = mNavs.get(NavBean.TYPE_REPOST); sortedNavs[1] = mNavs.get(NavBean.TYPE_COMMENT); sortedNavs[2] = mNavs.get(NavBean.TYPE_LIKE); stickNavHostSubject.initTabData(sortedNavs); scrollListener = new NavListViewScrollListener(stickyNavHostRoot, stickyNavHostHead); mListView.setOnScrollListener(scrollListener);//为listView设置滑动监听,内部处理了吸附view的显示与隐藏 } protected void initNavsData() { mNavs = new SparseArray<>(NAV_LENGTH); mNavs.put(NavBean.TYPE_REPOST, new NavBean(NavBean.TYPE_REPOST, new TestAdapter(20, "我是转发", this))); mNavs.put(NavBean.TYPE_COMMENT, new NavBean(NavBean.TYPE_COMMENT, new TestAdapter(20, "我是评论", this))); mNavs.put(NavBean.TYPE_LIKE, new NavBean(NavBean.TYPE_LIKE, new TestAdapter(20, "我是赞", this))); } private void initView() { mListView = (ListView) findViewById(R.id.list_view); stickyNavHostRoot = (StickyNavHost) findViewById(R.id.sticky_nav_layout); stickyNavHostRoot.setVisibility(View.INVISIBLE); View testHeaderView = LayoutInflater.from(this).inflate(R.layout.listview_head_view_test_layout, null); mListView.addHeaderView(testHeaderView); View inflateView = LayoutInflater.from(this).inflate(R.layout.sticky_nav_host_layout, null); stickyNavHostHead = (StickyNavHost) inflateView.findViewById(R.id.sticky_nav_layout); stickyNavHostHead.setVisibility(View.VISIBLE); mListView.addHeaderView(stickyNavHostHead); STICKY_POSITION_IN_HEADER = mListView.getHeaderViewsCount(); }
3、
MainActivity中点击切换导航栏Tab的回调:
@Override public void onTabItemSelected(@NavBean.TYPE int type) { NavBean currNav = mNavs.get(type); stickNavHostSubject.setSelectedType(type);//事件分发给注册者,注册者进行相应的变化 if (currNav.type == NavBean.TYPE_CURRENT)//等于当前选中的tab,可以屏蔽掉 return; NavBean.TYPE_CURRENT = currNav.type; scrollListener.setNav(currNav); mListView.setAdapter(currNav.adapter); if (stickyNavHostRoot.getVisibility() == View.VISIBLE) {//吸附在顶部的rootView正在展示 if (currNav.getFirstVisibleItem() < STICKY_POSITION_IN_HEADER) mListView.setSelectionFromTop(STICKY_POSITION_IN_HEADER, stickyNavHostRoot.getHeight() - 2); else mListView.setSelectionFromTop(currNav.getFirstVisibleItem(), currNav.getTopDistance()); } else {//吸附在顶部的rootView没有展示,说明在切换导航栏的时候是不需要进行滑动的,保持上次的位置即可 mListView.setSelectionFromTop(NavBean.firstVisibleItemUniversal, NavBean.topDistanceUniversal); } }
4、
StickyNavHostSubject做的事情就很简单了,和常见的观察者模式没区别:
public class StickNavHostSubject extends AbstractSubject<IStickyNavHostObserver> { private List<IStickyNavHostObserver> observers; public StickNavHostSubject() { observers = new ArrayList<>(); } public void attachObserver(IStickyNavHostObserver observer) { observers.add(observer); } public void detachObserver(IStickyNavHostObserver observer) { observers.remove(observer); } @Override public void initTabData(NavBean[] navs) { for (IStickyNavHostObserver observer : observers) observer.initTabData(navs); } @Override public void refreshTabData(NavBean nav) { for (IStickyNavHostObserver observer : observers) observer.refreshTabData(nav); } @Override public void setSelectedType(@NavBean.TYPE int type) { for (IStickyNavHostObserver observer : observers) observer.setSelectedType(type); } @Override public void setSelectedPosition(int position) { for (IStickyNavHostObserver observer : observers) observer.setSelectedPosition(position); }
Android 技能树
相关文章推荐
- Android仿微博、人人Feed详情页吸附导航栏
- 双ListView与顶部标题栏滑动事件处理(仿美团外卖商家详情界面滑动效果)
- android listview/ScrollView 去除顶部下拉时的半月形阴影和上下滑动顶部和底部的虚化
- 自定义ViewGroup实现多个单页面上下滑动效果
- Scrollview中嵌套ViewPager中嵌套ListView 滑动中tab固定顶部ScrollingTricks效果特效
- 自定义ViewGroup实现多个单页面上下滑动效果
- Android模仿实现微博详情页滑动固定顶部栏的效果实例
- Scrollview中嵌套ViewPager中嵌套ListView 滑动中tab固定顶部ScrollingTricks效果特效
- 使用Jquery实现顶部导航栏在页面滑动到自身所在位置之后固定在顶部的效果
- 使用RecyclerView实现ListView,GridView的效果(上下,左右滑动),拖拽与滑动删除
- 原来操控界面可以这么简单----安卓上下滑动缩放顶部图片,左右滑动结束当前Activity,及View柔和回弹效果
- 如何去掉滑动控件(比如ScrollView、ListView、ViewPager等)滑动到最顶部或最底部时出现的阴影效果。
- 使用复杂的view加上listview实现页面整体滑动效果
- 使用Jquery实现顶部导航栏在页面滑动到自身所在位置之后固定在顶部的效果
- android listview/ScrollView 去除顶部下拉时的半月形阴影和上下滑动顶部和底部的虚化
- ViewPager多页面滑动切换以及动画效果(转)
- Android ViewPager多页面滑动切换以及动画效果
- Android中用ViewPager实现多页面滑动切换及动画效果的实例
- Android ViewPager多页面滑动切换以及动画效果!!!
- android之ViewPager简单实现局部页面滑动效果