有没有知道新浪微博个人页面的h5页面下拉刷新新是怎么实现的

3667人阅读
android(65)
网上有很多很有名的开源框架,这里就来拉拉PullToRefresh这个框架,也就是我们平时用的下拉刷新啦,当然你问我这个有什么用啊?别人已经写好了,这里主要是学习以及练习,练习的次数多了,一切就顺其自然的会了.
废话少说,先上图,再上代码:
1.要想实现下拉刷新功能必须要有个下拉刷新的布局,是吧?
&?xml version=&1.0& encoding=&utf-8&?&
&LinearLayout xmlns:android=&/apk/res/android&
android:layout_width=&match_parent&
android:layout_height=&match_parent&
android:orientation=&vertical& &
&RelativeLayout
android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:paddingBottom=&10dip&
android:paddingTop=&10dip& &
&LinearLayout
android:id=&@+id/layout&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_centerInParent=&true&
android:gravity=&center&
android:orientation=&vertical& &
android:id=&@+id/tip&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:text=&@string/state& /&
android:id=&@+id/lastupdate_time&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content& /&
&/LinearLayout&
&ProgressBar
android:id=&@+id/progress&
style=&?android:attr/progressBarStyleSmall&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_toLeftOf=&@id/layout&
android:layout_marginRight=&20dip&
android:visibility=&gone& /&
&ImageView
android:id=&@+id/arrow&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_alignTop=&@+id/layout&
android:layout_marginRight=&19dp&
android:layout_toLeftOf=&@+id/layout&
android:src=&@drawable/arrow_down& /&
&/RelativeLayout&
&/LinearLayout&
2.你要把它加入到布局里面吧!headView=layoutInflater.from(context).inflate(R.layout.header_layout, null);
this.addHeaderView(headView);3.加入到布局直接显示出来也不符合需求啊,所以这一步需要隐藏布局,当然不能和前一篇博客()一样直接隐藏,直接隐藏满足不了如图的要求,我们这里采取的是设置头部布局的高度为实际高度的负值,这样就实现了隐藏功能,当下拉的时候,还不至于一次就全部显示出来,ok这种办法能够实现图中的要求headerHeight = headView.getMeasuredHeight();
setHeaderViewHeight(-headerHeight);
* 设置头部布局的高度
* @param i
private void setHeaderViewHeight(int headerHeight) {
headView.setPadding(headView.getPaddingLeft(), headerHeight,
headView.getPaddingRight(), headView.getPaddingBottom());
headView.invalidate();
4.跑起来之后,白瞎了,没实现想要的功能,也就是说没有把头部布局隐藏掉,哎,跟踪代码之后发现高度为0,郭神说过在measure之前,getMeasureWidth()和getMeasureHeight()都为零,好吧,那就先measure吧!
private void measureView(View view) {
ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp == null) {
lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
int width = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int tempHeight=lp.
if (tempHeight & 0) {
height = MeasureSpec.makeMeasureSpec(tempHeight,
MeasureSpec.EXACTLY);
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
view.measure(width, height);
5.隐藏成功了,下面就要实现具体的功能了,下拉刷新,松开刷新,正在刷新这三个状态,是通过手势改变状态,所以这里要实现onTouch,当然还有OnScrollListener
需要用到firstVisibleItem判断Listview向上滑动还是向下滑动,如果firstVisibleItem==0说明到达ListView的头部了,当然你还需要一个布尔值判断是否按下滑动
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
this.firstVisibleItem=firstVisibleI
重写onTouchEvent,通过firstVisibleItem和布尔值判断按下,抬起,滑动
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (firstVisibleItem == 0) {
isRemark =
startY = (int) ev.getY();
case MotionEvent.ACTION_MOVE:
onMove(ev);
case MotionEvent.ACTION_UP:
if (state==RELEASE) {
state=REFRASH;
reflashViewByState();
//更新数据
isRefreshListener.onRefresh();
}else if (state==PULL) {
state=NONE;
refreshDrawableState();
return super.onTouchEvent(ev);
代码不全,先解释一下,后面附上全部代码
按下时,如果firstVisibleItem为0,说明到达listview的顶部,并且可以按下,把isRemark赋值为true,滑动时改变头部布局的状态
* 移动过程的状态变换
* @param ev
private void onMove(MotionEvent ev) {
if (!isRemark) {
int tempY = (int) ev.getY();
int space = tempY - startY;
int topPadding = space - headerH
switch (state) {
case NONE:
if (space&0) {
state=PULL;
reflashViewByState();
case PULL:
setHeaderViewHeight(topPadding);
if (space&headerHeight+30&&scrollState==SCROLL_STATE_IDLE) {
state=RELEASE;
reflashViewByState();
case RELEASE:
setHeaderViewHeight(topPadding);
if (space&headerHeight+30) {
state=PULL;
reflashViewByState();
}else if (space&=0) {
state=NONE;
reflashViewByState();
根据滑动之后和动画前y值的变化判断滑动状态,当space大于零时,当前状态变为下拉刷新,如果space大于某个值时,当前状态变为松开可以刷新,当space大于零小于某个值时,当前状态为下拉刷新状态,当space小于零时,当前状态变为正常状态.当然状态改变时,界面也要随着改变/**
*根据状态刷新当前页面
private void reflashViewByState() {
TextView tip = (TextView) headView.findViewById(R.id.tip);
ImageView arrow = (ImageView) headView.findViewById(R.id.arrow);
ProgressBar progress = (ProgressBar) headView.findViewById(R.id.progress);
RotateAnimation anim = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(500);
anim.setFillAfter(true);
RotateAnimation anim1 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim1.setDuration(500);
anim1.setFillAfter(true);
switch (state) {
case NONE:
setHeaderViewHeight(-headerHeight);
arrow.clearAnimation();
case PULL:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText(&下拉刷新&);
arrow.clearAnimation();
arrow.setAnimation(anim1);
case RELEASE:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText(&松开刷新&);
arrow.clearAnimation();
arrow.setAnimation(anim);
case REFRASH:
setHeaderViewHeight(50);
arrow.setVisibility(View.GONE);
progress.setVisibility(View.VISIBLE);
tip.setText(&正在刷新&);
arrow.clearAnimation();
界面上主要改变的就是提示,箭头和progress,正常状态下,界面不可见,下拉刷新状态下,箭头可见并且朝下,提示信息为下拉刷新并且progress不可见,松开刷新状态,箭头朝上,progress不可见,提示信息为下拉刷新,正在加载状态箭头不可见,progress可见,提示信息改为正在刷新
当然,在变成正在加载状态时,还要加载更过数据
public interface IsRefreshListener{
public void onRefresh();
public void setIsRefreshListener(IsRefreshListener isRefreshListener){
this.isRefreshListener=isRefreshL
加载完数据后,还要通知listview刷新结束/**
* 获取完数据;
public void reflashComplete() {
state = NONE;
isRemark =
reflashViewByState();
TextView lastupdatetime = (TextView) headView
.findViewById(R.id.lastupdate_time);
SimpleDateFormat format = new SimpleDateFormat(&yyyy年MM月dd日 hh:mm:ss&);
Date date = new Date(System.currentTimeMillis());
String time = format.format(date);
lastupdatetime.setText(time);
自定义下拉刷新控件就这样完成了,不懂得留言吧,我尽量给你解答,自定义这东西,写多了也就知道怎么写了
下面附上自定义下拉刷新控件的全部代码:
package com.sdufe.thea.guo.
import java.text.SimpleDateF
import java.util.D
import android.content.C
import android.util.AttributeS
import android.view.LayoutI
import android.view.MotionE
import android.view.V
import android.view.ViewG
import android.view.animation.RotateA
import android.widget.AbsListV
import android.widget.ImageV
import android.widget.ProgressB
import android.widget.TextV
import android.widget.AbsListView.OnScrollL
import android.widget.ListV
import com.sdufe.thea.guo.R;
public class PullToRefreshListView extends ListView implements OnScrollListener {
View headV
int headerH
int firstVisibleI
int scrollS
boolean isR
int startY;
final int NONE = 0;
final int PULL = 1;
final int RELEASE = 2;
final int REFRASH = 3;
IsRefreshListener isRefreshL
public PullToRefreshListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
initView(context);
public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
public PullToRefreshListView(Context context) {
super(context);
initView(context);
private void initView(Context context) {
headView = LayoutInflater.from(context).inflate(R.layout.header_layout,
measureView(headView);
headerHeight = headView.getMeasuredHeight();
setHeaderViewHeight(-headerHeight);
addView(headView);
setOnScrollListener(this);
* 计算宽高
* @param view
private void measureView(View view) {
ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp == null) {
lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
int width = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int tempHeight = lp.
if (tempHeight & 0) {
height = MeasureSpec.makeMeasureSpec(tempHeight,
MeasureSpec.EXACTLY);
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
view.measure(width, height);
* 设置头部布局的高度
* @param i
private void setHeaderViewHeight(int headerHeight) {
headView.setPadding(headView.getPaddingLeft(), headerHeight,
headView.getPaddingRight(), headView.getPaddingBottom());
headView.invalidate();
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.scrollState = scrollS
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
this.firstVisibleItem = firstVisibleI
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (firstVisibleItem == 0) {
isRemark =
startY = (int) ev.getY();
case MotionEvent.ACTION_MOVE:
onMove(ev);
case MotionEvent.ACTION_UP:
if (state==RELEASE) {
state=REFRASH;
reflashViewByState();
//更新数据
isRefreshListener.onRefresh();
}else if (state==PULL) {
state=NONE;
refreshDrawableState();
return super.onTouchEvent(ev);
* 移动过程的状态变换
* @param ev
private void onMove(MotionEvent ev) {
if (!isRemark) {
int tempY = (int) ev.getY();
int space = tempY - startY;
int topPadding = space - headerH
switch (state) {
case NONE:
if (space&0) {
state=PULL;
reflashViewByState();
case PULL:
setHeaderViewHeight(topPadding);
if (space&headerHeight+30&&scrollState==SCROLL_STATE_IDLE) {
state=RELEASE;
reflashViewByState();
case RELEASE:
setHeaderViewHeight(topPadding);
if (space&headerHeight+30) {
state=PULL;
reflashViewByState();
}else if (space&=0) {
state=NONE;
reflashViewByState();
* 获取完数据;
public void reflashComplete() {
state = NONE;
isRemark =
reflashViewByState();
TextView lastupdatetime = (TextView) headView
.findViewById(R.id.lastupdate_time);
SimpleDateFormat format = new SimpleDateFormat(&yyyy年MM月dd日 hh:mm:ss&);
Date date = new Date(System.currentTimeMillis());
String time = format.format(date);
lastupdatetime.setText(time);
*根据状态刷新当前页面
private void reflashViewByState() {
TextView tip = (TextView) headView.findViewById(R.id.tip);
ImageView arrow = (ImageView) headView.findViewById(R.id.arrow);
ProgressBar progress = (ProgressBar) headView.findViewById(R.id.progress);
RotateAnimation anim = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(500);
anim.setFillAfter(true);
RotateAnimation anim1 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim1.setDuration(500);
anim1.setFillAfter(true);
switch (state) {
case NONE:
setHeaderViewHeight(-headerHeight);
arrow.clearAnimation();
case PULL:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText(&下拉刷新&);
arrow.clearAnimation();
arrow.setAnimation(anim1);
case RELEASE:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText(&松开刷新&);
arrow.clearAnimation();
arrow.setAnimation(anim);
case REFRASH:
setHeaderViewHeight(50);
arrow.setVisibility(View.GONE);
progress.setVisibility(View.VISIBLE);
tip.setText(&正在刷新&);
arrow.clearAnimation();
public interface IsRefreshListener{
public void onRefresh();
public void setIsRefreshListener(IsRefreshListener isRefreshListener){
this.isRefreshListener=isRefreshL
}下面就是怎么用了&com.sdufe.thea.guo.view.PullToRefreshListView
android:id=&@+id/pull_to_refresh&
android:layout_width=&match_parent&
android:layout_height=&wrap_content&/&下面的用法就跟listview差不多了,提示一点要实现IsRefreshListener接口,在onRefresh()里面加载更多数据
package com.sdufe.thea.
import java.util.ArrayL
import java.util.L
import com.sdufe.thea.guo.view.PullToRefreshListV
import com.sdufe.thea.guo.view.PullToRefreshListView.IsRefreshL
import android.os.B
import android.app.A
import android.view.M
import android.widget.ArrayA
public class MainActivity extends Activity implements IsRefreshListener{
PullToRefreshListView listV
ArrayAdapter&String&
List&String&
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (PullToRefreshListView) findViewById(R.id.pull_to_refresh);
initData();
adapter = new ArrayAdapter&String&(this,
android.R.layout.simple_list_item_1, list);
listView.setAdapter(adapter);
private void initData() {
list = new ArrayList&String&();
list.add(&&);
list.add(&&);
list.add(&&);
public void onRefresh() {
list.add(&爸爸&);
list.add(&妈妈&);
list.add(&我&);
adapter.notifyDataSetChanged();
listView.reflashComplete();
今天就到此结束啦,不懂的,留言
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:241413次
积分:3937
积分:3937
排名:第7128名
原创:79篇
评论:445条
(2)(3)(3)(3)(2)(1)(1)(2)(4)(1)(1)(1)(4)(2)(1)(1)(3)(7)(3)(4)(8)(13)(3)(4)(6)(2)2015年7月 Web 开发大版内专家分月排行榜第三
2015年7月 Web 开发大版内专家分月排行榜第三
2015年7月 Web 开发大版内专家分月排行榜第三
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。&>&&>&&>&&>&类似微信信息界面的下拉刷新实现
类似微信信息界面的下拉刷新实现
上传大小:162KB
一个类似微信信息界面的下拉刷新,Demo中的实现方案经过测试可以使用
综合评分:4.1(89位用户评分)
所需积分:0
下载次数:890
审核通过送C币
创建者:doomlord
创建者:amixuse
创建者:otldan
课程推荐相关知识库
上传者其他资源上传者专辑
移动开发热门标签
VIP会员动态
您因违反CSDN下载频道规则而被锁定帐户,如有疑问,请联络:!
android服务器底层网络模块的设计方法
所需积分:0
剩余积分:720
您当前C币:0
可兑换下载积分:0
兑换下载分:
兑换失败,您当前C币不够,请先充值C币
消耗C币:0
你当前的下载分为234。
类似微信信息界面的下拉刷新实现
会员到期时间:
剩余下载次数:
你还不是VIP会员
开通VIP会员权限,免积分下载
你下载资源过于频繁,请输入验证码
您因违反CSDN下载频道规则而被锁定帐户,如有疑问,请联络:!
若举报审核通过,可奖励20下载分
被举报人:
yancy518118
举报的资源分:
请选择类型
资源无法下载
资源无法使用
标题与实际内容不符
含有危害国家安全内容
含有反动色情等内容
含广告内容
版权问题,侵犯个人或公司的版权
*详细原因:iOS新浪微博个人页面(个人)实现思路 - 简书
iOS新浪微博个人页面(个人)实现思路
最近项目中碰到的问题,个人界面布局优化,需求是类似新浪微博个人主页那种样子,在网上找了好久也没找到特别合适的demo,所以就结合各种demo自己写了一个效果
简单说下我的思路,我发现新浪微博的“主页微博相册”那一栏并不是在tableView的tableHeaderView中,而是独立于控制器的view上,详情可见图中的ScrollIndicator,是从其下穿过的
我将头像的View和segmentView添加到控制器的View上,将三个子控制器添加到控制器的childViewControllers,设置每个tableview的contentInset
- (void)creatHeaderBgView
UIView *headImageView = [UIView new];
self.bgHeadView = headImageV
[self.view addSubview:headImageView];
[headImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.mas_equalTo(0);
make.height.mas_equalTo(XFHeadViewH + 20);
UIImageView *headBgImageView = [UIImageView new];
headBgImageView.image = [UIImage imageNamed:@"headView_bg"];
headBgImageView.contentMode = UIViewContentModeScaleAspectF
headBgImageView.layer.masksToBounds = YES;
[headImageView addSubview:headBgImageView];
[headBgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.left.right.mas_equalTo(0);
UIView *segmentView = [UIView new];
segmentView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:segmentView];
[segmentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(headImageView.mas_bottom).mas_equalTo(0);
make.right.left.mas_equalTo(0);
make.height.mas_equalTo(XFTabBarH);
self.segmentView = segmentV
UIImageView *iconView = [UIImageView new];
iconView.layer.cornerRadius = 50;
iconView.layer.masksToBounds = YES;
iconView.backgroundColor = [UIColor blueColor];
[headImageView addSubview:iconView];
[iconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(0);
make.size.mas_equalTo(CGSizeMake(100, 100));
make.bottom.mas_equalTo(-64);
self.tableView.contentInset=UIEdgeInsetsMake(XFHeadViewH+XFTabBarH,0,0,0);
当拖动tableView的时候计算tableView的偏移量,通过代理传给容器控制器,改变头视图的高度约束,至于导航栏透明度变化的效果这里就不多说了,网上有很多。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
// 获取当前偏移量
CGFloat offsetY = scrollView.contentOffset.y;
// 获取偏移量差值
CGFloat delta = offsetY - _lastOffsetY;
CGFloat headH = XFHeadViewH - delta + 20;
if (headH & XFHeadViewMinH) {
headH = XFHeadViewMinH;
CGFloat alpha = delta / (XFHeadViewH - XFHeadViewMinH);
[[[self.navigationController.navigationBar subviews] objectAtIndex:0] setAlpha:alpha];
_currtentOffSet = scrollView.contentOffset.y;
if ([self.delegate respondsToSelector:@selector(newPersonInfoTableViewControllerOffsetChange:)]) {
[self.delegate newPersonInfoTableViewControllerOffsetChange:headH];注意细节:
三个tableview的滚动都可以推动顶部图片和切换栏的滚动。
只要切换栏没有贴住导航栏,3个tableview都是从第一个cell开始显示。
切换栏贴住导航栏时,每个tableview的显示位置会被保持。
切换栏贴住导航栏,手指在当前页面拖动使切换栏脱离导航栏然后再贴住导航栏,另外2个tableview的显示状态被保持。
研究微博个人主页容易发现这里使用了3个tableview。我在主控制器上添加3个子控制器,每一个子控制器管理一个tableview,然后将3个tableview添加到主控制器view上,所有的子控制器都继承自BaseTableViewController,方便后面的滚动监听。
每个tableview右边的指示条滚动范围都包含了顶部视图(图片)。在之前的版本中,我没有让tableview们共用一个顶部视图,而是给它们设置了同样的tableHeaderView(包含同样的imageView和同样的将会被切换栏覆盖的空白区域)。虽然这种结构能够实现微博个人主页的效果,但是不方便扩展,比如给tableview增加滑动手势,来左右切换要显示的界面。
在最新版本中,我给每个tableview都设置了240高度(图片高度200+切换栏高度40)的空白UIView作为tableHeaerView,并让3个tableview共用的一个headerView(包括要显示的图片和切换栏)。首先将headerView加入到当前显示的tableview 上,跟着tableview滚动。当偏移量能够达到切换栏贴着导航栏时,将headerView加入到主控制器view中,固定位置;当偏移量要使切换栏脱离导航栏时,又将headerView加入到当前显示的tableview中;要切换当前显示的tableview,也需要根据偏移量来判断加入到哪个view上。
我在这里采用代理方式来监听每个tableview在Y轴上的偏移量,主控制器作为BaseTableViewController的代理。 协议方法:
@protocol TableViewScrollingProtocol &NSObject&
- (void)tableViewScroll:(UITableView *)tableView offsetY:(CGFloat)offsetY;
- (void)tableViewWillBeginDecelerating:(UITableView *)tableView offsetY:(CGFloat)offsetY;
- (void)tableViewDidEndDecelerating:(UITableView *)tableView offsetY:(CGFloat)offsetY;
- (void)tableViewScroll:(UITableView *)tableView offsetY:(CGFloat)offsetY{
if (offsetY & headerImgHeight - topBarHeight) {
if (![_headerView.superview isEqual:self.view]) {
[self.view insertSubview:_headerView belowSubview:_navView];
CGRect rect = self.headerView.
rect.origin.y = topBarHeight - headerImgH
self.headerView.frame =
if (![_headerView.superview isEqual:tableView]) {
for (UIView *view in tableView.subviews) {
if ([view isKindOfClass:[UIImageView class]]) {
[tableView insertSubview:_headerView belowSubview:view];
CGRect rect = self.headerView.
rect.origin.y = 0;
self.headerView.frame =
这段代码主要是实时监听tableview的Y轴偏移量,如果当前offsetY大于136(图片高度200减去导航栏状态栏高度64))的话,就将headerView添加到主控器view上,此时切换栏紧贴导航栏。如果小于的话,就将headerView添加到当前显示的tableView上,跟随tableview滑动。
切换栏在没有贴住导航栏时,三个tableview的Y轴偏移量是同样的,贴住时就不再一样。 我在主控制器里设置了一个字典来存储每个tableview的偏移量,key为tableview所在控制器的地址(之前我的使用的是tableview的地址,造成的问题是在tableview上拖拽,会造成其他两个控制器view被提前加载一次)。这样我就可以随时记录和改变每个偏移量,然后在切换到要显示的tableview时,直接设置给它的contentoffset.y就可以了。
- (void)tableViewDidEndDragging:(UITableView *)tableView offsetY:(CGFloat)offsetY {
_segCtrl.userInteractionEnabled = YES;
NSString *addressStr = [NSString stringWithFormat:@"%p", _showingVC];
if (offsetY & headerImgHeight - topBarHeight) {
[self.offsetYDict enumerateKeysAndObjectsUsingBlock:^(NSString
_Nonnull obj, BOOL * _Nonnull stop) {
if ([key isEqualToString:addressStr]) {
_offsetYDict[key] = @(offsetY);
} else if ([_offsetYDict[key] floatValue] &= headerImgHeight - topBarHeight) {
_offsetYDict[key] = @(headerImgHeight - topBarHeight);
if (offsetY &= headerImgHeight - topBarHeight) {
[self.offsetYDict enumerateKeysAndObjectsUsingBlock:^(NSString
_Nonnull obj, BOOL * _Nonnull stop) {
_offsetYDict[key] = @(offsetY);
当手指离开屏幕时,如果当前tableview没有速度了,就要在tableViewDidEndDragging:offsetY:方法里面获取tableview的偏移量。如果offsetY大于136的话,先把自己的存起来。而另外两个tableview,如果它们之前的偏移量小于等于136,现在就要改成136了(切换过去的话,让它们刚好偏移到切换栏贴住导航栏);如果offsetY小于等于136,就将三个tableview的存储偏移量设为一样。如果当前tableview还有速度,就要在tableViewDidEndDecelerating: offsetY:方法里面设置,处理方式一样。
- (void)segmentedControlChangedValue:(HMSegmentedControl*)sender {
[_showingVC.view removeFromSuperview];
BaseTableViewController *newVC = self.childViewControllers[sender.selectedSegmentIndex];
if (!newVC.view.superview) {
[self.view addSubview:newVC.view];
newVC.view.frame = self.view.
NSString *nextAddressStr = [NSString stringWithFormat:@"%p", newVC];
CGFloat offsetY = [_offsetYDict[nextAddressStr] floatValue];
newVC.tableView.contentOffset = CGPointMake(0, offsetY);
[self.view insertSubview:newVC.view belowSubview:self.navView];
if (offsetY &= headerImgHeight - topBarHeight) {
[newVC.view addSubview:_headerView];
for (UIView *view in newVC.view.subviews) {
if ([view isKindOfClass:[UIImageView class]]) {
[newVC.view insertSubview:_headerView belowSubview:view];
CGRect rect = self.headerView.
rect.origin.y = 0;
self.headerView.frame =
[self.view insertSubview:_headerView belowSubview:_navView];
CGRect rect = self.headerView.
rect.origin.y = topBarHeight - headerImgH
self.headerView.frame =
_showingVC = newVC;
这段代码就是移除之前的tableview,添加将要显示的。 唯一注意点就是要根据偏移量来判断将headerView加到主控制器view上还是tableview上。
1.关于导航栏颜色渐变的处理,我这里采用的是将系统导航栏设为无色。
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController.navigationBar setBackgroundImage:[UIImage new]forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = [UIImage new];
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.navigationController.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage =
然后添加一个navView(UIView)盖在导航栏上,然后滚动监听改变其alpha就好了。
2.为了记录准确的偏移量,跟微博一样,采取了在滚动过程中第一次单击切换栏使tableView停止滚动,第二次单击切换栏才切换tableView的做法。因为tableView自带了单击停止滚动的效果,所以如果切换栏没有贴住导航栏时,它的父控件headerView作为tableView的一部分,单击切换栏就可以让tableView停止。如果切换栏贴住导航栏时,它的父控件headerView就是控制器view的一部分了,我的做法是此时屏蔽掉headerView对触摸事件的响应,这样子切换栏后面的tableView部分就可以响应该单击事件了。详情请看源码,分析过程请看/p/2f664e71c527
3.状态栏颜色变化已经添加,gif图没有体现出来。详情请看源码,分析过程请看/p/ee1c9c91a477
4.研究发现新浪微博的顶部视图并没有下拉放大的效果,而是图片隐藏了一部分,下拉会显示出来。下拉放大实现起来比较容易,并且我觉得意义不大,在此就不在添加了。
Demo下载地址:/wobangnidashui/WeiboHomepage
热点阅读:
小主,按键盘右方向键 → 翻页可以跳过片头呢
本文标题:
原文链接:
和本文相似的内容:
日,佛山市发生了一起特殊凶杀案:犯罪嫌疑人李善周雇凶两名,将受害者张东成控制后,李善周亲自手持一柄尖刀,对着张东成的下体猛刺,活生生地将张东成的阴茎和

我要回帖

更多关于 swiper 下拉刷新页面 的文章

 

随机推荐