【原版的:参赛作品】窥秘懒---android打开下拉手势刷新时代
小飒的成长史原创作品:窥视懒人的秘密---android下拉刷新开启手势的新纪元转载请注明出处
*****************************************************************
前言:窥视懒人那些不为人知的秘密
*****************************************************************
作为一个程序猿,哪有不勤奋的道理。当我们都在为技术奋不顾身的时候。偏偏懒人创造了世界。
有的时候真心没有办法理解。为什么?
为什么懒人什么都不做。却能创造出一个又一个的奇迹,创造出一个又一个的经典。
非常早都听过一句话:懒人创造世界。
由于懒得记住那些复杂的DOS命令,于是,比尔盖茨继承图形界面的设计。完毕了一次图形界面与机器的完美结合,当全世界的电脑都一模一样的是,他也成为了世界首富;
由于懒得整天带个随身听听音乐。于是,乔布斯公布了iPod。不仅改变了人们赞赏音乐的方式。还改变了整个音乐产业。
由于懒得开每天电脑。于是移动互联网应运而生。由于懒得点击那些那些难过的button,开发人员们不断的改进科技,一次来迎合越来越懒的人的需求。
是的。讲到这里。我们仿佛明确了一个道理。
所谓懒惰不是真的懒,而是为了,他们在满足我们高效高速的生活同一时候,极力让产品更加的人性化。让产品更能服务懒人的生活。
懒人们的福音,而创造世界的人。恰恰看到了这一点。他们是为懒人思考的懒人。
当众多的开发框架,开源项目在网上激起一波又一波的项目模仿浪潮,我们能够看到的是,我们的周围充斥着一样的东西。那就是懒人逻辑。模仿懒人逻辑。
说了这么多。都没有引出我们今天的主题。不是由于我没有料,而是,在引出料的同一时候,希望自己能过有很多其它的思考。
那些料究竟是为了迎合谁的胃口而被调试出来的。是的,我们就是为了满足不同人的口味,而在不断的调试着一种一种的料。以求这样的料在投入市场的时候,能做出适合很多其它人的产品。
在一次小的面试中,面试老师问道了我一个问题,像QQ一样的软件,都有一个功能叫下拉刷新,它是怎么实现的。
这个我们起初在回答的时候。肯定都是仅仅停留在去解释它的实现代码的含义。却不清楚为什么要这种实现,又为什么有这种实现。
在看了几天的开源项目后,在此。我想对自己的理解做一次总结。
******************************************************************************
面对一个东西。我们总要问了:为什么?
******************************************************************************
1.为什么有滑动这种实现效果?
下拉刷新技术的发明者是Twitter以前最好的第三方clientTweetie的创始人洛伦.布里切特(Loren Brichter)。Twitter于2010年收购该公司时就已获取该项技术的专利权。
在《宅男成功:28岁的应用设计教父》一文中,我们能够看到这种设计师出自什么情况下。随着简约和设计在竞争异常激烈的应用程序行业变得越加重要。布里切特也正与凭着各种点子成为众所周知在应用设计领域有影响力的人。
原文摘录:
Mr. Brichter, whose design aesthetic is inspired by information theorists like Edward Tufte, a proponent of minimizing extraneous information in graphic designs, says he thinks up new features for apps based on how people move objects in the real world.
布里切特的设计美学受到了爱德华?塔夫特(Edward Tufte)等信息理论家的启示,塔夫特主张在图形设计里将无关的信息降低到最小程度。布里切特说他是依据人们在现实世界里移动物体的方式构思出应用程序的新功能的。 'Everything should come from somewhere and go somewhere,' he says, adding that he's irked by apps that have menus that pop up or collapse on themselves because the interactions aren't real. 'The most important thing is obviousness. The problem is overdesign.'
他说:“不论什么事物都应该有来源有去处。 ”他还说自己非常不喜欢那些菜单自己弹出或者收起的应用程序,由于那种互动是不真实的。“最重要的事情是要显而易见。问题出在过度设计上。”
在这片文章中。我们看到这种话,仿佛知道了些什么。在设计下拉刷新的过程中。他的设计美学受到了爱德华 塔夫特(Edward Tufte)等信息理论家的启示,塔夫特主张在图形设计里将无关的信息降低到最小程度。
布里特说他是人们在线式世界里移动物体的方式构思出应用程序的新功能的。而正是这样希望用更少的动作,去实如今懒人们看来没有必要太多的动作。形成的效果设计,满足的人机交互中最简单的一个理论:降低人们的记忆符合。
为什么这么说呢?由于,软件最大的特点就是与机器进行交流。而人要与机器交流就要通过软件。
假设软件中操作步骤过多,就是easy阻碍人的使用感。那么用户体验就会由于的开发人员的疏忽而变的糟糕。
当下拉刷新出如今GitHub。一度成为软件中最流行的效果。当然,发现最新技术的人都是对技术有着灵敏的嗅觉。随时探測技术中的流行趋势。
2.为什么要这样实现?
下拉刷新,我们常见的效果应该是例如以下图所看到的:这样的事QQ界面中的下拉刷新
在GitHub中总共同拥有十种下拉刷新的效果,各自是:
------ ListView
------ ExpandableListView
------ GridView
------ WebView
------ ScrollView
------ Horizontal ScrollView
------ ViewPager
------ ListView Fragment
------ WebView Advanced
------ ListView in ViewPager
以下,我先介绍一下。怎么获取得到下拉刷新的开源项目:
首先,在GitHub搜索Android-PullToRefresh-master
其次,下载文件,解压
将项目加入到eclipse中后。有非常多的问题出现。比方library会一直报错。这种话。就要又一次载入一下。library的引用库文件。这个项目是通过项目与项目之间的关联性,整合到一起的。
为什么要这样实现?一来是由于。下拉刷新简单方便。有用详细。
二来对于开发人员来说也是一项新的改变。改变曾经button点击的时代。从简单的button监听。用户看不到刷新的过程。带下拉刷新,用户參与到整个刷新的过程,连续的动画,让这样的效果瞬间风靡移动互联网应用也就是情理之中的事了。
3.为什么android功能代码要这样写?
在開始抛析功能代码之前,我们先想想它的实现场景。
当用户进行下拉刷新操作时,设计效果经过的三个步骤:
第一步:当手指下拉的时候,在listView的上方有出现一个向下的箭头,和pull to refresh。。。
的字样。
(可见当手指下拉的时候。箭头的imageView,和textView从隐藏到显示)
第二步:在手指下拉超过了一定的高度时,箭头方向反转向上。和release to refresh 。
。。的字样。
(可见当用户在操作出现过度的时候要实施进行提醒,而此时,箭头通过一个反转的动画,使整个效果看起来精美,连贯)
第三步:在手指松开时箭头消失。出现ProgressBar旋转载入图标和Loading的字样。(可见载入的过程摆在用户面前。自己的简化了操作的复杂性,同一时候保持了用户在操作中的參与度。
)
第四步:当刷新完毕的时候,UI恢复到刷新前的状态。头部填充布局都消失。(这样就悄无声息的实现了刷新的整个过程,如此简单的设计。却有着不简单的思考)
就这样,我们開始分析代码的实现过程吧。
对于android项目来说。这三步都被封装在一个类中,就是PullToRefreshListView自己定义布局中。通过对布局的声明和调用,就可以实现这种一个过程。
我们都知道。在listView中,能够在该控件的上端加入布局。如图所看到的:
对于功能代码的解析,我们分为两个方面
一方面:要先建立一个头部布局的layout文件
1.建立布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pull_to_refresh_header"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF"
android:gravity="center"
android:paddingBottom="15dip"
android:paddingTop="10dip" > <ProgressBar
android:id="@+id/pull_to_refresh_progress"
style="? android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="30dip"
android:layout_marginRight="20dip"
android:layout_marginTop="10dip"
android:indeterminate="true"
android:visibility="gone" /> <ImageView
android:id="@+id/pull_to_refresh_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="30dip"
android:layout_marginRight="20dip"
android:gravity="center"
android:src="@drawable/arrow"
android:visibility="gone" /> <TextView
android:id="@+id/pull_to_refresh_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#000000"
android:gravity="center"
android:paddingTop="5dip"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold" /> <TextView
android:id="@+id/pull_to_refresh_updated_at"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/pull_to_refresh_text"
android:layout_gravity="center"
android:gravity="center"
android:textAppearance="? android:attr/textAppearanceSmall"
android:visibility="gone" /> </RelativeLayout>
2.对建立的头部布局进行注冊,以及填充到listView中
Code
/**
* 初始化控件和箭头动画
* @param context
*/
private void init(Context context) {
mFlipAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mFlipAnimation.setInterpolator(new LinearInterpolator());
mFlipAnimation.setDuration(250);
mFlipAnimation.setFillAfter(true);
mReverseFlipAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
mReverseFlipAnimation.setDuration(250);
mReverseFlipAnimation.setFillAfter(true); mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mRefreshView = (RelativeLayout) mInflater.inflate(
R.layout.pull_to_refresh_header, this, false);
mRefreshViewText = (TextView) mRefreshView
.findViewById(R.id.pull_to_refresh_text);
mRefreshViewImage = (ImageView) mRefreshView
.findViewById(R.id.pull_to_refresh_image);
mRefreshViewProgress = (ProgressBar) mRefreshView
.findViewById(R.id.pull_to_refresh_progress);
mRefreshViewLastUpdated = (TextView) mRefreshView
.findViewById(R.id.pull_to_refresh_updated_at); mRefreshViewImage.setMinimumHeight(50);
mRefreshOriginalTopPadding = mRefreshView.getPaddingTop(); mRefreshState = TAP_TO_REFRESH;
//将上述布局文件以及动画效果 增加ListView的头部
addHeaderView(mRefreshView); super.setOnScrollListener(this); measureView(mRefreshView);
mRefreshViewHeight = mRefreshView.getMeasuredHeight(); }
还有一方面:通过代码控制实现效果的转换
1.监听手势的变化
Code
/**
* 重写的一个触摸事件处理
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
final int y = (int) event.getY();
mBounceHack = false; switch (event.getAction()) { case MotionEvent.ACTION_UP:
if (!isVerticalScrollBarEnabled()) {
setVerticalScrollBarEnabled(true);
}
if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
//拖动距离达到一定距离的时候 (须要刷新)
if ((mRefreshView.getBottom() >= mRefreshViewHeight || mRefreshView
.getTop() >= 0) && mRefreshState == RELEASE_TO_REFRESH) {
//将状态设置为 正在刷新
mRefreshState = REFRESHING;
//准备刷新
prepareForRefresh();
//刷新
onRefresh();
//假设取消拖动 或者 拖的距离不够
} else if (mRefreshView.getBottom() < mRefreshViewHeight
|| mRefreshView.getTop() <= 0) {
//终止刷新
resetHeader();
setSelection(1);
}
}
break;
case MotionEvent.ACTION_DOWN:
//获取按下的y轴的位置
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
//计算边距
applyHeaderPadding(event);
break;
}
return super.onTouchEvent(event);
}
2.获取headerView的边距和高度
Code
/**
* 获取headerView的边距
* @param ev
*/
private void applyHeaderPadding(MotionEvent ev) { int pointerCount = ev.getHistorySize(); for (int p = 0; p < pointerCount; p++) {
if (mRefreshState == RELEASE_TO_REFRESH) {
if (isVerticalFadingEdgeEnabled()) {
setVerticalScrollBarEnabled(false);
} int historicalY = (int) ev.getHistoricalY(p);
//控制下拉的程度 拉动效果
int topPadding = (int) (((historicalY - mLastMotionY) - mRefreshViewHeight) / 1.7); mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
topPadding, mRefreshView.getPaddingRight(),
mRefreshView.getPaddingBottom());
}
}
}
Code
/**
* 将HeaderView的边距 重置为初始的数值
*/
private void resetHeaderPadding() {
mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
mRefreshOriginalTopPadding, mRefreshView.getPaddingRight(),
mRefreshView.getPaddingBottom());
}
Code
/**
* 将整个HeaderView重置为下拉之前的状态
*/
private void resetHeader() {
if (mRefreshState != TAP_TO_REFRESH) {
mRefreshState = TAP_TO_REFRESH; resetHeaderPadding();
//将图片又一次换成箭头
mRefreshViewImage
.setImageResource(R.drawable.arrow);
//清除动画效果
mRefreshViewImage.clearAnimation();
//隐藏图标以及进度条
mRefreshViewImage.setVisibility(View.GONE);
mRefreshViewProgress.setVisibility(View.GONE);
}
}
Code
/**
* 估算headView的宽和高
* @param child
*/
private void measureView(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
} int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
3.通过手势移动的位置,计算高度。并对滑动过程中效果的变化进行更新操作
Code
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
//在headerView全然可见的时候 将文字设置为"松开载入..." 同一时候翻转箭头
if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
&& mRefreshState != REFRESHING) {
if (firstVisibleItem == 0) {
mRefreshViewImage.setVisibility(View.VISIBLE);
if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20 || mRefreshView
.getTop() >= 0) && mRefreshState != RELEASE_TO_REFRESH) {
mRefreshViewText.setText("松开载入...");
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mFlipAnimation);
mRefreshState = RELEASE_TO_REFRESH;
} else if (mRefreshView.getBottom() < mRefreshViewHeight + 20
&& mRefreshState != PULL_TO_REFRESH) {
mRefreshViewText.setText("下拉刷新...");
if (mRefreshState != TAP_TO_REFRESH) {
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mReverseFlipAnimation);
}
mRefreshState = PULL_TO_REFRESH;
}
} else {
mRefreshViewImage.setVisibility(View.GONE);
resetHeader();
}
} else if (mCurrentScrollState == SCROLL_STATE_FLING
&& firstVisibleItem == 0 && mRefreshState != REFRESHING) {
setSelection(1);
mBounceHack = true;
} else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {
setSelection(1);
} if (mOnScrollListener != null) {
mOnScrollListener.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
} //
if (mCurrentScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
//获取最后一条数据的索引
lastVisibleIndex = firstVisibleItem + visibleItemCount - 1;
//当全部的数据 都已经载入出来了(全部的条目数等于最大条目数) 移除掉底部的footerView
if (totalItemCount == MaxDateNum + 1) {
removeFooterView(moreView);
Toast.makeText(context, "数据全部载入完毕。没有很多其它数据。", Toast.LENGTH_LONG)
.show();
}
}
}
Code
/**
* 滚动变化监听器
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mCurrentScrollState = scrollState; if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
//当滑动究竟部的时候 运行自己主动载入功能
if (mCurrentScrollState == OnScrollListener.SCROLL_STATE_IDLE
&& lastVisibleIndex == this.getAdapter().getCount()) {
//、、、、、、、、、 异步载入数据的代码
pg.setVisibility(View.VISIBLE);
bt.setVisibility(View.GONE);
}
}
4.定义一系列的调用方法,以便上层代码进行调用。
Code
/**
* 准备刷新
*/
public void prepareForRefresh() {
resetHeaderPadding();//恢复HeaderView的边距
//将图片隐藏
mRefreshViewImage.setVisibility(View.GONE);
//将图片置为null
mRefreshViewImage.setImageDrawable(null);
mRefreshViewProgress.setVisibility(View.VISIBLE); mRefreshViewText.setText("载入中..."); mRefreshState = REFRESHING;
} public void onRefresh() { if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
}
/**
* 当ListView载入完 能够调用该方法 设置最后更新时间
* @param lastUpdated
*/
public void onRefreshComplete(CharSequence lastUpdated) {
setLastUpdated(lastUpdated);
onRefreshComplete();
}
/**
*当ListView载入完 能够调用该方法 可是没有设置最后更新时间
*/
public void onRefreshComplete() { resetHeader(); // 假设refreshview在载入结束后可见,下滑到下一个条目
if (mRefreshView.getBottom() > 0) {
invalidateViews();
setSelection(1);
}
}
Code
/**
* 刷新监听器接口
* @author ChnAdo
*
*/
public interface OnRefreshListener {
/**
* 须要刷新时调用该方法
*/
public void onRefresh();
}
完整的功能代码例如以下:
Code
public class PullToRefreshListView extends ListView implements OnScrollListener { private Context context; private static final int TAP_TO_REFRESH = 1;
private static final int PULL_TO_REFRESH = 2;
private static final int RELEASE_TO_REFRESH = 3;
private static final int REFRESHING = 4; private OnRefreshListener mOnRefreshListener; private OnScrollListener mOnScrollListener;
private LayoutInflater mInflater; private RelativeLayout mRefreshView;
private TextView mRefreshViewText;
private ImageView mRefreshViewImage;
private ProgressBar mRefreshViewProgress;
private TextView mRefreshViewLastUpdated; private int mCurrentScrollState; private int mRefreshState; private RotateAnimation mFlipAnimation;
private RotateAnimation mReverseFlipAnimation; private int mRefreshViewHeight;
private int mRefreshOriginalTopPadding;
private int mLastMotionY; private boolean mBounceHack; private int MaxDateNum;
private View moreView;
public Button bt;
private ProgressBar pg;
private int lastVisibleIndex;
public boolean isclick; public PullToRefreshListView(Context context) {
super(context);
init(context);
this.context = context;
} public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
this.context = context;
} public PullToRefreshListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init(context);
this.context = context;
}
/**
* 初始化控件和箭头动画
* @param context
*/
private void init(Context context) {
mFlipAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mFlipAnimation.setInterpolator(new LinearInterpolator());
mFlipAnimation.setDuration(250);
mFlipAnimation.setFillAfter(true);
mReverseFlipAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
mReverseFlipAnimation.setDuration(250);
mReverseFlipAnimation.setFillAfter(true); mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mRefreshView = (RelativeLayout) mInflater.inflate(
R.layout.pull_to_refresh_header, this, false);
mRefreshViewText = (TextView) mRefreshView
.findViewById(R.id.pull_to_refresh_text);
mRefreshViewImage = (ImageView) mRefreshView
.findViewById(R.id.pull_to_refresh_image);
mRefreshViewProgress = (ProgressBar) mRefreshView
.findViewById(R.id.pull_to_refresh_progress);
mRefreshViewLastUpdated = (TextView) mRefreshView
.findViewById(R.id.pull_to_refresh_updated_at); mRefreshViewImage.setMinimumHeight(50);
mRefreshOriginalTopPadding = mRefreshView.getPaddingTop(); mRefreshState = TAP_TO_REFRESH;
//将上述布局文件以及动画效果 增加ListView的头部
addHeaderView(mRefreshView); super.setOnScrollListener(this); measureView(mRefreshView);
mRefreshViewHeight = mRefreshView.getMeasuredHeight(); //给ListView载入一个FooterView
MaxDateNum = 20;
moreView = LayoutInflater.from(context)
.inflate(R.layout.moredata, null);
bt = (Button) moreView.findViewById(R.id.bt_load);
pg = (ProgressBar) moreView.findViewById(R.id.pg);
addFooterView(moreView);
//给底部的button实现一个监听事件
bt.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
//点这个button 让进度条可见 button自身隐藏
pg.setVisibility(View.VISIBLE);
bt.setVisibility(View.GONE);
isclick = true;
}
});
} @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setSelection(1);
} @Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter); setSelection(1);
}
/**
* 设置一个滚动(滑动)监听器
*/
@Override
public void setOnScrollListener(AbsListView.OnScrollListener l) {
mOnScrollListener = l;
}
/**
* 当ListView的列表须要刷新的时候 又一次回调的一个监听器
* @param onRefreshListener
*/
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
mOnRefreshListener = onRefreshListener;
}
/**
* 设置文字标题显示 比如能够显示近期刷新时间等等
* @param lastUpdated
*/
public void setLastUpdated(CharSequence lastUpdated) {
if (lastUpdated != null) {
mRefreshViewLastUpdated.setVisibility(View.VISIBLE);
mRefreshViewLastUpdated.setText(lastUpdated);
} else {
mRefreshViewLastUpdated.setVisibility(View.GONE);
}
}
/**
* 重写的一个触摸事件处理
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
final int y = (int) event.getY();
mBounceHack = false; switch (event.getAction()) { case MotionEvent.ACTION_UP:
if (!isVerticalScrollBarEnabled()) {
setVerticalScrollBarEnabled(true);
}
if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
//拖动距离达到一定距离的时候 (须要刷新)
if ((mRefreshView.getBottom() >= mRefreshViewHeight || mRefreshView
.getTop() >= 0) && mRefreshState == RELEASE_TO_REFRESH) {
//将状态设置为 正在刷新
mRefreshState = REFRESHING;
//准备刷新
prepareForRefresh();
//刷新
onRefresh();
//假设取消拖动 或者 拖的距离不够
} else if (mRefreshView.getBottom() < mRefreshViewHeight
|| mRefreshView.getTop() <= 0) {
//终止刷新
resetHeader();
setSelection(1);
}
}
break;
case MotionEvent.ACTION_DOWN:
//获取按下的y轴的位置
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
//计算边距
applyHeaderPadding(event);
break;
}
return super.onTouchEvent(event);
}
/**
* 获取headerView的边距
* @param ev
*/
private void applyHeaderPadding(MotionEvent ev) { int pointerCount = ev.getHistorySize(); for (int p = 0; p < pointerCount; p++) {
if (mRefreshState == RELEASE_TO_REFRESH) {
if (isVerticalFadingEdgeEnabled()) {
setVerticalScrollBarEnabled(false);
} int historicalY = (int) ev.getHistoricalY(p);
//控制下拉的程度 拉动效果
int topPadding = (int) (((historicalY - mLastMotionY) - mRefreshViewHeight) / 1.7); mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
topPadding, mRefreshView.getPaddingRight(),
mRefreshView.getPaddingBottom());
}
}
}
/**
* 将HeaderView的边距 重置为初始的数值
*/
private void resetHeaderPadding() {
mRefreshView.setPadding(mRefreshView.getPaddingLeft(),
mRefreshOriginalTopPadding, mRefreshView.getPaddingRight(),
mRefreshView.getPaddingBottom());
}
/**
* 将整个HeaderView重置为下拉之前的状态
*/
private void resetHeader() {
if (mRefreshState != TAP_TO_REFRESH) {
mRefreshState = TAP_TO_REFRESH; resetHeaderPadding();
//将图片又一次换成箭头
mRefreshViewImage
.setImageResource(R.drawable.arrow);
//清除动画效果
mRefreshViewImage.clearAnimation();
//隐藏图标以及进度条
mRefreshViewImage.setVisibility(View.GONE);
mRefreshViewProgress.setVisibility(View.GONE);
}
}
/**
* 估算headView的宽和高
* @param child
*/
private void measureView(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
} int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
} @Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
//在headerView全然可见的时候 将文字设置为"松开载入..." 同一时候翻转箭头
if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
&& mRefreshState != REFRESHING) {
if (firstVisibleItem == 0) {
mRefreshViewImage.setVisibility(View.VISIBLE);
if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20 || mRefreshView
.getTop() >= 0) && mRefreshState != RELEASE_TO_REFRESH) {
mRefreshViewText.setText("松开载入...");
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mFlipAnimation);
mRefreshState = RELEASE_TO_REFRESH;
} else if (mRefreshView.getBottom() < mRefreshViewHeight + 20
&& mRefreshState != PULL_TO_REFRESH) {
mRefreshViewText.setText("下拉刷新...");
if (mRefreshState != TAP_TO_REFRESH) {
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mReverseFlipAnimation);
}
mRefreshState = PULL_TO_REFRESH;
}
} else {
mRefreshViewImage.setVisibility(View.GONE);
resetHeader();
}
} else if (mCurrentScrollState == SCROLL_STATE_FLING
&& firstVisibleItem == 0 && mRefreshState != REFRESHING) {
setSelection(1);
mBounceHack = true;
} else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {
setSelection(1);
} if (mOnScrollListener != null) {
mOnScrollListener.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
} //
if (mCurrentScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
//获取最后一条数据的索引
lastVisibleIndex = firstVisibleItem + visibleItemCount - 1;
//当全部的数据 都已经载入出来了(全部的条目数等于最大条目数) 移除掉底部的footerView
if (totalItemCount == MaxDateNum + 1) {
removeFooterView(moreView);
Toast.makeText(context, "数据全部载入完毕,没有很多其它数据! ", Toast.LENGTH_LONG)
.show();
}
}
} /**
* 滚动变化监听器
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mCurrentScrollState = scrollState; if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
//当滑动究竟部的时候 运行自己主动载入功能
if (mCurrentScrollState == OnScrollListener.SCROLL_STATE_IDLE
&& lastVisibleIndex == this.getAdapter().getCount()) {
//、、、、、、、、、 异步载入数据的代码
pg.setVisibility(View.VISIBLE);
bt.setVisibility(View.GONE);
}
}
/**
* 准备刷新
*/
public void prepareForRefresh() {
resetHeaderPadding();//恢复HeaderView的边距
//将图片隐藏
mRefreshViewImage.setVisibility(View.GONE);
//将图片置为null
mRefreshViewImage.setImageDrawable(null);
mRefreshViewProgress.setVisibility(View.VISIBLE); mRefreshViewText.setText("载入中..."); mRefreshState = REFRESHING;
} public void onRefresh() { if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
}
/**
* 当ListView载入完 能够调用该方法 设置最后更新时间
* @param lastUpdated
*/
public void onRefreshComplete(CharSequence lastUpdated) {
setLastUpdated(lastUpdated);
onRefreshComplete();
}
/**
*当ListView载入完 能够调用该方法 可是没有设置最后更新时间
*/
public void onRefreshComplete() { resetHeader(); // 假设refreshview在载入结束后可见,下滑到下一个条目
if (mRefreshView.getBottom() > 0) {
invalidateViews();
setSelection(1);
}
}
/**
* 刷新监听器接口
* @author ChnAdo
*
*/
public interface OnRefreshListener {
/**
* 须要刷新时调用该方法
*/
public void onRefresh();
} /**
* onMoreComplete() 刷新
*/
public void onMoreComplete() {
bt.setVisibility(View.GONE);//设置button消失
pg.setVisibility(View.VISIBLE);//设置载入滚动栏显示
isclick = false;
}
//结束进度条
public void dismissProgress() {
bt.setVisibility(View.GONE);
pg.setVisibility(View.INVISIBLE); }
}
在使用该自己定义listView的Activity中,要实现两个方法。
Code
/**
* 实现blogslist上滑载入
*/
blogslist.setOnScrollListener(new OnScrollListener() { @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
&& !blogslist.isclick
&& visiableItemCount == visiableLastIndex) {
addArrayList();
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
AllBlogsActivity.visiableItemCount = totalItemCount;
visiableLastIndex = firstVisibleItem + visibleItemCount;
}
});
至此,我们下拉刷新的样例就完毕了。事实上,非常多代码,每个程序猿都会的就是学习,利用和改造。
可是,在学习他们的成果的时候,自己是否也在想,我为什么没有这种想法。看起来如此之简单,实现起来如此之easy,使用起来如此之爽。偏偏,我们总是后知后觉。
没错,思考。
。。
在生活中,我们不缺乏的就是经验。可是将经验之谈转换通过思考转化成简单的逻辑。在由简单的逻辑去创造出懒人们的产品,是的,
这句话没错:懒人创造世界。
版权声明:本文博客原创文章,博客,未经同意,不得转载。
【原版的:参赛作品】窥秘懒---android打开下拉手势刷新时代的更多相关文章
- Xamarin. Android实现下拉刷新功能
PS:发现文章被其他网站或者博客抓取后发表为原创了,给图片加了个水印 下拉刷新功能在安卓和iOS中非常常见,一般实现这样的功能都是直接使用第三方的库,网上能找到很多这样的开源库.然而在Xamarin. ...
- Android智能下拉刷新加载框架—看这些就够了
一些值得学习的几个下拉刷新上拉加载开源库 Android智能下拉刷新框架-SmartRefreshLayout 支持所有的 View(AbsListView.RecyclerView.WebView. ...
- android一个下拉放大库bug的解决过程及思考
android一个下拉放大库bug的解决过程及思考 起因 项目中要做一个下拉缩放图片的效果,搜索了下github上面,找到了两个方案. https://github.com/Frank-Zhu/Pul ...
- 【Android】下拉刷新实现
关于这方面的文章百度下有很多,我就只写写我自己实现过程. 我觉得学习一门语言不是做了几个项目就可以认为自己会了,这只是暂时的,若没有笔记,时间长了,你是怎么解决某些问题,估计连你自己都忘了,又得费时费 ...
- Android实现下拉导航选择菜单效果
本文介绍在Android中如何实现下拉导航选择菜单效果. 关于下拉导航选择菜单效果在新闻客户端中用的比较多,当然也可以用在其他的项目中,这样可以很方便的选择更多的菜单.我们可以让我们的应用顶部有左 ...
- Android PullToRefresh下拉刷新控件的简单使用
PullToRefresh这个开源库早就听说了,不过一直没用过.作为一个经典的的开源库,我觉得还是有必要认识一下. 打开github上的网址:https://github.com/chrisbanes ...
- Android原生下拉刷新SwipeRefreshLayout实践
本篇文章翻译自Ravi Tamada的Android Swipe Down to Refresh ListView Tutorial 首先来看一下效果图 你应该发现很多的android app比如Tw ...
- Android 实现下拉刷新和上拉加载更多的RECYCLERVIEW和SCROLLVIEW
PullRefreshRecyclerView.java /** * 类说明:下拉刷新上拉加载更多的RecyclerView * Author: gaobaiq * Date: 2016/5/9 18 ...
- Android SwipeRefreshLayout 下拉刷新——Hi_博客 Android App 开发笔记
以前写下拉刷新 感觉好费劲,要判断ListView是否滚到顶部,还要加载头布局,还要控制 头布局的状态,等等一大堆.感觉麻烦死了.今天学习了SwipeRefreshLayout 的用法,来分享一下,有 ...
随机推荐
- SQLite外键
数据库工具:SQLite Manager(V0.7.7) SQLite版本号:V3.6.19+ SQLite Manager 默认是不开启外键的. 那么怎样,使用它创建一个带有外键的表呢? 一.开启外 ...
- NSIS常用代码整理
原文 NSIS常用代码整理 这是一些常用的NSIS代码,少轻狂特意整理出来,方便大家随时查看使用.不定期更新哦~~~ 1 ;获取操作系统盘符 2 ReadEnvStr $R0 SYSTEMDRIVE ...
- 【SSH 基金会】SSH框架--struts进一步的详细解释(两)
继上篇博客 既然我们知道了不使用struts给我们带来这么多弊端,那么以下我们来看看struts是怎样封装的.怎么解决我们出现的问题的? 先来说一下struts的基本流程,帮助大家理解以下的代码: S ...
- Https 客户端与服务器交互过程梳理(转)
本文试图以通俗易通的方式介绍Https的工作原理,不纠结具体的术语,不考证严格的流程.我相信弄懂了原理之后,到了具体操作和实现的时候,方向就不会错,然后条条大路通罗马.阅读文本需要提前大致了解对称加密 ...
- Jquery 插件初学习
参考文章:插件开发精品教程,让你的jQuery提升一个台阶 刚刚学了一下jquery的插件插件开发,写个demo记录.练习一下.毕竟,输出才是最好的学习. 这个也不过是最基础的一个插件写法,只是,自己 ...
- async和await关键字实现异步编程
async和await关键字实现异步编程 异步编程 概念 异步编程核心为异步操作,该操作一旦启动将在一段时间内完成.所谓异步,关键是实现了两点:(1)正在执行的此操作,不会阻塞原来的线程(2)一旦 ...
- Putty是一个专业的SSH连接客户端
http://www.putty.ws/PuTTY-LinuxVPS Putty是一个专业的SSH连接客户端,当然可以用来连接Linux操作系统的VPS.下文是Putty连接工具的使用方法与详细教程, ...
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(7)-DBSession的封装
原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(7)-DBSession的封装 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇) (1):框架搭建 ...
- Asp.Net超时问题汇总
在数据库或者请求操作时,如果选择的时间段过短或操作数据量过大,就会遇到"请求超时"的的问题,网络上提供很多解决方案,但普遍不完善,根据个人经验及参考网络解决方案,先将其汇总如下: ...
- 改动ubuntu/linux文件夹显示颜色
通过secureCRT登陆linux,假设背景颜色选为黑色,非常可能在使用ls命令时看不清楚文件夹名,这时候我们能够通过一个简单的方式将文件夹变为一个显眼的颜色,比如"黄色". [ ...