自定义控件——ListView下拉刷新和上拉加载
2016-10-26 22:52
344 查看
进入冰冷的季节了。好想在被窝敲代码。。。。
首先放一下效果添加HeadView实现下拉刷新
添加FooterView实现上拉加载
创建类继承ListView ,构造方法继承前两个
/** * 重写创建时用的构造方法 * * @param context */ public PullPushListView(Context context) { super(context); } /** * 重写赋值参数时用的构造方法 * * @param context * @param attrs */ public PullPushListView(Context context, AttributeSet attrs) { super(context, attrs); //初始化头部信息 initHeaderView(); //初始化尾部信息 initFooterView(); }
4.下拉刷新的实现原理 通过padding属性,当属性为负数的时候就会隐藏如图:
5.上拉加载同样原理,不再演示–
首先我们分为两部分去学习
第一部分: 下拉部分–首先我们要想加入一个headView 需要我们对这个要加入到顶部的条目进行布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <RelativeLayout android:layout_width="50dp" android:layout_height="50dp"> <ImageView android:id="@+id/iv_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@drawable/arrow" /> <ProgressBar android:id="@+id/iv_circle" style="@android:style/Widget.ProgressBar" android:layout_width="30dp" android:layout_height="30dp" android:lay 4000 out_centerInParent="true" android:indeterminateDrawable="@drawable/progress_medium_white" android:visibility="invisible" /> </RelativeLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/tv_loadtext" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="下拉刷新" android:textColor="#ffff0000" android:textSize="18sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:layout_marginTop="5dp" android:gravity="center" android:text="上次刷新 2016-10-23 12:41:58" /> </LinearLayout> </LinearLayout>
1.1 初始化headView,将设定的头部信息隐藏,主要是对要inflate的头部布局进行测量
/** * 初始化HeaderView */ private void initHeaderView() { //为自定义ListView添加头部信息 headerView = View.inflate(getContext(), R.layout.item_headview, null); tv_loadtext = (TextView) headerView.findViewById(R.id.tv_loadtext); iv_arrow = (ImageView) headerView.findViewById(R.id.iv_arrow); pb_circle = (ProgressBar) headerView.findViewById(R.id.iv_circle); //将progressBar 设定为Gone pb_circle.setVisibility(View.GONE); //通知进行布局测量<<<<<<卧槽尼玛的,不能测量RelativeLayout布局开头>>>>>>> headerView.measure(0, 0); //获取测量高度,在之前需要测量一下,否则无法获 取正确数据 meatureHeight = headerView.getMeasuredHeight(); //通知进行布局测量 headerView.measure(0, 0); Log.i(TAG, "PullPushListView: " + meatureHeight); //添加headerView到页面 super.addHeaderView(headerView); //整体隐藏headerView inithideheaderView(-meatureHeight); headerView.setClickable(false); STATE_CURRENT = STATE_PULL; }
1.2 通过onTouchEvent(MotationEvent event);,对触摸事件处理实现动态拉出头部条目,以及涉及到状态的改变
在此之前,我们需要设定三个状态,用于记录当前是那种状态
1.2.1状态可以通过设定三个全局变量
/** * 下拉状态 */ private static final int STATE_PULL = 0; /** * 松开刷新状态 */ private static final int STATE_PUSH = 1; /** * 正在刷新状态 */ private static final int STATE_REFRESH = 2; /** * 当前状态 */ private int STATE_CURRENT;
1.2.2 也可以通过获取TextView 中的条目内容获取,不过麻烦,这里舍弃
下面是触摸事件的实现
/** * headerView 根据传入的数值动态改变HeaderView据上边界的高度 */ private void inithideheaderView(int top) { headerView.setPadding(0, top, 0, 0); } /** * 对ListView 添加触摸事件 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { //手指按下事件 case MotionEvent.ACTION_DOWN: if (STATE_CURRENT == STATE_REFRESH) {//如果状态是正在刷新状态,就不执行其他操作 iv_arrow.setVisibility(GONE); pb_circle.setVisibility(View.VISIBLE); return super.onTouchEvent(event); } down_dot = event.getY();//如果不是正在刷新状态记录按下的点 break; //手指移动事件 case MotionEvent.ACTION_MOVE: if (STATE_CURRENT == STATE_REFRESH && getFirstVisiblePosition() == 0) {//如果状态是正在刷新状态,就不执行其他操作 iv_arrow.setVisibility(GONE); pb_circle.setVisibility(View.VISIBLE); return super.onTouchEvent(event); } else if ((STATE_CURRENT == STATE_PULL || STATE_CURRENT == STATE_PUSH) && getFirstVisiblePosition() == 0) { //手指移动的点 float move_dot = event.getY(); //距离按下点的相对向下移动的距离 float movePullDistance = move_dot - down_dot; //判断是否向下滑动,第一个条目是否是0, if (movePullDistance > 0 && getFirstVisiblePosition() == 0 && movePullDistance < meatureHeight) { inithideheaderView((int) movePullDistance - meatureHeight); //向下滑动,动态改变HeardView距离上边界的距离,但是还没有到达修改箭头的边界 if (STATE_CURRENT == STATE_PUSH) { tv_loadtext.setText("下拉刷新"); rotateArrow_changeText(); STATE_CURRENT = STATE_PULL;//修改状态,在这个范围内不再修改状态 } } else if (movePullDistance == meatureHeight) { STATE_CURRENT = STATE_CURRENT == STATE_PUSH ? STATE_PUSH : STATE_PULL;//判断状态 } else if (movePullDistance > meatureHeight && movePullDistance < 2 * meatureHeight) { inithideheaderView((int) ((movePullDistance - meatureHeight) / 2 + 0)); //旋转箭头,修改文本,下拉幅度到边界值大于一个HeadView高度 if (STATE_CURRENT == STATE_PULL) {//如果是下拉状态,改变 tv_loadtext.setText("松开刷新"); rotateArrow_changeText(); STATE_CURRENT = STATE_PUSH;//修改状态,在这个范围内不再修改状态 } } return super.onTouchEvent(event); } break; //手指抬起事件 case MotionEvent.ACTION_UP: if (STATE_CURRENT == STATE_PUSH) {//如果是松开刷新状态那就改变状态 inithideheaderView(0); //松开手指后,图片修改为旋转progressBar,修改文本 pb_circle.setVisibility(View.VISIBLE); //必须将动画清除掉。。不然无法隐藏 iv_arrow.clearAnimation(); iv_arrow.setVisibility(GONE); tv_loadtext.setText("正在刷新"); STATE_CURRENT = STATE_REFRESH;//修改为刷新状态 if (mOnRefreshStateListener != null && !refreshingState) {//如果不是正在刷新的状态,而且回调接口非空 refreshingState = true; mOnRefreshStateListener.OnRefresh();//执行回调的接口 } } else if (STATE_CURRENT == STATE_PULL) { inithideheaderView(-meatureHeight); pb_circle.setVisibility(View.GONE); iv_arrow.setVisibility(View.VISIBLE); //因为已经清除了动画,所以这里自动回去了,不必在设定转回原点 tv_loadtext.setText("下拉刷新"); STATE_CURRENT = STATE_PULL; } break; } return super.onTouchEvent(event);//必须有这个,这个是让ListView滑动的 }
1.3 刷新中需要设定一些小动画,这些动画实现方法,如下
/** * 创建旋转动画 */ private void setRotateAnimation(float fromDegrees, float toDegrees) { mRotateAnimation = new RotateAnimation(fromDegrees, toDegrees, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mRotateAnimation.setDuration(500); mRotateAnimation.setFillAfter(true); } /** * 刷新完成后的状态 */ public void refreshComplement() { inithideheaderView(-meatureHeight); pb_circle.setVisibility(View.GONE); iv_arrow.setVisibility(View.VISIBLE); //因为已经清除了动画,所以这里自动回去了,不必在设定转回原点 tv_loadtext.setText("下拉刷新"); STATE_CURRENT = STATE_PULL; refreshingState = false; } /** * 旋转箭头,修改文本 */ private void rotateArrow_changeText() { if (STATE_CURRENT == STATE_PULL) {//如果状态为下拉状态 fromDegrees = 0f; toDegrees = 180f; //iv_arrow.clearAnimation();//先 f53d 清除一下动画效果 setRotateAnimation(fromDegrees, toDegrees); iv_arrow.startAnimation(mRotateAnimation); } else if (STATE_CURRENT == STATE_PUSH) { fromDegrees = 180f; toDegrees = 360f; //iv_arrow.clearAnimation();//先清除一下动画效果 setRotateAnimation(fromDegrees, toDegrees); iv_arrow.startAnimation(mRotateAnimation); } }
写到这儿,下拉刷新的方法基本完成,在下一步就是在主Activity中创建监听,在自定义ListView中实现回调接口,
/** * 创建是否刷新的监听 */ public void setOnRefreshStateListener(OnRefreshStateListener listener) { mOnRefreshStateListener = listener; } /** * 定义正在刷新回调接口 */ public interface OnRefreshStateListener { void OnRefresh(); }
主Activity中调用监听方法
pplv_test.setOnRefreshStateListener(new PullPushListView.OnRefreshStateListener() { @Override public void OnRefresh() { handler.postDelayed(new Runnable() { @Override public void run() { objects.add(0, "卧槽,刷新出来了"); adapter.notifyDataSetChanged(); pplv_test.refreshComplement();//通知控件刷新操作已经完成 } }, 3000); } });
华丽分割线
第二部分:实现上拉加载,同样的原理使用padding隐藏,不过要注意的是,同样使用-padding数值,因为 -数值才能类似于隐藏效果2.1 footerView的布局效果,布局没什么好说的
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal"> <ProgressBar android:id="@+id/pb_loadmore" style="@android:style/Widget.ProgressBar.Small" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginBottom="13dp" android:layout_marginTop="13dp" android:indeterminateDrawable="@drawable/progress_medium_white" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="加载更多" android:textColor="#ff0000" android:textSize="17sp" /> </LinearLayout>
2.2 初始化footerView 数据
/** * 添加尾部头目,加载更多 */ private void initFooterView() { //打气筒添加布局 footerView = View.inflate(getContext(), R.layout.item_footerview, null); //获取测量的宽高 footerView.measure(0, 0); footerViewHeight = footerView.getMeasuredHeight(); int top = -footerViewHeight; //设定初始padding的位置 initHideFooterView(top); //将布局添加到Listview 中 super.addFooterView(footerView); super.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //如果滑动状态为空闲,且滑动到了最后一条条目 if (scrollState == OnScrollListener.SCROLL_STATE_IDLE && getLastVisiblePosition() == getCount() - 1 && !loadState) { setSelection(getCount() - 1);//设定选择最后一条条目 initHideFooterView(0); loadState = true; mOnLoadStateListener.onLoadState(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); } /** * 动态设定FooterView的Padding * * @param top 动态设定 */ private void initHideFooterView(int top) { footerView.setPadding(0, top, 0, 0); } /**
2.3 这里上拉刷新的操作不再OnTouch()中实现了,我们在OnscrollListener()方法中实现
super.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //如果滑动状态为空闲,且滑动到了最后一条条目 if (scrollState == OnScrollListener.SCROLL_STATE_IDLE && getLastVisiblePosition() == getCount() - 1 && !loadState) { setSelection(getCount() - 1);//设定选择最后一条条目 initHideFooterView(0); loadState = true; mOnLoadStateListener.onLoadState(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } });
2.4 实现接口回调,以及在主Activity中实现监听
/** * 创建是否加载更多的刷新 */ public void setOnLoadStateListener(onLoadStateListener listener) { mOnLoadStateListener = listener; } /** * 完成加载,修改状态 */ public void loadComplement() { initHideFooterView(-footerViewHeight); loadState = false; } /** * 定义正在加载的回调接口 */ public interface onLoadStateListener { void onLoadState(); }
主方法中创建加载监听
pplv_test.setOnLoadStateListener(new PullPushListView.onLoadStateListener() { @Override public void onLoadState() { handler.postDelayed(new Runnable() { @Override public void run() { objects.add("卧槽,加载出来了"); adapter.notifyDataSetChanged(); pplv_test.loadComplement();//通知控件刷新操作已经完成 } }, 3000); } });
github源码地址
相关文章推荐
- 自定义控件-下拉刷新和上拉加载的listView
- Android自定义控件——ListView的下拉刷新与上拉加载
- Android自定义控件——ListView的下拉刷新与上拉加载
- Android自定义控件——ListView的下拉刷新与上拉加载
- Android自定义控件——ListView的下拉刷新与上拉加载
- 自定义控件:含下拉刷新和上拉加载 ListView 的原理
- Android自定义控件——ListView的下拉刷新与上拉加载
- 自定义控件实现ListView下拉刷新和上拉加载
- Android自定义控件——ListView的下拉刷新与上拉加载
- Android自定义控件——ListView的下拉刷新与上拉加载
- 采用github上的开源项目Android-PullToRefresh实现ListView的下拉刷新和上拉加载
- 如何使用XListview实现listview的下拉刷新和上拉加载
- 自定义ListView的下拉刷新和上拉加载更多
- 下拉刷新和上拉加载(pulltorefreshlistview)
- android WJYScorllTableView可上拉加载下拉刷新且可以左右滚动的listview
- ListView封装实现下拉刷新和上拉加载(方式1)
- listview 代码简洁功能强大的下拉刷新和上拉加载
- Android 自定义ListView 实现下拉刷新 上拉加载功能
- ListView封装实现下拉刷新和上拉加载(方式2)
- ListView和GridView利用pull-to-rerfesh 实现下拉刷新和上拉加载更多