自定义渐变式炫酷动画的ListView下拉刷新

自定义渐变式炫酷动画的ListView下拉刷新

装载请注明出处:http://blog.csdn.net/u012891600/article/details/55144013


主要要点

listview刷新过程中主要有三个步骤当前:状态为下拉刷新,当前状态为下拉刷新,当前状态为放开刷新,当前状态为正在刷新;主要思路为三个步骤分别对应三个自定义的view;即ibuRefreshFirstStepView,ibuRefreshSecondStepView,ibuRefreshThirdStepView。

效果图

这里写图片描述

代码块

ibuRefreshFirstStepView代码,例如:


    private Bitmap initialBitmap;
    private float mCurrentProgress;
    private Bitmap scaledBitmap;

    public ibuRefreshFirstStepView(Context context, AttributeSet attrs,
                                   int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    public ibuRefreshFirstStepView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

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

    private void init(Context context) {
        //这个就是那个火箭图片
        initialBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.img_huojian1));

    }

    /**
     * 重写onMeasure方法主要是设置wrap_content时 View的大小
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //根据设置的宽度来计算高度  设置为符合第二阶段娃娃图片的宽高比例
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureWidth(widthMeasureSpec)*initialBitmap.getHeight()/initialBitmap.getWidth());
    }

    /**
     * 当wrap_content的时候,宽度即为第二阶段娃娃图片的宽度
     * @param widMeasureSpec
     * @return
     */
    private int measureWidth(int widMeasureSpec){
        int result = 0;
        int size = MeasureSpec.getSize(widMeasureSpec);
        int mode = MeasureSpec.getMode(widMeasureSpec);
        if (mode == MeasureSpec.EXACTLY){
            result = size;
        }else{
            result = initialBitmap.getWidth();
            if (mode == MeasureSpec.AT_MOST){
                result = Math.min(result,size);
            }
        }
        return result;
        }

    /**
     * 在onLayout里面获得测量后View的宽高
     * @param changed
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // 给火箭图片进行等比例的缩放
        scaledBitmap = Bitmap.createScaledBitmap(initialBitmap,89,110, false);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //这个方法是对画布进行缩放,从而达到椭圆形图片的缩放,第一个参数为宽度缩放比例,第二个参数为高度缩放比例,
//      canvas.scale(mCurrentProgress, mCurrentProgress, measuredWidth/2, measuredHeight/2);
        //将等比例缩放后的椭圆形画在画布上面
        canvas.drawBitmap(scaledBitmap,90,dip2px(getContext(),80*mCurrentProgress),null);

    }
    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
    /**
     * 设置缩放比例,从0到1  0为最小 1为最大
     * @param currentProgress
     */
    public void setCurrentProgress(float currentProgress){
        mCurrentProgress = currentProgress;
    }


}

代码块

ibuRefreshSecondStepView代码,例如:


    private Bitmap endBitmap,scaledBitmap;

    public ibuRefreshSecondStepView(Context context, AttributeSet attrs,
                                    int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public ibuRefreshSecondStepView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

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

    private void init() {
        endBitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.img_huojian2), 89, 110, false);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureWidth(widthMeasureSpec) * endBitmap.getHeight() / endBitmap.getWidth());
    }
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        scaledBitmap = Bitmap.createScaledBitmap(endBitmap, 89, 110, false);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(endBitmap, 90, dip2px(getContext(), 80 * 1), null);

    }
    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
    private int measureWidth(int widthMeasureSpec){
        int result = 0;
        int size = MeasureSpec.getSize(widthMeasureSpec);
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        }else {
            result = endBitmap.getWidth();
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }
}

代码块

ibuRefreshThirdStepView代码,例如:


    private Bitmap endBitmap,scaledBitmap;

    public ibuRefreshThirdStepView(Context context, AttributeSet attrs,
                                   int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public ibuRefreshThirdStepView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

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

    private void init() {
        endBitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.img_huojian3), 89, 170, false);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(endBitmap, 90, dip2px(getContext(), 40 * 1), null);
    }
    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureWidth(widthMeasureSpec)*endBitmap.getHeight()/endBitmap.getWidth());
    }

    private int measureWidth(int widthMeasureSpec){
        int result = 0;
        int size = MeasureSpec.getSize(widthMeasureSpec);
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        }else {
            result = endBitmap.getWidth();
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

代码块

IbuListView 代码,例如:

    private static final int DONE = 0;
    private static final int PULL_TO_REFRESH = 1;
    private static final int RELEASE_TO_REFRESH = 2;
    private static final int REFRESHING = 3;
    private static final int RATIO = 3;
    private RelativeLayout headerView;
    private int headerViewHeight;
    private float startY;
    private float offsetY;
    private TextView tv_pull_to_refresh;
    private OnMeiTuanRefreshListener mOnRefreshListener;
    private int state;
    private int mFirstVisibleItem;
    private boolean isRecord;
    private boolean isEnd;
    private boolean isRefreable;
    private FrameLayout mAnimContainer;
//  private Animation animation;
    private SimpleDateFormat format;
    private ibuRefreshFirstStepView mFirstView;
    private ibuRefreshSecondStepView mSecondView;
    private AnimationDrawable secondAnim;
    private ibuRefreshThirdStepView mThirdView;
    private AnimationDrawable thirdAnim;

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

    public IbuListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public IbuListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public interface OnMeiTuanRefreshListener{
        void onRefresh();
    }

    /**
     * 回调接口,想实现下拉刷新的listview实现此接口
     * @param onRefreshListener
     */
    public void setOnMeiTuanRefreshListener(OnMeiTuanRefreshListener onRefreshListener){
        mOnRefreshListener = onRefreshListener;
        isRefreable = true;
    }

    /**
     * 刷新完毕,从主线程发送过来,并且改变headerView的状态和文字动画信息
     */
    public void setOnRefreshComplete(){
        //一定要将isEnd设置为true,以便于下次的下拉刷新
        isEnd = true;
        state = DONE;

        changeHeaderByState(state);
    }

    private ImageView imageViewBack,imageView_B;
    private void init(Context context) {
        setOverScrollMode(View.OVER_SCROLL_NEVER);
        setOnScrollListener(this);

        headerView = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.ibu_item, null, false);
        imageViewBack= (ImageView) headerView.findViewById(R.id.icon_back);
        imageView_B= (ImageView) headerView.findViewById(R.id.image_b);
        mFirstView = (ibuRefreshFirstStepView) headerView.findViewById(R.id.first_view);
        tv_pull_to_refresh = (TextView) headerView.findViewById(R.id.tv_pull_to_refresh);
        mSecondView = (ibuRefreshSecondStepView) headerView.findViewById(R.id.second_view);

        mThirdView = (ibuRefreshThirdStepView) headerView.findViewById(R.id.third_view);


        measureView(headerView);
        addHeaderView(headerView);
        headerViewHeight = headerView.getMeasuredHeight();
        headerView.setPadding(0, -headerViewHeight, 0, 0);
        Log.i("zhangqi","headerViewHeight="+headerViewHeight);

        state = DONE;
        isEnd = true;
        isRefreable = false;
    }




    @Override
    public void onScrollStateChanged(AbsListView absListView, int i) {
    }
    @Override
    public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mFirstVisibleItem = firstVisibleItem;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (isEnd) {//如果现在时结束的状态,即刷新完毕了,可以再次刷新了,在onRefreshComplete中设置
            if (isRefreable) {//如果现在是可刷新状态   在setOnMeiTuanListener中设置为true
                switch (ev.getAction()){
                    //用户按下
                case MotionEvent.ACTION_DOWN:
                    //如果当前是在listview顶部并且没有记录y坐标
                    if (mFirstVisibleItem == 0 && !isRecord) {
                        //将isRecord置为true,说明现在已记录y坐标
                        isRecord = true;
                        //将当前y坐标赋值给startY起始y坐标
                        startY = ev.getY();
                    }
                    imageView_B.setVisibility(VISIBLE);
                    break;
                //用户滑动
                case MotionEvent.ACTION_MOVE:
                    //再次得到y坐标,用来和startY相减来计算offsetY位移值
                    float tempY = ev.getY();
                    //再起判断一下是否为listview顶部并且没有记录y坐标
                    if (mFirstVisibleItem == 0 && !isRecord) {
                        isRecord = true;
                        startY = tempY;
                    }
                    //如果当前状态不是正在刷新的状态,并且已经记录了y坐标
                    if (state!=REFRESHING && isRecord ) {
                        //计算y的偏移量
                        offsetY = tempY - startY;
                        //计算当前滑动的高度
                        float currentHeight = (-headerViewHeight+offsetY/3);
                        //用当前滑动的高度和头部headerView的总高度进行比 计算出当前滑动的百分比 0到1
                        float currentProgress = 1+currentHeight/headerViewHeight;
                        //如果当前百分比大于1了,将其设置为1,目的是让第一个状态的椭圆不再继续变大
                        if (currentProgress>=1) {
                            currentProgress = 1;
                        }
                        //如果当前的状态是放开刷新,并且已经记录y坐标
                        if (state == RELEASE_TO_REFRESH && isRecord) {
                            setSelection(0);
                            //如果当前滑动的距离小于headerView的总高度
                            if (-headerViewHeight+offsetY/RATIO<0) {
                                //将状态置为下拉刷新状态
                                state = PULL_TO_REFRESH;
                                //根据状态改变headerView,主要是更新动画和文字等信息
                                changeHeaderByState(state);
                                //如果当前y的位移值小于0,即为headerView隐藏了
                            }else if (offsetY<=0) {
                                //将状态变为done
                                state = DONE;
                                //根据状态改变headerView,主要是更新动画和文字等信息
                                changeHeaderByState(state);
                            }
                        }
                        //如果当前状态为下拉刷新并且已经记录y坐标
                        if (state == PULL_TO_REFRESH && isRecord) {
                            setSelection(0);
                            //如果下拉距离大于等于headerView的总高度
                            if (-headerViewHeight+offsetY/RATIO>=0) {
                                //将状态变为放开刷新
                                state = RELEASE_TO_REFRESH;
                                //根据状态改变headerView,主要是更新动画和文字等信息
                                changeHeaderByState(state);
                                //如果当前y的位移值小于0,即为headerView隐藏了
                            }else if (offsetY<=0) {
                                //将状态变为done
                                state = DONE;
                                //根据状态改变headerView,主要是更新动画和文字等信息
                                changeHeaderByState(state);
                            }
                        }
                        //如果当前状态为done并且已经记录y坐标
                        if (state == DONE && isRecord) {
                            //如果位移值大于0
                            if (offsetY>=0) {
                                //将状态改为下拉刷新状态
                                state = PULL_TO_REFRESH;
                            }
                        }
                        //如果为下拉刷新状态
                        if (state == PULL_TO_REFRESH) {
                            //则改变headerView的padding来实现下拉的效果
                            headerView.setPadding(0,(int)(-headerViewHeight+offsetY/RATIO) ,0,0);
                            //给第一个状态的View设置当前进度值
                            mFirstView.setCurrentProgress(currentProgress);
                            //重画
                            mFirstView.postInvalidate();
                        }
                        //如果为放开刷新状态
                        if (state == RELEASE_TO_REFRESH) {
                            //改变headerView的padding值
                            headerView.setPadding(0,(int)(-headerViewHeight+offsetY/RATIO) ,0, 0);
                            //给第一个状态的View设置当前进度值
                            mFirstView.setCurrentProgress(currentProgress);
                            //重画
                            mFirstView.postInvalidate();
                        }
                    }
                    break;
                //当用户手指抬起时
                case MotionEvent.ACTION_UP:
                    //如果当前状态为下拉刷新状态
                    if (state == PULL_TO_REFRESH) {
                        //平滑的隐藏headerView
                        this.smoothScrollBy((int)(-headerViewHeight+offsetY/RATIO)+headerViewHeight, 500);
                        //根据状态改变headerView
                        changeHeaderByState(state);
                    }
                    //如果当前状态为放开刷新
                    if (state == RELEASE_TO_REFRESH) {
                        //平滑的滑到正好显示headerView
                        this.smoothScrollBy((int)(-headerViewHeight+offsetY/RATIO), 500);
                        //将当前状态设置为正在刷新
                        state = REFRESHING;
                        //回调接口的onRefresh方法
                        mOnRefreshListener.onRefresh();
                        //根据状态改变headerView
                        changeHeaderByState(state);
                    }
                    //这一套手势执行完,一定别忘了将记录y坐标的isRecord改为false,以便于下一次手势的执行
                    isRecord = false;
                    break;
                }

            }
        }
        return super.onTouchEvent(ev);
    }

    private Animation animation;
    /**
     * 根据状态改变headerView的动画和文字显示
     * @param state
     */
    private void changeHeaderByState(int state){
        switch (state) {
        case DONE://如果的隐藏的状态
            //设置headerView的padding为隐藏
            headerView.setPadding(0, -headerViewHeight, 0, 0);
            //第一状态的view显示出来
            mFirstView.setVisibility(View.VISIBLE);


            imageView_B.setVisibility(VISIBLE);
            tv_pull_to_refresh.setText("下拉刷新");
            //第二状态的view隐藏起来
            mSecondView.setVisibility(View.GONE);
            //停止第二状态的动画
//          secondAnim.stop();
            //第三状态的view隐藏起来
            mThirdView.setVisibility(View.GONE);
            //停止第三状态的动画
//          thirdAnim.stop();
            break;
        case RELEASE_TO_REFRESH://当前状态为放开刷新
            //文字显示为放开刷新
            tv_pull_to_refresh.setText("放开刷新");
            //第一状态view隐藏起来
            mFirstView.setVisibility(View.GONE);
            //第二状态view显示出来
            mSecondView.setVisibility(View.VISIBLE);
            //播放第二状态的动画
//          secondAnim.start();
            //第三状态view隐藏起来
            mThirdView.setVisibility(View.GONE);
            //停止第三状态的动画
//          thirdAnim.stop();
            break;
        case PULL_TO_REFRESH://当前状态为下拉刷新
            imageView_B.setVisibility(VISIBLE);
            //设置文字为下拉刷新
            tv_pull_to_refresh.setText("下拉刷新");
            //第一状态view显示出来
            mFirstView.setVisibility(View.VISIBLE);
            //第二状态view隐藏起来
            mSecondView.setVisibility(View.GONE);
            //第二状态动画停止
//          secondAnim.stop();
            //第三状态view隐藏起来
            mThirdView.setVisibility(View.GONE);
            //第三状态动画停止
//          thirdAnim.stop();
            break;
        case REFRESHING://当前状态为正在刷新
            //文字设置为正在刷新
            tv_pull_to_refresh.setText("正在刷新");
            //第一状态view隐藏起来
            mFirstView.setVisibility(View.GONE);
            //第三状态view显示出来
            mThirdView.setVisibility(View.VISIBLE);
            //第二状态view隐藏起来
            mSecondView.setVisibility(View.GONE);
            //停止第二状态动画
//          secondAnim.stop();
            //启动第三状态view
//          thirdAnim.start();
            imageView_B.setVisibility(GONE);
             animation = new TranslateAnimation(0, 0, 0, 600);
            animation.setDuration(3000);
            imageViewBack.setAnimation(animation);
            break;
        default:
            break;
        }
    }


    private void measureView(View child) {
        ViewGroup.LayoutParams p = child.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_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);
    }


}

github代码:

项目代码下载(https://github.com/molu0007/IBU_ListView)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值