Android可自定義神奇動(dòng)效的卡片切換視圖實(shí)例
前言
面對(duì)眾多卡片層疊效果,我們的產(chǎn)品童鞋也突發(fā)奇想,搞出了另一種卡片層疊切換展示的交互,而且產(chǎn)品狗們居然要求多做幾種動(dòng)效給他們看,好讓他們選擇,這簡(jiǎn)直就是要搞事情啊,what are you 弄啥咧?!
“哥哥我做不到啊.....啊.....呸”,做為一名有節(jié)操的程序猿,自然是不能說(shuō)出這么沒(méi)有出息的話,哥就滿足你們,于是,出了個(gè)可自定義動(dòng)效的卡片切換視圖,效果如下所示
思路
首先,要展示出卡片層疊的視覺(jué)效果。在這里,我們通過(guò)方塊的縮放大小差異以及在Y方向上的位置差異,來(lái)展現(xiàn)這種視覺(jué)效果。
其次,要能夠方便的定義卡片視圖內(nèi)容。我們通過(guò)都很熟悉的設(shè)置Adapter的方式來(lái)構(gòu)建內(nèi)容視圖。
最后,要能夠自定義動(dòng)效。我們通過(guò)定義一個(gè)由0到1的ValueAnimator,即每個(gè)動(dòng)畫的過(guò)程,其實(shí)就是該ValueAnimator在一個(gè)動(dòng)畫周期內(nèi),從0變化到1的過(guò)程,0表示動(dòng)畫剛開始,1表示動(dòng)畫結(jié)束了,0.5則表示這一輪動(dòng)畫已經(jīng)執(zhí)行到了一半。這樣,通過(guò)轉(zhuǎn)換器以及插值器,我們就可以根據(jù)ValueAnimator實(shí)時(shí)的值,來(lái)設(shè)置當(dāng)前正在執(zhí)行動(dòng)畫的卡片應(yīng)該有的“樣子”。
總覽
我們給出三種基本的動(dòng)畫模式
/* * ANIM_TYPE_FRONT:被選中的卡片通過(guò)自定義動(dòng)效移至第一,其他的卡片通過(guò)通用動(dòng)效補(bǔ)位 * ANIM_TYPE_SWITCH:選中的卡片和第一張卡片互換位置,并都是自定義動(dòng)效 * ANIM_TYPE_FRONT_TO_LAST:第一張圖片通過(guò)自定義動(dòng)效移至最后,其他卡片通過(guò)通用動(dòng)效補(bǔ)位 */ public static final int ANIM_TYPE_FRONT = 0, ANIM_TYPE_SWITCH = 1, ANIM_TYPE_FRONT_TO_LAST = 2;
并通過(guò)Helper類來(lái)處理所有的動(dòng)畫邏輯,以及Adapter來(lái)生成卡片視圖
private CardAnimationHelper mAnimationHelper; private BaseAdapter mAdapter;
在onMeasure時(shí)根據(jù)卡片寬高比來(lái)設(shè)置卡片的尺寸,在此請(qǐng)注意,當(dāng)前情況下卡片寬度與整體容器寬度一致,后續(xù)通過(guò)自定義的方式,通過(guò)縮放來(lái)產(chǎn)生卡片的視覺(jué)效果。
private float mCardRatio = CARD_SIZE_RATIO;//寬高比:卡片高 / 卡片寬 private int mCardWidth, mCardHeight;//卡片寬高 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ... if (mCardWidth == 0 || mCardHeight == 0) { setCardSize(true); } } /* * 根據(jù)卡片比例計(jì)算卡片寬高,并傳入Helper */ private void setCardSize(boolean resetAdapter) { mCardWidth = getMeasuredWidth(); mCardHeight = (int) (mCardWidth * mCardRatio); mAnimationHelper.setCardSize(mCardWidth, mCardHeight); mAnimationHelper.initAdapterView(mAdapter, resetAdapter); }
那么如此之后,自然Helper中就保存了視圖的主要數(shù)據(jù)與參數(shù)
//卡片列表 private LinkedList<CardItem> mCards; //當(dāng)前正在向后以及向前移動(dòng)的卡片 private CardItem mCardToBack, mCardToFront; //正在向后以及向前移動(dòng)卡片的位置 private int mPositionToBack = 0, mPositionToFront = 0; //動(dòng)畫運(yùn)行的ValueAnimator private ValueAnimator mValueAnimator; //當(dāng)前的動(dòng)畫系數(shù) private float mCurrentFraction = 1; ...//以及一系列的轉(zhuǎn)換器與插值器
細(xì)節(jié)
那么,動(dòng)畫到底是如何實(shí)現(xiàn)的,以及如何自定義的呢,我們以通用動(dòng)畫為例,來(lái)看看動(dòng)畫的主要流程
首先,在ValueAnimator更新的時(shí)候,獲得當(dāng)前的動(dòng)畫系數(shù),依次來(lái)執(zhí)行動(dòng)畫
/** * ValueAnimator動(dòng)畫更新 */ @Override public void onAnimationUpdate(ValueAnimator animation) { //獲取當(dāng)前動(dòng)畫系數(shù) mCurrentFraction = (float) animation.getAnimatedValue(); //通過(guò)插值器獲取插值系數(shù) fractionInterpolated = mAnimInterpolator.getInterpolation(mCurrentFraction); doAnimationCommon(mCurrentFraction, fractionInterpolated); ... }
接著,對(duì)需要執(zhí)行動(dòng)畫的卡片,執(zhí)行動(dòng)畫,以ANIM_TYPE_FRONT動(dòng)畫模式為例,當(dāng)選中的卡片移動(dòng)到最前的時(shí)候,原來(lái)在這張卡片之前的所有卡片,都要向后移動(dòng)一位,來(lái)留出第一個(gè)的位置
/** * 執(zhí)行通用動(dòng)畫 */ private void doAnimationCommon(float fraction, float fractionInterpolated) { //如果當(dāng)前動(dòng)畫模式為選中的卡片移到最前 if (mAnimType == InfiniteCardView.ANIM_TYPE_FRONT) { //則遍歷在選中卡片之前的卡片 for (int i = 0; i < mPositionToFront; i++) { CardItem card = mCards.get(i); //對(duì)卡片執(zhí)行動(dòng)畫,從當(dāng)前位置移動(dòng)到后一個(gè)位置 doAnimationCommonView(card.view, fraction, fractionInterpolated, i, i + 1); ... } }... }
最后,通過(guò)轉(zhuǎn)換器,對(duì)卡片進(jìn)行自定義動(dòng)畫處理
/** * 對(duì)視圖執(zhí)行通用動(dòng)畫 * @param view 卡片視圖 * @param fromPosition 從該位置 * @param toPosition 移動(dòng)到該位置 */ private void doAnimationCommonView(View view, float fraction, float fractionInterpolated, int fromPosition, int toPosition) { //通用轉(zhuǎn)換器轉(zhuǎn)換動(dòng)畫 mTransformerCommon.transformAnimation(view, fraction, mCardWidth, mCardHeight, fromPosition, toPosition); if (mAnimInterpolator != null) { //通用轉(zhuǎn)換器轉(zhuǎn)換插值動(dòng)畫 mTransformerCommon.transformInterpolatedAnimation(view, fractionInterpolated, mCardWidth,mCardHeight, fromPosition, toPosition); } }
而轉(zhuǎn)換器的具體實(shí)現(xiàn)則以DefaultCommonTransformer為例
@Override public void transformAnimation(View view, float fraction, int cardWidth, int cardHeight, int fromPosition, int toPosition) { //需要跨越的卡片數(shù)量 int positionCount = fromPosition - toPosition; //以0.8做為第一張的縮放尺寸,每向后一張縮小0.1 //(0.8f - 0.1f * fromPosition) = 當(dāng)前位置的縮放尺寸 //(0.1f * fraction * positionCount) = 移動(dòng)過(guò)程中需要改變的縮放尺寸 float scale = (0.8f - 0.1f * fromPosition) + (0.1f * fraction * positionCount); ViewHelper.setScaleX(view, scale); ViewHelper.setScaleY(view, scale); //在Y方向的偏移量,每向后一張,向上偏移卡片寬度的0.02 //-cardHeight * (0.8f - scale) * 0.5f 對(duì)卡片做整體居中處理 ViewHelper.setTranslationY(view, -cardHeight * (0.8f - scale) * 0.5f - cardWidth * (0.02f * fromPosition - 0.02f * fraction * positionCount)); }
對(duì)于向第一位移動(dòng)的選中卡片,也是同理,只不過(guò)是根據(jù)該卡片對(duì)應(yīng)的轉(zhuǎn)換器來(lái)進(jìn)行自定義動(dòng)畫的轉(zhuǎn)換。
最后的效果,就像演示圖中第一次點(diǎn)擊,圖片向前翻轉(zhuǎn)到第一位的效果一樣。
對(duì)于產(chǎn)品狗突如其來(lái)的想法,咱們程序猿不善于口水仗的,就只能用代碼來(lái)讓他們來(lái)服氣了。畢竟,大家還都是伐木累嘛,哈哈。
當(dāng)實(shí)現(xiàn)某個(gè)東西遇到困難時(shí),不妨想想Android系統(tǒng)自身的一些實(shí)現(xiàn)方式,比如參考ListView的Adapter,ViewPager定義翻頁(yè)動(dòng)畫的Transformer等等,總會(huì)有意想不到的啟發(fā)。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接
- Android之在linux終端執(zhí)行shell腳本直接打印當(dāng)前運(yùn)行app的日志的實(shí)現(xiàn)方法
- Android Java調(diào)用自己C++類庫(kù)的實(shí)例講解
- Android添加音頻的幾種方法
- Android四大組件之Activity詳解
- Android四大組件之Service詳解
- Android四大組件之BroadcastReceiver詳解
- Spring線程池ThreadPoolExecutor配置并且得到任務(wù)執(zhí)行的結(jié)果
- 線程池ThreadPoolExecutor使用簡(jiǎn)介與方法實(shí)例
- Java自帶定時(shí)任務(wù)ScheduledThreadPoolExecutor實(shí)現(xiàn)定時(shí)器和延時(shí)加載功能
- Android之線程池ThreadPoolExecutor的簡(jiǎn)介
相關(guān)文章
Android實(shí)現(xiàn)仿今日頭條點(diǎn)贊動(dòng)畫效果實(shí)例
我想看到今日頭條的點(diǎn)贊效果,應(yīng)該都覺(jué)得很絢麗吧,下面這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)仿今日頭條點(diǎn)贊動(dòng)畫效果的相關(guān)資料,文中通過(guò)示例代價(jià)介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02Android之ArcSlidingHelper制作圓弧滑動(dòng)效果
這篇文章主要介紹了Android之ArcSlidingHelper制作圓弧滑動(dòng)效果,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08Android啟動(dòng)APP時(shí)黑屏白屏的解決方法
這篇文章主要為大家詳細(xì)介紹了Android啟動(dòng)APP時(shí)黑屏白屏的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07Android沉浸式狀態(tài)欄微技巧(帶你真正理解沉浸式模式)
因?yàn)锳ndroid官方從來(lái)沒(méi)有給出過(guò)沉浸式狀態(tài)欄這樣的命名,只有沉浸式模式(Immersive Mode)這種說(shuō)法.下面通過(guò)本文給大家介紹Android沉浸式狀態(tài)欄微技巧,需要的朋友參考下2016-12-12Android CameraX結(jié)合LibYUV和GPUImage自定義相機(jī)濾鏡
之前使用Camera實(shí)現(xiàn)了一個(gè)自定義相機(jī)濾鏡(Android自定義相機(jī)濾鏡 ),但是運(yùn)行起來(lái)有點(diǎn)卡頓,這次用Camerax來(lái)實(shí)現(xiàn)一樣的效果發(fā)現(xiàn)很流暢,在此記錄一下,也希望能幫到有需要的同學(xué)2021-12-12快速解決安卓7.0系統(tǒng)寫入SD卡權(quán)限失敗的問(wèn)題
今天小編就為大家分享一篇快速解決安卓7.0系統(tǒng)寫入SD卡權(quán)限失敗的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08Android自定義view之圍棋動(dòng)畫效果的實(shí)現(xiàn)
這篇文章主要介紹了Android自定義view之圍棋動(dòng)畫效果的實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Android自定義Style實(shí)現(xiàn)方法
Android自定義Style實(shí)現(xiàn)方法,需要的朋友可以參考一下2013-06-06