本文的合集已經(jīng)編著成書,高級(jí)Android開發(fā)強(qiáng)化實(shí)戰(zhàn)呻待,歡迎各位讀友的建議和指導(dǎo)。在京東即可購(gòu)買:https://item.jd.com/12385680.html
AppBar作為Android5.0的重要?jiǎng)赢嬓Ч? 非常絢麗的UI, 通過內(nèi)容驅(qū)動(dòng), 可以減少頁(yè)面的訪問, 更加便捷的傳遞主題思想. 那么我們看看如何使用.
本文源碼的GitHub下載地址
1. 準(zhǔn)備
創(chuàng)建一個(gè)Navigation Drawer的工程, 修改主題顏色.
<resources>
<color name="colorPrimary">#FF1493</color>
<color name="colorPrimaryDark">#FF1493</color>
<color name="colorAccent">#FF4081</color>
</resources>
修改抽屜的漸變顏色side_nav_bar.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:startColor="#FF34"
android:centerColor="#FF3E96"
android:endColor="#FF1493"
android:type="linear"
android:angle="135"/>
</shape>
2. ViewPager
修改app_bar_main.xml, 在CoordinatorLayout中添加ViewPager.
<android.support.v4.view.ViewPager
android:id="@+id/main_vp_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
設(shè)置ViewPager的Fragment內(nèi)容
/**
* 簡(jiǎn)單的Fragment
* <p/>
* Created by wangchenlong on 15/11/9.
*/
public class SimpleFragment extends Fragment {
private static final String ARG_SELECTION_NUM = "arg_selection_num";
@Bind(R.id.main_tv_text) TextView mTvText;
public SimpleFragment() {
}
public static SimpleFragment newInstance(int selectionNum) {
SimpleFragment simpleFragment = new SimpleFragment();
Bundle args = new Bundle();
args.putInt(ARG_SELECTION_NUM, selectionNum);
simpleFragment.setArguments(args);
return simpleFragment;
}
@Nullable @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mTvText.setText("Page " + String.valueOf(getArguments().getInt(ARG_SELECTION_NUM)));
}
@Override public void onDestroyView() {
super.onDestroyView();
ButterKnife.unbind(this);
}
}
設(shè)置ViewPager的適配器
/**
* ViewPager的適配器
* <p/>
* Created by wangchenlong on 15/11/9.
*/
public class SimpleAdapter extends FragmentPagerAdapter {
private static final String[] TITLE = {"SELECTION 1", "SELECTION 2", "SELECTION 3"};
public SimpleAdapter(FragmentManager fm) {
super(fm);
}
@Override public Fragment getItem(int position) {
return SimpleFragment.newInstance(position + 1);
}
@Override public int getCount() {
return TITLE.length;
}
@Override public CharSequence getPageTitle(int position) {
if (position >= 0 && position < TITLE.length) {
return TITLE[position];
}
return null;
}
}
在MainActivity中添加ViewPager邏輯
mVpContainer.setAdapter(new SimpleAdapter(getSupportFragmentManager()));
3. AppBarLayout
修改AppBarLayout, 添加CollapsingToolbarLayout.
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="400dp"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="16dp"
app:expandedTitleMarginEnd="16dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/toolbar_iv_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:contentDescription="@null"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@drawable/taeyeon"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.CollapsingToolbarLayout>
刪除Toolbar的background顏色, 避免Toolbar擋住圖片;
AppBarLayout設(shè)置fitsSystemWindows
覆蓋StatusBar背景;
CollapsingToolbarLayout包含圖片和Toolbar.
4. TabLayout
在AppBarLayout中, 添加TabLayout
<android.support.design.widget.TabLayout
android:id="@+id/toolbar_tl_tab"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
app:layout_scrollFlags="scroll"/>
TabLayout添加
layout_scrollFlags
確保滑動(dòng).
把ViewPager加入TabLayout
mTlTab.setupWithViewPager(mVpContainer);
基本功能已經(jīng)完成, 再添加隨著頁(yè)面切換AppBarLayout的圖片視圖.
5. 漸變效果
在圖片視圖中, 再添加一層圖片, 模擬漸變動(dòng)畫.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax">
<ImageView
android:id="@+id/toolbar_iv_outgoing"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:contentDescription="@null"
android:scaleType="centerCrop"
android:visibility="gone"/>
<ImageView
android:id="@+id/toolbar_iv_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:contentDescription="@null"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@drawable/tiffany"
app:layout_collapseMode="parallax"/>
</FrameLayout>
修改Adapter, 為每個(gè)頁(yè)面添加一個(gè)圖片
/**
* ViewPager的適配器
* <p/>
* Created by wangchenlong on 15/11/9.
*/
public class SimpleAdapter extends FragmentPagerAdapter {
private static final Section[] SECTIONS = {
new Section("Tiffany", R.drawable.tiffany),
new Section("Taeyeon", R.drawable.taeyeon),
new Section("Yoona", R.drawable.yoona)
};
public SimpleAdapter(FragmentManager fm) {
super(fm);
}
@Override public Fragment getItem(int position) {
return SimpleFragment.newInstance(position + 1);
}
@Override public int getCount() {
return SECTIONS.length;
}
@Override public CharSequence getPageTitle(int position) {
if (position >= 0 && position < SECTIONS.length) {
return SECTIONS[position].getTitle();
}
return null;
}
@DrawableRes
public int getDrawable(int position) {
if (position >= 0 && position < SECTIONS.length) {
return SECTIONS[position].getDrawable();
}
return -1;
}
private static final class Section {
private final String mTitle; // 標(biāo)題
@DrawableRes private final int mDrawable; // 圖片
public Section(String title, int drawable) {
mTitle = title;
mDrawable = drawable;
}
public String getTitle() {
return mTitle;
}
public int getDrawable() {
return mDrawable;
}
}
}
兩個(gè)圖像頁(yè)面的漸變動(dòng)畫
/**
* 漸變的動(dòng)畫效果
* <p/>
* Created by wangchenlong on 15/11/9.
*/
public class ImageAnimator {
private final SimpleAdapter mAdapter; // 適配器
private final ImageView mTargetImage; // 原始圖片
private final ImageView mOutgoingImage; // 漸變圖片
private int mActualStart; // 實(shí)際起始位置
public ImageAnimator(SimpleAdapter adapter, ImageView targetImage, ImageView outgoingImage) {
mAdapter = adapter;
mTargetImage = targetImage;
mOutgoingImage = outgoingImage;
}
/**
* 啟動(dòng)動(dòng)畫, 之后選擇向前或向后滑動(dòng)
*
* @param startPosition 起始位置
* @param endPosition 終止位置
*/
public void start(int startPosition, int endPosition) {
mActualStart = startPosition;
// 終止位置的圖片
@DrawableRes int incomeId = mAdapter.getDrawable(endPosition);
// 原始圖片
mOutgoingImage.setImageDrawable(mTargetImage.getDrawable()); // 原始的圖片
mOutgoingImage.setVisibility(View.VISIBLE);
mOutgoingImage.setAlpha(1.0f);
// 目標(biāo)圖片
mTargetImage.setImageResource(incomeId);
}
/**
* 滑動(dòng)結(jié)束的動(dòng)畫效果
*
* @param endPosition 滑動(dòng)位置
*/
public void end(int endPosition) {
@DrawableRes int incomeId = mAdapter.getDrawable(endPosition);
mTargetImage.setTranslationX(0f);
// 設(shè)置原始圖片
if (endPosition == mActualStart) {
mTargetImage.setImageDrawable(mOutgoingImage.getDrawable());
} else {
mTargetImage.setImageResource(incomeId);
mTargetImage.setAlpha(1f);
mOutgoingImage.setVisibility(View.GONE);
}
}
// 向后滾動(dòng), 比如0->1, offset滾動(dòng)的距離(0->1), 目標(biāo)漸漸淡出
public void forward(float positionOffset) {
mTargetImage.setAlpha(positionOffset);
}
// 向前滾動(dòng), 比如1->0, offset滾動(dòng)的距離(1->0), 目標(biāo)漸漸淡出
public void backwards(float positionOffset) {
mTargetImage.setAlpha(1 - positionOffset);
}
}
注釋已經(jīng)寫的很詳細(xì)了, 注意的是向前向后滑動(dòng), position的值不同. 向前滑動(dòng), position的值是當(dāng)前值減一; 向后滑動(dòng), position的值不變. 也就是說, position總是較小值. 偏移量, 向后滑動(dòng)時(shí), 從0至1; 向前滑動(dòng)時(shí), 從1至0.
ViewPager的監(jiān)聽, 使用動(dòng)畫效果
/**
* ViewPager滑動(dòng)頁(yè)面監(jiān)聽
* <p/>
* Created by wangchenlong on 15/11/9.
*/
public class PagerChangeListener implements ViewPager.OnPageChangeListener {
private ImageAnimator mImageAnimator;
private int mCurrentPosition;
private int mFinalPosition;
private boolean mIsScrolling = false;
public PagerChangeListener(ImageAnimator imageAnimator) {
mImageAnimator = imageAnimator;
}
public static PagerChangeListener newInstance(SimpleAdapter adapter, ImageView originImage, ImageView outgoingImage) {
ImageAnimator imageAnimator = new ImageAnimator(adapter, originImage, outgoingImage);
return new PagerChangeListener(imageAnimator);
}
/**
* 滑動(dòng)監(jiān)聽
*
* @param position 當(dāng)前位置
* @param positionOffset 偏移[當(dāng)前值+-1]
* @param positionOffsetPixels 偏移像素
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.e("DEBUG-WCL", "position: " + position + ", positionOffset: " + positionOffset);
// 以前滑動(dòng), 現(xiàn)在終止
if (isFinishedScrolling(position, positionOffset)) {
finishScroll(position);
}
// 判斷前后滑動(dòng)
if (isStartingScrollToPrevious(position, positionOffset)) {
startScroll(position);
} else if (isStartingScrollToNext(position, positionOffset)) {
startScroll(position + 1); // 向后滾動(dòng)需要加1
}
// 向后滾動(dòng)
if (isScrollingToNext(position, positionOffset)) {
mImageAnimator.forward(positionOffset);
} else if (isScrollingToPrevious(position, positionOffset)) { // 向前滾動(dòng)
mImageAnimator.backwards(positionOffset);
}
}
/**
* 終止滑動(dòng)
* 滑動(dòng) && [偏移是0&&滑動(dòng)終點(diǎn)] || 動(dòng)畫之中
*
* @param position 位置
* @param positionOffset 偏移量
* @return 終止滑動(dòng)
*/
public boolean isFinishedScrolling(int position, float positionOffset) {
return mIsScrolling && (positionOffset == 0f && position == mFinalPosition);
}
/**
* 從靜止到開始滑動(dòng), 下一個(gè)
* 未滑動(dòng) && 位置是當(dāng)前位置 && 偏移量不是0
*
* @param position 位置
* @param positionOffset 偏移量
* @return 是否
*/
private boolean isStartingScrollToNext(int position, float positionOffset) {
return !mIsScrolling && position == mCurrentPosition && positionOffset != 0f;
}
/**
* 從靜止到開始滑動(dòng), 前一個(gè)[position會(huì)-1]
*
* @param position 位置
* @param positionOffset 偏移量
* @return 是否
*/
private boolean isStartingScrollToPrevious(int position, float positionOffset) {
return !mIsScrolling && position != mCurrentPosition && positionOffset != 0f;
}
/**
* 開始滾動(dòng), 向后
*
* @param position 位置
* @param positionOffset 偏移
* @return 是否
*/
private boolean isScrollingToNext(int position, float positionOffset) {
return mIsScrolling && position == mCurrentPosition && positionOffset != 0f;
}
/**
* 開始滾動(dòng), 向前
*
* @param position 位置
* @param positionOffset 偏移
* @return 是否
*/
private boolean isScrollingToPrevious(int position, float positionOffset) {
return mIsScrolling && position != mCurrentPosition && positionOffset != 0f;
}
/**
* 開始滑動(dòng)
* 滾動(dòng)開始, 結(jié)束位置是position[前滾時(shí)position會(huì)自動(dòng)減一], 動(dòng)畫從當(dāng)前位置到結(jié)束位置.
*
* @param position 滾動(dòng)結(jié)束之后的位置
*/
private void startScroll(int position) {
mIsScrolling = true;
mFinalPosition = position;
// 開始滾動(dòng)動(dòng)畫
mImageAnimator.start(mCurrentPosition, position);
}
/**
* 如果正在滾動(dòng), 結(jié)束時(shí), 固定position位置, 停止?jié)L動(dòng), 調(diào)動(dòng)截止動(dòng)畫
*
* @param position 位置
*/
private void finishScroll(int position) {
if (mIsScrolling) {
mCurrentPosition = position;
mIsScrolling = false;
mImageAnimator.end(position);
}
}
@Override
public void onPageScrollStateChanged(int state) {
//NO-OP
}
@Override
public void onPageSelected(int position) {
if (!mIsScrolling) {
mIsScrolling = true;
mFinalPosition = position;
mImageAnimator.start(mCurrentPosition, position);
}
}
}
詳細(xì)內(nèi)容參見注釋
ViewPager添加滾動(dòng)監(jiān)聽.
SimpleAdapter adapter = new SimpleAdapter(getSupportFragmentManager());
mVpContainer.setAdapter(adapter);
mVpContainer.addOnPageChangeListener(PagerChangeListener.newInstance(adapter, mIvTarget, mIvOutgoing));
6. 頁(yè)面滾動(dòng)
在ViewPager上, 嵌套NestedScrollView.
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v4.view.ViewPager
android:id="@+id/main_vp_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.v4.widget.NestedScrollView>
android:fillViewport
允許ScrollView內(nèi)的組件填充, 否則ViewPager顯示.
設(shè)置標(biāo)題, MainActivity.
setTitle("Girls' Generation");
動(dòng)畫效果
7. 偏移滾動(dòng)
額, 與其說是AppBar, 不如說是ViewPager, 再添加一些動(dòng)畫效果, 和修復(fù)Bug.
把圖片的左右10%, 隱藏起來, 滑動(dòng)時(shí), 一邊漸變一邊側(cè)移.
在app_bar_main
中, 替換FrameLayout
為PercentFrameLayout
, 寬度設(shè)置為120%
.
<android.support.percent.PercentFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax">
<ImageView
android:id="@+id/toolbar_iv_outgoing"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:adjustViewBounds="true"
android:contentDescription="@null"
android:scaleType="centerCrop"
android:visibility="gone"
app:layout_widthPercent="120%"/>
<ImageView
android:id="@+id/toolbar_iv_target"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:adjustViewBounds="true"
android:contentDescription="@null"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@drawable/tiffany"
app:layout_collapseMode="parallax"
app:layout_widthPercent="120%"/>
</android.support.percent.PercentFrameLayout>
修改滑動(dòng)動(dòng)畫(Image), 在向前向后滑動(dòng)時(shí), setTranslationX
X軸平移.
/**
* 漸變的動(dòng)畫效果
* <p/>
* Created by wangchenlong on 15/11/9.
*/
public class ImageAnimator {
private static final float FACTOR = 0.1f;
private final SimpleAdapter mAdapter; // 適配器
private final ImageView mTargetImage; // 原始圖片
private final ImageView mOutgoingImage; // 漸變圖片
private int mActualStart; // 實(shí)際起始位置
private int mStart;
private int mEnd;
public ImageAnimator(SimpleAdapter adapter, ImageView targetImage, ImageView outgoingImage) {
mAdapter = adapter;
mTargetImage = targetImage;
mOutgoingImage = outgoingImage;
}
/**
* 啟動(dòng)動(dòng)畫, 之后選擇向前或向后滑動(dòng)
*
* @param startPosition 起始位置
* @param endPosition 終止位置
*/
public void start(int startPosition, int endPosition) {
mActualStart = startPosition;
// 終止位置的圖片
@DrawableRes int incomeId = mAdapter.getDrawable(endPosition);
// 原始圖片
mOutgoingImage.setImageDrawable(mTargetImage.getDrawable()); // 原始的圖片
// 起始圖片
mOutgoingImage.setTranslationX(0f);
mOutgoingImage.setVisibility(View.VISIBLE);
mOutgoingImage.setAlpha(1.0f);
// 目標(biāo)圖片
mTargetImage.setImageResource(incomeId);
mStart = Math.min(startPosition, endPosition);
mEnd = Math.max(startPosition, endPosition);
}
/**
* 滑動(dòng)結(jié)束的動(dòng)畫效果
*
* @param endPosition 滑動(dòng)位置
*/
public void end(int endPosition) {
@DrawableRes int incomeId = mAdapter.getDrawable(endPosition);
mTargetImage.setTranslationX(0f);
// 設(shè)置原始圖片
if (endPosition == mActualStart) {
mTargetImage.setImageDrawable(mOutgoingImage.getDrawable());
} else {
mTargetImage.setImageResource(incomeId);
mTargetImage.setAlpha(1f);
mOutgoingImage.setVisibility(View.GONE);
}
}
// 向前滾動(dòng), 比如0->1, offset滾動(dòng)的距離(0->1), 目標(biāo)漸漸淡出
public void forward(float positionOffset) {
Log.e("DEBUG-WCL", "forward-positionOffset: " + positionOffset);
int width = mTargetImage.getWidth();
mOutgoingImage.setTranslationX(-positionOffset * (FACTOR * width));
mTargetImage.setTranslationX((1 - positionOffset) * (FACTOR * width));
mTargetImage.setAlpha(positionOffset);
}
// 向后滾動(dòng), 比如1->0, offset滾動(dòng)的距離(1->0), 目標(biāo)漸漸淡入
public void backwards(float positionOffset) {
Log.e("DEBUG-WCL", "backwards-positionOffset: " + positionOffset);
int width = mTargetImage.getWidth();
mOutgoingImage.setTranslationX((1 - positionOffset) * (FACTOR * width));
mTargetImage.setTranslationX(-(positionOffset) * (FACTOR * width));
mTargetImage.setAlpha(1 - positionOffset);
}
// 判斷停止
public boolean isWithin(int position) {
return position >= mStart && position < mEnd;
}
}
修復(fù)滾動(dòng)Bug, 連續(xù)滾動(dòng)時(shí), 及時(shí)更新當(dāng)前圖片, 否則圖片異常.
/**
* 終止滑動(dòng)
* 滑動(dòng) && [偏移是0&&滑動(dòng)終點(diǎn)] || 動(dòng)畫之中 || 未在區(qū)間即連續(xù)滾動(dòng)
*
* @param position 位置
* @param positionOffset 偏移量
* @return 終止滑動(dòng)
*/
public boolean isFinishedScrolling(int position, float positionOffset) {
return mIsScrolling && (positionOffset == 0f && position == mFinalPosition) || !mImageAnimator.isWithin(position);
}
全部功能都已經(jīng)完成了. AppBar的知識(shí)就這些了.
OK, that's all! Enjoy it!