Android自定義漸變式炫酷ListView下拉刷新動(dòng)畫(huà)
本文實(shí)例為大家分享了自定義漸變式炫酷動(dòng)畫(huà)的ListView下拉刷新,供大家參考,具體內(nèi)容如下
主要要點(diǎn)
listview刷新過(guò)程中主要有三個(gè)步驟當(dāng)前:狀態(tài)為下拉刷新,當(dāng)前狀態(tài)為下拉刷新,當(dāng)前狀態(tài)為放開(kāi)刷新,當(dāng)前狀態(tài)為正在刷新;主要思路為三個(gè)步驟分別對(duì)應(yīng)三個(gè)自定義的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) {
//這個(gè)就是那個(gè)火箭圖片
initialBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.img_huojian1));
}
/**
* 重寫(xiě)onMeasure方法主要是設(shè)置wrap_content時(shí) View的大小
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//根據(jù)設(shè)置的寬度來(lái)計(jì)算高度 設(shè)置為符合第二階段娃娃圖片的寬高比例
setMeasuredDimension(measureWidth(widthMeasureSpec),measureWidth(widthMeasureSpec)*initialBitmap.getHeight()/initialBitmap.getWidth());
}
/**
* 當(dāng)wrap_content的時(shí)候,寬度即為第二階段娃娃圖片的寬度
* @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里面獲得測(cè)量后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);
// 給火箭圖片進(jìn)行等比例的縮放
scaledBitmap = Bitmap.createScaledBitmap(initialBitmap,89,110, false);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//這個(gè)方法是對(duì)畫(huà)布進(jìn)行縮放,從而達(dá)到橢圓形圖片的縮放,第一個(gè)參數(shù)為寬度縮放比例,第二個(gè)參數(shù)為高度縮放比例,
// canvas.scale(mCurrentProgress, mCurrentProgress, measuredWidth/2, measuredHeight/2);
//將等比例縮放后的橢圓形畫(huà)在畫(huà)布上面
canvas.drawBitmap(scaledBitmap,90,dip2px(getContext(),80*mCurrentProgress),null);
}
/**
* 根據(jù)手機(jī)的分辨率從 dp 的單位 轉(zhuǎn)成為 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 設(shè)置縮放比例,從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);
}
/**
* 根據(jù)手機(jī)的分辨率從 dp 的單位 轉(zhuǎn)成為 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);
}
/**
* 根據(jù)手機(jī)的分辨率從 dp 的單位 轉(zhuǎn)成為 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();
}
/**
* 回調(diào)接口,想實(shí)現(xiàn)下拉刷新的listview實(shí)現(xiàn)此接口
* @param onRefreshListener
*/
public void setOnMeiTuanRefreshListener(OnMeiTuanRefreshListener onRefreshListener){
mOnRefreshListener = onRefreshListener;
isRefreable = true;
}
/**
* 刷新完畢,從主線程發(fā)送過(guò)來(lái),并且改變headerView的狀態(tài)和文字動(dòng)畫(huà)信息
*/
public void setOnRefreshComplete(){
//一定要將isEnd設(shè)置為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) {//如果現(xiàn)在時(shí)結(jié)束的狀態(tài),即刷新完畢了,可以再次刷新了,在onRefreshComplete中設(shè)置
if (isRefreable) {//如果現(xiàn)在是可刷新?tīng)顟B(tài) 在setOnMeiTuanListener中設(shè)置為true
switch (ev.getAction()){
//用戶按下
case MotionEvent.ACTION_DOWN:
//如果當(dāng)前是在listview頂部并且沒(méi)有記錄y坐標(biāo)
if (mFirstVisibleItem == 0 && !isRecord) {
//將isRecord置為true,說(shuō)明現(xiàn)在已記錄y坐標(biāo)
isRecord = true;
//將當(dāng)前y坐標(biāo)賦值給startY起始y坐標(biāo)
startY = ev.getY();
}
imageView_B.setVisibility(VISIBLE);
break;
//用戶滑動(dòng)
case MotionEvent.ACTION_MOVE:
//再次得到y(tǒng)坐標(biāo),用來(lái)和startY相減來(lái)計(jì)算offsetY位移值
float tempY = ev.getY();
//再起判斷一下是否為listview頂部并且沒(méi)有記錄y坐標(biāo)
if (mFirstVisibleItem == 0 && !isRecord) {
isRecord = true;
startY = tempY;
}
//如果當(dāng)前狀態(tài)不是正在刷新的狀態(tài),并且已經(jīng)記錄了y坐標(biāo)
if (state!=REFRESHING && isRecord ) {
//計(jì)算y的偏移量
offsetY = tempY - startY;
//計(jì)算當(dāng)前滑動(dòng)的高度
float currentHeight = (-headerViewHeight+offsetY/3);
//用當(dāng)前滑動(dòng)的高度和頭部headerView的總高度進(jìn)行比 計(jì)算出當(dāng)前滑動(dòng)的百分比 0到1
float currentProgress = 1+currentHeight/headerViewHeight;
//如果當(dāng)前百分比大于1了,將其設(shè)置為1,目的是讓第一個(gè)狀態(tài)的橢圓不再繼續(xù)變大
if (currentProgress>=1) {
currentProgress = 1;
}
//如果當(dāng)前的狀態(tài)是放開(kāi)刷新,并且已經(jīng)記錄y坐標(biāo)
if (state == RELEASE_TO_REFRESH && isRecord) {
setSelection(0);
//如果當(dāng)前滑動(dòng)的距離小于headerView的總高度
if (-headerViewHeight+offsetY/RATIO<0) {
//將狀態(tài)置為下拉刷新?tīng)顟B(tài)
state = PULL_TO_REFRESH;
//根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫(huà)和文字等信息
changeHeaderByState(state);
//如果當(dāng)前y的位移值小于0,即為headerView隱藏了
}else if (offsetY<=0) {
//將狀態(tài)變?yōu)閐one
state = DONE;
//根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫(huà)和文字等信息
changeHeaderByState(state);
}
}
//如果當(dāng)前狀態(tài)為下拉刷新并且已經(jīng)記錄y坐標(biāo)
if (state == PULL_TO_REFRESH && isRecord) {
setSelection(0);
//如果下拉距離大于等于headerView的總高度
if (-headerViewHeight+offsetY/RATIO>=0) {
//將狀態(tài)變?yōu)榉砰_(kāi)刷新
state = RELEASE_TO_REFRESH;
//根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫(huà)和文字等信息
changeHeaderByState(state);
//如果當(dāng)前y的位移值小于0,即為headerView隱藏了
}else if (offsetY<=0) {
//將狀態(tài)變?yōu)閐one
state = DONE;
//根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫(huà)和文字等信息
changeHeaderByState(state);
}
}
//如果當(dāng)前狀態(tài)為done并且已經(jīng)記錄y坐標(biāo)
if (state == DONE && isRecord) {
//如果位移值大于0
if (offsetY>=0) {
//將狀態(tài)改為下拉刷新?tīng)顟B(tài)
state = PULL_TO_REFRESH;
}
}
//如果為下拉刷新?tīng)顟B(tài)
if (state == PULL_TO_REFRESH) {
//則改變headerView的padding來(lái)實(shí)現(xiàn)下拉的效果
headerView.setPadding(0,(int)(-headerViewHeight+offsetY/RATIO) ,0,0);
//給第一個(gè)狀態(tài)的View設(shè)置當(dāng)前進(jìn)度值
mFirstView.setCurrentProgress(currentProgress);
//重畫(huà)
mFirstView.postInvalidate();
}
//如果為放開(kāi)刷新?tīng)顟B(tài)
if (state == RELEASE_TO_REFRESH) {
//改變headerView的padding值
headerView.setPadding(0,(int)(-headerViewHeight+offsetY/RATIO) ,0, 0);
//給第一個(gè)狀態(tài)的View設(shè)置當(dāng)前進(jìn)度值
mFirstView.setCurrentProgress(currentProgress);
//重畫(huà)
mFirstView.postInvalidate();
}
}
break;
//當(dāng)用戶手指抬起時(shí)
case MotionEvent.ACTION_UP:
//如果當(dāng)前狀態(tài)為下拉刷新?tīng)顟B(tài)
if (state == PULL_TO_REFRESH) {
//平滑的隱藏headerView
this.smoothScrollBy((int)(-headerViewHeight+offsetY/RATIO)+headerViewHeight, 500);
//根據(jù)狀態(tài)改變headerView
changeHeaderByState(state);
}
//如果當(dāng)前狀態(tài)為放開(kāi)刷新
if (state == RELEASE_TO_REFRESH) {
//平滑的滑到正好顯示headerView
this.smoothScrollBy((int)(-headerViewHeight+offsetY/RATIO), 500);
//將當(dāng)前狀態(tài)設(shè)置為正在刷新
state = REFRESHING;
//回調(diào)接口的onRefresh方法
mOnRefreshListener.onRefresh();
//根據(jù)狀態(tài)改變headerView
changeHeaderByState(state);
}
//這一套手勢(shì)執(zhí)行完,一定別忘了將記錄y坐標(biāo)的isRecord改為false,以便于下一次手勢(shì)的執(zhí)行
isRecord = false;
break;
}
}
}
return super.onTouchEvent(ev);
}
private Animation animation;
/**
* 根據(jù)狀態(tài)改變headerView的動(dòng)畫(huà)和文字顯示
* @param state
*/
private void changeHeaderByState(int state){
switch (state) {
case DONE://如果的隱藏的狀態(tài)
//設(shè)置headerView的padding為隱藏
headerView.setPadding(0, -headerViewHeight, 0, 0);
//第一狀態(tài)的view顯示出來(lái)
mFirstView.setVisibility(View.VISIBLE);
imageView_B.setVisibility(VISIBLE);
tv_pull_to_refresh.setText("下拉刷新");
//第二狀態(tài)的view隱藏起來(lái)
mSecondView.setVisibility(View.GONE);
//停止第二狀態(tài)的動(dòng)畫(huà)
secondAnim.stop();
//第三狀態(tài)的view隱藏起來(lái)
mThirdView.setVisibility(View.GONE);
//停止第三狀態(tài)的動(dòng)畫(huà)
thirdAnim.stop();
break;
case RELEASE_TO_REFRESH://當(dāng)前狀態(tài)為放開(kāi)刷新
//文字顯示為放開(kāi)刷新
tv_pull_to_refresh.setText("放開(kāi)刷新");
//第一狀態(tài)view隱藏起來(lái)
mFirstView.setVisibility(View.GONE);
//第二狀態(tài)view顯示出來(lái)
mSecondView.setVisibility(View.VISIBLE);
//播放第二狀態(tài)的動(dòng)畫(huà)
secondAnim.start();
//第三狀態(tài)view隱藏起來(lái)
mThirdView.setVisibility(View.GONE);
//停止第三狀態(tài)的動(dòng)畫(huà)
thirdAnim.stop();
break;
case PULL_TO_REFRESH://當(dāng)前狀態(tài)為下拉刷新
imageView_B.setVisibility(VISIBLE);
//設(shè)置文字為下拉刷新
tv_pull_to_refresh.setText("下拉刷新");
//第一狀態(tài)view顯示出來(lái)
mFirstView.setVisibility(View.VISIBLE);
//第二狀態(tài)view隱藏起來(lái)
mSecondView.setVisibility(View.GONE);
//第二狀態(tài)動(dòng)畫(huà)停止
secondAnim.stop();
//第三狀態(tài)view隱藏起來(lái)
mThirdView.setVisibility(View.GONE);
//第三狀態(tài)動(dòng)畫(huà)停止
thirdAnim.stop();
break;
case REFRESHING://當(dāng)前狀態(tài)為正在刷新
//文字設(shè)置為正在刷新
tv_pull_to_refresh.setText("正在刷新");
//第一狀態(tài)view隱藏起來(lái)
mFirstView.setVisibility(View.GONE);
//第三狀態(tài)view顯示出來(lái)
mThirdView.setVisibility(View.VISIBLE);
//第二狀態(tài)view隱藏起來(lái)
mSecondView.setVisibility(View.GONE);
//停止第二狀態(tài)動(dòng)畫(huà)
secondAnim.stop();
//啟動(dòng)第三狀態(tài)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代碼:
項(xiàng)目代碼下載(https://github.com/molu0007/IBU_ListView)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android編程實(shí)現(xiàn)根據(jù)不同日期計(jì)算天數(shù)差的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)根據(jù)不同日期計(jì)算天數(shù)差的方法,涉及Android調(diào)用日期類(lèi)Calendar實(shí)現(xiàn)時(shí)間運(yùn)算的相關(guān)技巧,需要的朋友可以參考下2016-03-03
Android創(chuàng)建文件時(shí)出現(xiàn)java.io.IOException:?Operation?not?permitte
最近使用android10創(chuàng)建文件失敗,并拋出權(quán)限異常,這篇文章主要給大家介紹了Android創(chuàng)建文件時(shí)出現(xiàn)java.io.IOException:?Operation?not?permitted異常的解決方法,需要的朋友可以參考下2023-05-05
Android實(shí)現(xiàn)文件壓縮與解壓工具類(lèi)
這篇文章主要為大家詳細(xì)介紹了如何使用Android實(shí)現(xiàn)一個(gè)文件壓縮與解壓工具類(lèi),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04
Android 開(kāi)發(fā)系統(tǒng)自帶語(yǔ)音模塊應(yīng)用
本篇文章 主要介紹 Android 開(kāi)發(fā)自帶語(yǔ)音模塊實(shí)例,在開(kāi)發(fā)Android系統(tǒng)中會(huì)用到系統(tǒng)語(yǔ)音搜索模塊,這里給大家一個(gè)參考實(shí)例2016-07-07
android自動(dòng)工具類(lèi)TextUtils使用詳解
這篇文章主要介紹了android自動(dòng)工具類(lèi)TextUtils的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
Android自定義TipView仿QQ長(zhǎng)按后的提示窗口
這篇文章主要介紹了Android自定義TipView仿QQ長(zhǎng)按后的提示窗口,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
Android結(jié)合kotlin使用coroutine的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Android結(jié)合kotlin使用coroutine的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12

