自定义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
原创粉丝点击