我见网上都是继续ListView 实现 OnScrollListener接口,而我的项目却不能这样去做,我的布局有些复杂,不是网上简单的就一个页面,于是我决定用内部类来实现,我发现这些方法不光对一个界面的ListView有效,而且对不对位置的ListView也有效,所以以后可以直接复制这些代码实现快速开发,图就不上了,直接上重要的代码,由于这涉及到公司的项目,代码也不会上那么全,上关键的吧,注释很全了
这是有ListView的那个类public class EightcallActivity extends Activity implements OnClickListener, OnKeyListener {
private ListView lv;这就是那个listView了,它是在界面布局的左边,标题的下边
声明一些要用到的变量,全局的,这些都是网上都可以找到的
private static final String TAG = "listview";
private final static int RELEASE_To_REFRESH = 0; // 释放 //正常状态
private final static int PULL_To_REFRESH = 1;// 下拉刷新 //进入下拉刷新状态
private final static int REFRESHING = 2; // 正在刷新 //进入松手刷新状态
private final static int DONE = 3; // 刷新完成
private final static int LOADING = 4; // 加载数据// //松手后反弹后加载状态
private int state; //记录刷新状态
// 实际的padding的距离与界面上偏移距离的比例
private final static int RATIO = 3;
private LayoutInflater inflater;
private TextView tipsTextview;
private TextView lastUpdatedTextView;
private ImageView arrowImageView;
private ProgressBar progressBar;
private RotateAnimation animation;
private RotateAnimation reverseAnimation;
// 用于保证startY的值在一个完整的touch事件中只被记录一次
private boolean isRecored; //是否返回,(记录了Y的值 就需要隐藏起来)
private int headContentWidth;
private int headContentHeight;
private int startY;
private int firstItemIndex;
private boolean isBack; //箭头是否返回
private OnRefreshListener refreshListener;
private boolean isRefreshable; //是否可刷新
在Oncreate中:
headView = mInflate.inflate(R.layout.head, null);
lv.addHeaderView(headView);
arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView);
arrowImageView.setMinimumWidth(30);
arrowImageView.setMinimumHeight(50);
progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar);
tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);
lastUpdatedTextView = (TextView)headView.findViewById(R.id.head_lastUpdatedTextView);
measureView(headView); // 然后在init的上述代码后面加上调用measureView后,使用getMeasureHeight()方法获取header的高度:
headContentHeight = headView.getMeasuredHeight(); //81
headContentWidth = headView.getMeasuredWidth(); // 126
onRefreshComplete();
//初始状态是 隐藏掉head 布局
headView.setPadding(0, -1 * headContentHeight, 0, 0);
headView.invalidate();
animation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);
animation.setInterpolator(new LinearInterpolator());
animation.setDuration(250); //动画时间
animation.setFillAfter(true); // 设置为true,将坚持完成
reverseAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
reverseAnimation.setInterpolator(new LinearInterpolator());
reverseAnimation.setDuration(200);
reverseAnimation.setFillAfter(true);
//默认状态
state = DONE;
isRefreshable = true;
lv.setAdapter(adapter);
下面的比较重要了,两个内部类,触摸也实现了点击事件,所以在这里不用写点击事件了,在此处理点击事件
lv.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
x = (int) event.getX();
y = (int) event.getY();
position = lv.pointToPosition(x, y);
}
if(event.getAction() == MotionEvent.ACTION_UP){
if(x == (int)event.getX() && y == (int)event.getY()){
if(position !=0){
position -= 1;
}
if (butcalltemp == BUTTONCALLOUT) {
showItemCallOutContextMemu(position);
} else {
showItemCallInContextMemu(position);
}
}
}
onTouchSwitch(event);
return true;
}
});
//behind add
lv.setOnScrollListener(new OnScrollListener() {
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
firstItemIndex = firstVisibleItem;
}
});
下面这几个方法也没什么好说的了,几乎就是在网上照抄
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);
Log.i("dimison","p is null new p = "+ p); // this is run
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width); //为什么是0+0,不太懂,网上都是这样写的
Log.i("dimison","p.width = "+p.width); //-1
int lpHeight = p.height;
Log.i("dimison","lpHeight = "+lpHeight); //-2
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
Log.i("dimison","childHeightSpec = "+childHeightSpec);
Log.i("dimison","MeasureSpec.EXACTLY = "+MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
Log.i("dimison","childHeightSpec = "+childHeightSpec); //this is run 0
Log.i("dimison","MeasureSpec.UNSPECIFIED = "+MeasureSpec.UNSPECIFIED); // 0
}
child.measure(childWidthSpec, childHeightSpec);
}
private void onTouchSwitch(MotionEvent event){
if (isRefreshable) {
switch (event.getAction()) {
// 在down时候记录当前Y的位置
case MotionEvent.ACTION_DOWN:
if (firstItemIndex == 0 && !isRecored) {
isRecored = true;
startY = (int) event.getY();
Log.v(TAG, "在down时候记录当前位置‘");
}
break;
//松开的时候
case MotionEvent.ACTION_UP:
if (state != REFRESHING && state != LOADING) {
if (state == DONE) {
// 什么都不做
}
// 由下拉刷新状态,到done状态
if (state == PULL_To_REFRESH) {
state = DONE;
changeHeaderViewByState();
Log.v(TAG, "由下拉刷新状态,到done状态");
}
//由松开刷新状态,到done状态
if (state == RELEASE_To_REFRESH) {
state = REFRESHING;
changeHeaderViewByState();
onRefresh();
Log.v(TAG, "由松开刷新状态,到done状态");
}
}
isRecored = false;
isBack = false;
break;
case MotionEvent.ACTION_MOVE:
int tempY = (int) event.getY();
/**
* 手指移动过程中tempY数据会不断变化,当滑动到firstItemIndex,即到达顶部,
* 需要记录手指所在屏幕的位置: startY = tempY ,后面作位置比较使用
*
* 如果手指继续向下推,tempY继续变化,当tempY-startY>0,即是需要显示header部分
*
* 此时需要更改状态:state = PULL_To_REFRESH
*/
if (!isRecored && firstItemIndex == 0) {
Log.v(TAG, "在move时候记录下位置");
isRecored = true;
startY = tempY;
}
if (state != REFRESHING && isRecored && state != LOADING) {
// 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
// 可以松手去刷新了
if (state == RELEASE_To_REFRESH) {
lv.setSelection(0);//让它在第一行首显示正在刷新的提示
// 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步(大概是3/4的位置时)
if (((tempY - startY)/RATIO < headContentHeight)
&& (tempY - startY) > 0) {
state = PULL_To_REFRESH;
changeHeaderViewByState();
Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");
}
// 一下子推到顶了
else if (tempY - startY <= 0) {
state = DONE;
changeHeaderViewByState();
Log.v(TAG, "由松开刷新状态转变到done状态");
}
// 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步
else {
// 不用进行特别的操作,只用更新paddingTop的值就行了
}
}
// 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
if (state == PULL_To_REFRESH) {
lv.setSelection(0);
/**
* 下拉到可以进入RELEASE_TO_REFRESH的状态
* 等于headContentHeight时,即是正好完全显示header部分
* 大于headContentHeight时,即是超出header部分更多
* 当header部分能够完全显示或者超出显示,
* 需要更改状态: state = RELEASE_To_REFRESH
*/
// 下拉到可以进入RELEASE_TO_REFRESH的状态
if ((tempY - startY) / RATIO >= headContentHeight) {
state = RELEASE_To_REFRESH;
isBack = true;
changeHeaderViewByState();
Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");
}
// 上推到顶了,没有显示header部分时,应该恢复DONE状态
else if (tempY - startY <= 0) {
state = DONE;
changeHeaderViewByState();
Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");
}
}
// done状态下
if (state == DONE) {
if (tempY - startY > 0) {
/**
* 手指移动过程中tempY数据会不断变化,当滑动到firstItemIndex,即到达顶部,
* 需要记录手指所在屏幕的位置: startY = tempY ,后面作位置比较使用
* 如果手指继续向下推,tempY继续变化,当tempY-startY>0,即是需要显示header部分
* 此时需要更改状态:state = PULL_To_REFRESH
*/
state = PULL_To_REFRESH;
changeHeaderViewByState();
}
}
// 更新headView的size(更新headView的paddingTop)
if (state == PULL_To_REFRESH) {
headView.setPadding(0, -1 * headContentHeight
+ (tempY - startY) / RATIO, 0, 0);
}
// 更新headView的paddingTop(继续)
if (state == RELEASE_To_REFRESH) {
headView.setPadding(0, (tempY - startY) / RATIO
- headContentHeight, 0, 0);
}
}
break;
}
}
}
private void changeHeaderViewByState(){
switch (state) {
//松开刷新状态
case RELEASE_To_REFRESH:
arrowImageView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
tipsTextview.setVisibility(View.VISIBLE);
lastUpdatedTextView.setVisibility(View.VISIBLE);
arrowImageView.clearAnimation();
arrowImageView.startAnimation(animation);
tipsTextview.setText("松开刷新");
Log.v(TAG, "当前状态,松开刷新");
break;
//下拉刷新
case PULL_To_REFRESH:
progressBar.setVisibility(View.GONE);
tipsTextview.setVisibility(View.VISIBLE);
lastUpdatedTextView.setVisibility(View.VISIBLE);
arrowImageView.clearAnimation();
arrowImageView.setVisibility(View.VISIBLE);
// 是由RELEASE_To_REFRESH状态转变来的
//箭头反转向上
if (isBack) {
isBack = false;
arrowImageView.clearAnimation();
arrowImageView.startAnimation(reverseAnimation);
tipsTextview.setText("下拉刷新");
} else {
tipsTextview.setText("下拉刷新");
}
Log.v(TAG, "当前状态,下拉刷新");
break;
//正在刷新状态
case REFRESHING:
headView.setPadding(0, 0, 0, 0);
progressBar.setVisibility(View.VISIBLE);
arrowImageView.clearAnimation();
arrowImageView.setVisibility(View.GONE);
tipsTextview.setText("正在刷新...");
lastUpdatedTextView.setVisibility(View.VISIBLE);
Log.v(TAG, "当前状态,正在刷新...");
break;
//刷新完毕
case DONE:
headView.setPadding(0, -1 * headContentHeight, 0, 0);
progressBar.setVisibility(View.GONE);
arrowImageView.clearAnimation();
arrowImageView.setImageResource(R.drawable.downicon);
tipsTextview.setText("下拉刷新");
lastUpdatedTextView.setVisibility(View.VISIBLE);
isRefreshable = true;
Log.v(TAG, "当前状态,done");
break;
}
}
public void onRefreshComplete() {
state = DONE;
SimpleDateFormat format=new SimpleDateFormat("yyyy年MM月dd日 HH:mm");
String date=format.format(new Date());
lastUpdatedTextView.setText("最近更新:" + date);
changeHeaderViewByState();
}
private void onRefresh() {
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
pullRefreshing();
}
}).start();
}
private void pullRefreshing() {
if (datas.size() == 0) {
handler.sendEmptyMessage(7);
return;
}
String str = JsonUtil.readJson(urlIsOnLine, userid, yqhId, cid, meetId,
phones, getListPhones());
JSONObject json = null;
try {
json = new JSONObject(str);
str = json.getString("staus");
} catch (JSONException e2) {
e2.printStackTrace();
}
while (str.equalsIgnoreCase("ok")) {
try {
// 得到呼叫人的电话号码
str = json.getString("data");
// 得到分钟数
if (json.getString("time") != null) {
times = json.getString("time");
}
// 得到在线人数
onLineCount = json.getString("count");
} catch (JSONException e) {
e.printStackTrace();
}
if (str != null) {
String[] p = str.split(",");
// 呼叫人的号码
for (int i = 0; i < p.length; i++) {
if (datas.contains(p[i])) {
for (int j = 0; j < datas.size(); j++) {
if (p[i].equals(datas.get(j).getPhone())) {
isOnLine[j] = true; // 表示在线
}
}
}
}
}
}
handler.sendEmptyMessage(7);
}
配置文件
head.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffffff">
<!-- 内容 -->
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/head_contentLayout"
android:paddingLeft="0dp"
>
<!-- 箭头图像、进度条 -->
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="0dip"
android:layout_centerVertical="true">
<!-- 箭头 -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:src="@drawable/downicon"
android:id="@+id/head_arrowImageView"
/>
<!-- 进度条 -->
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleSmall"
android:id="@+id/head_progressBar"
android:visibility="gone"
/>
</FrameLayout>
<!-- 提示、最近更新 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_toRightOf="@+id/frameLayout"
android:paddingLeft="0dip"
android:orientation="vertical"
>
<!-- 提示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textColor="@color/rb"
android:textSize="13sp"
android:layout_marginTop="13dip"
android:id="@+id/head_tipsTextView"
/>
<!-- 最近更新 -->
<TextView
android:id="@+id/head_lastUpdatedTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dip"
android:layout_marginTop="3dip"
android:text="上次更新 :"
android:textColor="@color/rb"
android:textSize="10sp" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
======================================================================================================
其它几位关于这方面的文章请参考
http://www.2cto.com/kf/201111/110559.html 这位博主是从国外网站一位很不错的工程师复制过来经加工注释后的,
http://blog.csdn.net/nono_love_lilith/article/details/7100845
http://blog.sina.com.cn/s/blog_7575ed8b01013cnp.html 这位是我已经试过的
http://blog.sina.com.cn/s/blog_6e5195850101485b.html ;
http://www.uml.org.cn/mobiledev/201212242.asp
http://www.cnblogs.com/transmuse/archive/2010/12/01/1893799.html