自定义PullToRefresh HeadLayout

通过阅读PullToRefresh开源的源码,自我总结下重新实现一个刷新动态图的方法。


11:自定义个HeaderLayout 继承自 LoadingLayout 


public class MyLoadingLayout extends LoadingLayout {
static final int FLIP_ANIMATION_DURATION = 150;

    private final Animation mRotateAnimation, mResetRotateAnimation;

    public MyLoadingLayout(Context context, PullToRefreshBase.Mode mode, PullToRefreshBase.Orientation scrollDirection, TypedArray attrs) {
super(context, mode, scrollDirection, attrs);

        final int rotateAngle = mode == PullToRefreshBase.Mode.PULL_FROM_START ? -180 : 180;

<span style="white-space:pre">	</span>mRotateAnimation = new RotateAnimation(0, rotateAngle, Animation.RELATIVE_TO_SELF, 0.5f,
<span style="white-space:pre">	</span>Animation.RELATIVE_TO_SELF, 0.5f);
<span style="white-space:pre">	</span>mRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR);
<span style="white-space:pre">	</span>mRotateAnimation.setDuration(FLIP_ANIMATION_DURATION);
<span style="white-space:pre">	</span>mRotateAnimation.setFillAfter(true);

<span style="white-space:pre">	</span>mResetRotateAnimation = new RotateAnimation(rotateAngle, 0, Animation.RELATIVE_TO_SELF, 0.5f,
<span style="white-space:pre">	</span>Animation.RELATIVE_TO_SELF, 0.5f);
<span style="white-space:pre">	</span>mResetRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR);
<span style="white-space:pre">	</span>mResetRotateAnimation.setDuration(FLIP_ANIMATION_DURATION);
<span style="white-space:pre">	</span>mResetRotateAnimation.setFillAfter(true);
}

@Override
protected void onLoadingDrawableSet(Drawable imageDrawable) {
<span style="white-space:pre">	</span>if (null != imageDrawable) {
<span style="white-space:pre">	</span>final int dHeight = imageDrawable.getIntrinsicHeight();
            final int dWidth = imageDrawable.getIntrinsicWidth();

<span style="white-space:pre">	</span>/**
             * We need to set the width/height of the ImageView so that it is
             * square with each side the size of the largest drawable dimension.
             * This is so that it doesn't clip when rotated.
             */
<span style="white-space:pre">	</span>ViewGroup.LayoutParams lp = mHeaderImage.getLayoutParams();
<span style="white-space:pre">	</span>lp.width = lp.height = Math.max(dHeight, dWidth);
<span style="white-space:pre">	</span>mHeaderImage.requestLayout();

/**
             * We now rotate the Drawable so that is at the correct rotation,
             * and is centered.
             */
<span style="white-space:pre">	</span>mHeaderImage.setScaleType(ImageView.ScaleType.MATRIX);
<span style="white-space:pre">	</span>Matrix matrix = new Matrix();
<span style="white-space:pre">	</span>matrix.postTranslate((lp.width - dWidth) / 2f, (lp.height - dHeight) / 2f);
<span style="white-space:pre">	</span>matrix.postRotate(getDrawableRotationAngle(), lp.width / 2f, lp.height / 2f);
<span style="white-space:pre">	</span>mHeaderImage.setImageMatrix(matrix);
<span style="white-space:pre">	</span>}
    }

@Override
protected void onPullImpl(float scaleOfLayout) {
// NO-OP
}

@Override
protected void pullToRefreshImpl() {
// Only start reset Animation, we've previously show the rotate anim
if (mRotateAnimation == mHeaderImage.getAnimation()) {
mHeaderImage.startAnimation(mResetRotateAnimation);
}
    }

@Override
protected void refreshingImpl() {
mHeaderImage.clearAnimation();
mHeaderImage.setVisibility(View.GONE);
mHeaderProgress.setVisibility(View.GONE);
mHeaderLoadingView.setVisibility(View.VISIBLE);
}

@Override
protected void releaseToRefreshImpl() {
mHeaderImage.startAnimation(mRotateAnimation);
}

@Override
protected void resetImpl() {
mHeaderImage.clearAnimation();
mHeaderProgress.setVisibility(View.GONE);
mHeaderImage.setVisibility(View.VISIBLE);
mHeaderLoadingView.stopAnimation();
mHeaderLoadingView.setVisibility(View.GONE);
}

@Override
protected int getDefaultDrawableResId() {
return R.mipmap.indicator_arrow;
}

private float getDrawableRotationAngle() {
float angle = 0f;
        switch (mMode) {
case PULL_FROM_END:
if (mScrollDirection == PullToRefreshBase.Orientation.HORIZONTAL) {
                    angle = 90f;
} else {
                    angle = 180f;
}
break;

            case PULL_FROM_START:
if (mScrollDirection == PullToRefreshBase.Orientation.HORIZONTAL) {
                    angle = 270f;
}
break;

            default:
break;
}

return angle;
}
}


2.自定义动画View

public class MyAnimationView extends View {

public static final float RADIUS = 20f;
    private Paint mPaint;
    private Paint outPaint;
    private float minRadius = 12f;
    private float Router  = minRadius;
    private float Rinner = minRadius;
    private boolean startAnim = true;
    private int innerInt = 255;
    private int outerInt = 255;
    private ValueAnimator anim;

    public MyAnimationView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
outPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.GRAY);
outPaint.setColor(Color.GRAY);
}

@Override
protected void onDraw(Canvas canvas) {
        drawCircle(canvas);
        if (startAnim)
            startAnimation();
}

private void drawCircle(Canvas canvas) {
mPaint.setAlpha(innerInt);
outPaint.setAlpha(outerInt);
canvas.drawCircle(RADIUS, getHeight()/2, Router, outPaint);
canvas.drawCircle(RADIUS * 3 , getHeight() / 2, Rinner, mPaint);
canvas.drawCircle(RADIUS * 5, getHeight() / 2, Router, outPaint);
}

private void startAnimation() {

anim = ValueAnimator.ofFloat(0,1);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {

innerInt = 144 + (int)(144*(0.5f - (float)animation.getAnimatedValue()));
outerInt = 144 + (int)(144*((float)animation.getAnimatedValue()-0.5f));

Router = minRadius + minRadius* (0.5f - (float)animation.getAnimatedValue());
Rinner = minRadius + minRadius* ((float)animation.getAnimatedValue()-0.5f);
invalidate();
}
        });

anim.setDuration(500);
anim.setRepeatCount(Animation.INFINITE);
anim.setRepeatMode(Animation.REVERSE);
anim.start();
startAnim = false;
}

public void stopAnimation(){
if (anim !=null)
anim.cancel();
startAnim = true;
}

}
3.在Headerlayout 布局中添加自己的View
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical" >

    <ImageView
android:id="@+id/pull_to_refresh_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />

    <ProgressBar
android:id="@+id/pull_to_refresh_progress"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="gone" />
    <com.handmark.pulltorefresh.library.internal.MyAnimationView
android:id="@+id/pull_to_refresh_myview"
android:layout_width="100dp"
android:layout_height="50dp"
android:visibility="gone"/>
</FrameLayout>

4.在PullToRefreshBase中添加自己的模式

public static enum AnimationStyle {
/**
    * This is the default for Android-PullToRefresh. Allows you to use any
    * drawable, which is automatically rotated and used as a Progress Bar.
    */
ROTATE,

/**
    * This is the old default, and what is commonly used on iOS. Uses an
    * arrow image which flips depending on where the user has scrolled.
    */
FLIP,

MY;

   static AnimationStyle getDefault() {
return MY;
}

/**
    * Maps an int to a specific mode. This is needed when saving state, or
    * inflating the view from XML where the mode is given through a attr
    * int.
    * 
    * @param modeInt - int to map a Mode to
    * @return Mode that modeInt maps to, or ROTATE by default.
    */
static AnimationStyle mapIntToValue(int modeInt) {
switch (modeInt) {
case 0x0:
default:
return ROTATE;
         case 0x1:
return FLIP;
}
   }

   LoadingLayout createLoadingLayout(Context context, Mode mode, Orientation scrollDirection, TypedArray attrs) {
switch (this) {
case ROTATE:
default:
return new RotateLoadingLayout(context, mode, scrollDirection, attrs);
         case FLIP:
return new FlipLoadingLayout(context, mode, scrollDirection, attrs);
         case MY:
return new MyLoadingLayout(context, mode, scrollDirection, attrs);
}
   }
}

5.修改各个时段的文字

public final void refreshing() {
if (null != mHeaderText) {

int i = new Random().nextInt(3) + 1;
      if ( i == 1)
mRefreshingLabel =  mContext.getResources().getString(R.string.pull_to_refresh_refreshing_label);
      else if(i==2)
mRefreshingLabel = mContext.getResources().getString(R.string.pull_to_refresh_refreshing_lable_2);
      else  if (i ==3)
mRefreshingLabel = mContext.getResources().getString(R.string.pull_to_refresh_refreshing_lable_3);

SpannableString s = new SpannableString(mRefreshingLabel);
s.setSpan(new AbsoluteSizeSpan(12,true),4,mRefreshingLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mHeaderText.setText(s);
}

if (mUseIntrinsicAnimation) {
      ((AnimationDrawable) mHeaderImage.getDrawable()).start();
} else {
// Now call the callback
refreshingImpl();
}

if (null != mSubHeaderText) {
mSubHeaderText.setVisibility(View.GONE);
}
}




现在第三方下拉刷新的库非常多,但是比较常用的就是PullToRefresh了,因为支持的控件比较多 但是缺点就是代码量很大 原生可定制性比较差,所以花了一些时间研究了下这个库。 话不多说,说干就干。我们先集成PullToRefresh到自己的项目中。集成后项目的结构 集成完库,开始写一个简单的ListView看一下默认的效果。 1、PullToRefreshListView快速创建,看效果 MainActivity布局文件 控件初始化和加载适配器 模拟联网,两秒后设置加载完成 Adapter就是基础listView写法就不贴代码了和item自己随便写个简单的布局就好,接下来就可以看效果了。 看到这个上拉和下拉的效果,估计好多小伙伴的需求并不是要这个效果,都会要加一些自己的文字或者动画。下面我们就开始简单的定制这个上拉和下拉的布局 2、文字定制 修改文字比较简单,控件已经提供了相应的方法,直接上代码 紧紧文字的修改是远远不够的,我还要修改动画怎么办呢?下面就是引入动画 3、动画定制 接下来的定制都是需要去库里面修改代码了,可要打起精神,防止出错。 在PullTorefreshBase这个类中扎到 方法;修改以下 //此处实现了我们需要 修改的动画方法: //修改代码实现 自己的下载刷新动画 LoadingLayout createLoadingLayout(Context context, Mode mode, Orientation scrollDirection, TypedArray attrs) { switch (this) { case ROTATE: default: //帧动画 frameAnimationLayout为 自定义动画类 //通过mode判断是header还是footer if (Mode.PULL_FROM_START == mode) { return new FrameAnimationLayout(context, mode, scrollDirection, attrs); } else if (Mode.PULL_FROM_END == mode) { return new RotateLoadingLayout(context, mode, scrollDirection, attrs); } // return new RotateLoadingLayout(context, mode, scrollDirection, attrs); case FLIP: return new FlipLoadingLayout(context, mode, scrollDirection, attrs); } } 重点就在通过mode参数判断是下拉还是上拉,我可以分别修改动画。但是我还想在控件中加入一些其他文字,这就需要修改Header布局文件,进入资源文件找到,问项目中用的是ListView,所以要修改vertical这个布局,具体修改就看你公司需求自己定制了。 这里要说的是重点!因为这个布局文件header和footer共用的,如果有控件要在代码中操作它的显示与隐藏,要在LoadingLayout类的构造器中初始化对象。 在下面几行代码中,还是通过mode这个参数判断是上拉还是下拉,来区分你的操作,来显示不同的头和脚。 接下来修复添加自己的控件后会有显示方面的bug, 需要在showInvisibleViews和hideAllViews两个方法中分别把需要操作的控件分别显示和隐藏,要不下拉或者上拉距离过 大会导致控件偶尔不隐藏的bug。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值