-
捲動更新的ListView (使用SimpleAdapter)30
附件程式是由下面網站的ListDemo修改來的
http://blog.joomla.org.tw/android/178-ListView%E4%B9%8B%E4%B8%80%EF%BC%9AAdapter%E4%BB%8B%E7%B4%B9%E8%88%87%E4%BD%BF%E7%94%A8.html
想再多設置捲動更新的部分
想要畫面在最上頭向下拉會更新資料;
畫面在最底部時向上拉會加載資料
各位大大 教教我~~~
感激不盡2013年8月27日 16:05
2个答案 按时间排序 按投票排序
-
一、ListView的表面理解就是List的格式显示的View,也就是要一行一行的显示,对ListView添加适配器这是必不可少的。所以我们修改以后的ListView当然也可以添加适配器等等常见的对ListView的操作;
二、要捕捉我们对列表界面的操作,下拉或者滚动到底部必须要有对事件的监听器,这里就尤为重要了,我们知道对按钮添加监听器,我们可以捕获到我们对某个按钮施加了操作,然后就执行我们的操作,这里的下拉或者滚动到底部,我们当然也需要捕捉到这个动作,然后执行我们想要执行的动作;
三、原理我们知道啦,现在就剩下怎么去捕获这个动作,然后捕获动作后我们怎么添加入我们想要的操作。只要捕获到动作,你想干什么我不关心,因为需求各式各样;
四、在捕获到动作以后我们开始给用户点反应。。。。。
下边我们来看看这个Demo的代码
一、继承ListView
package com.example.pulldownview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
/**
* <p>一个可以监听ListView是否滚动到最顶部或最底部的自定义控件</p>
* 只能监听由触摸产生的,如果是ListView本身Flying导致的,则不能监听</br>
* 如果加以改进,可以实现监听scroll滚动的具体位置等
* @author solo ho</br> Email:kjsoloho@gmail.com
*/
public class ScrollOverListView extends ListView {
private static final String TAG = "ScrollOverListView";
private static final boolean DEBUG = false;
private int mLastY;
private int mTopPosition;
private int mBottomPosition;
public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public ScrollOverListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ScrollOverListView(Context context) {
super(context);
init();
}
private void init(){
mTopPosition = 0;
mBottomPosition = 0;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
if (DEBUG) Log.d(TAG, "onInterceptTouchEvent Action down");
mLastY = (int) ev.getRawY();
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final int y = (int) ev.getRawY();
boolean isHandled = false;
switch(action){
case MotionEvent.ACTION_DOWN:{
if (DEBUG) Log.d(TAG, "action down");
mLastY = y;
isHandled = mOnScrollOverListener.onMotionDown(ev);
if (isHandled) {
break;
}
break;
}
case MotionEvent.ACTION_MOVE:{
if (DEBUG) Log.d(TAG, "action move");
final int childCount = getChildCount();
if(childCount == 0) {
break;
}
final int itemCount = getAdapter().getCount() - mBottomPosition;
final int deltaY = y - mLastY;
if (DEBUG) Log.d(TAG, "lastY=" + mLastY +" y=" + y);
final int firstTop = getChildAt(0).getTop();
final int listPadding = getListPaddingTop();
final int lastBottom = getChildAt(childCount - 1).getBottom();
final int end = getHeight() - getPaddingBottom();
final int firstVisiblePosition = getFirstVisiblePosition();
isHandled = mOnScrollOverListener.onMotionMove(ev, deltaY);
if(isHandled){
break;
}
//DLog.d("firstVisiblePosition=%d firstTop=%d listPaddingTop=%d deltaY=%d", firstVisiblePosition, firstTop, listPadding, deltaY);
if (firstVisiblePosition <= mTopPosition && firstTop >= listPadding && deltaY > 0) {
if (DEBUG) Log.d(TAG, "action move pull down");
isHandled = mOnScrollOverListener.onListViewTopAndPullDown(ev, deltaY);
if(isHandled){
break;
}
}
// DLog.d("lastBottom=%d end=%d deltaY=%d", lastBottom, end, deltaY);
if (firstVisiblePosition + childCount >= itemCount && lastBottom <= end && deltaY < 0) {
if (DEBUG) Log.d(TAG, "action move pull up");
isHandled = mOnScrollOverListener.onListViewBottomAndPullUp(ev, deltaY);
if(isHandled){
break;
}
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:{
if (DEBUG) Log.d(TAG, "action move pull up");
isHandled = mOnScrollOverListener.onMotionUp(ev);
if (isHandled) {
break;
}
break;
}
}
mLastY = y;
if (isHandled) {
return true;
}
return super.onTouchEvent(ev);
}
/**空的*/
private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener(){
@Override
public boolean onListViewTopAndPullDown(MotionEvent event, int delta) {
return false;
}
@Override
public boolean onListViewBottomAndPullUp(MotionEvent event, int delta) {
return false;
}
@Override
public boolean onMotionDown(MotionEvent ev) {
return false;
}
@Override
public boolean onMotionMove(MotionEvent ev, int delta) {
return false;
}
@Override
public boolean onMotionUp(MotionEvent ev) {
return false;
}
};
// =============================== public method ===============================
/**
* 可以自定义其中一个条目为头部,头部触发的事件将以这个为准,默认为第一个
*
* @param index 正数第几个,必须在条目数范围之内
*/
public void setTopPosition(int index){
if(index < 0)
throw new IllegalArgumentException("Top position must > 0");
mTopPosition = index;
}
/**
* 可以自定义其中一个条目为尾部,尾部触发的事件将以这个为准,默认为最后一个
*
* @param index 倒数第几个,必须在条目数范围之内
*/
public void setBottomPosition(int index){
if(index < 0)
throw new IllegalArgumentException("Bottom position must > 0");
mBottomPosition = index;
}
/**
* 设置这个Listener可以监听是否到达顶端,或者是否到达低端等事件</br>
*
* @see OnScrollOverListener
*/
public void setOnScrollOverListener(OnScrollOverListener onScrollOverListener){
mOnScrollOverListener = onScrollOverListener;
}
/**
* 滚动监听接口</br>
* @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener)
*
* @author solo ho</br> Email:kjsoloho@gmail.com
*/
public interface OnScrollOverListener {
/**
* 到达最顶部触发
*
* @param delta 手指点击移动产生的偏移量
* @return
*/
boolean onListViewTopAndPullDown(MotionEvent event, int delta);
/**
* 到达最底部触发
*
* @param delta 手指点击移动产生的偏移量
* @return
*/
boolean onListViewBottomAndPullUp(MotionEvent event, int delta);
/**
* 手指触摸按下触发,相当于{@link MotionEvent#ACTION_DOWN}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionDown(MotionEvent ev);
/**
* 手指触摸移动触发,相当于{@link MotionEvent#ACTION_MOVE}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionMove(MotionEvent ev, int delta);
/**
* 手指触摸后提起触发,相当于{@link MotionEvent#ACTION_UP}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionUp(MotionEvent ev);
}
}
二、下来刷新
package com.example.pulldownview;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.example.pulldownview.ScrollOverListView.OnScrollOverListener;
/**
* 下拉刷新控件</br>
* 真正实现下拉刷新的是这个控件,
* ScrollOverListView只是提供触摸的事件等
* @author Solo Email:kjsoloho@gmail.com
*/
public class PullDownView extends LinearLayout implements OnScrollOverListener, OnScrollListener {
private static final String TAG = "PullDownView";
private static final boolean DEBUG = false;
private static final int AUTO_INCREMENTAL = 10; // 自增量,用于回弹
private static final int WHAT_SET_HEADER_HEIGHT = 1;// Handler what 设置高度
private static SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm");
private View mHeaderView;
private LayoutParams mHeaderViewParams;
private TextView mHeaderViewDateView;
private TextView mHeaderTextView;
private ImageView mHeaderArrowView;
private View mHeaderLoadingView;
private View mFooterView;
private TextView mFooterTextView;
private View mFooterLoadingView;
private ScrollOverListView mListView;
private OnPullDownListener mOnPullDownListener;
private RotateAnimation mRotateOTo180Animation;
private RotateAnimation mRotate180To0Animation;
private Context mContext;
private Field overScrollModeField;
private int mMoveDeviation; // 移动误差
private int mHeaderIncremental; // 头部文件自增量增量
private int mDefaultHeaderViewHeight; // 头部文件原本的高度
private int mStartIndex; // 当前List可显示第一项
//private int mEndIndex; // 当前List可显示最后一项
private float mMotionDownLastY; // 按下时候的Y轴坐标
private boolean mEnablePullDown; // 是否可以下拉
private boolean mIsPullUpDone; // 是否回推完成
private boolean mEnableLoadMore; // 是否启用更多
private boolean mEnableAutoFetchMore; // 是否允许自动获取更多
private boolean mIsNoMoreData; // 没有更多的数据了
private boolean mIsDidLoad; // 是否加载了数据
// 头部文件的状态
private static final int HEADER_VIEW_STATE_IDLE = 0; // 空闲
private static final int HEADER_VIEW_STATE_NOT_OVER_HEIGHT = 1; // 没有超过默认高度
private static final int HEADER_VIEW_STATE_OVER_HEIGHT = 2; // 超过默认高度
private int mHeaderViewState = HEADER_VIEW_STATE_IDLE;
private static final int STATE_NONE = 0;
private static final int STATE_REFRESHING = 1; // 刷新中
private static final int STATE_LOADING_MORE = 2; // 加载更多中
private static final int STATE_DRAGING = 4; // 拖动中
private static final int STATE_MOTION_DOWN = 8; // 按下
private int state = STATE_NONE;
public PullDownView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeaderViewAndFooterViewAndListView(context);
}
public PullDownView(Context context) {
super(context);
initHeaderViewAndFooterViewAndListView(context);
}
/*
* ==================================
* Public method
* 外部使用,具体就是用这几个就可以了
*
* ==================================
*/
/**
* 刷新事件接口
* @author Solo Email:kjsoloho@gmail.com
*/
public interface OnPullDownListener {
void onRefresh();
void onLoadMore();
}
/**
* 通知加载完了数据,要放在Adapter.notifyDataSetChanged后面
* 当你加载完数据的时候,调用这个notifyDidLoad()
* 才会隐藏头部,并初始化数据等
*/
public void notifyDidDataLoad(boolean isNoMoreData) {
mIsDidLoad = true;
mIsNoMoreData = isNoMoreData;
mFooterView.setVisibility(View.VISIBLE);
updateFooter();
mListView.setFooterDividersEnabled(true);
mHeaderViewParams.height = 0;
mHeaderView.setLayoutParams(mHeaderViewParams);
updateHeader();
doListViewIdleActionOnDataDidLoad();
}
/**
* 通知已经刷新完了,要放在Adapter.notifyDataSetChanged后面
* 当你执行完刷新任务之后,调用这个notifyDidRefresh()
* 才会隐藏掉头部文件等操作
*/
public void notifyDidRefresh(boolean isNoMoreData) {
mIsNoMoreData = isNoMoreData;
updateFooter();
state &= ~STATE_REFRESHING;
mHeaderViewState = HEADER_VIEW_STATE_IDLE;
setHeaderHeight(0);
updateHeader();
doListViewIdleActionOnDataDidLoad();
}
/**
* 通知已经获取完更多了,要放在Adapter.notifyDataSetChanged后面
* 当你执行完更多任务之后,调用这个notyfyDidMore()
* 才会隐藏加载圈等操作
*/
public void notifyDidLoadMore(boolean isNoMoreData) {
mIsNoMoreData = isNoMoreData;
state &= ~STATE_LOADING_MORE;
updateFooter();
}
/**
* 设置监听器
* @param listener
*/
public void setOnPullDownListener(OnPullDownListener listener){
mOnPullDownListener = listener;
}
/**
* 获取内嵌的listview
* @return ScrollOverListView
*/
public ScrollOverListView getListView(){
return mListView;
}
/**
* 是否开启自动获取更多</br>
* 自动获取更多,并在触摸到达底部的时候自动刷新
* @param index 倒数第几个触发
*/
public void enableAutoFetchMore(boolean enable, int index){
if(!mEnableLoadMore) return;
mEnableAutoFetchMore = enable;
if(enable){
mListView.setBottomPosition(index);
}else{
updateFooter();
}
}
/**
* 是否启用加载更多</br>
* 只能在初始化的时候调用
*/
public void enableLoadMore(boolean enable) {
mEnableLoadMore = enable;
if (!enable) {
// TODO 暂时使用这种错开的方法
// 更好的办法是写到属性里面
mUIHandler.post(new Runnable() {
@Override
public void run() {
removeFooter();
}
});
}
}
/**
* 是否启用下拉刷新
*/
public void enablePullDown(boolean enable) {
mEnablePullDown = enable;
}
/*
* ==================================
* Private method
* 具体实现下拉刷新等操作
*
* ==================================
*/
/**
* 初始化界面
*/
private void initHeaderViewAndFooterViewAndListView(Context context){
setOrientation(LinearLayout.VERTICAL);
mContext = context;
/*
* 自定义头部文件
* 放在这里是因为考虑到很多界面都需要使用
* 如果要修改,和它相关的设置都要更改
*/
mEnablePullDown = true;
mHeaderView = LayoutInflater.from(context).inflate(R.layout.pulldown_header, null);
mHeaderViewParams = new LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
mHeaderIncremental = mDefaultHeaderViewHeight;
addView(mHeaderView, 0, mHeaderViewParams);
mDefaultHeaderViewHeight = getResources().getDimensionPixelSize(R.dimen.pulldown_headerview_height);
mMoveDeviation = getResources().getDimensionPixelSize(R.dimen.pulldown_move_deviation);
mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_text);
mHeaderArrowView = (ImageView) mHeaderView.findViewById(R.id.pulldown_header_arrow);
mHeaderViewDateView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_date);
mHeaderLoadingView = mHeaderView.findViewById(R.id.pulldown_header_loading);
// 注意,图片旋转之后,再执行旋转,坐标会重新开始计算
mRotateOTo180Animation = new RotateAnimation(0, 180,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotateOTo180Animation.setDuration(250);
mRotateOTo180Animation.setFillAfter(true);
mRotate180To0Animation = new RotateAnimation(180, 0,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotate180To0Animation.setDuration(250);
mRotate180To0Animation.setFillAfter(true);
/*
* 自定义脚部文件
*/
mEnableLoadMore = true;
mFooterView = LayoutInflater.from(mContext).inflate(R.layout.pulldown_footer, null);
mFooterView.setVisibility(View.GONE);
mFooterTextView = (TextView) mFooterView.findViewById(R.id.pulldown_footer_text);
mFooterLoadingView = mFooterView.findViewById(R.id.pulldown_footer_loading);
mFooterView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mIsNoMoreData || !mIsDidLoad || (state & STATE_LOADING_MORE) == STATE_LOADING_MORE) return;
ScrollOverListView listView = mListView;
if (listView.getCount() - listView.getHeaderViewsCount() - listView.getFooterViewsCount() > 0) {
state |= STATE_LOADING_MORE;
updateFooter();
mOnPullDownListener.onLoadMore();
}
}
});
/*
* ScrollOverListView 同样是考虑到基本都要使用,所以放在这里
* 同时因为,需要它的监听事件
*/
mListView = new ScrollOverListView(context);
mListView.setFooterDividersEnabled(false);
mListView.setId(android.R.id.list);
mListView.addFooterView(mFooterView);
mListView.setOnScrollOverListener(this);
mListView.setOnScrollListener(this);
// 因为2.3之后的某些ListView控件自己实现了pull阴影动画效果
// 所以我们在这里屏蔽他们
try {
Method method = AbsListView.class.getDeclaredMethod("setOverScrollMode", int.class);
method.setAccessible(true);
method.invoke(mListView, 2);//View.OVER_SCROLL_NEVER
} catch (Exception e) {
e.printStackTrace();
}
addView(mListView, android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.FILL_PARENT);
// set ListView animation
/*
AnimationSet set = new AnimationSet(true);
Animation animation = new AlphaAnimation(0.0f, 1.0f);
animation.setDuration(50);
set.addAnimation(animation);
animation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0.0f,Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, -1.0f,Animation.RELATIVE_TO_SELF, 0.0f
);
animation.setDuration(100);
set.addAnimation(animation);
LayoutAnimationController controller = new LayoutAnimationController(set, 0.5f);
mListView.setLayoutAnimation(controller);
*/
// 空的listener
mOnPullDownListener = new OnPullDownListener() {
@Override
public void onRefresh() {}
@Override
public void onLoadMore() {}
};
}
/**
* 去掉底部文件
*/
private void removeFooter(){
if(mListView.getFooterViewsCount() > 0 && mListView != null && mFooterView != null){
mListView.removeFooterView(mFooterView);
}
}
/**
* 更新头部文件</br>
* 如果刷新中显示Loading,
* 如果拖动中,显示拖动效果,
* 否则显示更新时间。
*/
private void updateHeader() {
if ((state & STATE_REFRESHING) == STATE_REFRESHING) {
mHeaderArrowView.clearAnimation();
mHeaderArrowView.setVisibility(View.GONE);
mHeaderLoadingView.setVisibility(View.VISIBLE);
mHeaderTextView.setText("正在刷新中...");
} else if ((state & STATE_DRAGING) == STATE_DRAGING) {
if(mHeaderViewParams.height >= mDefaultHeaderViewHeight){
if(mHeaderViewState == HEADER_VIEW_STATE_OVER_HEIGHT) return;
mHeaderArrowView.setVisibility(View.VISIBLE);
mHeaderLoadingView.setVisibility(View.GONE);
mHeaderViewDateView.setVisibility(View.VISIBLE);
mHeaderViewState = HEADER_VIEW_STATE_OVER_HEIGHT;
mHeaderTextView.setText("松开可以刷新");
mHeaderArrowView.startAnimation(mRotateOTo180Animation);
}else{
if(mHeaderViewState == HEADER_VIEW_STATE_NOT_OVER_HEIGHT
|| mHeaderViewState == HEADER_VIEW_STATE_IDLE) return;
mHeaderArrowView.setVisibility(View.VISIBLE);
mHeaderLoadingView.setVisibility(View.GONE);
mHeaderViewDateView.setVisibility(View.VISIBLE);
mHeaderViewState = HEADER_VIEW_STATE_NOT_OVER_HEIGHT;
mHeaderTextView.setText("下拉可以刷新");
mHeaderArrowView.startAnimation(mRotate180To0Animation);
}
} else {
mHeaderLoadingView.setVisibility(View.GONE);
mHeaderViewDateView.setVisibility(View.VISIBLE);
mHeaderArrowView.setVisibility(View.VISIBLE);
mHeaderTextView.setText("下拉可以刷新");
mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));
}
}
/**
* 更新脚部文件</br>
* 正常显示"更多",
* 如果没有更多的数据就显示"已加载完全部",
* 如果加载中就显示"加载中..."。
*/
private void updateFooter() {
if (!mEnableLoadMore) return;
if (mIsNoMoreData) {
mFooterTextView.setText("已加载完全部");
mFooterLoadingView.setVisibility(View.GONE);
} else if ((state & STATE_LOADING_MORE) == STATE_LOADING_MORE) {
mFooterTextView.setText("加载更多中...");
mFooterLoadingView.setVisibility(View.VISIBLE);
} else {
mFooterTextView.setText("更多");
mFooterLoadingView.setVisibility(View.GONE);
}
}
private void setHeaderHeight(final int height){
mHeaderIncremental = height;
mHeaderViewParams.height = height;
mHeaderView.setLayoutParams(mHeaderViewParams);
}
/**
* 自动隐藏动画
*/
class HideHeaderViewTask extends TimerTask{
@Override
public void run() {
if((state & STATE_MOTION_DOWN) == STATE_MOTION_DOWN) {
cancel();
return;
}
mHeaderIncremental -= AUTO_INCREMENTAL;
if(mHeaderIncremental > 0){
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
}else{
mHeaderIncremental = 0;
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
cancel();
}
}
}
/**
* 自动显示动画
*/
class ShowHeaderViewTask extends TimerTask{
@Override
public void run() {
if((state & STATE_MOTION_DOWN) == STATE_MOTION_DOWN) {
cancel();
return;
}
mHeaderIncremental -= AUTO_INCREMENTAL;
if(mHeaderIncremental > mDefaultHeaderViewHeight){
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
}else{
mHeaderIncremental = mDefaultHeaderViewHeight;
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
if (mIsDidLoad && (state & STATE_REFRESHING) != STATE_REFRESHING) {
state |= STATE_REFRESHING;
mUIHandler.post(new Runnable() {
@Override
public void run() {
// 要清除掉动画,否则无法隐藏
updateHeader();
mHeaderArrowView.clearAnimation();
mHeaderArrowView.setVisibility(View.INVISIBLE);
mHeaderLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onRefresh();
}
});
}
cancel();
}
}
}
private Handler mUIHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case WHAT_SET_HEADER_HEIGHT :{
setHeaderHeight(mHeaderIncremental);
return;
}
}
}
};
/**
* 当刷新完数据时,手动调用onIdle事件
*/
private void doListViewIdleActionOnDataDidLoad(){
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if(mOnListViewIdleListener != null){
// 不需要考虑FooterView
final int firstVisiblePosition = mListView.getFirstVisiblePosition();
final int childCount = mListView.getChildCount();
//Log.d(TAG, "[doListViewIdleActionOnDataDidLoad] firstVisiblePosition:" + firstVisiblePosition + " childCount:" + childCount);
mOnListViewIdleListener.onIdle(firstVisiblePosition, childCount);
}
}
}, 0);
}
/**
* 条目是否填满整个屏幕
*/
private boolean isFillScreenItem(){
final int firstVisiblePosition = mListView.getFirstVisiblePosition();
final int lastVisiblePosition = mListView.getLastVisiblePosition() - mListView.getFooterViewsCount();
final int visibleItemCount = lastVisiblePosition - firstVisiblePosition + 1;
final int totalItemCount = mListView.getCount() - mListView.getFooterViewsCount();
if(visibleItemCount < totalItemCount) return true;
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
state |= STATE_MOTION_DOWN;
mIsPullUpDone = false;
mMotionDownLastY = ev.getRawY();
Log.d(TAG, "pulldownview.onIntercept:" + mMotionDownLastY);
}
return super.onInterceptTouchEvent(ev);
}
/*
* ==================================
* 实现 OnScrollOverListener接口
*
*
* ==================================
*/
@Override
public boolean onListViewTopAndPullDown(MotionEvent event, int delta) {
// 因为某些ListView控件自己已经添加了下拉效果
// 所以我们必须返回true,控制ListView不会滚动,达到屏蔽的效果
if ((state & STATE_REFRESHING) == STATE_REFRESHING || !mEnablePullDown || !mIsDidLoad) {
return true;
}
if (mListView.getCount() - mListView.getFooterViewsCount() == 0) {
return true;
}
// 如果开始按下到滑动距离不超过误差值,则不滑动
if(mHeaderViewParams.height <= 0){
final int absMotionY = (int) Math.abs(event.getRawY() - mMotionDownLastY);
if(absMotionY < mMoveDeviation) {
return true;
}
}
int absDelta = Math.abs(delta);
final int i = (int) Math.ceil((double)absDelta / 2);
mHeaderIncremental += i;
if(mHeaderIncremental >= 0){ // && mIncremental <= mMaxHeight
setHeaderHeight(mHeaderIncremental);
updateHeader();
}
return true;
}
@Override
public boolean onListViewBottomAndPullUp(MotionEvent event, int delta) {
if((state & STATE_LOADING_MORE) == STATE_LOADING_MORE
|| !mIsDidLoad || !mEnableAutoFetchMore || mIsNoMoreData) return false;
ScrollOverListView listView = mListView;
if (listView.getCount() - listView.getHeaderViewsCount() - listView.getFooterViewsCount() > 0) {
state |= STATE_LOADING_MORE;
updateFooter();
mOnPullDownListener.onLoadMore();
}
return true;
}
@Override
public boolean onMotionDown(MotionEvent ev) {
return false;
}
@Override
public boolean onMotionMove(MotionEvent ev, int delta) {
state |= STATE_DRAGING;
//当头部文件回推消失的时候,不允许滚动
if(mIsPullUpDone) return true;
// onTopDown在顶部,并向上回推和onTopUp相对
if(mHeaderViewParams.height > 0 && delta < 0){
final int absDelta = Math.abs(delta);
final int i = (int) Math.ceil((double)absDelta / 2);
mHeaderIncremental -= i;
if(mHeaderIncremental > 0){
setHeaderHeight(mHeaderIncremental);
updateHeader();
}else{
mHeaderViewState = HEADER_VIEW_STATE_IDLE;
mHeaderIncremental = 0;
setHeaderHeight(mHeaderIncremental);
mIsPullUpDone = true;
}
return true;
}
return false;
}
@Override
public boolean onMotionUp(MotionEvent ev) {
state &= ~STATE_DRAGING;
state &= ~STATE_MOTION_DOWN;
// 避免和点击事件冲突
if(mHeaderViewParams.height > 0 || mIsPullUpDone){
// 判断头文件拉动的距离与设定的高度,小了就隐藏,多了就固定高度
int x = mHeaderIncremental - mDefaultHeaderViewHeight;
Timer timer = new Timer(true);
if(x < 0){
timer.scheduleAtFixedRate(new HideHeaderViewTask(), 0, 10);
}else{
timer.scheduleAtFixedRate(new ShowHeaderViewTask(), 0, 10);
}
return true;
}
return false;//TODO
}
/*
* =====================================
* 监听ListView停止事件
* =====================================
*/
private OnListViewIdleListener mOnListViewIdleListener;
public interface OnListViewIdleListener {
void onIdle(int startIndex, int count);
}
public void setOnListViewIdleListener(OnListViewIdleListener listener){
if(mListView != null){
mOnListViewIdleListener = listener;
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(scrollState == SCROLL_STATE_IDLE){
if (DEBUG) Log.d(TAG, "IDLE");
if(mOnListViewIdleListener != null){
int count = mListView.getChildCount();
final int childEndIndex = mStartIndex + mListView.getChildCount() -1;
final int listEndIndex = mListView.getCount() -1;
if(childEndIndex == listEndIndex){
count -= mListView.getFooterViewsCount();
}
mOnListViewIdleListener.onIdle(mStartIndex, count);
}
}
switch (scrollState) {
case SCROLL_STATE_FLING:
if (DEBUG) Log.d(TAG, "FLING");
break;
case SCROLL_STATE_TOUCH_SCROLL:
if (DEBUG) Log.d(TAG, "SCROLL");
default:
break;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (DEBUG) Log.d(TAG, "onScroll");
mStartIndex = firstVisibleItem;
// 因为三星I91002.3版本如果设置over_scroll_never会出现回弹问题,所以在这里处理一下
// 更新12-9-9 2:08:晕死,测试三星2.3.3版本如果反射使用这些属性会报错。
/*
final ScrollOverListView localListView = this.mListView;
final boolean hasItem = localListView.getCount() > 0;
try {
if (overScrollModeField == null) return;
final Integer mode = (Integer) overScrollModeField.get(localListView);
if (firstVisibleItem <= 0 && hasItem) {
if (mode != View.OVER_SCROLL_NEVER) {
if (DEBUG) Log.w(TAG, "set over scroll never");
overScrollModeField.set(localListView, View.OVER_SCROLL_NEVER);
}
} else if (firstVisibleItem + visibleItemCount >= totalItemCount && hasItem) {
if (mode != View.OVER_SCROLL_ALWAYS) {
if (DEBUG) Log.w(TAG, "set over scroll always");
overScrollModeField.set(localListView, View.OVER_SCROLL_ALWAYS);
}
}
} catch (Exception e) {
e.printStackTrace();
}
*/
}
}
三、Demo界面
package com.example.pulldownview;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.example.pulldownview.PullDownView.OnPullDownListener;
public class MainActivity extends Activity {
private PullDownView pullDownView;
private ScrollOverListView listView;
private MyAdapter adapter;
private List<String> arrays;
private LayoutInflater inflater;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inflater = getLayoutInflater();
pullDownView = (PullDownView) findViewById(R.id.pulldownview);
pullDownView.enableAutoFetchMore(true, 0);
listView = pullDownView.getListView();
adapter = new MyAdapter();
listView.setAdapter(adapter);
initArrays(new Handler(){
@Override
public void handleMessage(Message msg) {
arrays = (List<String>) msg.obj;
adapter.notifyDataSetChanged();
pullDownView.notifyDidDataLoad(false);
}
});
pullDownView.setOnPullDownListener(new OnPullDownListener() {
@Override
public void onRefresh() {
getNewString(new Handler(){
@Override
public void handleMessage(Message msg) {
arrays.add(0, (String) msg.obj);
adapter.notifyDataSetChanged();
pullDownView.notifyDidRefresh(arrays.isEmpty());
}
});
}
@Override
public void onLoadMore() {
getNewString(new Handler(){
@Override
public void handleMessage(Message msg) {
arrays.add((String) msg.obj);
adapter.notifyDataSetChanged();
pullDownView.notifyDidLoadMore(arrays.isEmpty());
}
});
}
});
}
private void initArrays(final Handler handler) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (Exception e) {
Thread.interrupted();
e.printStackTrace();
}
List<String> as = new ArrayList<String>();
as.add("first");
as.add("second");
as.add("third");
as.add("four");
as.add("five");
as.add("first");
as.add("second");
as.add("third");
as.add("four");
as.add("five");
as.add("first");
as.add("second");
as.add("third");
as.add("four");
as.add("five");
as.add("first");
as.add("second");
as.add("third");
as.add("four");
as.add("five");
as.add("first");
as.add("second");
as.add("third");
as.add("four");
as.add("five");
handler.obtainMessage(0, as).sendToTarget();
}
}).start();
}
private void getNewString(final Handler handler) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (Exception e) {
Thread.interrupted();
e.printStackTrace();
}
handler.obtainMessage(0, "New Text " + System.currentTimeMillis()).sendToTarget();
}
}).start();
}
private class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return arrays == null ? 0 : arrays.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.item_list, null);
holder.textView = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(arrays.get(position));
return convertView;
}
}
private static class ViewHolder {
TextView textView;
}
}2013年8月27日 23:40
-
listView.setOnScrollListener(new ScrollListener()); private final class ScrollListener implements OnScrollListener{ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { final int loadtotal = totalItemCount; //获取当前屏幕最后Item的ID int lastItemid = listView.getLastVisiblePosition(); //达到数据的最后一条记录 if((lastItemid+1) == totalItemCount){ //TODO 更新 } } public void onScrollStateChanged(AbsListView view, int scrollState) { } } }
2013年8月27日 21:18
相关推荐
ListView使用simpleAdapter填充实现,数据结构是HashMap,对应的我的博客地址是: http://blog.csdn.net/u012320459/article/details/47667869
本篇文章将深入探讨ListView和SimpleAdapter的基本使用以及它们在实际应用中的结合。 ### 1. ListView简介 ListView是Android SDK提供的一种可以显示多行、多列数据的视图控件,适用于数据列表的展示。它的主要特点...
Android中尝试气泡短信编程初探实例 ListView使用SimpleAdapter适配器详解 具体参考小魏博客:http://blog.csdn.net/xiaowei_cqu/article/details/7045497
在这个场景中,我们探讨了如何使用`SimpleAdapter`和自定义`Adapter`来填充ListView,并且在自定义Adapter中添加了删除功能。下面我们将详细讲解这两个方法。 首先,`SimpleAdapter`是Android SDK提供的一种简单...
总结一下,本实例展示了如何使用ListView和SimpleAdapter创建一个基本的导航功能。通过构建数据源、定义列表项布局、创建适配器以及监听点击事件,我们可以实现一个简洁易用的列表导航界面。这个例子对于初学者理解...
这个实例是关于如何使用SimpleAdapter来自定义ListView的列显示。SimpleAdapter是Android提供的一种便捷方式,它可以帮助我们快速将数据绑定到ListView上,而无需编写复杂的Adapter类。 在Android中,ListView通常...
在本教程中,我们将深入探讨如何使用SimpleAdapter创建ListView,并理解其工作原理。 首先,让我们了解ListView的基本结构。ListView由一系列可滚动的项目(ListView项)组成,每个项目通常代表数据集中的一个条目...
ListView 适配器 SimpleAdapter 和 SimpleCursorAdapter 细节总结 ...SimpleAdapter 和 SimpleCursorAdapter 是两个常用的 ListView 适配器,它们都可以用来绑定数据到 ListView 中,但它们有所不同的使用场景和特点。
Listview+SimpleAdapter+网页跳转具体实现步骤,将对应代码复制至对应布局文件和功能文件即可使用
使用`SimpleAdapter`时,需要提前创建一个XML布局文件来定义ListView项的视图结构。每个视图(TextView、ImageView等)可以通过`android:id`属性来设置映射到`to`数组中的相应位置。 5. **创建并设置SimpleAdapter...
本教程将深入探讨如何使用SimpleAdapter和自定义Adapter与ListView协同工作,以实现数据的动态展示。以下是对相关知识点的详细说明: 1. **ListView**: ListView是Android中的一个视图容器,可以显示一系列可滚动的...
本教程将深入探讨如何使用SimpleAdapter来实现ListView的进阶功能。 首先,我们需要理解SimpleAdapter的基本工作原理。SimpleAdapter主要负责把数据转换成ListView中可显示的视图(View)。它接受五个参数:上下文...
本篇文章将深入讲解如何使用`SimpleAdapter`来创建ListView。 首先,我们需要了解`SimpleAdapter`的基本用法。`SimpleAdapter`接受五个参数: 1. `Context`: 当前上下文环境,通常为Activity或Fragment。 2. `List...
虽然`SimpleAdapter`简单易用,但默认情况下,ListView的性能可能不如使用`BaseAdapter`或`CursorAdapter`高,因为它们支持视图复用。在处理大量数据时,考虑使用`ViewHolder`模式和自定义适配器以提升性能。 总之...
本篇文章将详细讲解如何在Android应用中使用ListView和SimpleAdapter来构建一个通用的数据展示功能。 首先,理解ListView的核心概念。ListView通过适配器(Adapter)与数据源进行绑定,适配器的作用是将数据转化为...
本教程将深入讲解如何使用SimpleAdapter来适配ListView,并通过提供的"Android ListView列表之SimpleAdapter适配Demo源码"进行示例解析。 首先,了解SimpleAdapter的基本概念。SimpleAdapter是Adapter的一个子类,...
本教程将深入探讨如何使用ListView结合SimpleAdapter来构建一个简单的列表布局。 首先,`SimpleAdapter`是Android SDK提供的一种便捷的适配器,它将数据与视图绑定,方便地将数据集(通常是数组或列表)转换为...
本范例将详细介绍如何使用`SimpleAdapter`来实现ListView,并通过注解帮助理解每个步骤。 首先,`SimpleAdapter`适用于数据结构相对简单的场景,比如数据只包含几个字符串或基本类型。它通过映射数据到预定义的布局...
在本教程中,我们将深入探讨如何使用Java的JDBC(Java Database Connectivity)接口来从MySQL数据库中读取数据,并将这些数据动态地显示在Android应用的ListView组件上,同时利用SimpleAdapter进行数据绑定。...