




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
Ultra-Pull-To-Refresh自定義下拉刷新視差動畫效果刷新自定義動畫的分析首先是Ultra-Pull-To-Refresh的特點,此庫提供了一個Layout類:PtrFramLayout作為Wrapper來包涵ContentView,今天用到兩個方法:第一個PtrFramLayout#setHeaderView(View)用來設(shè)置頭部顯示的刷新View,第二個PtrFramLayout#addPtrUIHandler(PtrHandler)用來設(shè)置監(jiān)聽用戶下拉狀態(tài)、下拉offset、刷新完成狀態(tài)等。其次是動畫的,根據(jù)效果圖,第一點是下拉的時候人物從左側(cè)走過來到中間,到中間后手指再繼續(xù)往下拉,此時人物也不走了,第二點是當手指松開時或者處于下拉狀態(tài)時,人物不停的走動,并且背景產(chǎn)生一個相對位移,給人的視覺上造成一個視覺差,也就是我們想要的視差動畫了,這就是整個視差動畫的實現(xiàn)步驟。那么幾個動畫拆分開來就是,人物向右中間移動、人物原地踏步、背景無限向左移動。頭View和刷新Layout的實現(xiàn)我把實現(xiàn)步驟分開講解,方便讀者理解:實現(xiàn)自定義的頭View。繼承PtrFramLayout實現(xiàn)一個ParallaxPtrFrameLayout,設(shè)置自定頭和PtrHandler監(jiān)聽下拉動作。實現(xiàn)人物向左走的動畫。松開手時背景不停的向右移動,人物在原地邁步,形成一個視差上的向右走的動畫。自定義頭部View頭View的底下是這樣一個圖:那么一個圖是如何做到不停的向左移動還是無限重復(fù)的呢?用HTML做很簡單,但是Android中并沒有repeat這樣的屬性,于是我們想到:在屏幕上放一個ImageView向左移動100%,在這張圖的右側(cè)再放一個ImageView,以同樣的速度向左移動100%,結(jié)果就是當屏幕上的圖移動到左邊外屏幕的時候,屏幕右邊的圖剛好移動到屏幕上完全顯示,然后我們的動畫又有重復(fù)播放的屬性,結(jié)合起來就產(chǎn)生了一個背景無限長的動畫效果。對于人物原地踏步就很簡單了,直接用一個ImageView不停的切換圖形成一個人物在走動的視覺效果。所以我們用兩個ImageView作為背景圖來相間向左移動,用一個ImageView不停的切換圖模擬人物走動,來達到一個人物走動的視差效果,我打算用FrameLayout來作為頭View的Layout,所以布局用merge包裹了一下:refresh_parallax.xml<?xmlversion="1.0"encoding="utf-8"?><mergexmlns:android="/apk/res/android"><ImageViewandroid:id="@+id/iv_background_1"android:layout_width="match_parent"android:layout_height="wrap_content"android:contentDescription="@string/app_name"android:src="@drawable/refresh_down_background"/><ImageViewandroid:id="@+id/iv_background_2"android:layout_width="match_parent"android:layout_height="wrap_content"android:contentDescription="@string/app_name"android:src="@drawable/refresh_down_background"/><ImageViewandroid:id="@+id/iv_refresh_icon"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom"android:contentDescription="@string/app_name"android:scaleType="center"/></merge>然后定義頭View加載剛才寫好的布局,因為PtrFrameLayout是通過PtrHandler接口來監(jiān)聽下拉狀態(tài)和刷新狀態(tài),然后以狀態(tài)為依據(jù)來刷新頭View的動畫,所以頭View直接實現(xiàn)PtrHandler接口,然后操作自身的狀態(tài)和動畫也就更加方便了,所以頭View初步的代碼是:ParallaxHeaderpublicclassParallaxHeaderextendsFrameLayoutimplementsPtrUIHandler{ImageViewmIvBack1;ImageViewmIvBack2;ImageViewmIvIcon;privatevoidinitialize(){//加載剛才的LayoutInflater.from(getContext()).inflate(R.layout.refresh_parallax,this);//設(shè)置一個藍色天空的背景。setBackgroundColor(ContextCompat.getColor(getContext(),R.color.refresh_background));mIvBack1=(ImageView)findViewById(R.id.iv_background_1);mIvBack2=(ImageView)findViewById(R.id.iv_background_2);mIvIcon=(ImageView)findViewById(R.id.iv_refresh_icon);}publicParallaxHeader(Contextcontext){this(context,null,0);}publicParallaxHeader(Contextcontext,AttributeSetattrs){this(context,attrs,0);}publicParallaxHeader(Contextcontext,AttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr);}@OverridepublicvoidonUIReset(PtrFrameLayoutframe){//重置頭View的動畫狀態(tài),一般停止刷新動畫。}@OverridepublicvoidonUIRefreshPrepare(PtrFrameLayoutframe){//準備刷新的UI。}@OverridepublicvoidonUIRefreshBegin(PtrFrameLayoutframe){//開始刷新的UI動畫。}@OverridepublicvoidonUIRefreshComplete(PtrFrameLayoutframe){//刷新完成,停止刷新動畫。}@OverridepublicvoidonUIPositionChange(PtrFrameLayoutframe,booleanisUnderTouch,bytestatus,PtrIndicatorptrIndicator){//手指下拉的時候的狀態(tài),我們的下拉動畫的控制就是通過這個方法://frame是刷新的rootlayout。//isUnderTouch是手指是否按下,因為還有自動刷新,手指肯定是松開狀態(tài)。//status是現(xiàn)在的加載狀態(tài),準備、加載中、完成:PREPARE、LOADING、COMPLETE。//ptrIndicator是一些下拉偏移量的參數(shù)封裝。}}里面的代碼很簡單,就是加載剛才定義好的頭View對應(yīng)的Layout.xml文件,然后把兩個背景View和人物View給找出來。頭View定義好了,接下來定義刷新的Layout。實現(xiàn)ParallaxPtrFrameLayout加載頭ViewUltra-Pull-To-Refresh的刷新Layout都是繼承PtrFramLayout,然后設(shè)置頭View和刷新狀態(tài)監(jiān)聽等,所以我們定義一個ParallaxPtrFrameLayout繼承PtrFrameLayout,在里面設(shè)置頭View和PtrHandler等來回調(diào)操作的頭View的動畫,很簡單的幾行代碼:publicclassParallaxPtrFrameLayoutextendsPtrFrameLayout{publicParallaxPtrFrameLayout(Contextcontext){super(context);initViews();}publicParallaxPtrFrameLayout(Contextcontext,AttributeSetattrs){super(context,attrs);initViews();}publicParallaxPtrFrameLayout(Contextcontext,AttributeSetattrs,intdefStyle){super(context,attrs,defStyle);initViews();}privatevoidinitViews(){//這里初始化上面的頭View:ParallaxHeaderparallaxHeader=newParallaxHeader(getContext());//這里設(shè)置頭View為上面自定義的頭View:setHeaderView(parallaxHeader);//下拉和刷新狀態(tài)監(jiān)聽://因為ParallaxHeader已經(jīng)實現(xiàn)過PtrUIHandler接口,所以直接設(shè)置為ParallaxHeader:addPtrUIHandler(parallaxHeader);}}由于Ultra-Pull-To-Refresh的合理設(shè)計,到這里為止,我們的頭View和刷新的Layout就完成了,接下來就專心研究動畫吧。動畫的實現(xiàn)上文也提過了,這里的動畫拆分開幾個,一是下拉的時候人物向右中間移動,二是刷新的時候人物不停的原地踏步,三是刷新的時候背景一個向左平移,為了方便理解,這里把下拉時候人物向右中間移動放到最后來講。一、人物原地踏步動畫首先想到的就是幀動畫,沒錯就是這家伙,用幀動畫可以做到每多少時間換一張圖片,所以我們的人物有三張不同的動畫,不停的切換就形成了一個人物走動并車輪轉(zhuǎn)動的效果:我們用幀動畫控制每張圖顯示100毫秒,然后就切換下一張圖,這樣便達到我們說的人物走動的效果了,用xml來實現(xiàn):<?xmlversion="1.0"encoding="utf-8"?><animation-listxmlns:android="/apk/res/android"android:oneshot="false"><itemandroid:drawable="@drawable/refresh_down_icon_1"android:duration="100"/><itemandroid:drawable="@drawable/refresh_down_icon_2"android:duration="100"/><itemandroid:drawable="@drawable/refresh_down_icon_3"android:duration="100"/></animation-list>因為這是一個幀動畫,需要在代碼中觸發(fā),所以我們要把這個動畫放在drawable文件夾,并且把這個drawable當圖片設(shè)置頭View中的人物ImageView:<ImageViewandroid:id="@+id/iv_refresh_icon"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom"android:scaleType="center"android:src="@drawable/refresh_down_icon"/>二、背景無限向左移動這個動畫就厲害了word哥,當時我先做出來,然后給iOS的同學講實現(xiàn)的原理,他還是花了點時間來理解的,所以我再費點口舌解釋一下。這里是兩個ImageView,一個在屏幕正中央,并且占據(jù)整屏寬,一個在屏幕外的右側(cè),寬度等于屏幕寬度。動畫開始時,屏幕上的ImageView開始一步步向左移動100%,屏幕之外的ImageView以同樣的速度向左移動100%,當屏幕上的ImageView移動到左邊外屏幕的時候,屏幕右邊的圖剛好移動到屏幕上完全顯示,然后我們的動畫又有重復(fù)播放的屬性,結(jié)合起來就產(chǎn)生了一個背景無限長的動畫效果。為了方便大家理解,我畫了一張圖:圖是畫的有點簡陋了,但是很好理解,當頭View剛出來的時候只顯示ImageView1,當刷新的時候ImageView1和ImageView2同時向左移動,看起來就是連貫的一張圖(實際xml中兩張圖是沒有空隙的),等ImageView1移出屏幕時,ImageView2剛好充斥滿屏幕,然后我們給動畫加上重復(fù)播放屬性,然后又從圖1開始重復(fù)到圖三,就形成了一個無限向左移動的街道。所以我們給第一張圖的動畫是,在2S內(nèi),勻速移動,從屏幕上移動到屏幕左外邊,然后再次重復(fù)動作:<?xmlversion="1.0"encoding="utf-8"?><setxmlns:android="/apk/res/android"android:interpolator="@android:anim/linear_interpolator"><translateandroid:duration="2000"android:fromXDelta="0%"android:interpolator="@android:anim/linear_interpolator"android:repeatCount="infinite"android:repeatMode="restart"android:toXDelta="-100%"/></set>我們給第二張圖的動畫是,在2S內(nèi),勻速移動,從屏幕右外邊移動到屏幕上,然后再次重復(fù)動作:<?xmlversion="1.0"encoding="utf-8"?><setxmlns:android="/apk/res/android"android:interpolator="@android:anim/linear_interpolator"><translateandroid:duration="2000"android:fromXDelta="100%"android:interpolator="@android:anim/linear_interpolator"android:repeatCount="infinite"android:repeatMode="restart"android:toXDelta="0%"/></set>三個動畫到這里就定義完了,接下來就是怎么控制動畫了。三、動畫和下拉動作、刷新狀態(tài)的結(jié)合要控制動畫就要把三個動畫加載出來,我們繼續(xù)回到頭View``ParallaxHeader中。首先要加載人物的動畫,因為是幀動畫,所以要用到AnimationDrawable:privateAnimationDrawablemAnimationDrawable;privatevoidinitialize(){...mIvIcon=(ImageView)findViewById(R.id.iv_refresh_icon);mAnimationDrawable=(AnimationDrawable)mIvIcon.getDrawable();}然后加在兩個背景的位移動畫:privateAnimationDrawablemAnimationDrawable;privateAnimationmBackAnim1;privateAnimationmBackAnim2;privatevoidinitialize(){...mIvIcon=(ImageView)findViewById(R.id.iv_refresh_icon);mAnimationDrawable=(AnimationDrawable)mIvIcon.getDrawable();mBackAnim1=AnimationUtils.loadAnimation(getContext(),R.anim.refresh_down_background_1);mBackAnim2=AnimationUtils.loadAnimation(getContext(),R.anim.refresh_down_background_2);}接著為了方便調(diào)用,也減少代碼邏輯的復(fù)雜度,我們需要定義兩個方法來控制動畫的結(jié)束和開始,同時為了動畫不被重復(fù)開始和停止,定義一個變量來記錄動畫是否是運行的:/***記錄動畫是否在執(zhí)行。*/privatebooleanisRunAnimation=false;/***開始刷新動畫。*/privatevoidstartAnimation(){if(!isRunAnimation){isRunAnimation=true;mIvBack1.startAnimation(mBackAnim1);mIvBack2.startAnimation(mBackAnim2);mIvIcon.setImageDrawable(mAnimationDrawable);mAnimationDrawable.start();}}/***停止刷新動畫。*/privatevoidstopAnimation(){if(isRunAnimation){isRunAnimation=false;mIvBack1.clearAnimation();mIvBack2.clearAnimation();mAnimationDrawable.stop();}}到這里基本上已經(jīng)完成了,我們可以把上面PrallaxHeader中的下拉監(jiān)聽和刷新狀態(tài)代碼補全了:@OverridepublicvoidonUIReset(PtrFrameLayoutframe){//重置頭View的動畫狀態(tài),一般停止刷新動畫。stopAnimation();}@OverridepublicvoidonUIRefreshPrepare(PtrFrameLayoutframe){//準備刷新的UI。}@OverridepublicvoidonUIRefreshBegin(PtrFrameLayoutframe){//開始刷新的UI動畫。stopAnimation();startAnimation();}@OverridepublicvoidonUIRefreshComplete(PtrFrameLayoutframe){//刷新完成,停止刷新動畫。stopAnimation();}@OverridepublicvoidonUIPositionChange(PtrFrameLayoutframe,booleanisUnderTouch,bytestatus,PtrIndicatorptrIndicator){//手指下拉的時候的狀態(tài),我們的下拉動畫的控制就是通過這個方法://frame是刷新的rootlayout。//isUnderTouch是手指是否按下,因為還有自動刷新,手指肯定是松開狀態(tài)。//status是現(xiàn)在的加載狀態(tài),準備、加載中、完成:PREPARE、LOADING、COMPLETE。//ptrIndicator是一些下拉偏移量的參數(shù)封裝。}onUIRefreshPrepare()是準備UI,這里不需要實現(xiàn),除了最后一個方法在下拉的時候觸發(fā)外,其它都已經(jīng)實現(xiàn)了,如果你基礎(chǔ)還過關(guān),你可以照著本博客敲出來上面講的所有代碼,然后在你的Layout中用一個ParallaxPtrFrameLayout包涵一個布局,運行起來,然后下拉后松開看看,已經(jīng)看到了文章開頭刷新狀態(tài)時的背景左移,人物走路的動畫了。不過我是個追求完美的人,所以我必須要實現(xiàn)下拉的時候人物走向中間的動畫。四、下拉時,人物走向中間不可避免,這里我們要拿到下拉時的總offset,還要拿到手指已經(jīng)下拉的offset,然后算出一個百分比,結(jié)合從屏幕最左邊到屏幕中間的位置,算出當前人物需要走到哪里。這里有一個注意的點,就是人物要走到屏幕中間的位置,這個位置可不是屏幕寬度/2,應(yīng)該等于屏幕寬度/2-人物View寬度/2。因為人物是從屏幕最左邊x=0開始移動,如果移動到x=屏幕寬/2這個位置,那么人物就看起來偏右了。好吧說這么多,不如再來個圖解釋一下:這里的問號代表的是Y,這個不用關(guān)心,我們只需要關(guān)心X方向的平移,這里人物ImageView的X是以左邊開始算的,讓它移動到屏幕中間的時候它就會是圖一所示,此時如果我們將人物ImageView向左移動半個人的距離,剛好是到屏幕中間,所以人物每次需要移動的距離是(屏幕寬度/2-人物View寬度/2)。那么下面我們把代碼擼起:/***人物到屏幕中間的x點。*/privateintlimitX;/***計算人物到屏幕中間的x點。*/privatevoidcalcLimitX(){limitX=DisplayUtils.screenWidth/2;intmIconIvWidth=mIvIcon.getMeasuredWidth();limitX-=(mIconIvWidth/2);}@OverridepublicvoidonUIPositionChange(PtrFrameLayoutframe,booleanisUnderTouch,bytestatus,PtrIndicatorptrIndicator){//獲取總的頭部可下拉的距離:finalintoffsetToRefresh=frame.getOffsetToRefresh();//獲取當前手指已經(jīng)下拉的距離:finalintcurrentPos=ptrIndicator.getCurrentPosY();//當前距離小于總的下拉距離時才計算移動if(currentPos<=offsetToRefresh){//計算人物到屏幕中間的x點。calcLimitX();//根據(jù)下拉距離占可下拉高度的比例,算出向右走的距離:doublepercent=(double)currentPos/offsetToRefresh;inttargetX=(int)(limitX*percent);//人物向右走:mIvIcon.setTranslationX(targetX);//人物向右移動算出來還不夠,因為還有換圖片才能模擬出人物走動的效果。//當百分比是1030507090時顯示第一張圖。//當百分比是20406080100時顯示第二張圖。//當百分比是5152535455565758595時顯示第三張圖。//這樣就模擬出了下拉時人物向右走的效果了。intnewPercent=(int)(percent*100);if(newPercent%10==0){doublei=newPercent/10;if(i%2==0){mIvIcon.setImageResource(R.drawable.refresh_down_icon_3);}else{mIvIcon.setImageResource(R.drawable.refresh_down_icon_1);}}elseif(newPercent%5==0){mIvIcon.setImageResource(R.drawable.refresh_down_icon_2);}}}這里廢話就再不多說了,一切都在代碼注釋中,所以下面貼出ParallaxHeader的完整代碼,本文源碼下載鏈接在文章末尾:publicclassParallaxHeaderextendsFrameLayoutimplementsPtrUIHandler{ImageViewmIvBack1;ImageViewmIvBack2;ImageViewmIvIcon;privateAnimationmBackAnim1;privateAnimationmBackAnim2;privateAnimationDrawablemAnimationDrawable;privatebooleanisRunAnimation=false;privateintlimitX;privatevoidinitialize(){LayoutInflater.from(getContext()).inflate(R.layout.refresh_parallax,this);setBackgroundColor(ContextCompat.getColor(getContext(),R.color.refresh_background));mIvBack1=(ImageView)findViewById(R.id.iv_background_1);mIvBack2=(ImageView)findViewById(R.id.iv_background_2);mIvIcon=(ImageView)findViewById(R.id.iv_refresh_icon);mAnimationDrawable=(AnimationDrawable)mIvIcon.getDrawable();mBackAnim1=AnimationUtils.loadAnimation(getContext(),R.anim.refresh_down_background_1);mBackAnim2=AnimationUtils.loadAnimation(getContext(),R.anim.refresh_down_background_2);}publicParallaxHeader(Contextcontext){this(context,null,0);}publicParallaxHeader(Contextcontext,AttributeSetattrs){this(context,attrs,0);}publicParallaxHeader(Contextcontext,AttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr);initialize();}/***開始刷新動畫。*/privatevoidstartAnimation(){if(!isRunAnimation){isRunAnimation=true;mIvBack1.startAnimation(mBackAnim1);mIvBack2.startAnimation(mBackAnim2);mIvIcon.setImageDrawable(mAnimationDrawable);mAnimationDrawable.start();}}/***停止刷新動畫。*/privatevoidstopAnimation(){if(isRunAnimation){isRunAnimation=false;mIvBack1.clearAnimation();mIvBack2.clearAnimation();mAnimationDrawable.stop();}}@OverridepublicvoidonUIReset(PtrFrameLayoutframe){
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- T/GIEHA 013-2019商用廚房油煙管道系統(tǒng)清洗規(guī)范
- T/GIA 019-2023尾礦庫地下水環(huán)境監(jiān)測現(xiàn)狀調(diào)查信息采集技術(shù)指南
- 兩類高階分數(shù)階微分方程邊值問題解的研究
- 考慮多重不確定性的綜合能源微網(wǎng)優(yōu)化調(diào)度研究
- 2025溫商實驗室安全考試題庫及答案
- 2025山東省考公務(wù)員面試題目及答案
- 2025熱點公務(wù)員面試題及答案
- 2025年工業(yè)互聯(lián)網(wǎng)平臺可信執(zhí)行環(huán)境(TEE)在智能園區(qū)管理中的應(yīng)用分析報告
- 2025年數(shù)字藝術(shù)市場:藝術(shù)創(chuàng)作與交易市場政策環(huán)境對版權(quán)保護的影響報告
- 廣東省清遠市農(nóng)業(yè)生產(chǎn)托管服務(wù)發(fā)展研究
- 折彎工藝培訓(xùn)
- 大學生干部競選學生會干部競選207
- 2025-2030年煤炭貿(mào)易產(chǎn)業(yè)發(fā)展分析及發(fā)展趨勢與投資前景預(yù)測報告
- 農(nóng)業(yè)灌溉系統(tǒng)全掌握-故障排查與維護實戰(zhàn)指南
- 中國金融黑灰產(chǎn)治理研究報告 2024
- 行政管理??乒舶踩芾碓囶}及答案
- 高碳鉻鐵生產(chǎn)流程
- 學?!靶@餐”專項整治推進工作情況匯報范文
- 委托清算協(xié)議書范本
- 福州教育學院附屬中學2025年高三全真四模數(shù)學試題試卷
- 醫(yī)院網(wǎng)絡(luò)與信息安全應(yīng)急預(yù)案
評論
0/150
提交評論