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

(本文讲解了在Android中实现列表下拉刷新的动态效果的过程,文末附有源码。)

看完本文,您可以学到:

1.下拉刷新的实现原理

2.自定义Android控件,重写其ListView

3.ScrollListener滚动监听

4.Adapter适配器的使用

话不多说,先来看看效果图:

接下来我们一步一步地实现以上的效果。

一、图文并茂的ListViewItem

看一下这一步的效果图:

首先,我们要实现的是带下拉刷新效果的ListView。所以我们选择自己重写原生控件ListView。只需要写一个类继承它就可以了,先不添加任何的具体实现。

RefreshListView.java:

public class RefreshListView extends ListView {
	public RefreshListView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	public RefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
	}
}

要实现图文并茂的ListViewItem,接下去就要自己定义它Item的布局,这个可以无限发挥,我这里就只取图和文做一个简单的实现:

listview_item.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="match_parent"
    android:gravity="center_vertical"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:textSize="25sp" />

</LinearLayout>

在这个布局中,我就只放了一个Image和一个Text。您可以自己定义地更复杂。

然后需要我们注意的是,既然我们自己定义了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.example.mylistviewrefresh.RefreshListView
        android:id="@+id/listview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:cacheColorHint="#00000000"
        android:dividerHeight="5dip" />

</RelativeLayout>

可以一眼看出我们修改了它的控件标签,改为我们自己定义的类的完全路径。

最后是主角MainActivity.java,里面的一些代码我详细地给了注释。这里要注意的是适配器的使用。

public class MainActivity extends Activity {
	private RefreshListView listView;
	private SimpleAdapter simple_adapter;
	private List<Map<String, Object>> list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		listView = (RefreshListView) findViewById(R.id.listview);
		iniData();   //初始化数据,我们给它加20条Item
		// 设置SimpleAdapter监听器
		/**
		 * SimpleAdapter的五个参数的含义:
		 * 第一个:context上下文
		 * 第二个:用于显示的数据,map的list
		 * 第三个:Item的布局,即我们自定义的那个文件
		 * 第四个:与第二个参数紧密联系,与第五个紧密联系,是在map中的键值
		 * 第五个:我们看到是id(int类型)的数组,这个数组里的东西是哪里来的?是我们自己在布局文件中定义的,忘记的读者可以回过头去看一下
		 * 这几个参数独立开来可能不知道是干吗的,但是我觉得联合在一起就挺好理解了。
		 */
		simple_adapter = new SimpleAdapter(MainActivity.this, list,
				R.layout.listview_item, new String[] { "image", "text" },
				new int[] { R.id.image, R.id.text });
		//设置适配器
		listView.setAdapter(simple_adapter);

	}

	// 初始化SimpleAdapter数据集
	private List<Map<String, Object>> iniData() {
		list = new ArrayList<Map<String, Object>>();
		for (int i = 0; i < 20; i++) {
			Map<String, Object> map = new HashMap<String, Object>();
			//解释下这里的数据,key对应SimpleAdapter的第三个参数,必须都包含它们。值对应第五个参数,分别是图片和文字
			map.put("text", i);
			map.put("image", R.drawable.ic_launcher);
			list.add(map);
		}

		return list;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

到了这一步,应该能够实现图片+文字的listview了吧,喝口茶,我们继续看下去。

二、加入隐藏的Header

这里我们要说一下下拉刷新的实现思路了:

首先,我们平常用到的下拉刷新,都是在下拉后屏幕上方显示出一些之前被隐藏的控件,类似下拉的箭头、progress bar等等。

那我们可以直接把它们设置为不可见吗?显然是不可以的。因为这些空间的显示与否,有一个渐变的过程,不是刷一下就出来的。

所以我们应该这样做:

加入一个隐藏的布局,放在屏幕上方。根据下拉的范围来显示响应的控件。

这一步,我们要实现的是加入隐藏的布局,具体怎样根据下拉的状态来实时调整Header的显示状态,我们在下文细说。

我们为了需要隐藏的header再自定义个新的布局,header.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="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="下拉可以刷新!" />

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

        <ImageView
            android:id="@+id/arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toLeftOf="@id/layout"
            android:layout_marginRight="20dip"
            android:src="@drawable/pull_to_refresh_arrow" />

        <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" />
    </RelativeLayout>

</LinearLayout>

这个布局中包含了提示语“下拉可以刷新”、最新更新时间、下拉箭头的图片(已经预先放在drawable文件夹中了,读者可以自己找个图片放进去,命名为pull_to_refresh_arrow.phg)、一个更新时才显示的progressbar(现在是隐藏的)。

为了把这个布局加到我们定义的List,我们需要改写之前自定义的RefreshLIstview控件:

public class RefreshListView extends ListView {
	View header;// 顶部布局文件;
	int headerHeight;// 顶部布局文件的高度;
	public RefreshListView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	public RefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	/**
	 * 初始化界面,添加顶部布局文件到 listview
	 */
	private void initView(Context context) {
		LayoutInflater inflater = LayoutInflater.from(context);
		header = inflater.inflate(R.layout.header, null);
		measureView(header);
		headerHeight = header.getMeasuredHeight();
		Log.i("tag", "headerHeight = " + headerHeight);
		//topPadding(-headerHeight);   //这一行被我注释了,如果你去除注释,就可以显示出来了
		this.addHeaderView(header);
	}

	/**
	 * 通知父布局,占用的宽,高;
	 */
	private void measureView(View view) {
		ViewGroup.LayoutParams p = view.getLayoutParams();
		if (p == null) {
			p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT);
		}
		int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
		int height;
		int tempHeight = p.height;
		if (tempHeight > 0) {
			height = MeasureSpec.makeMeasureSpec(tempHeight,
					MeasureSpec.EXACTLY);
		} else {
			height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
		}
		view.measure(width, height);
	}

	/**
	 * 设置header布局 上边距;
	 */
	private void topPadding(int topPadding) {
		header.setPadding(header.getPaddingLeft(), topPadding,
				header.getPaddingRight(), header.getPaddingBottom());
		header.invalidate();
	}
}

主要实现的是把新的header加入进去,同时把它隐藏。现在我把隐藏header的那行代码注释了,我们看看现在的效果:

如果去除注释,header就被隐藏。

三、加入滚动监听实时调整Header显示状态以及刷新后添加数据

思路:

添加屏幕触摸监听和屏幕滚动监听。

触摸监听尤其重要:

在触摸时记录下触摸坐标的Y值即startY,然后在移动过程中监听当前的Y值,根据两者的插值判断当前的移动距离,与一些临界值做比较

比较之后得出当前的状态:提示下拉状态、提示释放状态、刷新状态。根据当前的状态来刷新header布局的显示情况。

滚动监听的作用是判断当前是否是列表的顶端(通过判断当前可见的第一个item的position是否为0),以及在之后判断屏幕的滚动状态。

另外在自定义的Listview类中定义了一个接口,在mainactivity中实现这个接口,用来对数据进行刷新。我们在刷新的时候用了Handler延迟了两秒,以清晰地看到刷新的效果。

修改后的MainActivity以及ListView:

ListView:  (里面很多注释,自己看着应该很好理解)

public class RefreshListView extends ListView implements OnScrollListener {
	View header;// 顶部布局文件;
	int headerHeight;// 顶部布局文件的高度;
	int firstVisibleItem;// 当前第一个可见的item的位置;
	int scrollState;// listview 当前滚动状态;
	boolean isRemark;// 标记,当前是在listview最顶端摁下的;
	int startY;// 摁下时的Y值;

	int state;// 当前的状态;
	final int NONE = 0;// 正常状态;
	final int PULL = 1;// 提示下拉状态;
	final int RELEASE = 2;// 提示释放状态;
	final int REFRESHING = 3;// 刷新状态;
	IRefreshListener iRefreshListener;//刷新数据的接口
	public RefreshListView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	public RefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	/**
	 * 初始化界面,添加顶部布局文件到 listview
	 *
	 * @param context
	 */
	private void initView(Context context) {
		LayoutInflater inflater = LayoutInflater.from(context);
		header = inflater.inflate(R.layout.header, null);
		measureView(header);
		headerHeight = header.getMeasuredHeight();
		Log.i("tag", "headerHeight = " + headerHeight);
		topPadding(-headerHeight);
		this.addHeaderView(header);
		this.setOnScrollListener(this);
	}

	/**
	 * 通知父布局,占用的宽,高;
	 *
	 * @param view
	 */
	private void measureView(View view) {
		ViewGroup.LayoutParams p = view.getLayoutParams();
		if (p == null) {
			p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT);
		}
		int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
		int height;
		int tempHeight = p.height;
		if (tempHeight > 0) {
			height = MeasureSpec.makeMeasureSpec(tempHeight,
					MeasureSpec.EXACTLY);
		} else {
			height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
		}
		view.measure(width, height);
	}

	/**
	 * 设置header 布局 上边距;
	 *
	 * @param topPadding
	 */
	private void topPadding(int topPadding) {
		header.setPadding(header.getPaddingLeft(), topPadding,
				header.getPaddingRight(), header.getPaddingBottom());
		header.invalidate();
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		// TODO Auto-generated method stub
		this.firstVisibleItem = firstVisibleItem;
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		// TODO Auto-generated method stub
		this.scrollState = scrollState;
	}

	/**
	 * 对屏幕触摸的监控,
	 * 先判断当前是否是在顶端。如果是在最顶端,记录下你开始滑动的Y值
	 * 然后在滑动过程中(监听到的是ACTION_MOVE),不断地判断当前滑动的范围是否到达应该刷新的程度。
	 * (根据当前的Y-之前的startY的值 与我们的控件的高度之间关系来判断)
	 * 然后在监听到手指松开时,根据当前的状态(我们在onmove()中计算的),做相应的操作。
	 */
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		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 = REFRESHING;
				// 加载最新数据;
				refreshViewByState();
				iRefreshListener.onRefresh();
			} else if (state == PULL) {
				//提示下拉状态状态,如果放掉的话,把一切还原,什么都没有做
				state = NONE;
				isRemark = false;
				refreshViewByState();
			}
			break;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * 判断移动过程操作:
	 * 如果不是顶端,不需要做任何的操作
	 * 否则就获取当前的Y值,与开始的Y值做比较。
	 * 判断下拉的高度,与我们定义的一些临界值做判断(其实这个临界值你可以自己定义)
	 *
	 * @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;  //正在下拉
				refreshViewByState();
			}
			break;
		case PULL:
			topPadding(topPadding);
			//如果大于一定高度,并且滚动状态是正在滚动时,就到了松开可以刷新的状态
			if (space > headerHeight + 30
					&& scrollState == SCROLL_STATE_TOUCH_SCROLL) {
				state = RELEASE;
				refreshViewByState();
			}
			break;
		case RELEASE:
			topPadding(topPadding);
			//在提示松开刷新时,如果你往上拖,距离小于一定高度时,提示下拉可以刷新
			if (space < headerHeight + 30) {
				state = PULL;
				refreshViewByState();
			}
			break;
		}
	}

	/**
	 * 根据当前状态,改变界面显示;
	 */
	private void refreshViewByState() {
		//如果要提高性能,这些应该在oncreate中写,但是。。那里面参数太多了,为了大家读代码更舒服,就写在这里了。
		TextView tip = (TextView) header.findViewById(R.id.tip);
		ImageView arrow = (ImageView) header.findViewById(R.id.arrow);
		ProgressBar progress = (ProgressBar) header.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:     //正常状态不显示
			arrow.clearAnimation();
			topPadding(-headerHeight);
			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 REFRESHING:
			topPadding(50);
			arrow.setVisibility(View.GONE);
			progress.setVisibility(View.VISIBLE);
			tip.setText("正在刷新...");
			arrow.clearAnimation();
			break;
		}
	}

	/**
	 * 获取完数据之后
	 */
	public void refreshComplete() {
		state = NONE;
		isRemark = false;
		refreshViewByState();
		TextView lastupdatetime = (TextView) header
				.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);
	}

	public void setInterface(IRefreshListener iRefreshListener){
		this.iRefreshListener = iRefreshListener;
	}

	/**
	 * 刷新数据接口
	 * @author Administrator
	 */
	public interface IRefreshListener{
		public void onRefresh();
	}
}

MainActivity: (添加了接口回调,即在listview中调用main的添加数据的方法)

public class MainActivity extends Activity  implements IRefreshListener {
	private RefreshListView listView;
	private SimpleAdapter simple_adapter;
	private List<Map<String, Object>> list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		listView = (RefreshListView) findViewById(R.id.listview);
		iniData();   //初始化数据,我们给它加20条Item
		// 设置SimpleAdapter监听器
		/**
		 * SimpleAdapter的五个参数的含义:
		 * 第一个:context上下文
		 * 第二个:用于显示的数据,map的list
		 * 第三个:Item的布局,即我们自定义的那个文件
		 * 第四个:与第二个参数紧密联系,与第五个紧密联系,是在map中的键值
		 * 第五个:我们看到是id(int类型)的数组,这个数组里的东西是哪里来的?是我们自己在布局文件中定义的,忘记的读者可以回过头去看一下
		 * 这几个参数独立开来可能不知道是干吗的,但是我觉得联合在一起就挺好理解了。
		 */
		simple_adapter = new SimpleAdapter(MainActivity.this, list,
				R.layout.listview_item, new String[] { "image", "text" },
				new int[] { R.id.image, R.id.text });
		//设置适配器
		listView.setAdapter(simple_adapter);
		//设置更新数据的接口
        listView.setInterface(this);
	}

	// 初始化SimpleAdapter数据集
	private List<Map<String, Object>> iniData() {
		list = new ArrayList<Map<String, Object>>();
		for (int i = 0; i < 20; i++) {
			Map<String, Object> map = new HashMap<String, Object>();
			//解释下这里的数据,key对应SimpleAdapter的第三个参数,必须都包含它们。值对应第五个参数,分别是图片和文字
			map.put("text", i);
			map.put("image", R.drawable.ic_launcher);
			list.add(map);
		}

		return list;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	/**
	 * 接口回调,在RefreshListView中可以调用此方法进行数据添加。
	 */
	@Override
	public void onRefresh() {
		// TODO 自动生成的方法存根
		Handler handler = new Handler();
		handler.postDelayed(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				Map<String, Object> map = new HashMap<String, Object>();
				map.put("text", "滚动添加 ");
				map.put("image", R.drawable.ic_launcher);
				list.add(0, map);
				listView.setAdapter(simple_adapter);
				simple_adapter.notifyDataSetChanged();
				listView.refreshComplete();
			}
		}, 2000);
	}

}

到了这里,我们就实现了文章开头的效果图了。

========================================

写在后面:

源代码已上传到我的Github,或者到网盘下载。

任何问题,欢迎留言交流!

时间: 2024-07-29 08:00:01

Android 轻松实现仿QQ空间下拉刷新的相关文章

安卓开发之Kotlin和java双实现仿qq空间下拉图片拉伸

先不扯淡看今天要实现的效果: 话说使用Kotlin实现安卓功能,那我们还是要做一点准备工作,so,你得加一点插件到eclipse或android studio.然并卵,你现在还在使用eclipse开发的话我只能提供地址Kotlin Plugin for Eclipse,然后我使用的还是死丢丢. 死丢丢(android studio)集成kotlin安卓开发 要使用android studio开发kotlin的安卓app,那么你必须有开发kotlin的环境: Kotlin插件.打开Android

android 自定义scrollview 仿QQ空间效果 下拉伸缩顶部图片,上拉回弹 上拉滚动顶部title 颜色渐变

首先要知道  自定义scrollview 仿QQ效果 下拉伸缩放大顶部图片 的原理是监听ontouch事件,在MotionEvent.ACTION_MOVE事件时候,使用不同倍数的系数,重置布局位置[注此处是伸缩隐藏,不是同比例放大] inner.layout(normal.left, (int) (normal.top + inner_move_H), normal.right, (int) (normal.bottom + inner_move_H)); 关于“自定义scrollview 仿

Android最佳实践之Notification、下拉刷新、内存及性能建议等

Notification通知 参考地址:http://developer.android.com/training/notify-user/index.html 通知(Notification)是Android中使用的非常多的一个事件提示机制. 创建一个Notification 例子中的Notification是基于Support Library中的NotificationCompat.Builder类.我们使用时要继承这个类,它提供了各个平台最好的Notification支持. 创建一个Not

Android轮播图封装,下拉刷新相结合

自定义轮播图CarouselView 自定义下拉刷新PullRefreshListView 马上就要正式做毕业设计了,一些零碎时间写其中的一个模块,现记录下来,以备以后忘记时使用.欢迎大神不吝纠正. 效果图: layout_carousel.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com

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

网上有很多很有名的开源框架,这里就来拉拉PullToRefresh这个框架,也就是我们平时用的下拉刷新啦,当然你问我这个有什么用啊?别人已经写好了,这里主要是学习以及练习,练习的次数多了,一切就顺其自然的会了. 废话少说,先上图,再上代码: 1.要想实现下拉刷新功能必须要有个下拉刷新的布局,是吧? <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="htt

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

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

Android自定义控件之仿汽车之家下拉刷新

关于下拉刷新的实现原理我在上篇文章Android自定义控件之仿美团下拉刷新中已经详细介绍过了,这篇文章主要介绍表盘的动画实现原理 汽车之家的下拉刷新分为三个状态: 第一个状态为下拉刷新状态(pull to refresh),在这个状态下是一个表盘随着下拉的距离动态改变指针的角度 第二个状态为放开刷新状态(release to refresh),在这个状态下是指针角度变化的一个动画 第一个状态的实现: 这个效果我们使用自定义View来实现,我们从汽车之家apk中拿到下拉刷新所用到的两张图片: 我们

Android LRecyclerView 操作案例分享-实现下拉刷新、滑动到底部自动加载

一直想抽空写下这个开源项目,但是各种原因没有抽时间,今天还是趁着工作间隙写下了这篇博客,与大家分享. 简介 LRecyclerView是支持addHeaderView. addFooterView.下拉刷新.分页加载数据的RecyclerView. 它对 RecyclerView 控件进行了拓展,给RecyclerView增加HeaderView.FooterView,并且不需要对你的Adapter做任何修改. 主要功能 下拉刷新.滑动到底部自动加载下页数据: 可以方便添加Header和Foot

android 开发:使用SwipeRefreshLayout实现下拉刷新

SwipeRefreshLayout google官方的下拉刷新控件,我是使用在ListView上的. 定义布局文件: 1 LinearLayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_width="match_parent" 5 android:layout_h