自定义ListView实现下拉刷新功能
来源:互联网 发布:淘宝会员积分规则 编辑:程序博客网 时间:2024/05/21 06:41
前言
这几天自己想搞个微博玩玩,仔细研究了一下微博的功能,各种强大啊。一口吃不了一个胖子,所以功能还是要一个一个实现的,就先从下拉刷新入手了,其实自己对实现下拉刷新一点思路也没有,正巧在慕课网上看到了相关视频,所以才会有本篇文章。
正文
好,国际惯例,有图有真相。
以上是效果图。做的太糙大家见谅。
首先自定义一个下拉刷新头
header_layout.xml
<?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:orientation="vertical" > <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="10dp" android:paddingTop="10dp" > <LinearLayout android:id="@+id/layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:orientation="vertical" > <TextView android:id="@+id/tip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="下拉可以刷新" android:textColor="#000" android:textStyle="bold" /> </LinearLayout> <ImageView android:id="@+id/arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="20dp" android:layout_toLeftOf="@id/layout" android:src="@drawable/pull_refresh_down" /> <ProgressBar android:id="@+id/progress" style="@style/PullToRefreshProgressStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="20dp" android:layout_toLeftOf="@id/layout" android:visibility="gone" /> </RelativeLayout></LinearLayout>
头部布局很简单,大家可能会发现我的ProgressBar的样式是自定义的。关于自定义ProgressBar在这里简单提一下,因为我也是初学。
在drawable文件夹下新建pull_to_refresh_progress_bg.xml 下面是代码
pull_to_refresh_progress_bg.xml
<?xml version="1.0" encoding="utf-8"?><rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/pull_refresh_loading" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:toDegrees="360" ></rotate>可以看到,这就是一个简单的旋转动画,pull_refresh_loading 是一个圆圈图片,fromDegrees="0"的意思是从0度开始旋转,toDegrees="360"是到360度,这两个属性合起来就是旋转一周,pivotX="50%" pivotY="50%"是以图片的中心旋转。就介绍这么多了,想了解更多大家可以去网上搜Animation的用法,很多的。
这只是一个旋转动画,并不是style,所以还需要在values/styles.xml中增加以下代码。
<style name="PullToRefreshProgressStyle"> <item name="android:indeterminateDrawable">@drawable/pull_to_refresh_progress_bg</item> <item name="android:minWidth">30dip</item> <item name="android:maxWidth">30dip</item> <item name="android:minHeight">30dip</item> <item name="android:maxHeight">30dip</item> </style>这个就没什么说的了。首先是引用之前的动画配置文件,剩下的四条是设置他的宽高的。PullToRefreshProgressStyle就是我们在header_layout的ProgressBar中的使用的样式。
好,下面进入来看看我们的下拉刷新的具体代码实现
PullToRefreshListView.java
import android.content.Context;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.animation.RotateAnimation;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ImageView;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;import com.wkl.pulltorefreshdemo.R;/** * 下拉刷新ListView * * @author wkl * */public class PullToRefreshListView extends ListView implements OnScrollListener {// 文字提示private TextView tip;// 箭头private ImageView arrow;// 圆形进度条private ProgressBar progress;// 普通状态private static final int NORMAL = 0;// 下拉状态private static final int PULL = 1;// 提示释放private static final int RELEASE = 2;// 正在刷新private static final int REFRESHING = 3;// 当前状态private int state;private LayoutInflater inflater;// header布局private View header;// header的高度private int headerHeight;// 第一个可见的itemprivate int firstVisibleItem;// 逆时针旋转动画private RotateAnimation animNi;// 顺时针旋转动画private RotateAnimation animShun;// 滚动状态private int scrollState;// 手指按下标记private boolean flag = false;// 手指按下起始点private int startY;private OnRefreshListener listener;// 标记private int count = 0;public PullToRefreshListView(Context context) {super(context);init(context);}public PullToRefreshListView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public PullToRefreshListView(Context context, AttributeSet attrs,int defStyle) {super(context, attrs, defStyle);init(context);}/** * 初始化 * * @param context */private void init(Context context) {animNi = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF,0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);animNi.setFillAfter(true);animNi.setDuration(250);animShun = new RotateAnimation(-180, 0,RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);animShun.setFillAfter(true);animShun.setDuration(250);inflater = LayoutInflater.from(context);header = inflater.inflate(R.layout.header_layout, this, false);tip = (TextView) header.findViewById(R.id.tip);arrow = (ImageView) header.findViewById(R.id.arrow);progress = (ProgressBar) header.findViewById(R.id.progress);addHeaderView(header);measureView(header);headerHeight = header.getMeasuredHeight();setTopPadding(-headerHeight);setOnScrollListener(this);}/** * 设置header的TopPadding * * @param topPadding */private void setTopPadding(int topPadding) {header.setPadding(header.getPaddingLeft(), topPadding,header.getPaddingRight(), header.getPaddingBottom());}/** * 通知父布局占用的空间 * * @param child */private void measureView(View child) {ViewGroup.LayoutParams p = child.getLayoutParams();if (p == null) {p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);}int childWidthSpec = ViewGroup.getChildMeasureSpec(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);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:if (firstVisibleItem == 0) {flag = true;startY = (int) ev.getY();}break;case MotionEvent.ACTION_MOVE:onMove(ev);break;case MotionEvent.ACTION_UP:count = 0;if (state == RELEASE) {state = REFRESHING;changeViewByState();// 预留加载数据listener.onRefresh();// 预留加载数据} else if (state == PULL) {state = NORMAL;flag = false;changeViewByState();}break;}return super.onTouchEvent(ev);}/** * 根据state改变header */private void changeViewByState() {switch (state) {case NORMAL:arrow.clearAnimation();setTopPadding(-headerHeight);break;case PULL:arrow.setVisibility(View.VISIBLE);progress.setVisibility(View.GONE);if (count != 0) {arrow.clearAnimation();arrow.startAnimation(animShun);tip.setText("下拉可以刷新");}break;case RELEASE:arrow.setVisibility(View.VISIBLE);progress.setVisibility(View.GONE);arrow.clearAnimation();count = 1;arrow.startAnimation(animNi);tip.setText("释放可以刷新");break;case REFRESHING:setTopPadding(10);arrow.clearAnimation();arrow.setVisibility(View.GONE);progress.setVisibility(View.VISIBLE);tip.setText("正在加载...");break;}}/** * 移动事件 * * @param ev */private void onMove(MotionEvent ev) {if (!flag) {return;}int tempY = (int) ev.getY();int space = tempY - startY;int topPadding = space - headerHeight;switch (state) {case NORMAL:if (space > 0) {state = PULL;changeViewByState();}break;case PULL:setTopPadding(topPadding);if (space > headerHeight + 20&& scrollState == SCROLL_STATE_TOUCH_SCROLL) {state = RELEASE;changeViewByState();}break;case RELEASE:setTopPadding(topPadding);if (space < headerHeight + 20) {state = PULL;changeViewByState();} else if (space <= 0) {state = NORMAL;flag = false;changeViewByState();}break;}}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {this.scrollState = scrollState;}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {this.firstVisibleItem = firstVisibleItem;}/** * 刷新数据完成 */public void refreshComplete() {state = NORMAL;flag = false;changeViewByState();}/** * 设置数据更新回调 */public void setOnRefreshListener(OnRefreshListener listener) {this.listener = listener;}/** * 刷新数据回调接口 * * @author wkl * */public interface OnRefreshListener {/** * 通知activity进行更新数据 */public void onRefresh();}}上面的代码都有注释,其实不难,主要就是在下拉时的状态的判断以及更新header的显示。这里使用的是接口回调的方式来通知activity进行加载最新数据的。
下面来看如何来使用我们自定义的ListView
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <com.wkl.view.PullToRefreshListView android:id="@+id/pull_to_refresh_listview" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#fff" android:cacheColorHint="#00000000" android:dividerHeight="2dp" /></RelativeLayout>可以看到我们是直接引用的。需要注意的是,引用的时候必须写上类名的全称,否则会找不到类。
下面是activity的代码。也很简单。
MainActivity.java
import java.util.ArrayList;import java.util.List;import com.wkl.adapter.MyAdapter;import com.wkl.view.PullToRefreshListView;import android.app.Activity;import android.os.Bundle;import android.os.Handler;public class MainActivity extends Activity implementsPullToRefreshListView.OnRefreshListener {private PullToRefreshListView listview;private MyAdapter adapter;private List<String> data;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);data = new ArrayList<String>();data.add("aaa");data.add("bbb");data.add("ccc");data.add("ddd");data.add("eee");data.add("fff");data.add("ggg");data.add("hhh");data.add("iii");data.add("jjj");data.add("kkk");data.add("lll");data.add("mmm");data.add("nnn");data.add("ooo");data.add("ppp");data.add("qqq");adapter = new MyAdapter(this, data);listview = (PullToRefreshListView) findViewById(R.id.pull_to_refresh_listview);listview.setOnRefreshListener(this);listview.setAdapter(adapter);}@Overridepublic void onRefresh() {new Handler().postDelayed(new Runnable() {@Overridepublic void run() {// 1获取最新数据data.add(0, "新数据");// 2更新界面adapter.notifyDataSetChanged();// 3通知listviewlistview.refreshComplete();}}, 2000);}}这里需要注意的是需要用到下拉刷新的activity或者fragment需要实现PullToRefreshListView.OnRefreshListener,否则在刷新的时候会报空指针。onRefresh()方法中的handler是为了模拟加载时间延迟用的,效果更加逼真。好了,下拉刷新就介绍这么多。
最后
由于本人技术能力有限,毕竟我也是初学者,对代码有任何疑问或者改进的意见,请大家在下面回复。
源码下载电梯直达
0 0
- 自定义ListView实现下拉刷新功能
- 实现listview下拉刷新功能
- Android 自定义ListView 实现下拉刷新 上拉加载功能
- 自定义ListView实现下拉刷新上拉加载功能
- 自定义ListView实现下拉刷新
- 自定义ListView实现下拉刷新
- 继承ListView,自定义下拉刷新功能
- 实现ListView的下拉刷新功能
- android 自定义listview实现下拉刷新(一)
- 自定义listview实现下拉刷新的效果
- android 自定义ListView实现下拉刷新
- 自定义ListView,下拉刷新
- 自定义下拉刷新ListView
- 自定义listView下拉刷新
- 自定义listview下拉刷新
- 自定义下拉刷新listview
- android 自定义listview——实现上拉刷新下拉加载的功能
- Android开发之自定义控件--ListView的下拉刷新功能
- 解决Struts中文乱码问题
- 定时器的使用
- POJ_3267_The Cow Lexicon_动态规划
- 调用Sina股票数据
- Unity3d 如何将Animation动画被Animator使用?
- 自定义ListView实现下拉刷新功能
- 生产环境线上测试的惨淡人生
- 每个程序员都该知道的10大编码原则
- 腾讯Android研发2015年面筋
- 简单的Hql拼接...
- Shell脚本编程的常识
- u盘不能自动卸载的问题
- BZOJ 1207 DP
- C++中assert函数的用法介绍