自定义控件有三种扩展方式, 一个是自定义组合控件,就是继承布局把一些控件组合到一起。 另一个是继承自定义控件,继承控件,增添一些控件所没有的功能。在原有的基础上重写他的函数。增加方法。 最后一个是继承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&¤tState != PULL_REFERESH) { currentState = PULL_REFERESH; //根据状态更新HeaderView updateHeaderView(); } else if (headerMoveHeight>=0&¤tState != 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的位置使用加载更多布局。
|