前提
最近在做一個(gè)關(guān)于Camera的app我們的ux設(shè)計(jì)師根據(jù)Snapchat設(shè)計(jì)了我現(xiàn)在正在做的app腾么,剛開(kāi)始打開(kāi)Snapchat這個(gè)app的時(shí)候看他這個(gè)動(dòng)畫(huà)確實(shí)很炫酷,很多細(xì)節(jié)上面的動(dòng)畫(huà)飘蚯,打開(kāi)app的時(shí)候是在ios上的 我就懷疑android 是不是能實(shí)現(xiàn)一樣的效果析命,于是仔細(xì)的觀察了這個(gè)主頁(yè)的布局方式和動(dòng)畫(huà)的效果兴泥。其實(shí)很好實(shí)現(xiàn)工育。下面我們來(lái)一一分析到底如何做到這樣的動(dòng)畫(huà)。
原理分析
- 惡心的是搓彻,這個(gè)app需要翻墻你才能用如绸,那我們仔細(xì)看一下他這個(gè)動(dòng)畫(huà)的過(guò)程
- 主頁(yè)act 肯定是一個(gè)viewpager,然后三個(gè)fragment (我認(rèn)為是兩個(gè)fragment旭贬,第二個(gè)只是個(gè)透明的fragment怔接,占個(gè)茅坑而已)
- 然后大家仔細(xì)看看當(dāng)我在左右滑動(dòng)的過(guò)程中,底部的三個(gè)icon 左右移動(dòng) 放大縮小稀轨,同時(shí)還有一個(gè)icon 當(dāng)我們移動(dòng)到中間的時(shí)候會(huì)發(fā)現(xiàn)從底部移動(dòng)上來(lái)了
- 同時(shí)backgroundcolor也跟著動(dòng)態(tài)的變色扼脐。
- toolbar也是跟著不同的icon hide show title的改變。
- 然后就是中間拍照的時(shí)候 長(zhǎng)安會(huì)出現(xiàn) 一行表情動(dòng)態(tài) 改面部識(shí)別奋刽,點(diǎn)擊每個(gè)icon 會(huì)有個(gè)從左往右 或相反的方向移動(dòng)然后縮放瓦侮。(這個(gè)功能也好做,一會(huì)慢慢講解)
- 還有就是狀態(tài)欄顏色的動(dòng)態(tài)改變佣谐,過(guò)渡顏色
上圖
小伙子們感覺(jué)怎么樣啊 是不是就是實(shí)現(xiàn)了你們想要的功能肚吏。
布局實(shí)現(xiàn)
- 最外層布局是ConstraintLayout 這個(gè)google提供的一個(gè)非常niubility的一個(gè)強(qiáng)大的RelativeLayout,如果還有小伙伴沒(méi)有用過(guò)的話,強(qiáng)烈建議去mark一下狭魂,讓你的布局適配所有機(jī)器ConstraintLayout學(xué)習(xí)鏈接分分鐘學(xué)會(huì)
- 然后就是viewpager and 很多icon 相互協(xié)調(diào)做出來(lái)的罚攀。
- 主要有一個(gè)中間按個(gè)大icon 和下放的那個(gè)小icon 需要用一個(gè)相對(duì)布局包裹起來(lái)党觅。
直接上布局文件 方便小伙伴的copy
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/home_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="70dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<ImageView
android:id="@+id/recommended"
android:layout_width="100dp"
android:layout_height="30dp"
android:layout_margin="10dp"
android:src="@drawable/choiceness_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent=".8" />
<ImageView
android:id="@+id/model_panorama"
android:layout_width="70dp"
android:layout_height="70dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/guideline"
app:srcCompat="@mipmap/panorama_model" />
<ImageView
android:id="@+id/model_capture"
android:layout_width="70dp"
android:layout_height="50dp"
android:layout_marginTop="15dp"
app:layout_constraintRight_toLeftOf="@id/model_panorama"
app:layout_constraintTop_toBottomOf="@id/guideline"
app:srcCompat="@mipmap/capture_model" />
<ImageView
android:id="@+id/model_record"
android:layout_width="70dp"
android:layout_height="50dp"
android:layout_marginTop="15dp"
app:layout_constraintLeft_toRightOf="@+id/model_panorama"
app:layout_constraintTop_toBottomOf="@id/guideline"
app:srcCompat="@mipmap/record_model" />
<ImageView
android:id="@+id/gallery"
android:layout_width="100dp"
android:layout_height="30dp"
android:layout_margin="10dp"
android:src="@drawable/gallery_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<RelativeLayout
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="150dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/guideline">
<ImageView
android:id="@+id/camera"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:src="@drawable/circle_sp" />
<ImageView
android:layout_marginTop="10dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/circle_sp"
android:layout_below="@+id/camera"
android:layout_centerHorizontal="true" />
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
看到了吧,ImageView viewpager RelativeLayout 幾個(gè)布局嵌套就完成了這個(gè)Snapchat的叼叼的動(dòng)畫(huà)效果斋泄,我也很懵逼杯瞻,分析一下確實(shí)這樣§牌看起來(lái)很牛啊又兵,其實(shí)實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,有需求的小伙伴可以去試試卒废。那么布局實(shí)現(xiàn)了 肯定最主要的當(dāng)然是在代碼中了沛厨。不廢話,來(lái)讓我們codeing吧
代碼
可能我的代碼會(huì)比較亂摔认,因?yàn)楫?dāng)初是想著寫(xiě)個(gè)demo看看效果的逆皮,所以就demo級(jí)別的代碼。
- 那么我們來(lái)解決原理分析中的第一個(gè)問(wèn)題 viewpager中的fragment和三個(gè)fragment吧参袱,這沒(méi)有什么含金量电谣。就是寫(xiě)個(gè)list集合添加到viewpager得adapter里面,然后把第二個(gè)fragment設(shè)置為透明狀態(tài)抹蚀。over
- 第二個(gè)問(wèn)題是剿牺,左右滑動(dòng)icon的移動(dòng)變大縮放的過(guò)程。大家想一想我們正常的view 是不是可以設(shè)置padding环壤。那么padding是干嘛的晒来。內(nèi)邊距。好的進(jìn)入主題郑现。你設(shè)置了一個(gè)icon的寬高固定后湃崩。然后在設(shè)置相應(yīng)的padding是不是對(duì)于的是不是相應(yīng)的寬高就就會(huì)被padding 占了,icon這不就邊小了嗎接箫?設(shè)置左右padding 這不就位移了嗎攒读?那么問(wèn)題來(lái)了
- 怎么才能控制滑動(dòng)過(guò)程中padding的動(dòng)態(tài)改變呢,這個(gè)不廢話嗎辛友,viewpager干嘛的 viewpager是不是有addOnPageChangeListener這個(gè)方法薄扁,這個(gè)回調(diào)中是不是??onPageScrolled(int position, float positionOffset, int positionOffsetPixels)這個(gè)方法,我們稍微滑動(dòng)一下废累,這里面的值都是一致在改變的邓梅,這就是設(shè)置動(dòng)態(tài)設(shè)置padding的原理 獲取當(dāng)前滑動(dòng)的距離,做一下參數(shù)的調(diào)整即可實(shí)現(xiàn)第二個(gè)問(wèn)題要做的事
- 第三個(gè)問(wèn)題backgroundcolor的顏色改變九默。和上面第二個(gè)問(wèn)題一樣震放。設(shè)置初始值startcolor 和endColor 來(lái)根據(jù)滑動(dòng)的距離來(lái)得到當(dāng)前的顏色。不懂原理的可以看一下我的另一篇文章:彩虹進(jìn)度條
- toolbar的顏色改變也是一樣驼修,設(shè)置alpha的值0-1 來(lái)動(dòng)態(tài)的改變
- 那么最后一個(gè)問(wèn)題也是一樣通過(guò)這個(gè)三方工具可以簡(jiǎn)單的實(shí)現(xiàn)這個(gè)功能殿遂,問(wèn)題四已經(jīng)拿到了color
- 問(wèn)題解決思路是這樣下面我就直接把代碼邏輯copy上了有不懂得可以留言
代碼
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
float widthOfPagers = viewPager.getWidth() * (adapter.getCount() - 1);
float top = 160.0f;
float LeftRight = 20.0f;
float alpha = 1.0f;
float ratioTop = top / viewPager.getWidth();
float ratioLeftRight = LeftRight / viewPager.getWidth();
float ratioAlpha = alpha/viewPager.getWidth();
// 當(dāng)前Pager的滑動(dòng)距離
float currentPosOfPager = position * viewPager.getWidth() + positionOffsetPixels;
if (position >= 1) {
startColor = Color.parseColor("#9D52CB");
endColor = Color.parseColor("#ffffff");
currentPosOfPager = widthOfPagers - currentPosOfPager;
} else {
startColor = Color.parseColor("#08A9F3");
endColor = Color.parseColor("#ffffff");
}
float sizeTop = (currentPosOfPager * ratioTop);
float sizeLeftRight = (currentPosOfPager * ratioLeftRight);
float sizeAlpha = currentPosOfPager * ratioAlpha;
container.setPadding(0, ((int) (top - sizeTop)), 0, 0);
camera.setPadding(((int) (LeftRight - sizeLeftRight)), ((int) (LeftRight - sizeLeftRight)), ((int) (LeftRight - sizeLeftRight)), ((int) (LeftRight - sizeLeftRight)));
recommended.setPadding(((int) (top - sizeTop)), 0, 0, 0);
gallery.setPadding(0, 0, ((int) (top - sizeTop)), 0);
float progress = (currentPosOfPager / ((float) viewPager.getWidth())) * 100.0f;
LogUtil.e(progress);
int currentColor = getCurrentColor((int) progress);
homeBackground.setBackgroundColor(currentColor);
StatusBarUtil.setColor(mContext, currentColor, 0);
modelCapture.setAlpha(sizeAlpha);
modelPanorama.setAlpha(sizeAlpha);
modelRecord.setAlpha(sizeAlpha);
LogUtil.e(" "+currentColor);
}
@Override
public void onPageSelected(int position) {
if (position == 1){
modelCapture.setVisibility(View.VISIBLE);
modelRecord.setVisibility(View.VISIBLE);
modelPanorama.setVisibility(View.VISIBLE);
}else{
modelCapture.setVisibility(View.GONE);
modelRecord.setVisibility(View.GONE);
modelPanorama.setVisibility(View.GONE);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
這個(gè)就是核心代碼诈铛。根據(jù)上面的講解然后在看這段代碼的話應(yīng)該很快的了解到原理實(shí)現(xiàn)。
ending
好了墨礁,基本的邏輯和布局上面已經(jīng)都考出來(lái)了幢竹,我就不上傳github了,小伙伴們copy 一下功能就能完成了恩静。今天就到這里了焕毫。88