今天是1.1号了,一直忙忙碌碌的加班,想要停留一下.在新的一年,为大家分享一点心得,教大家如何实现一个自定义的控件.就当新年的礼物吧~~
项目需要,想要自己实现一个下拉刷新同时还能上拉加载的自定义控件.
参考了网上众多的同类控件实现,目前主要有两种实现机制.
- 重写ListView,在ListView的头和尾加上Header/Footer.
- 类似SwipeRefreshLayout使用容器作为媒介,其他需要刷新的内容做为子控件存在.
两种方式我更赞同的是第二种. 第一种实现非常简单,只需要增加头尾的View.并实现ListView的过度滑动即可.但缺点也很明显,只适用于ListView的刷新控制.其他的View需要如法炮制.
第二种实现方式,我们期望的效果是对所有控件都适用.这点要求似乎很高.但是当我们对View的事件分发绘制流程很熟悉的时候,这种实现其实也算是非常简单的了.
那么我们直接来对第二种实现进行一探究竟.
先讲一下实现思路:
1. 自定义控件需要作为容器布局使用.
2. 需要在子控件不能继续滑动时拉出头或者尾部View.
3. 当头部或尾部View显示出来时,需要运行一段动画直到判定刷新结束.
思路的核心内容即是第2点,如何判定子控件如ScrollView已经滚动到头部或者底部了. 同理如果我们能准确判定所有类型的子控件何时滑动到头部或尾部的话,那么这个自定义的刷新布局就可以适用于所有类型的内容控件刷新.
首先,让我们来实现一个自定义View的基本框架:
package cn.andrewlu.app;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
/**
* 测试事件传递机制。
*
* @author andrewlu 2016.01.01
*
*/
public class SuperRefreshLayout extends FrameLayout {
private final static String TAG = "SuperRefreshLayout";
public SuperRefreshLayout(Context context) {
super(context);
onInit();
}
public SuperRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
onInit();
}
public SuperRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
onInit();
}
// 实现初始化内容.
private void onInit() {
}
// 实现手动调用下拉刷新的操作.比如第一次进界面时主动进行下拉刷新.
public void startDownRefresh() {
}
// 实现手动调用上拉加载的操作.
public void startUpRefresh() {
}
//是否允许下拉刷新行为.
public void setDownRefreshEnable(boolean enable){
}
//是否允许上拉加载行为.
public void setUpRefreshEnable(boolean enable){
}
// 手动调用完成刷新动作.以通知布局隐藏头或尾.
public void finishRefresh() {
}
public interface OnRefreshListener {
// 通知外面要开始刷新了.外面可以进行任意同步/异步的任务执行过程.如果是同步的话,直接返回true,将会结束刷新动作.
// 如果是异步执行任务比如网网络请求,可以返回false,然后在异步任务完成时,执行finishRefresh函数才能结束刷新动作.
public boolean onRefresh(SuperRefreshLayout refreshLayout,boolean isDownRefresh);
}
private OnRefreshListener mOnRefreshListener;
public void setOnRefreshListener(OnRefreshListener l) {
this.mOnRefreshListener = l;
}
}
以上框架为刷新控件定义了几个操作接口,以及向外发送通知的监听器.
这也是我们希望的功能:
1.能够自动根据手指的滑动执行刷新动作,并通知外界.
2.同时能够手动在希望刷新的地方调用start方法进行相应刷新.
3.当业务执行完成了,需要通知本控件停止刷新.
4.onRefresh接口应当执行在异步线程中,因此可以在其中执行耗时的任务,当然也可以执行异步任务.
5.任务完成的条件,onRefresh接口返回true,或者手动调用finishRefresh()
6.可以控件是否允许下拉或上拉.毕竟不是所有时候都需要同时能够上拉和下拉的.有时我们仅希望实现下拉即可.此时可以禁用上拉功能.
本节就讲到这里,以上作为任何一个自定义控件实现的基本框架思路:首先定义控件的基本功能接口.
下一节我们来实现如何下拉及上拉,即如何 让子控件往下移动或向上拖动.