Android自定义渐变式炫酷ListView下拉刷新动画
作者:莫禄 发布时间:2021-12-26 17:31:09
标签: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)
来源:http://blog.csdn.net/u012891600/article/details/55144013
0
投稿
猜你喜欢
- 动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的
- 前言Java中共有八种基本数据类型:byte,int,short,long,float,double,char,boolean。计算机中的基
- 之前在项目中会用到在Java在后台把数据填入Word文档的模板来提供前台下载,为了自己能随时查看当时的实现方案及方便他人学习我写了这篇博客,
- c#调用surfer软件,添加应用的方法:1.在项目文件上右键->添加引用2.选择COM标签页3.找到Surfer 9 type li
- 1-:生成一个签名密钥你可以用keytool命令生成一个私有密钥。在Windows上keytool命令放在JDK的bin目录中(比如C:\P
- 1、通过C#调用Java的方法:在C#中添加调用的一些代码,利用Unity提供的一些接口实现调用Java!private const str
- 代码从windows下visual studio到andriod平台迁移实现步骤:前言前言也是迁言,从windows的visual stud
- 线程封闭线程封闭一般通过以下三个方法:Ad-hoc线程封闭:程序控制实现,最糟糕,忽略堆栈封闭:局部变量,无并发问题ThreadLocal线
- Android内部没有控件来直接显示文档,跳转WPS或其他第三方文档App体验性不好,使用腾讯X5内核能很好的解决的这一问题。一、下载腾讯X
- 一、this关键字this是一个引用,它指向自身的这个对象。看内存分析图:假设我们在堆内存new了一个对象,在这个对象里面你想象着他有一个引
- 首先看下我们要分析的代码段如下:输出结果如下:输出结果(a).PNG输出结果(b).PNG输出结果(c).PNG括号里是一个二元式:(单词类
- 不依赖任何外界包,maven如何生成可以执行的jar?pom中不包含任何引用的情况下,只需要在pom中添加 maven-jar-plugin
- 1.1 概述分布式系统:分布式系统指由很多台计算机组成的一个整体!这个整体一致对外,并且处理同一请求!系统对内透明,对外不透明!内部的每台计
- foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有item,index,colle
- Retrofit 基本使用implementation 'com.squareup.retrofit2:retrofit:2.9.0
- 背景我们在android超级优化-线程监控与线程统一可以知道,我们能够通过asm插桩的方式,进行了线程的监控与线程的统一,通过一系列的黑科技
- 二分查找又称折半查找,它是一种效率较高的查找方法。折半查找的算法思想是将数列按有序化(递增或递减)排列,查找过程中采用跳跃式方式查找,即先以
- 矢量室内地图开发因为公司项目的需要,需要开发一套室内地图,并实现路线的规划功能。因为之前没做过这方面的开发,相关的资料也比较少,所以只能一个
- 本文实例讲述了Java System类用法。分享给大家供大家参考,具体如下:一 使用System类访问系统属性1 代码import java
- 一、简介Mybatis-Plus(简称MP)是一个 Mybatis 的一个增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发