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

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

主要要点

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)

相关推荐

  • Android程序美化之自定义ListView背景的方法

    本文实例讲述了Android程序美化之自定义ListView背景的方法.分享给大家供大家参考,具体如下: 在Android中,ListView是最常用的一个控件,在做UI设计的时候,很多人希望能够改变一下它的背景,使他能够符合整体的UI设计,改变背景背很简单只需要准备一张图片然后指定属性 android:background="@drawable/bg",不过不要高兴地太早,当你这么做以后,发现背景是变了,但是当你拖动,或者点击list空白位置的时候发现ListItem都变成黑色的了,

  • 详解Android中实现ListView左右滑动删除条目的方法

    使用Scroller实现绚丽的ListView左右滑动删除Item效果 这里来给大家带来使用Scroller的小例子,同时也能用来帮助初步解除的读者更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果.例如侧滑菜单,launcher,ListView的下拉刷新等等效果,我今天实现的是ListView的item的左右滑动删除item的效果,现在很多朋友看到这个效果应该是在Android的通知栏下拉中看到这个滑动删除的效果吧,我看到这个效果是在我之前的三星手

  • android 通过向viewpage中添加listview来完成滑动效果(类似于qq滑动界面)

    文件名:page.xml 复制代码 代码如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="fill_parent"    android:layout_height="fill

  • Android ListView滑动改变标题栏背景渐变效果

    先上ListView滑动改变标题栏背景渐变效果图,透明转变成不透明效果: 图1: 图2: 图3: 图4: 我用的是小米Note手机,状态栏高度是55px,后面会提到,这里先做个说明: 下面的内容包含了所有代码和一些测试数据: 代码: 代码很简单,也做了注释,这里就不废话了. 先来布局文件: activity的布局 activity_main_10 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/androi

  • Android实现为ListView同时设置点击时的背景和点击松手之后的背景

    本文实例讲述了Android实现为ListView同时设置点击时的背景和点击松手之后的背景.分享给大家供大家参考.具体分析如下: 这里要达到的效果是, (1)点击ListView的item时会有指定的背景, (2)松手之后,刚才点击的item也会有指定的背景 实现(1)很简单:在xml中为ListView设置listSelector即可. 复制代码 代码如下: <ListView  android:id="@+id/pop_listview_left"  android:layo

  • Android ListView的item背景色设置和item点击无响应的解决方法

    下面讲解以下在使用listview时最常见的几个问题.1.如何改变item的背景色和按下颜色 listview默认情况下,item的背景色是黑色,在用户点击时是黄色的.如果需要修改为自定义的背景颜色,一般情况下有三种方法: 1)设置listSelector 2)在布局文件中设置item的background 3)在adapter的getview中设置 这三种方法都能达到改变item默认的背景色和按下颜色,下面来分别讲解,但是在这之前需要先写好selector.xml文件; 复制代码 代码如下:

  • Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

    好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客:Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7) ,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客中断至今,中断这么久很是惭愧,后续我会尽量把该写的都补充出来.近来项目有个需求,要做个和QQ空间类似的菜单栏透明度渐变和下拉刷新带有阻尼回弹的效果.于是花点时间动手试了试,基本上达到了QQ空间的效果,截图如下: 通过观察QQ空间的运行效果,发现当往上滚动时菜单

  • Android中实现水平滑动(横向滑动)ListView示例

    水平的ListView-HorizontalListView的使用 Android中ListView默认的是竖直方向的滑动,由于项目的需求,需要ListView是水平滑动的.有很多的方式可以实现,但是比较好的一种方式就是自己封装一个控件,使用方式和ListView的使用方式是一样的.需要完善的地方:获取到的图片大小没有处理.在界面上展示的是图片的原大小.为了更好的展示效果,应该压缩成统一的尺寸. HorizontalListView.java 代码如下: /** * 横向的ListView *

  • Android 中实现ListView滑动隐藏标题栏的代码

    布局中listview要覆盖标题栏 int mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop(); //滑动监听 showHideTitleBar(true); ListView standby_lv = (ListView) findViewById(R.id.standby_lv); standby_lv.setOnTouchListener(new View.OnTouchListener() { @Override p

  • Android开发之ListView的head消失页面导航栏的渐变出现和隐藏

    1.Fragment页面xml布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"

随机推荐