Android自定义控制(五)仿新浪微博的下拉刷新

网上有很多很有名的开源框架,这里就来拉拉PullToRefresh这个框架,也就是我们平时用的下拉刷新啦,当然你问我这个有什么用啊?别人已经写好了,这里主要是学习以及练习,练习的次数多了,一切就顺其自然的会了.

废话少说,先上图,再上代码:

1.要想实现下拉刷新功能必须要有个下拉刷新的布局,是吧?

<?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="match_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dip"
        android:paddingTop="10dip" >

        <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:text="@string/state" />

            <TextView
                android:id="@+id/lastupdate_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>

        <ProgressBar
            android:id="@+id/progress"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toLeftOf="@id/layout"
            android:layout_marginRight="20dip"
            android:visibility="gone" />

        <ImageView
            android:id="@+id/arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignTop="@+id/layout"
            android:layout_marginRight="19dp"
            android:layout_toLeftOf="@+id/layout"
            android:src="@drawable/arrow_down" />

    </RelativeLayout>

</LinearLayout>

2.你要把它加入到布局里面吧!

headView=layoutInflater.from(context).inflate(R.layout.header_layout, null);
		this.addHeaderView(headView);

3.加入到布局直接显示出来也不符合需求啊,所以这一步需要隐藏布局,当然不能和前一篇博客(Android自定义控件(四)仿网易客户端上拉加载更多)一样直接隐藏,直接隐藏满足不了如图的要求,我们这里采取的是设置头部布局的高度为实际高度的负值,这样就实现了隐藏功能,当下拉的时候,还不至于一次就全部显示出来,ok这种办法能够实现图中的要求

headerHeight = headView.getMeasuredHeight();
		setHeaderViewHeight(-headerHeight);
/**
	 * 设置头部布局的高度
	 *
	 * @param i
	 */
	private void setHeaderViewHeight(int headerHeight) {
		headView.setPadding(headView.getPaddingLeft(), headerHeight,
				headView.getPaddingRight(), headView.getPaddingBottom());
		//重绘
		headView.invalidate();
	}

4.跑起来之后,白瞎了,没实现想要的功能,也就是说没有把头部布局隐藏掉,哎,跟踪代码之后发现高度为0,郭神说过在measure之前,getMeasureWidth()和getMeasureHeight()都为零,好吧,那就先measure吧!

private void measureView(View view) {
		ViewGroup.LayoutParams lp = view.getLayoutParams();
		if (lp == null) {
			lp = new ViewGroup.LayoutParams(
					ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT);
		}
		int width = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
		int height;
		int tempHeight=lp.height;
		if (tempHeight > 0) {
			height = MeasureSpec.makeMeasureSpec(tempHeight,
					MeasureSpec.EXACTLY);
		} else {
			height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
		}
		view.measure(width, height);
	}

5.隐藏成功了,下面就要实现具体的功能了,下拉刷新,松开刷新,正在刷新这三个状态,是通过手势改变状态,所以这里要实现onTouch,当然还有OnScrollListener

需要用到firstVisibleItem判断Listview向上滑动还是向下滑动,如果firstVisibleItem==0说明到达ListView的头部了,当然你还需要一个布尔值判断是否按下滑动

@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		this.firstVisibleItem=firstVisibleItem;
	}

重写onTouchEvent,通过firstVisibleItem和布尔值判断按下,抬起,滑动

@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (firstVisibleItem == 0) {
				isRemark = true;
				startY = (int) ev.getY();
			}
			break;

		case MotionEvent.ACTION_MOVE:
			onMove(ev);
			break;

		case MotionEvent.ACTION_UP:
			if (state==RELEASE) {
				state=REFRASH;
				reflashViewByState();
				//更新数据
				isRefreshListener.onRefresh();
			}else if (state==PULL) {
				state=NONE;
				isRemark=false;
				refreshDrawableState();
			}
			break;
		}
		return super.onTouchEvent(ev);
	}

代码不全,先解释一下,后面附上全部代码

按下时,如果firstVisibleItem为0,说明到达listview的顶部,并且可以按下,把isRemark赋值为true,滑动时改变头部布局的状态

/**
	 * 移动过程的状态变换
	 *
	 * @param ev
	 */
	private void onMove(MotionEvent ev) {
		if (!isRemark) {
			return;
		}
		int tempY = (int) ev.getY();
		int space = tempY - startY;
		int topPadding = space - headerHeight;
		switch (state) {
		case NONE:
			if (space>0) {
				state=PULL;
				reflashViewByState();
			}
			break;

		case PULL:
			setHeaderViewHeight(topPadding);
			if (space>headerHeight+30&&scrollState==SCROLL_STATE_IDLE) {
				state=RELEASE;
				reflashViewByState();
			}
			break;

		case RELEASE:
			setHeaderViewHeight(topPadding);
			if (space<headerHeight+30) {
				state=PULL;
				reflashViewByState();
			}else if (space<=0) {
				state=NONE;
				reflashViewByState();
			}
			break;

		}
	}

根据滑动之后和动画前y值的变化判断滑动状态,当space大于零时,当前状态变为下拉刷新,如果space大于某个值时,当前状态变为松开可以刷新,当space大于零小于某个值时,当前状态为下拉刷新状态,当space小于零时,当前状态变为正常状态.当然状态改变时,界面也要随着改变

/**
	 *根据状态刷新当前页面
	 */
	private void reflashViewByState() {
		TextView tip = (TextView) headView.findViewById(R.id.tip);
		ImageView arrow = (ImageView) headView.findViewById(R.id.arrow);
		ProgressBar progress = (ProgressBar) headView.findViewById(R.id.progress);
		RotateAnimation anim = new RotateAnimation(0, 180,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		anim.setDuration(500);
		anim.setFillAfter(true);
		RotateAnimation anim1 = new RotateAnimation(180, 0,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		anim1.setDuration(500);
		anim1.setFillAfter(true);
		switch (state) {
		case NONE:
			setHeaderViewHeight(-headerHeight);
			arrow.clearAnimation();
			break;

		case PULL:
			arrow.setVisibility(View.VISIBLE);
			progress.setVisibility(View.GONE);
			tip.setText("下拉刷新");
			arrow.clearAnimation();
			arrow.setAnimation(anim1);
			break;

		case RELEASE:
			arrow.setVisibility(View.VISIBLE);
			progress.setVisibility(View.GONE);
			tip.setText("松开刷新");
			arrow.clearAnimation();
			arrow.setAnimation(anim);
			break;

		case REFRASH:
			setHeaderViewHeight(50);
			arrow.setVisibility(View.GONE);
			progress.setVisibility(View.VISIBLE);
			tip.setText("正在刷新");
			arrow.clearAnimation();
			break;
		}
	}

界面上主要改变的就是提示,箭头和progress,正常状态下,界面不可见,下拉刷新状态下,箭头可见并且朝下,提示信息为下拉刷新并且progress不可见,松开刷新状态,箭头朝上,progress不可见,提示信息为下拉刷新,正在加载状态箭头不可见,progress可见,提示信息改为正在刷新

当然,在变成正在加载状态时,还要加载更过数据

public interface IsRefreshListener{
		public void onRefresh();
	}

	public void setIsRefreshListener(IsRefreshListener isRefreshListener){
		this.isRefreshListener=isRefreshListener;
	}

加载完数据后,还要通知listview刷新结束

/**
	 * 获取完数据;
	 */
	public void reflashComplete() {
		state = NONE;
		isRemark = false;
		reflashViewByState();
		TextView lastupdatetime = (TextView) headView
				.findViewById(R.id.lastupdate_time);
		SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
		Date date = new Date(System.currentTimeMillis());
		String time = format.format(date);
		lastupdatetime.setText(time);
	}

自定义下拉刷新控件就这样完成了,不懂得留言吧,我尽量给你解答,自定义这东西,写多了也就知道怎么写了

下面附上自定义下拉刷新控件的全部代码:

package com.sdufe.thea.guo.view;

import java.text.SimpleDateFormat;
import java.util.Date;

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.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;

import com.sdufe.thea.guo.R;

public class PullToRefreshListView extends ListView implements OnScrollListener {

	View headView;
	int headerHeight;
	int firstVisibleItem;
	int scrollState;
	boolean isRemark;
	int startY;

	int state;
	final int NONE = 0;
	final int PULL = 1;
	final int RELEASE = 2;
	final int REFRASH = 3;

	IsRefreshListener isRefreshListener;

	public PullToRefreshListView(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
		initView(context);
	}

	public PullToRefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initView(context);
	}

	public PullToRefreshListView(Context context) {
		super(context);
		initView(context);
	}

	private void initView(Context context) {
		headView = LayoutInflater.from(context).inflate(R.layout.header_layout,
				null);
		measureView(headView);
		headerHeight = headView.getMeasuredHeight();
		setHeaderViewHeight(-headerHeight);
		addView(headView);
		setOnScrollListener(this);
	}

	/**
	 * 计算宽高
	 *
	 * @param view
	 */
	private void measureView(View view) {
		ViewGroup.LayoutParams lp = view.getLayoutParams();
		if (lp == null) {
			lp = new ViewGroup.LayoutParams(
					ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT);
		}
		int width = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
		int height;
		int tempHeight = lp.height;
		if (tempHeight > 0) {
			height = MeasureSpec.makeMeasureSpec(tempHeight,
					MeasureSpec.EXACTLY);
		} else {
			height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
		}
		view.measure(width, height);
	}

	/**
	 * 设置头部布局的高度
	 *
	 * @param i
	 */
	private void setHeaderViewHeight(int headerHeight) {
		headView.setPadding(headView.getPaddingLeft(), headerHeight,
				headView.getPaddingRight(), headView.getPaddingBottom());
		// 重绘
		headView.invalidate();
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		this.scrollState = scrollState;
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		this.firstVisibleItem = firstVisibleItem;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (firstVisibleItem == 0) {
				isRemark = true;
				startY = (int) ev.getY();
			}
			break;

		case MotionEvent.ACTION_MOVE:
			onMove(ev);
			break;

		case MotionEvent.ACTION_UP:
			if (state==RELEASE) {
				state=REFRASH;
				reflashViewByState();
				//更新数据
				isRefreshListener.onRefresh();
			}else if (state==PULL) {
				state=NONE;
				isRemark=false;
				refreshDrawableState();
			}
			break;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * 移动过程的状态变换
	 *
	 * @param ev
	 */
	private void onMove(MotionEvent ev) {
		if (!isRemark) {
			return;
		}
		int tempY = (int) ev.getY();
		int space = tempY - startY;
		int topPadding = space - headerHeight;
		switch (state) {
		case NONE:
			if (space>0) {
				state=PULL;
				reflashViewByState();
			}
			break;

		case PULL:
			setHeaderViewHeight(topPadding);
			if (space>headerHeight+30&&scrollState==SCROLL_STATE_IDLE) {
				state=RELEASE;
				reflashViewByState();
			}
			break;

		case RELEASE:
			setHeaderViewHeight(topPadding);
			if (space<headerHeight+30) {
				state=PULL;
				reflashViewByState();
			}else if (space<=0) {
				state=NONE;
				reflashViewByState();
			}
			break;

		}
	}

	/**
	 * 获取完数据;
	 */
	public void reflashComplete() {
		state = NONE;
		isRemark = false;
		reflashViewByState();
		TextView lastupdatetime = (TextView) headView
				.findViewById(R.id.lastupdate_time);
		SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
		Date date = new Date(System.currentTimeMillis());
		String time = format.format(date);
		lastupdatetime.setText(time);
	}

	/**
	 *根据状态刷新当前页面
	 */
	private void reflashViewByState() {
		TextView tip = (TextView) headView.findViewById(R.id.tip);
		ImageView arrow = (ImageView) headView.findViewById(R.id.arrow);
		ProgressBar progress = (ProgressBar) headView.findViewById(R.id.progress);
		RotateAnimation anim = new RotateAnimation(0, 180,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		anim.setDuration(500);
		anim.setFillAfter(true);
		RotateAnimation anim1 = new RotateAnimation(180, 0,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		anim1.setDuration(500);
		anim1.setFillAfter(true);
		switch (state) {
		case NONE:
			setHeaderViewHeight(-headerHeight);
			arrow.clearAnimation();
			break;

		case PULL:
			arrow.setVisibility(View.VISIBLE);
			progress.setVisibility(View.GONE);
			tip.setText("下拉刷新");
			arrow.clearAnimation();
			arrow.setAnimation(anim1);
			break;

		case RELEASE:
			arrow.setVisibility(View.VISIBLE);
			progress.setVisibility(View.GONE);
			tip.setText("松开刷新");
			arrow.clearAnimation();
			arrow.setAnimation(anim);
			break;

		case REFRASH:
			setHeaderViewHeight(50);
			arrow.setVisibility(View.GONE);
			progress.setVisibility(View.VISIBLE);
			tip.setText("正在刷新");
			arrow.clearAnimation();
			break;
		}
	}

	public interface IsRefreshListener{
		public void onRefresh();
	}

	public void setIsRefreshListener(IsRefreshListener isRefreshListener){
		this.isRefreshListener=isRefreshListener;
	}
}

下面就是怎么用了

<com.sdufe.thea.guo.view.PullToRefreshListView
        android:id="@+id/pull_to_refresh"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

下面的用法就跟listview差不多了,提示一点要实现IsRefreshListener接口,在onRefresh()里面加载更多数据

package com.sdufe.thea.guo;

import java.util.ArrayList;
import java.util.List;

import com.sdufe.thea.guo.view.PullToRefreshListView;
import com.sdufe.thea.guo.view.PullToRefreshListView.IsRefreshListener;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.ArrayAdapter;

public class MainActivity extends Activity implements IsRefreshListener{

	PullToRefreshListView listView;
	ArrayAdapter<String> adapter;
	List<String> list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		listView = (PullToRefreshListView) findViewById(R.id.pull_to_refresh);
		initData();
		adapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_list_item_1, list);
		listView.setAdapter(adapter);
	}

	private void initData() {
		list = new ArrayList<String>();
		list.add("123456789");
		list.add("123456789");
		list.add("123456789");
	}

	@Override
	public void onRefresh() {
		list.add("爸爸");
		list.add("妈妈");
		list.add("我");
	        adapter.notifyDataSetChanged();
		listView.reflashComplete();
	}

}

今天就到此结束啦,不懂的,留言

时间: 2024-08-06 20:13:11

Android自定义控制(五)仿新浪微博的下拉刷新的相关文章

Android 自定义对话框,进度条,下拉刷新等

这个demo集合了自定义对话框,进度条,下拉刷新以及popup弹出框等.是学习了网上开源项目后,抽取集合了常用对话框,进度条,下拉刷新以及popup弹出框等.现在结构目录简单,更易于学习和扩展使用.注释都卸载代码.下面进行简单的介绍以及部分代码展示. 本文demo下载:点击 1.整体实现的效果图 2.项目结构图 这上面项目结构图也是一目了然,有什么功能展示.大家也看到了,这上面类有点多,如果全部贴出来,不大可能,有兴趣下载本文源码. 3.看看基础类BaseActivity 我就贴一下基础类,还有

Android自定义TabActivity(实现仿新浪微博底部菜单更新UI)

如今Android上很多应用都采用底部菜单控制更新的UI这种框架,例如新浪微博 点击底部菜单的选项可以更新界面.底部菜单可以使用TabHost来实现,不过用过TabHost的人都知道自定义TabHost究竟是有多麻烦的,原生TabHost的风格是不依附屏幕的底部的,要依附底部就要重写布局. TabHost设置的Container可以管理UI的显示,UI可以用LayoutInflater动态生成,也可以是Activity,但不好管理Activity的生命周期.然后用TabHost控制显示UI的显示

Android自定义下拉刷新动画--仿百度外卖下拉刷新

好久没写博客了,小编之前一段时间一直在找工作,从天津来到了我们的大帝都,感觉还不错.好了废话不多说了,开始我们今天的主题吧.现如今的APP各式各样,同样也带来了各种需求,一个下拉刷新都能玩出花样了,前两天订饭的时候不经意间看到了"百度外卖"的下拉刷新,今天的主题就是它–自定义下拉刷新动画. 看一下实现效果吧: 动画 我们先来看看Android中的动画吧: Android中的动画分为三种: Tween动画,这一类的动画提供了旋转.平移.缩放等效果. Alpha – 淡入淡出 Scale

Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件

前言: 因为公司人员变动原因,导致了博主四个月没有动安卓,一直在做IOS开发,如今接近年前,终于可以花一定的时间放在安卓上了.好了,废话不多说,今天我们要带来的效果是苹果版本的QQ下拉刷新.首先看一下目标效果以及demo效果:      因为此效果实现的步骤较多,所以今天博主要实现以上效果的第一步——打造一个通用的下拉刷新控件,具体效果如下: GIF图片比较大,还希望读者能耐心等待一下下从效果图中可以看出,我们的下拉刷新的滑动还是很流畅的,可能大多数开发者用的是XListview或者PullTo

Android 轻松实现仿QQ空间下拉刷新

(本文讲解了在Android中实现列表下拉刷新的动态效果的过程,文末附有源码.) 看完本文,您可以学到: 1.下拉刷新的实现原理 2.自定义Android控件,重写其ListView 3.ScrollListener滚动监听 4.Adapter适配器的使用 话不多说,先来看看效果图: 接下来我们一步一步地实现以上的效果. 一.图文并茂的ListViewItem 看一下这一步的效果图: 首先,我们要实现的是带下拉刷新效果的ListView.所以我们选择自己重写原生控件ListView.只需要写一个

进阶篇-用户界面:6.android studio使用github开源库实现下拉刷新

说实话,这是我第一次这么正儿八经的用github开源库,之前一直在听一些大神对这个世界级的开源库赞不绝口,今天终于体会到了.由于下拉刷新的类库是在eclipse下开发完成的,而eclipse如何使用如果导入网上的教程都非常详细.昨天我试了半天发现由于自己对android studio还不是很熟悉,所以引用类库的时候发现无从下手.但是今天早晨起来我唯一想做的事就是一定要实现这个下拉刷新的效果.我去网上找android studio导入类库的教程,导入PullToRefresh的教程,但是网上用的都

Android第三十七期 - 视觉动画下拉刷新

代码已经整理好,效果如下如图: 地址:http://down.51cto.com/data/2048012

Android第二十三期 - 256k的ListView下拉刷新和滚动加载数据

代码已经

Android 自定义ListView实现底部分页刷新与顶部下拉刷新

在项目开发中,由于数据过大时,需要进行分页加载或下拉刷新,来缓解一次性加载的过长等待.本篇博文实例讲解通过自定义的ListView实现底部分页加载和顶部下拉刷新的效果. 其效果图: 一.ListView 底部分页加载 整个底部分页加载,主要分一下几步: 1.加载底部自定义View; 2.响应OnScrollListener监听事件,onScroll方法记录最后可见的View Item以及整个totalItemCount.当onScrollStateChanged状态改变时, 当滑动到底端,并滑动