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

作者:莫禄 时间:2021-12-26 17:31:09 

本文实例为大家分享了自定义渐变式炫酷动画的ListView下拉刷新,供大家参考,具体内容如下

主要要点

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

效果图

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

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)

来源:http://blog.csdn.net/u012891600/article/details/55144013

标签:Android,ListView,下拉刷新
0
投稿

猜你喜欢

  • Java线程中卖火车票问题的深入讲解

    2022-07-22 07:38:52
  • JAVA 字符串加密、密码加密实现方法

    2023-11-28 04:08:09
  • Android sharedPreferences实现记住密码功能

    2022-09-11 13:58:37
  • C++编程中用put输出单个字符和cin输入流的用法

    2023-05-28 06:14:49
  • java常用工具类之DES和Base64加密解密类

    2023-12-20 17:48:31
  • Android开发-之五大布局详解

    2021-11-30 14:24:30
  • Android 扫描WIFI权限详解

    2023-07-03 11:34:32
  • java 中遍历取值异常(Hashtable Enumerator)解决办法

    2023-08-06 05:17:08
  • Android通讯录开发之删除功能的实现方法

    2021-07-06 10:43:53
  • Mybatis环境配置及测试详解

    2023-11-24 07:06:25
  • kotlin之协程的理解与使用详解

    2023-10-21 15:55:57
  • C#私有构造函数使用示例

    2021-12-31 02:14:53
  • java实现客房管理系统

    2022-10-15 23:56:33
  • Android实现声音采集回声与回声消除

    2022-09-28 12:13:17
  • Map集合之HashMap的使用及说明

    2022-07-23 15:56:56
  • IntelliJ IDEA中显示和关闭工具栏与目录栏的方法

    2023-09-01 23:18:59
  • SpringBoot微信扫码支付的实现示例

    2023-02-08 03:03:44
  • Java中ReentrantLock4种常见的坑

    2021-09-26 10:51:46
  • java组件fileupload文件上传demo

    2022-09-14 11:00:45
  • Java中ArrayList和LinkedList的遍历与性能分析

    2023-10-31 11:04:17
  • asp之家 软件编程 m.aspxhome.com