天天看點

AppBar簡述

AppBar簡述

本文的合集已經編著成書,進階Android開發強化實戰,歡迎各位讀友的建議和指導。在京東即可購買:https://item.jd.com/12385680.html

AppBar簡述

AppBar作為Android5.0的重要動畫效果, 非常絢麗的UI, 通過内容驅動, 可以減少頁面的通路, 更加便捷的傳遞主題思想. 那麼我們看看如何使用.

1. 準備

建立一個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"/>
           

設定ViewPager的Fragment内容

/**
 * 簡單的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);
    }
}
           

設定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 + );
    }

    @Override public int getCount() {
        return TITLE.length;
    }

    @Override public CharSequence getPageTitle(int position) {
        if (position >=  && 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設定

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

確定滑動.

把ViewPager加入TabLayout

基本功能已經完成, 再添加随着頁面切換AppBarLayout的圖檔視圖.

5. 漸變效果

在圖檔視圖中, 再添加一層圖檔, 模拟漸變動畫.

<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, 為每個頁面添加一個圖檔

/**
 * 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 + );
    }

    @Override public int getCount() {
        return SECTIONS.length;
    }

    @Override public CharSequence getPageTitle(int position) {
        if (position >=  && position < SECTIONS.length) {
            return SECTIONS[position].getTitle();
        }
        return null;
    }

    @DrawableRes
    public int getDrawable(int position) {
        if (position >=  && position < SECTIONS.length) {
            return SECTIONS[position].getDrawable();
        }
        return -;
    }

    private static final class Section {
        private final String mTitle; // 标題
        @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;
        }
    }
}
           

兩個圖像頁面的漸變動畫

/**
 * 漸變的動畫效果
 * <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; // 實際起始位置

    public ImageAnimator(SimpleAdapter adapter, ImageView targetImage, ImageView outgoingImage) {
        mAdapter = adapter;
        mTargetImage = targetImage;
        mOutgoingImage = outgoingImage;
    }

    /**
     * 啟動動畫, 之後選擇向前或向後滑動
     *
     * @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(f);

        // 目标圖檔
        mTargetImage.setImageResource(incomeId);
    }

    /**
     * 滑動結束的動畫效果
     *
     * @param endPosition 滑動位置
     */
    public void end(int endPosition) {
        @DrawableRes int incomeId = mAdapter.getDrawable(endPosition);
        mTargetImage.setTranslationX(f);

        // 設定原始圖檔
        if (endPosition == mActualStart) {
            mTargetImage.setImageDrawable(mOutgoingImage.getDrawable());
        } else {
            mTargetImage.setImageResource(incomeId);
            mTargetImage.setAlpha(f);
            mOutgoingImage.setVisibility(View.GONE);
        }
    }

    // 向後滾動, 比如0->1, offset滾動的距離(0->1), 目标漸漸淡出
    public void forward(float positionOffset) {
        mTargetImage.setAlpha(positionOffset);
    }

    // 向前滾動, 比如1->0, offset滾動的距離(1->0), 目标漸漸淡出
    public void backwards(float positionOffset) {
        mTargetImage.setAlpha( - positionOffset);
    }
}
           
注釋已經寫的很詳細了, 注意的是向前向後滑動, position的值不同. 向前滑動, position的值是目前值減一; 向後滑動, position的值不變. 也就是說, position總是較小值. 偏移量, 向後滑動時, 從0至1; 向前滑動時, 從1至0.

ViewPager的監聽, 使用動畫效果

/**
 * ViewPager滑動頁面監聽
 * <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);
    }

    /**
     * 滑動監聽
     *
     * @param position             目前位置
     * @param positionOffset       偏移[目前值+-1]
     * @param positionOffsetPixels 偏移像素
     */
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        Log.e("DEBUG-WCL", "position: " + position + ", positionOffset: " + positionOffset);

        // 以前滑動, 現在終止
        if (isFinishedScrolling(position, positionOffset)) {
            finishScroll(position);
        }

        // 判斷前後滑動
        if (isStartingScrollToPrevious(position, positionOffset)) {
            startScroll(position);
        } else if (isStartingScrollToNext(position, positionOffset)) {
            startScroll(position + ); // 向後滾動需要加1
        }

        // 向後滾動
        if (isScrollingToNext(position, positionOffset)) {
            mImageAnimator.forward(positionOffset);
        } else if (isScrollingToPrevious(position, positionOffset)) { // 向前滾動
            mImageAnimator.backwards(positionOffset);
        }
    }

    /**
     * 終止滑動
     * 滑動 && [偏移是0&&滑動終點] || 動畫之中
     *
     * @param position       位置
     * @param positionOffset 偏移量
     * @return 終止滑動
     */
    public boolean isFinishedScrolling(int position, float positionOffset) {
        return mIsScrolling && (positionOffset == f && position == mFinalPosition);
    }

    /**
     * 從靜止到開始滑動, 下一個
     * 未滑動 && 位置是目前位置 && 偏移量不是0
     *
     * @param position       位置
     * @param positionOffset 偏移量
     * @return 是否
     */
    private boolean isStartingScrollToNext(int position, float positionOffset) {
        return !mIsScrolling && position == mCurrentPosition && positionOffset != f;
    }

    /**
     * 從靜止到開始滑動, 前一個[position會-1]
     *
     * @param position       位置
     * @param positionOffset 偏移量
     * @return 是否
     */
    private boolean isStartingScrollToPrevious(int position, float positionOffset) {
        return !mIsScrolling && position != mCurrentPosition && positionOffset != f;
    }

    /**
     * 開始滾動, 向後
     *
     * @param position       位置
     * @param positionOffset 偏移
     * @return 是否
     */
    private boolean isScrollingToNext(int position, float positionOffset) {
        return mIsScrolling && position == mCurrentPosition && positionOffset != f;
    }

    /**
     * 開始滾動, 向前
     *
     * @param position       位置
     * @param positionOffset 偏移
     * @return 是否
     */
    private boolean isScrollingToPrevious(int position, float positionOffset) {
        return mIsScrolling && position != mCurrentPosition && positionOffset != f;
    }

    /**
     * 開始滑動
     * 滾動開始, 結束位置是position[前滾時position會自動減一], 動畫從目前位置到結束位置.
     *
     * @param position 滾動結束之後的位置
     */
    private void startScroll(int position) {
        mIsScrolling = true;
        mFinalPosition = position;

        // 開始滾動動畫
        mImageAnimator.start(mCurrentPosition, position);
    }

    /**
     * 如果正在滾動, 結束時, 固定position位置, 停止滾動, 調動截止動畫
     *
     * @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);
        }
    }
}
           
詳細内容參見注釋

ViewPager添加滾動監聽.

SimpleAdapter adapter = new SimpleAdapter(getSupportFragmentManager());
        mVpContainer.setAdapter(adapter);
        mVpContainer.addOnPageChangeListener(PagerChangeListener.newInstance(adapter, mIvTarget, mIvOutgoing));
           

6. 頁面滾動

在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内的元件填充, 否則ViewPager顯示.

設定标題, MainActivity.

setTitle("Girls' Generation");
           

動畫效果

AppBar簡述

7. 偏移滾動

額, 與其說是AppBar, 不如說是ViewPager, 再添加一些動畫效果, 和修複Bug.

把圖檔的左右10%, 隐藏起來, 滑動時, 一邊漸變一邊側移.

app_bar_main

中, 替換

FrameLayout

PercentFrameLayout

, 寬度設定為

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>
           

修改滑動動畫(Image), 在向前向後滑動時,

setTranslationX

X軸平移.

/**
 * 漸變的動畫效果
 * <p/>
 * Created by wangchenlong on 15/11/9.
 */
public class ImageAnimator {

    private static final float FACTOR = f;

    private final SimpleAdapter mAdapter; // 擴充卡
    private final ImageView mTargetImage; // 原始圖檔
    private final ImageView mOutgoingImage; // 漸變圖檔

    private int mActualStart; // 實際起始位置

    private int mStart;
    private int mEnd;

    public ImageAnimator(SimpleAdapter adapter, ImageView targetImage, ImageView outgoingImage) {
        mAdapter = adapter;
        mTargetImage = targetImage;
        mOutgoingImage = outgoingImage;
    }

    /**
     * 啟動動畫, 之後選擇向前或向後滑動
     *
     * @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(f);

        mOutgoingImage.setVisibility(View.VISIBLE);
        mOutgoingImage.setAlpha(f);

        // 目标圖檔
        mTargetImage.setImageResource(incomeId);

        mStart = Math.min(startPosition, endPosition);
        mEnd = Math.max(startPosition, endPosition);
    }

    /**
     * 滑動結束的動畫效果
     *
     * @param endPosition 滑動位置
     */
    public void end(int endPosition) {
        @DrawableRes int incomeId = mAdapter.getDrawable(endPosition);
        mTargetImage.setTranslationX(f);

        // 設定原始圖檔
        if (endPosition == mActualStart) {
            mTargetImage.setImageDrawable(mOutgoingImage.getDrawable());
        } else {
            mTargetImage.setImageResource(incomeId);
            mTargetImage.setAlpha(f);
            mOutgoingImage.setVisibility(View.GONE);
        }
    }

    // 向前滾動, 比如0->1, offset滾動的距離(0->1), 目标漸漸淡出
    public void forward(float positionOffset) {
        Log.e("DEBUG-WCL", "forward-positionOffset: " + positionOffset);
        int width = mTargetImage.getWidth();
        mOutgoingImage.setTranslationX(-positionOffset * (FACTOR * width));
        mTargetImage.setTranslationX(( - positionOffset) * (FACTOR * width));

        mTargetImage.setAlpha(positionOffset);
    }

    // 向後滾動, 比如1->0, offset滾動的距離(1->0), 目标漸漸淡入
    public void backwards(float positionOffset) {
        Log.e("DEBUG-WCL", "backwards-positionOffset: " + positionOffset);
        int width = mTargetImage.getWidth();
        mOutgoingImage.setTranslationX(( - positionOffset) * (FACTOR * width));
        mTargetImage.setTranslationX(-(positionOffset) * (FACTOR * width));

        mTargetImage.setAlpha( - positionOffset);
    }

    // 判斷停止
    public boolean isWithin(int position) {
        return position >= mStart && position < mEnd;
    }
}
           

修複滾動Bug, 連續滾動時, 及時更新目前圖檔, 否則圖檔異常.

/**
     * 終止滑動
     * 滑動 && [偏移是0&&滑動終點] || 動畫之中 || 未在區間即連續滾動
     *
     * @param position       位置
     * @param positionOffset 偏移量
     * @return 終止滑動
     */
    public boolean isFinishedScrolling(int position, float positionOffset) {
        return mIsScrolling && (positionOffset == f && position == mFinalPosition) || !mImageAnimator.isWithin(position);
    }
           

Github下載下傳位址

OK, 全部功能都已經完成了. AppBar的知識就這些了.