listview上下拉组件
在网上找了好多这类组件总是用得不太顺手,数据一加载多了就卡得不行,现在自己做了一个,代码基本上是最优化的,方便大家使用
先看看效果吧:
下面是所有代码:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.iaiai.activity" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name=".ListviewTestActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
<?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="fill_parent" android:orientation="vertical" > <com.iaiai.activity.DragListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minHeight="?android:attr/listPreferredItemHeight" android:orientation="horizontal" > <TextView android:id="@+id/title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="15sp" /></LinearLayout>
package com.iaiai.activity;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;/** * * <br/> * Title: ListviewTestActivity.java<br/> * E-Mail: 176291935@qq.com<br/> * QQ: 176291935<br/> * Http: iaiai.iteye.com<br/> * Create time: 2013-8-6 下午4:50:30<br/> * <br/> * @author 丸子 * @version 0.0.1 */public class ListviewTestActivity extends Activity {private DragListView listView;private List<String> titles = new ArrayList<String>();@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);listView = (DragListView) findViewById(R.id.listView);for(int i=0;i<30;i++){titles.add("标题:"+i);}MyAdapter adapter = new MyAdapter();listView.setAdapter(adapter);listView.setHandler(new DragListView.Handler() {@Overridepublic void upRefresh() {//listView.closeHeader();}@Overridepublic void downRefresh() {//listView.closeFooter();}});}public class MyAdapter extends BaseAdapter{@Overridepublic int getCount() {return titles.size();}@Overridepublic String getItem(int position) {return titles.get(position);}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {if (convertView == null) {convertView = LayoutInflater.from(ListviewTestActivity.this).inflate(R.layout.list_item, null);}TextView textView = (TextView) convertView.findViewById(R.id.title);textView.setText(titles.get(position));return convertView;}}}
package com.iaiai.activity;import java.text.SimpleDateFormat;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;/** * * <br/> * Title: DragListView.java<br/> * E-Mail: 176291935@qq.com<br/> * QQ: 176291935<br/> * Http: iaiai.iteye.com<br/> * Create time: 2013-8-5 下午3:06:25<br/> * <br/> * * @author 丸子 * @version 0.0.1 */public class DragListView extends ListView implements OnScrollListener, OnClickListener {public interface Handler {/** * 手指从上往下拉刷新之后会调用此方法 */void upRefresh();/** * 手指从下往上拉刷新之后会调用此方法 */void downRefresh();}public DragListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}public DragListView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public DragListView(Context context) {super(context);init(context);}/*** * 作用:测量 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);}private void init(Context context) {this.context = context;headerLayout = getHeaderLayout();addHeaderView(headerLayout, null, false);mHeadViewHeight = headerLayout.getMeasuredHeight();headerLayout.setPadding(0, -1 * mHeadViewHeight, 0, 0);footerLayout = getFooterLayout();addFooterView(footerLayout, null, false);setOnScrollListener(this);// ListView滚动监听}private LinearLayout getHeaderLayout() {LinearLayout headerLayout = new LinearLayout(context);headerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 0));headerLayout.setBackgroundResource(android.R.color.white);headerLayout.setGravity(Gravity.CENTER);headerLayout.setOrientation(LinearLayout.VERTICAL);LinearLayout layout = new LinearLayout(context);layout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));layout.setOrientation(LinearLayout.HORIZONTAL);layout.setPadding(0, dipTopx(10), 0, 0);headerProgressBar = new ProgressBar(context);headerProgressBar.setLayoutParams(new LayoutParams(dipTopx(20), dipTopx(20)) );headerProgressBar.setProgressDrawable(getResources().getDrawable(android.R.drawable.progress_horizontal));headerProgressBar.setVisibility(View.GONE);layout.addView(headerProgressBar);headerTextView = new TextView(context);headerTextView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));headerTextView.setText("下拉手指刷新");layout.addView(headerTextView);headerLayout.addView(layout);headerTextView_date = new TextView(context);headerTextView_date.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));headerTextView_date.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));headerTextView_date.setPadding(0, 0, 0, dipTopx(10));headerLayout.addView(headerTextView_date);measureView(headerLayout);return headerLayout;}private LinearLayout getFooterLayout() {LinearLayout footerLayout = new LinearLayout(context);footerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 0));footerLayout.setBackgroundResource(android.R.color.white);footerLayout.setOrientation(LinearLayout.VERTICAL);LinearLayout lineLayout = new LinearLayout(context);lineLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, dipTopx(1)));lineLayout.setBackgroundColor(this.getCacheColorHint());footerLayout.addView(lineLayout);LinearLayout layout = new LinearLayout(context);layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));layout.setPadding(0, dipTopx(10), 0, dipTopx(10));layout.setGravity(Gravity.CENTER);layout.setOrientation(LinearLayout.HORIZONTAL);footerProgressBar = new ProgressBar(context);footerProgressBar.setLayoutParams(new LayoutParams(dipTopx(20), dipTopx(20)));footerProgressBar.setProgressDrawable(getResources().getDrawable(android.R.drawable.progress_horizontal));footerProgressBar.setVisibility(View.GONE);layout.addView(footerProgressBar);footerTextView = new TextView(context);footerTextView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));footerTextView.setText("加载更多");layout.addView(footerTextView);footerLayout.addView(layout);measureView(footerLayout);footerLayout.setOnClickListener(this);return footerLayout;}// 拖拉ListView枚举所有状态private enum DListViewState {LV_NORMAL, // 普通状态LV_PULL_REFRESH, // 下拉状态(为超过mHeadViewHeight)LV_RELEASE_REFRESH, // 松开可刷新状态(超过mHeadViewHeight)LV_LOADING, // 加载状态LV_OVER; // 结束状态}public void setHandler(Handler handler) {this.handler = handler;}private Context context;private Handler handler;private LinearLayout headerLayout;private LinearLayout footerLayout;private ProgressBar headerProgressBar,footerProgressBar;private TextView headerTextView, headerTextView_date,footerTextView;private boolean isScroller = true;// 是否屏蔽ListView滑动private boolean mFirstItemIndex = false;// 当前视图能看到的第一个项的索引private boolean mIsRecord = false;// 用于保证startY的值在一个完整的touch事件中只被记录一次private int mStartY, mMoveY;// 按下是的y坐标,move时的y坐标private DListViewState mlistViewState = DListViewState.LV_NORMAL;// 拖拉状态.(自定义枚举)private DListViewState mfootViewState = DListViewState.LV_NORMAL;// 拖拉状态.(自定义枚举)private final static int RATIO = 2;// 手势下拉距离比.private int mHeadViewHeight;// headView的高(mHeadView)@Overridepublic void onClick(View v) {if(!getAdapter().isEmpty()){if(mfootViewState==DListViewState.LV_NORMAL){footerTextView.setText("正在刷新");mfootViewState = DListViewState.LV_LOADING;footerProgressBar.setVisibility(View.VISIBLE);handler.downRefresh();}}}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}/** * 关闭头 */public void closeHeader() {mlistViewState = DListViewState.LV_NORMAL;headerProgressBar.setVisibility(View.GONE);headerLayout.setPadding(0, -1 * mHeadViewHeight, 0, 0);setSelection(0);}/** * 关闭底 */public void closeFooter() {setSelection(getCount());footerTextView.setText("加载更多");footerProgressBar.setVisibility(View.GONE);mfootViewState = DListViewState.LV_NORMAL;}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {if (firstVisibleItem <= 0) {mFirstItemIndex = true;} else {mFirstItemIndex = false;}}@Overridepublic boolean onTouchEvent(MotionEvent ev) {switch (ev.getAction()) {// 按下case MotionEvent.ACTION_DOWN:doActionDown(ev);break;// 移动case MotionEvent.ACTION_MOVE:doActionMove(ev);break;// 抬起case MotionEvent.ACTION_UP:doActionUp(ev);break;default:break;}/*** * 如果是ListView本身的拉动,那么返回true,这样ListView不可以拖动. * 如果不是ListView的拉动,那么调用父类方法,这样就可以上拉执行. */if (isScroller) {return super.onTouchEvent(ev);} else {return true;}}/*** * 摁下操作 * * 作用:获取摁下是的y坐标 * * @param event */void doActionDown(MotionEvent event) {if(getAdapter().isEmpty()){return;}mStartY = (int) event.getY();if (mIsRecord == false && mFirstItemIndex) {mIsRecord = true;}}/*** * 拖拽移动操作 * * @param event */void doActionMove(MotionEvent event) {if(getAdapter().isEmpty()){return;}mMoveY = (int) event.getY();// 获取实时滑动y坐标// 检测是否是一次touch事件.if (mIsRecord == false && mFirstItemIndex) {mStartY = (int) event.getY();mIsRecord = true;}Log.i("********", "*****" + mIsRecord);// 向下啦headview移动距离为y移动的一半.(比较友好)int offset = (mMoveY - mStartY) / RATIO;/*** * 如果touch关闭或者正处于Loading状态的话 return. */if (mIsRecord == false || mlistViewState == DListViewState.LV_LOADING) {return;}switch (mlistViewState) {// 普通状态case LV_NORMAL: {Log.i("********", "******普通状态");// 如果<0,则意味着上滑动.if (offset > 0) {// 设置headView的padding属性.headerLayout.setPadding(0, offset - mHeadViewHeight, 0, 0);mlistViewState = DListViewState.LV_PULL_REFRESH;// 下拉状态}}break;// 下拉状态case LV_PULL_REFRESH: {setSelection(0);// 时时保持在顶部.headerTextView.setText("下拉手指刷新");headerTextView_date.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));// 设置headView的padding属性.headerLayout.setPadding(0, offset - mHeadViewHeight, 0, 0);if (offset < 0) {/*** * 要明白为什么isScroller = false; */isScroller = false;mlistViewState = DListViewState.LV_NORMAL;// 普通状态} else if (offset > mHeadViewHeight) {// 如果下拉的offset超过headView的高度则要执行刷新.mlistViewState = DListViewState.LV_RELEASE_REFRESH;// 更新为可刷新的下拉状态.}}break;// 可刷新状态case LV_RELEASE_REFRESH: {Log.i("********", "******可刷新状态");setSelection(0);// 时时保持在顶部headerTextView.setText("松开手指刷新");// 设置headView的padding属性.headerLayout.setPadding(0, offset - mHeadViewHeight, 0, 0);// 下拉offset>0,但是没有超过headView的高度.那么要goback 原装.if (offset >= 0 && offset <= mHeadViewHeight) {mlistViewState = DListViewState.LV_PULL_REFRESH;} else if (offset < 0) {mlistViewState = DListViewState.LV_NORMAL;} else {}}break;default:return;}}/*** * 手势抬起操作 * * @param event */public void doActionUp(MotionEvent event) {if(getAdapter().isEmpty()){return;}if((((int)event.getY())-mStartY)<-45&&getLastVisiblePosition()>=(getCount()-1)){//加载更多onClick(footerLayout);return;}mIsRecord = false;// 此时的touch事件完毕,要关闭。isScroller = true;// ListView可以Scrooler滑动.// 如果下拉状态处于loading状态.if (mFirstItemIndex) {if (mlistViewState != DListViewState.LV_LOADING) {// 处理相应状态.switch (mlistViewState) {// 普通状态case LV_NORMAL:break;// 下拉状态case LV_PULL_REFRESH:headerLayout.setPadding(0, -1 * mHeadViewHeight, 0, 0);mlistViewState = DListViewState.LV_NORMAL;break;// 刷新状态case LV_RELEASE_REFRESH:headerLayout.setPadding(0, 0, 0, 0);mlistViewState = DListViewState.LV_LOADING;headerTextView.setText("正在刷新");headerProgressBar.setVisibility(View.VISIBLE);if (handler != null)handler.upRefresh();// 上拉刷新break;}}}}private int dipTopx(float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}}