欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android仿百度外賣自定義下拉刷新效果

 更新時(shí)間:2016年04月11日 13:46:43   作者:Hankkin  
大家在使用百度外賣的訂餐的時(shí)候,會(huì)看到有個(gè)下拉刷新功能非常不錯(cuò),今天小編就通過代碼給大家介紹android仿百度外賣自定義下拉刷新,感興趣的朋友一起學(xué)習(xí)吧

現(xiàn)如今的APP各式各樣,同樣也帶來了各種需求,一個(gè)下拉刷新都能玩出花樣了,前兩天訂飯的時(shí)候不經(jīng)意間看到了“百度外賣”的下拉刷新,今天的主題就是它–自定義下拉刷新動(dòng)畫。

看一下實(shí)現(xiàn)效果吧:

這里寫圖片描述

動(dòng)畫

我們先來看看Android中的動(dòng)畫吧:

Android中的動(dòng)畫分為三種:

Tween動(dòng)畫,這一類的動(dòng)畫提供了旋轉(zhuǎn)、平移、縮放等效果。

Alpha – 淡入淡出

Scale – 縮放效果

Roate – 旋轉(zhuǎn)效果

Translate – 平移效果

Frame動(dòng)畫(幀動(dòng)畫),這一類動(dòng)畫可以創(chuàng)建一個(gè)Drawable序列,按照指定時(shí)間間歇一個(gè)一個(gè)顯示出來。

Property動(dòng)畫(屬性動(dòng)畫),Android3.0之后引入出來的屬性動(dòng)畫,它更改的是對(duì)象的實(shí)際屬性。

分析

這里寫圖片描述

我們可以看到百度外賣的下拉刷新的頭是一個(gè)騎車的快遞員在路上疾行,分析一下我們得到下面的動(dòng)畫:

背景圖片的平移動(dòng)畫

太陽的自旋轉(zhuǎn)動(dòng)畫

兩個(gè)小輪子的自旋轉(zhuǎn)動(dòng)畫

這就很簡(jiǎn)單了,接下來我們?nèi)グ俣韧饷娴膱D片資源文件里找到這幾張圖片:(下載百度外賣的apk直接解壓即可)

這里寫圖片描述

定義下拉刷新頭文件:headview.xml

這里注意一下:我們定義了兩張背景圖片的ImageView是為了可以實(shí)現(xiàn)背景的平移動(dòng)畫效果。

這里寫圖片描述

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_back1"
android:src="@drawable/pull_back"
android:layout_width="match_parent"
android:layout_height="100dp" />
<ImageView
android:id="@+id/iv_back2"
android:src="@drawable/pull_back"
android:layout_width="match_parent"
android:layout_height="100dp" />
<RelativeLayout
android:id="@+id/main"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_marginTop="45dp"
android:id="@+id/iv_rider"
android:background="@drawable/pull_rider"
android:layout_width="50dp"
android:layout_height="50dp" />
<ImageView
android:id="@+id/wheel1"
android:layout_marginLeft="10dp"
android:layout_marginTop="90dp"
android:background="@drawable/pull_wheel"
android:layout_width="15dp"
android:layout_height="15dp" />
<ImageView
android:id="@+id/wheel2"
android:layout_marginLeft="40dp"
android:layout_marginTop="90dp"
android:background="@drawable/pull_wheel"
android:layout_width="15dp"
android:layout_height="15dp" />
</RelativeLayout>
<ImageView
android:id="@+id/ivsun"
android:layout_marginTop="20dp"
android:layout_toRightOf="@+id/main"
android:background="@drawable/pull_sun"
android:layout_width="30dp"
android:layout_height="30dp" />
</RelativeLayout>

接下來我們定義動(dòng)畫效果:

背景圖片的平移效果:

實(shí)現(xiàn)兩個(gè)animation xml文件,一個(gè)起始位置在100%,結(jié)束位置在0%,設(shè)置repeat屬性為循環(huán)往復(fù)。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
<translate android:fromXDelta="100%p" android:toXDelta="0%p"
android:repeatMode="restart"
android:interpolator="@android:anim/linear_interpolator"
android:repeatCount="infinite"
android:duration="5000" />
</set>

另一個(gè)起始位置在0%,結(jié)束位置在-100%

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
<translate android:fromXDelta="0%p" android:toXDelta="-100%p"
android:repeatMode="restart"
android:interpolator="@android:anim/linear_interpolator"
android:repeatCount="infinite"
android:duration="5000" />
</set>

太陽圍繞中心旋轉(zhuǎn)動(dòng)畫:

從0-360度開始循環(huán)旋轉(zhuǎn),旋轉(zhuǎn)所用時(shí)間為1s,旋轉(zhuǎn)中心距離view的左定點(diǎn)上邊緣為50%的距離,也就是正中心。

下面是具體屬性:

android:fromDegrees 起始的角度度數(shù)

android:toDegrees 結(jié)束的角度度數(shù),負(fù)數(shù)表示逆時(shí)針,正數(shù)表示順時(shí)針。如10圈則比android:fromDegrees大3600即可

android:pivotX 旋轉(zhuǎn)中心的X坐標(biāo)

浮點(diǎn)數(shù)或是百分比。浮點(diǎn)數(shù)表示相對(duì)于Object的左邊緣,如5; 百分比表示相對(duì)于Object的左邊緣,如5%; 另一種百分比表示相對(duì)于父容器的左邊緣,如5%p; 一般設(shè)置為50%表示在Object中心

android:pivotY 旋轉(zhuǎn)中心的Y坐標(biāo)

浮點(diǎn)數(shù)或是百分比。浮點(diǎn)數(shù)表示相對(duì)于Object的上邊緣,如5; 百分比表示相對(duì)于Object的上邊緣,如5%; 另一種百分比表示相對(duì)于父容器的上邊緣,如5%p; 一般設(shè)置為50%表示在Object中心

android:duration 表示從android:fromDegrees轉(zhuǎn)動(dòng)到android:toDegrees所花費(fèi)的時(shí)間,單位為毫秒??梢杂脕碛?jì)算速度。

android:interpolator表示變化率,但不是運(yùn)行速度。一個(gè)插補(bǔ)屬性,可以將動(dòng)畫效果設(shè)置為加速,減速,反復(fù),反彈等。默認(rèn)為開始和結(jié)束慢中間快,

android:startOffset 在調(diào)用start函數(shù)之后等待開始運(yùn)行的時(shí)間,單位為毫秒,若為10,表示10ms后開始運(yùn)行

android:repeatCount 重復(fù)的次數(shù),默認(rèn)為0,必須是int,可以為-1表示不停止

android:repeatMode 重復(fù)的模式,默認(rèn)為restart,即重頭開始重新運(yùn)行,可以為reverse即從結(jié)束開始向前重新運(yùn)行。在android:repeatCount大于0或?yàn)閕nfinite時(shí)生效

android:detachWallpaper 表示是否在壁紙上運(yùn)行

android:zAdjustment 表示被animated的內(nèi)容在運(yùn)行時(shí)在z軸上的位置,默認(rèn)為normal。

normal保持內(nèi)容當(dāng)前的z軸順序

top運(yùn)行時(shí)在最頂層顯示

bottom運(yùn)行時(shí)在最底層顯示

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:fromDegrees="0"
android:toDegrees="360"
android:duration="1000"
android:repeatCount="-1"
android:pivotX="50%"
android:pivotY="50%" />
</set>

同理輪子的動(dòng)畫也一樣,不占代碼了。

動(dòng)畫定義完了我們開始定義下拉刷新列表,下拉刷新網(wǎng)上有很多,不詳細(xì)的說了,簡(jiǎn)單的改造一下,根據(jù)刷新狀態(tài)開啟關(guān)閉動(dòng)畫即可。

注釋寫的很詳細(xì),看一下代碼吧:

package com.hankkin.baidugoingrefreshlayout;
import android.widget.AbsListView;
import android.widget.ListView;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.RelativeLayout;
/**
* Created by Hankkin on 16/4/10.
*/
public class BaiDuRefreshListView extends ListView implements AbsListView.OnScrollListener{
private static final int DONE = 0; //刷新完畢狀態(tài)
private static final int PULL_TO_REFRESH = 1; //下拉刷新狀態(tài)
private static final int RELEASE_TO_REFRESH = 2; //釋放狀態(tài)
private static final int REFRESHING = 3; //正在刷新狀態(tài)
private static final int RATIO = 3;
private RelativeLayout headView; //下拉刷新頭
private int headViewHeight; //頭高度
private float startY; //開始Y坐標(biāo)
private float offsetY; //Y軸偏移量
private OnBaiduRefreshListener mOnRefreshListener; //刷新接口
private int state; //狀態(tài)值
private int mFirstVisibleItem; //第一項(xiàng)可見item索引
private boolean isRecord; //是否記錄
private boolean isEnd; //是否結(jié)束
private boolean isRefreable; //是否刷新
private ImageView ivWheel1,ivWheel2; //輪組圖片組件
private ImageView ivRider; //騎手圖片組件
private ImageView ivSun,ivBack1,ivBack2; //太陽、背景圖片1、背景圖片2
private Animation wheelAnimation,sunAnimation; //輪子、太陽動(dòng)畫
private Animation backAnimation1,backAnimation2; //兩張背景圖動(dòng)畫
public BaiDuRefreshListView(Context context) {
super(context);
init(context);
}
public BaiDuRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public BaiDuRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public interface OnBaiduRefreshListener{
void onRefresh();
}
/**
* 回調(diào)接口,想實(shí)現(xiàn)下拉刷新的listview實(shí)現(xiàn)此接口
* @param onRefreshListener
*/
public void setOnBaiduRefreshListener(OnBaiduRefreshListener onRefreshListener){
mOnRefreshListener = onRefreshListener;
isRefreable = true;
}
/**
* 刷新完畢,從主線程發(fā)送過來,并且改變headerView的狀態(tài)和文字動(dòng)畫信息
*/
public void setOnRefreshComplete(){
//一定要將isEnd設(shè)置為true,以便于下次的下拉刷新
isEnd = true;
state = DONE;
changeHeaderByState(state);
}
private void init(Context context) {
//關(guān)閉view的OverScroll
setOverScrollMode(OVER_SCROLL_NEVER);
setOnScrollListener(this);
//加載頭布局
headView = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.headview,this,false);
//測(cè)量頭布局
measureView(headView);
//給ListView添加頭布局
addHeaderView(headView);
//設(shè)置頭文件隱藏在ListView的第一項(xiàng)
headViewHeight = headView.getMeasuredHeight();
headView.setPadding(0, -headViewHeight, 0, 0);
//獲取頭布局圖片組件
ivRider = (ImageView) headView.findViewById(R.id.iv_rider);
ivSun = (ImageView) headView.findViewById(R.id.ivsun);
ivWheel1 = (ImageView) headView.findViewById(R.id.wheel1);
ivWheel2 = (ImageView) headView.findViewById(R.id.wheel2);
ivBack1 = (ImageView) headView.findViewById(R.id.iv_back1);
ivBack2 = (ImageView) headView.findViewById(R.id.iv_back2);
//獲取動(dòng)畫
wheelAnimation = AnimationUtils.loadAnimation(context, R.anim.tip);
sunAnimation = AnimationUtils.loadAnimation(context, R.anim.tip1);
backAnimation1 = AnimationUtils.loadAnimation(context, R.anim.a);
backAnimation2 = AnimationUtils.loadAnimation(context, R.anim.b);
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ài) 在setOnMeiTuanListener中設(shè)置為true
switch (ev.getAction()){
//用戶按下
case MotionEvent.ACTION_DOWN:
//如果當(dāng)前是在listview頂部并且沒有記錄y坐標(biāo)
if (mFirstVisibleItem == 0 && !isRecord) {
//將isRecord置為true,說明現(xiàn)在已記錄y坐標(biāo)
isRecord = true;
//將當(dāng)前y坐標(biāo)賦值給startY起始y坐標(biāo)
startY = ev.getY();
}
break;
//用戶滑動(dòng)
case MotionEvent.ACTION_MOVE:
//再次得到y(tǒng)坐標(biāo),用來和startY相減來計(jì)算offsetY位移值
float tempY = ev.getY();
//再起判斷一下是否為listview頂部并且沒有記錄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 = (-headViewHeight+offsetY/3);
//用當(dāng)前滑動(dòng)的高度和頭部headerView的總高度進(jìn)行比 計(jì)算出當(dāng)前滑動(dòng)的百分比 0到1
float currentProgress = 1+currentHeight/headViewHeight;
//如果當(dāng)前百分比大于1了,將其設(shè)置為1,目的是讓第一個(gè)狀態(tài)的橢圓不再繼續(xù)變大
if (currentProgress>=1) {
currentProgress = 1;
}
//如果當(dāng)前的狀態(tài)是放開刷新,并且已經(jīng)記錄y坐標(biāo)
if (state == RELEASE_TO_REFRESH && isRecord) {
setSelection(0);
//如果當(dāng)前滑動(dòng)的距離小于headerView的總高度
if (-headViewHeight+offsetY/RATIO<0) {
//將狀態(tài)置為下拉刷新狀態(tài)
state = PULL_TO_REFRESH;
//根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫和文字等信息
changeHeaderByState(state);
//如果當(dāng)前y的位移值小于0,即為headerView隱藏了
}else if (offsetY<=0) {
//將狀態(tài)變?yōu)閐one
state = DONE;
stopAnim();
//根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫和文字等信息
changeHeaderByState(state);
}
}
//如果當(dāng)前狀態(tài)為下拉刷新并且已經(jīng)記錄y坐標(biāo)
if (state == PULL_TO_REFRESH && isRecord) {
setSelection(0);
//如果下拉距離大于等于headerView的總高度
if (-headViewHeight+offsetY/RATIO>=0) {
//將狀態(tài)變?yōu)榉砰_刷新
state = RELEASE_TO_REFRESH;
//根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫和文字等信息
changeHeaderByState(state);
//如果當(dāng)前y的位移值小于0,即為headerView隱藏了
}else if (offsetY<=0) {
//將狀態(tài)變?yōu)閐one
state = DONE;
//根據(jù)狀態(tài)改變headerView,主要是更新動(dòng)畫和文字等信息
changeHeaderByState(state);
}
}
//如果當(dāng)前狀態(tài)為done并且已經(jīng)記錄y坐標(biāo)
if (state == DONE && isRecord) {
//如果位移值大于0
if (offsetY>=0) {
//將狀態(tài)改為下拉刷新狀態(tài)
state = PULL_TO_REFRESH;
changeHeaderByState(state);
}
}
//如果為下拉刷新狀態(tài)
if (state == PULL_TO_REFRESH) {
//則改變headerView的padding來實(shí)現(xiàn)下拉的效果
headView.setPadding(0,(int)(-headViewHeight+offsetY/RATIO) ,0,0);
}
//如果為放開刷新狀態(tài)
if (state == RELEASE_TO_REFRESH) {
//改變headerView的padding值
headView.setPadding(0,(int)(-headViewHeight+offsetY/RATIO) ,0, 0);
}
}
break;
//當(dāng)用戶手指抬起時(shí)
case MotionEvent.ACTION_UP:
//如果當(dāng)前狀態(tài)為下拉刷新狀態(tài)
if (state == PULL_TO_REFRESH) {
//平滑的隱藏headerView
this.smoothScrollBy((int)(-headViewHeight+offsetY/RATIO)+headViewHeight, 500);
//根據(jù)狀態(tài)改變headerView
changeHeaderByState(state);
}
//如果當(dāng)前狀態(tài)為放開刷新
if (state == RELEASE_TO_REFRESH) {
//平滑的滑到正好顯示headerView
this.smoothScrollBy((int)(-headViewHeight+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);
}
/**
* 根據(jù)狀態(tài)改變headerView的動(dòng)畫和文字顯示
* @param state
*/
private void changeHeaderByState(int state){
switch (state) {
case DONE://如果的隱藏的狀態(tài)
//設(shè)置headerView的padding為隱藏
headView.setPadding(0, -headViewHeight, 0, 0);
startAnim();
break;
case RELEASE_TO_REFRESH://當(dāng)前狀態(tài)為放開刷新
break;
case PULL_TO_REFRESH://當(dāng)前狀態(tài)為下拉刷新
startAnim();
break;
case REFRESHING://當(dāng)前狀態(tài)為正在刷新
break;
default:
break;
}
}
/**
* 測(cè)量View
* @param child
*/
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);
}
/**
* 開啟動(dòng)畫
*/
public void startAnim(){
ivBack1.startAnimation(backAnimation1);
ivBack2.startAnimation(backAnimation2);
ivSun.startAnimation(sunAnimation);
ivWheel1.startAnimation(wheelAnimation);
ivWheel2.startAnimation(wheelAnimation);
}
/**
* 關(guān)閉動(dòng)畫
*/
public void stopAnim(){
ivBack1.clearAnimation();
ivBack2.clearAnimation();
ivSun.clearAnimation();
ivWheel1.clearAnimation();
ivWheel2.clearAnimation();
}
}

好了,自定義下拉刷新動(dòng)畫我們就實(shí)現(xiàn)了,其實(shí)很簡(jiǎn)單,所有的下拉刷新動(dòng)畫都類似這樣實(shí)現(xiàn)的。源碼我已經(jīng)上傳到Github上了:

https://github.com/Hankkin/BaiduGoingRefreshLayout

求star啊。有不合理的地方還希望大家多多指正,共同進(jìn)步哈。

關(guān)于Android仿百度外賣自定義下拉刷新效果,小編就給大家介紹到這里,希望對(duì)大家有所幫助!

相關(guān)文章

最新評(píng)論