分享

自定义控件之下拉刷新列表

 Dragon_chen 2016-10-15
自定义控件有三种扩展方式,
一个是自定义组合控件,就是继承布局把一些控件组合到一起。
另一个是继承自定义控件,继承控件,增添一些控件所没有的功能。在原有的基础上重写他的函数。增加方法。
最后一个是继承view,自己实现测量,摆放,渲染。这个自由度最高,扩展性最大。能实现多姿多彩的控件。同样的最难。
下拉刷新列表算第二种,继承自定义控件。它继承ListView。

public class RefreshView extends ListView {

private View mHeaderView;
private int mHeaderHeight;
private float downY;
private float moveY;
private int offset;
private int headerMoveHeight;
//1个Int值可以代替多个boolean值进行触发器操作,即只执行一次,状态机。
private final int RELASE_REFRESH = 1;//释放刷新
private final int PULL_REFERESH = 0;//下拉刷新
private final int REFERESH_ING = 2;//刷新中
private int currentState = 3;
private RotateAnimation mArrowAnimation;
private RotateAnimation mArrowAniamtionReverse;
private ImageView mHeaderImage;
private ProgressBar mHeaderProgressBar;
private TextView mHanderText;
private RefreshListener mRefreshListener;

public RefreshView(Context context) {
super(context);
init();
}

public RefreshView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init();
}

public RefreshView(Context context, AttributeSet attributeSet, int defaultStyle) {
super(context, attributeSet, defaultStyle);
init();
}

private void init() {
initHeaderView();
initBottomView();
initAnimation();
}

private void initBottomView() {
View mBottomView = View.inflate(getContext(),R.layout.headerview,null);
addFooterView(mBottomView);
}

private void initHeaderView() {
mHeaderView = View.inflate(getContext(), R.layout.headerview, null);
//添加头布局
addHeaderView(mHeaderView);
//隐藏头布局,通过pading为负,此时onMesure还未执行不知道布局宽高,手动执行后,获取测量宽高
mHeaderView.measure(0, 0);
mHeaderHeight = mHeaderView.getMeasuredHeight();
mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);
mHeaderImage = (ImageView) mHeaderView.findViewById(R.id.headerimage);
mHeaderProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.progressbar);
mHanderText = (TextView) mHeaderView.findViewById(R.id.tv_refreshtop);
}

//重写onTouchEvent方法实现触摸UI界面变化,父类的onTouchEvent()含有代码不能删除
@Override
public boolean onTouchEvent(MotionEvent ev) {
//如果当前在刷新中,则不做HeaderView的处理
if (currentState == REFERESH_ING) {

return super.onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = ev.getY();

break;
case MotionEvent.ACTION_MOVE:
moveY = ev.getY();
offset = (int) (moveY - downY);

//当手指下移及第一行可见时,头布局才往下拉
if (offset > 0 && getFirstVisiblePosition() == 0) {
mHeaderView.setPadding(0, headerMoveHeight, 0, 0);
headerMoveHeight = -mHeaderHeight + offset;

//不完全显示则是下拉刷新状态
if (headerMoveHeight < 0&&currentState != PULL_REFERESH) {
currentState = PULL_REFERESH;
//根据状态更新HeaderView
updateHeaderView();
} else if (headerMoveHeight>=0&&currentState != RELASE_REFRESH)//不是释放刷新状态下才释放刷新。
{
currentState = RELASE_REFRESH;
//根据状态更新HeaderView
updateHeaderView();
}


}
break;
case MotionEvent.ACTION_UP:
//释放刷新状态
if (currentState == RELASE_REFRESH) {
mHeaderView.setPadding(0, 0, 0, 0);
currentState = REFERESH_ING;
} else {
//可以设个定时器让它自己滑回去。
mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);
currentState = 3;
}
//根据状态更新HeaderView
updateHeaderView();
break;
}

return super.onTouchEvent(ev);
}

private void initAnimation() {
mArrowAnimation = new RotateAnimation(
0, -180, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);
mArrowAnimation.setDuration(1000);
mArrowAnimation.setFillAfter(true);
mArrowAniamtionReverse = new RotateAnimation(-180, -360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mArrowAniamtionReverse.setDuration(1000);
mArrowAniamtionReverse.setFillAfter(true);
}

private void updateHeaderView() {
switch (currentState) {
case PULL_REFERESH:
//执行下拉刷新逻辑。旋转图片和更新Text
mHeaderImage.startAnimation(mArrowAnimation);
mHanderText.setText("下拉刷新");
break;
case RELASE_REFRESH:
mHeaderImage.startAnimation(mArrowAniamtionReverse);
mHanderText.setText("释放刷新");
break;
case REFERESH_ING:
//必须先清除动画,否则设置不了不可见
mHeaderImage.clearAnimation();
mHeaderImage.setVisibility(INVISIBLE);
mHeaderProgressBar.setVisibility(VISIBLE);
//调用刷新监听事件。
mRefreshListener.onRefresh();
mHanderText.setText("刷新中");
break;
}
}
//刷新结束后调用。由外界调用。
public void onRefreshCompeleted()
{
currentState=PULL_REFERESH;
mHanderText.setText("下拉刷新");
mHeaderImage.setVisibility(VISIBLE);
mHeaderProgressBar.setVisibility(INVISIBLE);
}
public interface RefreshListener{
void onRefresh();
}
public void setRefreshListener(RefreshListener refreshListener)
{
mRefreshListener = refreshListener;
}
}
首先分析下拉刷新列表的的功能需求。
ListView往下滑放手的时候,出现一个ProgressBar在转动,然后数据从第一行开始加载(设置为从0加入)。可以用ListView的HeaderView来实现。
ListVIew滑倒底部向上滑的时候,也出现一个ProgressBar。可以用LIstView的FooterView来实现。
首先定义一个继承ListVIew的类RefreshView,重写它的构造方法。为它设置HeaderView和FooterView(这里以HeaderView为例)要隐藏掉HeaderView。在下拉的时候再将它通过触摸事件一点一点显现出来。具体看重写的onTouchEvent()方法和initHeaderView方法。在状态触发方面可以使用一个int值代替多个boolean值的效果,在不断执行的函数中只触发一次。除非状态改变了。不是该状态值,才能将该状态值赋给当前状态。触发updateHeaderView函数。下拉设置了3个状态值,下拉刷新是默认状态,释放刷新是下拉的距离已经把HeaderView完全显示出来了的状态。刷新中是释放刷新状态下松手的状态。
接着就是事件的监听。设置了一个公有接口。里面的方法在刷新的时候调用。用于在listView添加数据。然后在内部向外部提供了一个刷新结束时让HeaderView恢复原状的函数onRefreshCompeleted()函数。
自定义圆形ProgressBar,设置它的indeterminateDrawable属性为drawable下的shape类型xml(可以Rotate里嵌套个shape)。该xml设置如下,主要是为了使ProgressBar自己转动。其中也设置了渐变。
<?xml version="1.0" encoding="utf-8" ?>
<rotate xmlns:android="http://schemas./apk/res/android"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="-360">
<!--内环相对于父控件比例innerRadiusRatio,
环厚度相对于父控件比例thicknessRatio
useLevel设置渲染层级
type渐变类型,扫描类型sweep,放射类型radial-->
<shape
android:innerRadiusRatio="2.5"
android:shape="ring"
android:thicknessRatio="10"
android:useLevel="false">
<gradient
android:startColor="#ff0000"
android:centerColor="#44ff0000"
android:endColor="#00000000"
android:type="sweep">

</gradient>
</shape>
</rotate>
若其中控件距离太紧可在xml中设置控件的margin值。
其次是footerView的使用。footerView比较简单不用下滑一步步显现。重写onScrollStateChange()方法。判断它的状态为idle且显现的最后一行为list.最大值-1时将footerView显现出来,此前与HeaderView用一样的方法隐藏起来。都是得到它的测量高度,再将它的负值付给padding(因为此时还没有测量,所以得不到控件的高度,要测量一下才能得到它的测量高度)
headerVIew要占据ListView中itemview的位置。即第一个itemview不是Position0而是在headerView之后,有多少个headerView。就占多少位。
第二种做下拉加载更多的方式是使用getItemType()返回不同类型使用不同布局,在每个数据集合大小+1的位置使用加载更多布局。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多