Android开发中利用ListView实现一个渐变式的下拉刷新动画

发布时间:2020-11-23 16:50:31

来源:亿速云

阅读:80

作者:Leah

本篇文章给大家分享的是有关Android开发中利用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);

}

}

以上就是Android开发中利用ListView实现一个渐变式的下拉刷新动画,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注亿速云行业资讯频道。

android listview下拉动画效果,Android开发中利用ListView实现一个渐变式的下拉刷新动画...相关推荐

  1. android打地鼠设计报告,android开发中利用handler制作一个打地鼠小游戏

    android开发中利用handler制作一个打地鼠小游戏 发布时间:2020-11-25 15:21:11 来源:亿速云 阅读:136 作者:Leah 这期内容当中小编将会给大家带来有关androi ...

  2. android listview左右滑动动画效果,Android基于ListView实现类似QQ空间的滚动翻页与滚动加载效果...

    本文实例讲述了Android基于ListView实现类似QQ空间的滚动翻页与滚动加载效果.分享给大家供大家参考,具体如下: 1. 滚动加载 listView.setOnScrollListener(n ...

  3. android动画view上移,在Android开发中使用View制作一个引导动画

    在Android开发中使用View制作一个引导动画 发布时间:2020-11-20 16:46:16 来源:亿速云 阅读:98 作者:Leah 这篇文章将为大家详细讲解有关在Android开发中使用V ...

  4. android 自定义view 动画效果,Android自定义view实现阻尼效果的加载动画

    效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又 ...

  5. android开发上下翻转动画,怎么在android中利用FlipAnimation实现一个3D垂直翻转动画...

    怎么在android中利用FlipAnimation实现一个3D垂直翻转动画 发布时间:2021-02-20 17:08:30 来源:亿速云 阅读:122 作者:Leah 这期内容当中小编将会给大家带 ...

  6. android 悬浮按钮 功能实现,怎么在Android中利用FloatingActionButton实现一个悬浮按钮效果...

    怎么在Android中利用FloatingActionButton实现一个悬浮按钮效果 发布时间:2020-12-02 17:41:30 来源:亿速云 阅读:238 作者:Leah 今天就跟大家聊聊有 ...

  7. 【web前端特效源码】使用 HTMLCSSJavaScript实现各种跳跃浮动慢跑翻转旋转坠落的魔幻文字动画效果~太上头了~/动画效果|前端开发|IT软件开发基础入门教程|网页制作|网站开发定制

    b站视频演示效果: [web前端特效源码]使用 HTML&CSS&JavaScript实现各种跳跃浮动慢跑翻转旋转坠落的魔幻文字动画效果~太上头了~/动画效果|前端开发|IT软件开 效 ...

  8. android progressdialog 背景色,怎么在android中利用ProgressDialog实现一个加载效果

    怎么在android中利用ProgressDialog实现一个加载效果 发布时间:2020-12-07 17:00:07 来源:亿速云 阅读:77 作者:Leah 怎么在android中利用Progr ...

  9. 【前端实例代码】使用 HTML CSS实现指纹扫描仪特效动画效果 |前端开发 网页制作 基础入门教程 网页开发中常见的样式与特效,收藏起来肯定用的上~

    b站视频演示效果: [web前端特效源码]使用 HTML CSS 和 JavaScript 实现指纹扫描仪特效动画效果 |前端开发 网页制作 基础入门教程 效果图: 完整代码: <!DOCTYP ...

最新文章

  1. 简单粗暴理解与实现机器学习之逻辑回归:逻辑回归介绍、应用场景、原理、损失以及优化...
  2. Nginx安装学习使用具体记录
  3. 浏览器是如何工作的系列:渲染引擎
  4. Linux 进程、父进程、子进程
  5. 历史上有关人工智能的七个片段
  6. 深度学习auc_机器学习集成学习与模型融合!
  7. 一组匹配中国大陆手机号的正则表达式
  8. spring集成web
  9. SQL Server商业智能–简介
  10. A*算法及其matlab实现
  11. Python常用的字符串操作
  12. Python eval 函数
  13. 工大瑞普Dynamips使用教程图解(傻瓜版)
  14. 免登陆Oracle官网下载JDK
  15. ppoe拨号被远程计算机终止,Windows各操作系统常见PPPoE拨号错误代码说明
  16. 百度编辑器ueditor表格无法显示边框以及边框颜色等系列问题解决方案
  17. c++实验3-个人所得税计算器
  18. 两个故事理解时间管理的必要
  19. 【2016浙江省赛:区间取模】E : Modulo Query | ZOJ - 3940
  20. 用dd实现linux硬盘备份

热门文章

  1. 用Python编写小工具下载OSM路网数据
  2. 如何科学的打开 Leetcode
  3. Netflix提出梯度提升决策树网络Hammock!
  4. Android Settings中显示自定义dialog
  5. 一个将当前目录下HEX文件的第一行数据删除的程序...
  6. 将HTML字符转换为DOM节点并动态添加到文档中
  7. loadrunner代理录制脚本方法介绍
  8. 十三、流程控制之if语句
  9. 视觉平衡与物理平衡_设计中的视觉平衡
  10. ssh作业批改系统_如何看待「全国至少十省份叫停家长批改作业」?能否从根源解决问题?...