接着上一篇,进行代码分析,先贴上简化后的几段关键代码:
一、自定义组合控件初始化
简单点说就是在组合控件初始化的时候就在初始位置添加头部视图。
public PullToRefreshView(Context context) {
super(context);
init();
}
private void init() {
// Load all of the animations we need in code rather than through XML
// 简化头部和脚部箭头转向动画
mInflater = LayoutInflater.from(getContext());
// header view 在此添加,保证是第一个添加到linearlayout的最上端
addHeaderView();
}
private void addHeaderView() {
// header view
mHeaderView = mInflater.inflate(R.layout.refresh_header, this, false);
// 简化头部视图内对象的创建过程
// header layout
measureView(mHeaderView); //自定义的子控件尺寸测量方法
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
mHeaderViewHeight);
// 设置topMargin的值为负的header View高度,即将其隐藏在最上方
params.topMargin = -(mHeaderViewHeight);
// mHeaderView.setLayoutParams(params1);
addView(mHeaderView, params);
}
二、子视图的尺寸测量
就是通过给子视图制定一些规则,让其宽度填充父视图,高度自适应,因为这里继承LinearLayout,但是没有指定Orientation,同时也可以强制头部宽度填充。
private void measureView(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 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);
}
三、添加下拉尾部
尾部视图的获取和头部相似,只不过这里重写了onFinishInflate()方法,确保将其天际到Linearlayout的最后。这里内容视图的方式和android官方的下拉刷新SwapRefreshView类似,都是硬编码获取指定位置的视图。官方的下拉刷新好像只允许包含一个子视图。
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// footer view 在此添加保证添加到linearlayout中的最后
addFooterView();
initContentAdapterView();
}
private void initContentAdapterView() {
int count = getChildCount();
if (count < 3) {
throw new IllegalArgumentException(
"this layout must contain 3 child views,and AdapterView or ScrollView must in the second position!");
}
View view = null;
for (int i = 0; i < count - 1; ++i) {
view = getChildAt(i);
if (view instanceof AdapterView<?>) {
mAdapterView = (AdapterView<?>) view;
}
if (view instanceof ScrollView) {
// finish later
mScrollView = (ScrollView) view;
}
}
if (mAdapterView == null && mScrollView == null) {
throw new IllegalArgumentException(
"must contain a AdapterView or ScrollView in this layout!");
}
}
四、头部下拉刷新和尾部上拉加载的时间监听器接口
这里用到的就是接口回调方式实现的刷新事件监听,在外层实现这些接口,可以执行刷新事件的流程。以及是否允许上拉和下拉的限制。
/**
* set headerRefreshListener
*
* @description
* @param headerRefreshListener
* hylin 2012-7-31上午11:43:58
*/
public void setOnHeaderRefreshListener(
OnHeaderRefreshListener headerRefreshListener) {
mOnHeaderRefreshListener = headerRefreshListener;
}
public void setOnFooterRefreshListener(
OnFooterRefreshListener footerRefreshListener) {
mOnFooterRefreshListener = footerRefreshListener;
}
/**
* Interface definition for a callback to be invoked when list/grid footer
* view should be refreshed.
*/
public interface OnFooterRefreshListener {
public void onFooterRefresh(PullToRefreshView view);
}
/**
* Interface definition for a callback to be invoked when list/grid header
* view should be refreshed.
*/
public interface OnHeaderRefreshListener {
public void onHeaderRefresh(PullToRefreshView view);
}
public boolean isEnablePullTorefresh() {
return enablePullTorefresh;
}
public void setEnablePullTorefresh(boolean enablePullTorefresh) {
this.enablePullTorefresh = enablePullTorefresh;
}
public boolean isEnablePullLoadMoreDataStatus() {
return enablePullLoadMoreDataStatus;
}
public void setEnablePullLoadMoreDataStatus(boolean enablePullLoadMoreDataStatus) {
this.enablePullLoadMoreDataStatus = enablePullLoadMoreDataStatus;
}
下一篇详解Touch事件拦截的逻辑,既下拉上拉的实现原理。