Material Design中全新的動(dòng)畫

文檔地址

Material Design中的動(dòng)畫將為用戶提供操作反饋并在用戶與您的應(yīng)用進(jìn)行互動(dòng)時(shí)提供視覺連續(xù)性墙贱。 Material Design將為按鈕與操作行為轉(zhuǎn)換提供一些默認(rèn)動(dòng)畫,而 Android 5.0(API Level 21)及更高版本可讓您定制這些動(dòng)畫,同時(shí)也可創(chuàng)建新動(dòng)畫:

一奸汇、觸摸反饋動(dòng)畫

效果圖:

Material Design的觸摸反饋可在用戶與 UI 元素互動(dòng)時(shí)峻呕,在接觸點(diǎn)上提供即時(shí)視覺確認(rèn)糜烹。 適用于按鈕的默認(rèn)觸摸動(dòng)畫使用全新 RippleDrawable類別娶靡,以波紋效果實(shí)現(xiàn)不同狀態(tài)間的轉(zhuǎn)換。
在大多數(shù)情況下揖膜,應(yīng)以下列方式指定視圖背景誓沸,在您的視圖 XML 中應(yīng)用此功能:

  • ?android:attr/selectableItemBackground 指定有界的波紋。
  • ?android:attr/selectableItemBackgroundBorderless 指定越過視圖邊界的波紋壹粟。 它將由一個(gè)非空背景的視圖的最近父項(xiàng)所繪制和設(shè)定邊界拜隧。

任何view處于可點(diǎn)擊狀態(tài),都可以使用RippleDrawable來達(dá)到水波紋特效趁仙,而且必須處于可點(diǎn)擊狀態(tài)洪添,才會(huì)出現(xiàn)波紋動(dòng)畫效果。

在代碼中可以這樣設(shè)置:

RippleDrawableColorStateList stateList = getResources().getColorStateList(R.color.tint_state_color);
RippleDrawable rippleDrawable = new RippleDrawable(stateList, null, null);
view.setBackground(rippleDrawable);

注意:selectableItemBackgroundBorderless是 API Level 21 中推出的新屬性幸撕。

此外薇组,您可利用 ripple元素將 RippleDrawable定義為一個(gè) XML 資源。
您可以為 RippleDrawable對(duì)象指定一種顏色坐儿。如果要改變默認(rèn)觸摸反饋顏色,請(qǐng)使用主題的 android:colorControlHighlight屬性宋光。
如果要了解更多信息貌矿,請(qǐng)參閱 RippleDrawable類別的 API 參考文檔。

我們來看看系統(tǒng)自帶的觸摸反饋動(dòng)畫是怎么實(shí)現(xiàn)的罪佳,為什么只需要在view的background或者foreground屬性設(shè)置成?android:attr/selectableItemBackground或者?android:attr/selectableItemBackgroundBorderless就可以實(shí)現(xiàn)波紋動(dòng)畫的效果逛漫?這兩個(gè)屬性點(diǎn)進(jìn)去,可以看到在路徑sdk/platforms/android-xx/data/res/values/attrs.xml文件中有定義這么兩個(gè)屬性:

<!-- Background drawable for bordered standalone items that need focus/pressed states. -->
<attr name="selectableItemBackground" format="reference" />
<!-- Background drawable for borderless standalone items that need focus/pressed states. -->
<attr name="selectableItemBackgroundBorderless" format="reference" />

我們想到赘艳,這兩個(gè)屬性既然是整個(gè)app中有效的酌毡,那可能會(huì)是在Theme中的屬性吧,那就去AndroidManifest文件中跟這個(gè)Theme一步步看下去蕾管,最后在Base.V21.Theme.AppCompat.Light這個(gè)style中看到確實(shí)是有這兩個(gè)item屬性:

<item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
<item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackgroundBorderless</item>

但是這里還是調(diào)用的系統(tǒng)的定義的屬性枷踏,繼續(xù)往下追,在android:Theme.Materialandroid:Theme.Material.Light中掰曾,可以看到:

<item name="selectableItemBackground">@drawable/item_background_material</item>
<item name="selectableItemBackgroundBorderless">@drawable/item_background_borderless_material</item>

然后sdk路徑下platforms\\android-xx\\data\\res\\drawable可以找到這些資源文件如下圖:

item_background_material的內(nèi)容是:

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?attr/colorControlHighlight">
    <item android:id="@id/mask">
        <color android:color="@color/white" />
    </item>
</ripple>

item_background_borderless_material的內(nèi)容是:

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?attr/colorControlHighlight" />

系統(tǒng)的做法是用ripple元素將 RippleDrawable定義為一個(gè) XML 資源旭蠕,而通過看View的源碼中在構(gòu)造方法中是這樣獲取background屬性的:

public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        this(context);

        final TypedArray a = context.obtainStyledAttributes(
                attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);

        if (mDebugViewAttributes) {
            saveAttributeData(attrs, a);
        }

        Drawable background = null;

        switch (attr) {
            case com.android.internal.R.styleable.View_background:
                background = a.getDrawable(attr);
                break;

        .
        .
        .
}

也就是說,這個(gè)background實(shí)際上就是RippleDrawable類旷坦。那我們就來看看這個(gè)RippleDrawable內(nèi)部到底是怎么做的吧掏熬。

首先官方文檔對(duì)RippleDrawable解釋是
Drawable that shows a ripple effect in response to state changes. The anchoring position of the ripple for a given state may be specified by calling setHotspot(float, float)with the corresponding state attribute identifier.
通過顯示出波紋效果來響應(yīng)狀態(tài)的改變,對(duì)于給定狀態(tài)的波紋的錨定位置可以通過調(diào)用具有對(duì)應(yīng)的狀態(tài)屬性標(biāo)識(shí)符的setHotspot(float秒梅,float)來指定旗芬。

RippleDrawable繼承自LayerDrawable,而LayerDrawable是繼承Drawable捆蜀,RippleDrawable又是為了響應(yīng)View的statechange疮丛,那就看看Drawable類中對(duì)點(diǎn)擊時(shí)的狀態(tài)處理吧辆琅。

public boolean setState(@NonNull final int[] stateSet) {
    if (!Arrays.equals(mStateSet, stateSet)) {
        mStateSet = stateSet;
        return onStateChange(stateSet);
    }
    return false;
}

給Drawable設(shè)置狀態(tài)屬性時(shí),會(huì)把狀態(tài)的數(shù)組傳給onStateChange方法这刷,在RippleDrawable中重寫了onStateChange婉烟。

@Override
protected boolean onStateChange(int[] stateSet) {
    final boolean changed = super.onStateChange(stateSet);

    boolean enabled = false;
    boolean pressed = false;
    boolean focused = false;
    boolean hovered = false;

    for (int state : stateSet) {
        if (state == R.attr.state_enabled) {
            enabled = true;
        } else if (state == R.attr.state_focused) {
            focused = true;
        } else if (state == R.attr.state_pressed) {
            pressed = true;
        } else if (state == R.attr.state_hovered) {
            hovered = true;
        }
    }

    setRippleActive(enabled && pressed);
    setBackgroundActive(hovered || focused || (enabled && pressed), focused || hovered);

    return changed;
}

看到setRippleActivesetBackgroundActive這兩個(gè)方法應(yīng)該可以猜到是什么意思了,接著看暇屋。

private void setRippleActive(boolean active) {
    if (mRippleActive != active) {
        mRippleActive = active;
        if (active) {
            tryRippleEnter();
        } else {
            tryRippleExit();
        }
    }
}

如果Drawable是enable=true且pressd=true時(shí)似袁,會(huì)調(diào)用tryRippleEnter方法

/**
 * Attempts to start an enter animation for the active hotspot. Fails if
 * there are too many animating ripples.
 */
private void tryRippleEnter() {
    if (mExitingRipplesCount >= MAX_RIPPLES) {
        // This should never happen unless the user is tapping like a maniac
        // or there is a bug that's preventing ripples from being removed.
        return;
    }

    if (mRipple == null) {
        final float x;
        final float y;
        if (mHasPending) {
            mHasPending = false;
            x = mPendingX;
            y = mPendingY;
        } else {
            x = mHotspotBounds.exactCenterX();
            y = mHotspotBounds.exactCenterY();
        }

        final boolean isBounded = isBounded();
        mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded, mForceSoftware);
    }

    mRipple.setup(mState.mMaxRadius, mDensity);
    mRipple.enter(false);
}

看到這里,我們可以知道要開始做波紋動(dòng)畫的效果了咐刨。mRipple 是RippleForeground類的實(shí)例昙衅,然而我沒有在RippleForeground類中找到setup和enter方法,但是RippleForeground繼承自RippleComponent類定鸟,于是而涉,我在這個(gè)類中發(fā)現(xiàn)了這兩個(gè)方法。

public final void setup(float maxRadius, int densityDpi) {
    if (maxRadius >= 0) {
        mHasMaxRadius = true;
        mTargetRadius = maxRadius;
    } else {
        mTargetRadius = getTargetRadius(mBounds);
    }

    mDensityScale = densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;

    onTargetRadiusChanged(mTargetRadius);
}
/**
 * Starts a ripple enter animation.
 *
 * @param fast whether the ripple should enter quickly
 */
public final void enter(boolean fast) {
    cancel();

    mSoftwareAnimator = createSoftwareEnter(fast);

    if (mSoftwareAnimator != null) {
        mSoftwareAnimator.start();
    }
}

setup是初始化一系列參數(shù)联予,enter創(chuàng)建一個(gè)動(dòng)畫并開始動(dòng)畫啼县。

@Override
protected Animator createSoftwareEnter(boolean fast) {
    // Bounded ripples don't have enter animations.
    if (mIsBounded) {
        return null;
    }

    final int duration = (int) (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensityScale) + 0.5);

    final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
    tweenRadius.setAutoCancel(true);
    tweenRadius.setDuration(duration);
    tweenRadius.setInterpolator(LINEAR_INTERPOLATOR);
    tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY);

    final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
    tweenOrigin.setAutoCancel(true);
    tweenOrigin.setDuration(duration);
    tweenOrigin.setInterpolator(LINEAR_INTERPOLATOR);
    tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY);

    final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
    opacity.setAutoCancel(true);
    opacity.setDuration(OPACITY_ENTER_DURATION_FAST);
    opacity.setInterpolator(LINEAR_INTERPOLATOR);

    final AnimatorSet set = new AnimatorSet();
    set.play(tweenOrigin).with(tweenRadius).with(opacity);

    return set;
}

從上面創(chuàng)建動(dòng)畫的代碼可以看到,實(shí)際上是一個(gè)組合的屬性動(dòng)畫沸久,然后自定義了三個(gè)屬性波紋半徑TWEEN_RADIUS季眷、波紋中心點(diǎn)TWEEN_ORIGIN和波紋的不透明度OPACITY窑睁。通過這三個(gè)屬性的過渡變化得到一個(gè)復(fù)合的動(dòng)畫。以上就是前景波紋動(dòng)畫效果的實(shí)現(xiàn)過程裳朋。

private void setBackgroundActive(boolean active, boolean focused) {
    if (mBackgroundActive != active) {
        mBackgroundActive = active;
        if (active) {
            tryBackgroundEnter(focused);
        } else {
            tryBackgroundExit();
        }
    }
}

mBackground是RippleBackground類的實(shí)例鲤嫡,與RippleForeground不同的是,背景動(dòng)畫只是改變了不透明度欺缘。

@Override
protected Animator createSoftwareEnter(boolean fast) {
    // Linear enter based on current opacity.
    final int maxDuration = fast ? OPACITY_ENTER_DURATION_FAST : OPACITY_ENTER_DURATION;
    final int duration = (int) ((1 - mOpacity) * maxDuration);

    final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
    opacity.setAutoCancel(true);
    opacity.setDuration(duration);
    opacity.setInterpolator(LINEAR_INTERPOLATOR);

    return opacity;
}

以上分析的都是手指觸摸view時(shí)產(chǎn)生的enter波紋動(dòng)畫蛤铜,當(dāng)手指抬起時(shí)state也會(huì)改變,會(huì)產(chǎn)生一個(gè)exit動(dòng)畫穆刻,這里就不詳細(xì)分析了差导。

二泣刹、使用揭露效果

效果圖:

當(dāng)需要顯示或隱藏一組UI元素時(shí)掀泳,揭露動(dòng)畫可為用戶提供視覺連續(xù)性注服。[ViewAnimationUtils.createCircularReveal()](https://developer.android.com/reference/android/view/ViewAnimationUtils.html#createCircularReveal(android.view.View, int, int, float, float))方法能夠?yàn)椴眉魠^(qū)域添加動(dòng)畫以揭露或隱藏視圖鸭你。

/* @param view The View will be clipped to the animating circle.要隱藏或顯示的view
 * @param centerX The x coordinate of the center of the animating circle, relative to <code>view</code>.動(dòng)畫開始的中心點(diǎn)X
 * @param centerY The y coordinate of the center of the animating circle, relative to <code>view</code>.動(dòng)畫開始的中心點(diǎn)Y
 * @param startRadius The starting radius of the animating circle.動(dòng)畫開始半徑
 * @param endRadius The ending radius of the animating circle.動(dòng)畫結(jié)束半徑
 */
public static Animator createCircularReveal(View view,
        int centerX,  int centerY, float startRadius, float endRadius) {
    return new RevealAnimator(view, centerX, centerY, startRadius, endRadius);
}

RevealAnimator和之前的動(dòng)畫使用沒什么區(qū)別屈张,同樣可以設(shè)置監(jiān)聽器和加速器來實(shí)現(xiàn)各種各樣的特效,該動(dòng)畫主要用在隱藏或者顯示一個(gè)view苇本,改變view的大小等過渡效果袜茧。

顯示view:

final TextView tv9 = (TextView) findViewById(R.id.tv9);

findViewById(R.id.content_main).setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        // get the center for the clipping circle
        int cx = (tv9.getRight() - tv9.getLeft()) / 2;
        int cy = (tv9.getBottom() - tv9.getTop()) / 2;

        // get the final radius for the clipping circle
        int finalRadius = Math.max(tv9.getWidth(), tv9.getHeight());

        // create the animator for this view (the start radius is zero)
        final Animator anim = ViewAnimationUtils.createCircularReveal(tv9, cx, cy, 0, finalRadius);

        tv9.setVisibility(View.VISIBLE);

        anim.start();
    }
});

隱藏view:

final TextView tv9 = (TextView) findViewById(R.id.tv9);

tv9.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        // get the center for the clipping circle
        int cx = (tv9.getRight() - tv9.getLeft()) / 2;
        int cy = (tv9.getBottom() - tv9.getTop()) / 2;

        // get the final radius for the clipping circle
        int initRadius = Math.max(tv9.getWidth(), tv9.getHeight());

        // create the animator for this view (the start radius is zero)
        final Animator anim = ViewAnimationUtils.createCircularReveal(tv9, cx, cy, initRadius, 0);

        anim.addListener(new AnimatorListenerAdapter() {
            @Override public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                // make the view visible and start the animation
                tv9.setVisibility(View.INVISIBLE);
            }
        });
        anim.start();
    }
});

沿著中心縮小:

Animator animator = ViewAnimationUtils.createCircularReveal(view, view.getWidth() / 2, view.getHeight() / 2, view.getWidth(), 0);
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(1000);
animator.start();

從左上角擴(kuò)展:

Animator animator = ViewAnimationUtils.createCircularReveal(view,0,0,0,(float) Math.hypot(view.getWidth(), view.getHeight()));
animator.setDuration(1000);
animator.start();

三瓣窄、使用轉(zhuǎn)場(chǎng)動(dòng)畫

效果圖以共享元素的轉(zhuǎn)場(chǎng)動(dòng)畫為例:

MaterialDesign應(yīng)用中的操作行為轉(zhuǎn)換透過通用元素之間的移動(dòng)和轉(zhuǎn)換提供不同狀態(tài)之間的視覺連接笛厦。可為進(jìn)入俺夕、退出轉(zhuǎn)換以及操作行為之間的共享元素轉(zhuǎn)換指定定制動(dòng)畫裳凸。在5.0之前,我們可以在startActivity之后調(diào)用overridePendingTransition來指定Activity的轉(zhuǎn)場(chǎng)動(dòng)畫劝贸。

  • 進(jìn)入轉(zhuǎn)換將決定操作行為中視圖如何進(jìn)入場(chǎng)景姨谷。例如,在分解進(jìn)入轉(zhuǎn)換中映九,視圖將從屏幕外進(jìn)入場(chǎng)景并飛往屏幕中心梦湘。
  • 退出轉(zhuǎn)換將決定操作行為中應(yīng)用行為的顯示視圖如何退出場(chǎng)景。例如件甥,在分解退出轉(zhuǎn)換中捌议,視圖將從屏幕中心退出場(chǎng)景。
  • 共享元素轉(zhuǎn)換將決定兩個(gè)操作行為轉(zhuǎn)換之間共享的視圖如何在這些操作行為中轉(zhuǎn)換引有。 例如瓣颅,如果兩個(gè)操作行為擁有相同的圖像,但其位置與大小不同譬正,changeImageTransform共享元素轉(zhuǎn)換將在這些操作行為之間平滑地轉(zhuǎn)換與縮放圖像宫补。

Android 5.0(API Level 21)支持這些進(jìn)入與退出轉(zhuǎn)換:(普通過渡動(dòng)畫)

  • 分解 - 從場(chǎng)景中心移入或移出視圖。
  • 滑動(dòng) - 從場(chǎng)景邊緣移入或移出視圖曾我。
  • 淡入淡出 - 通過調(diào)整透明度在場(chǎng)景中增添或移除視圖粉怕。

也支持這些共享元素轉(zhuǎn)換:(共享元素的過渡動(dòng)畫)

  • changeBounds - 為目標(biāo)視圖的大小添加動(dòng)畫。
  • changeClipBounds - 為目標(biāo)視圖的裁剪大小添加動(dòng)畫您单。
  • changeTransform - 為目標(biāo)視圖的縮放斋荞、旋轉(zhuǎn)和位移添加動(dòng)畫。
  • changeImageTransform - 為目標(biāo)圖片的縮放虐秦、旋轉(zhuǎn)和位移添加動(dòng)畫平酿。

指定轉(zhuǎn)場(chǎng)動(dòng)畫

要想使用新的轉(zhuǎn)場(chǎng)動(dòng)畫凤优,可以繼承Material Design主題后在style風(fēng)格中指定:

<!-- 允許使用transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- 指定進(jìn)入、退出蜈彼、返回筑辨、重新進(jìn)入時(shí)的transitions -->
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item>
<item name="android:windowReturnTransition">@transition/explode</item>
<item name="android:windowReenterTransition">@transition/explode</item>
<!-- 指定進(jìn)入、退出幸逆、返回棍辕、重新進(jìn)入時(shí)的共享transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/change_image_transform</item>
<item name="android:windowSharedElementExitTransition">@transition/change_image_transform</item>
<item name="android:windowSharedElementReturnTransition">@transition/change_image_transform</item>
<item name="android:windowSharedElementReenterTransition">@transition/change_image_transform</item>

其中,change_image_transform定義如下:

<!-- res/transition/change_image_transform.xml -->
<!-- (see also Shared Transitions below) -->
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
  <changeImageTransform/>
</transitionSet>

如果要帶代碼中開啟窗口內(nèi)容轉(zhuǎn)換还绘,需要調(diào)用Window.requestFeature()方法楚昭。

// 允許使用transitions
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

// 指定進(jìn)入、退出拍顷、返回抚太、重新進(jìn)入時(shí)的transitions
getWindow().setEnterTransition(new Explode());
getWindow().setExitTransition(new Explode());
getWindow().setReturnTransition(new Explode());
getWindow().setReenterTransition(new Explode());

// 指定進(jìn)入、退出昔案、返回尿贫、重新進(jìn)入時(shí)的共享transitions
getWindow().setSharedElementEnterTransition(new ChangeTransform());
getWindow().setSharedElementExitTransition(new ChangeTransform());
getWindow().setSharedElementReturnTransition(new ChangeTransform());
getWindow().setSharedElementReenterTransition(new ChangeTransform());

普通轉(zhuǎn)場(chǎng)動(dòng)畫:

所有繼承自visibility類都可以作為進(jìn)入、退出的過度動(dòng)畫踏揣。如果我們想自定義進(jìn)入和退出時(shí)的動(dòng)畫效果庆亡,只需要繼承Visibility,重載onAppear和onDisappear方法來定義進(jìn)入喝退出的動(dòng)畫捞稿。系統(tǒng)提供了三種默認(rèn)方式:

  • explode 從屏幕中心移入或移出視圖
  • slide 從屏幕邊緣移入或移出視圖
  • fade 改變視圖的透明度

想在xml中指定自定義的進(jìn)入又谋、退出的過度動(dòng)畫需要先對(duì)動(dòng)畫進(jìn)行定義:

<transition class="my.app.transition.CustomTransition"/>

注意:其中CustomTransition是我們自定義的動(dòng)畫,它必須繼承自Visibility娱局。

想以普通轉(zhuǎn)場(chǎng)動(dòng)畫的方式啟動(dòng)一個(gè)Activity搂根,必須在startActivity函數(shù)中傳遞一個(gè)ActivityOptions的Bundle對(duì)象:

ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity); 
startActivity(intent, options.toBundle());

如果想讓返回也具備轉(zhuǎn)場(chǎng)效果,那么在返回的Activity中不要再調(diào)用finish函數(shù)铃辖,而是應(yīng)該使finishAfterTransition來結(jié)束一個(gè)Activity,該函數(shù)會(huì)等待動(dòng)畫執(zhí)行完畢才結(jié)束該Activity猪叙。

共享轉(zhuǎn)場(chǎng)動(dòng)畫:

如果要在兩個(gè)具有共享元素的Activity之間使用轉(zhuǎn)場(chǎng)動(dòng)畫娇斩,那么:

  • 1、在題中啟用窗口內(nèi)容轉(zhuǎn)換穴翩。android:windowContentTransitions
  • 2犬第、在Theme中指定一個(gè)共享元素轉(zhuǎn)換。
  • 3芒帕、將transitions定義為xml資源歉嗓。
  • 4、利用 android:transitionName屬性對(duì)兩個(gè)布局中的共享元素指定一個(gè)通用名稱背蟆。
  • 5鉴分、使用 ActivityOptions.makeSceneTransitionAnimation()方法哮幢。
// get the element that receives the click event
final View imgContainerView = findViewById(R.id.img_container);

// get the common element for the transition in this activity
final View androidRobotView = findViewById(R.id.image_small);

// define a click listener
imgContainerView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(this, Activity2.class);
        // create the transition animation - the images in the layouts
        // of both activities are defined with android:transitionName="robot"
        ActivityOptions options = ActivityOptions
            .makeSceneTransitionAnimation(this, androidRobotView, "robot");
        // start the new activity
        startActivity(intent, options.toBundle());
    }
});

如果要在代碼中生成共享view,那么需要調(diào)用View.setTransitionName()方法對(duì)兩個(gè)布局中的共享元素指定一個(gè)通用名稱志珍。
如果有多個(gè)共享元素橙垢,則可以通過Pair進(jìn)行包裝處理:

ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity,
      Pair.create(view1, "name1"),//這里view1、view2如果是TextView或者ImageView等伦糯,需要轉(zhuǎn)成View類型才可以
      Pair.create(view2, "name2"));      
startActivity(intent,.toBundle());

返回時(shí)如果需要具備轉(zhuǎn)場(chǎng)動(dòng)畫柜某,那么也需要用finish函數(shù)替代finishAfterTransition來結(jié)束一個(gè)Activity。

使用曲線運(yùn)動(dòng)

因?yàn)榍€運(yùn)動(dòng)和屬性動(dòng)畫以及貝塞爾曲線這些東西混雜在一起敛纲,所以準(zhǔn)備把這節(jié)拿出來單獨(dú)寫喂击。這里就不多說了。

視圖狀態(tài)改變

Android 5.0在原有的圖片選擇器和顏色選擇器上進(jìn)行了增強(qiáng)淤翔,不僅是控件能根據(jù)不同的狀態(tài)顯示不同的背景圖片翰绊,還能在兩種狀態(tài)切換時(shí)指定一個(gè)動(dòng)畫,來增加過渡效果办铡,吸引用戶眼球辞做,以突出重點(diǎn)內(nèi)容。

StateListAnimator類和圖片選擇器寡具,顏色選擇器類似秤茅,可以根據(jù)view的狀態(tài)改變呈現(xiàn)不同的動(dòng)畫效果,通過xml我們可以構(gòu)建對(duì)應(yīng)不同狀態(tài)的動(dòng)畫合集童叠,其使用方式也非常簡(jiǎn)單框喳,在對(duì)應(yīng)的狀態(tài)指定一個(gè)屬性動(dòng)畫即可:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:propertyName="translationZ"
                            android:duration="200"
                            android:valueTo="20dp"
                            android:valueType="floatType"/>
        </set>
    </item>
    <item android:state_enabled="true" android:state_pressed="false">
        <set>
            <objectAnimator android:propertyName="translationZ"
                            android:duration="200"
                            android:valueTo="0"
                            android:valueType="floatType"/>
        </set>
    </item>
</selector>

代碼中這樣加載即可:

TextView tv11 = (TextView) findViewById(R.id.tv11);
StateListAnimator stateLAnim = AnimatorInflater.loadStateListAnimator(this,R.drawable.selector_for_button);
tv11.setStateListAnimator(stateLAnim);

繼承了Material主題后,按鈕默認(rèn)擁有了z屬性動(dòng)畫厦坛。如果想取消這種默認(rèn)狀態(tài)五垮,可以把狀態(tài)動(dòng)畫指定為null。

除了StateListAnimator類指定狀態(tài)切換的屬性動(dòng)畫外杜秸,還可以通過AnimatedStateListDrawable來指定狀態(tài)切換的幀動(dòng)畫:

<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/pressed" android:drawable="@drawable/btn_check_15" android:state_pressed="true"/>
    <item android:id="@+id/normal"  android:drawable="@drawable/btn_check_0"/>
    <transition android:fromId="@+id/normal" android:toId="@+id/pressed">
        <animation-list>
            <item android:duration="20" android:drawable="@drawable/btn_check_0"/>
            <item android:duration="20" android:drawable="@drawable/btn_check_1"/>
            <item android:duration="20" android:drawable="@drawable/btn_check_2"/>
        </animation-list>
    </transition>
</animated-selector>

幀動(dòng)畫的資源文件直接在xml中作為view的background即可放仗。

矢量圖動(dòng)畫

效果圖:


先在drawable中定義一張矢量圖:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="200dp"
    android:width="200dp"
    android:viewportHeight="400"
    android:viewportWidth="400">
      
    <group
        android:name="rotationGroup"
        android:pivotX="0"
        android:pivotY="0">
           
        <path
            android:name="star"
            android:pathData="M 100,100 h 200 l -200 150 100 -250 100 250 z"
            android:strokeColor="@color/colorPrimary"
            android:strokeLineCap="round"
            android:strokeWidth="10"/>
          
    </group>
</vector>

然后在anim中定義動(dòng)畫:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:propertyName="trimPathStart"
        android:valueFrom="0"
        android:valueTo="1"
        android:valueType="floatType"
        android:duration="2000"
        android:repeatMode="reverse"
        android:repeatCount="-1"
        android:interpolator="@android:interpolator/accelerate_decelerate"/>
</set>

最后在drawable中定義一個(gè)animated-vector:將動(dòng)畫資源指定給drawable屬性值的矢量圖。

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector_drawable">
    <target
        android:name="star"
        android:animation="@anim/animation"/>
   
</animated-vector>

注意:這里drawable屬性值是前面我們定義的矢量圖撬碟,target中name要和矢量圖中path的name一樣诞挨,animation就是前面定義的動(dòng)畫資源文件。

在view的xml中使用以及在代碼中開始動(dòng)畫:

<ImageView
    android:id="@+id/iv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="20dp"
    app:srcCompat="@drawable/anim_vector_drawable"
    android:layout_gravity="center"/>
ImageView iv = (ImageView) findViewById(R.id.iv);
Drawable drawable = iv.getDrawable();
if (drawable instanceof Animatable) {
    ((Animatable) drawable).start();
}

文章中演示的Demo地址

參考文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呢蛤,一起剝皮案震驚了整個(gè)濱河市惶傻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌其障,老刑警劉巖银室,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜈敢,警方通過查閱死者的電腦和手機(jī)辜荠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扶认,“玉大人侨拦,你說我怎么就攤上這事》觯” “怎么了狱从?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)叠纹。 經(jīng)常有香客問我季研,道長(zhǎng),這世上最難降的妖魔是什么誉察? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任与涡,我火速辦了婚禮,結(jié)果婚禮上持偏,老公的妹妹穿的比我還像新娘驼卖。我一直安慰自己,他們只是感情好鸿秆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布酌畜。 她就那樣靜靜地躺著,像睡著了一般卿叽。 火紅的嫁衣襯著肌膚如雪桥胞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天考婴,我揣著相機(jī)與錄音贩虾,去河邊找鬼。 笑死沥阱,一個(gè)胖子當(dāng)著我的面吹牛缎罢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播考杉,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼屁使,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了奔则?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蔽午,失蹤者是張志新(化名)和其女友劉穎易茬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抽莱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年范抓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片食铐。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匕垫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出虐呻,到底是詐尸還是另有隱情象泵,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布斟叼,位于F島的核電站偶惠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏朗涩。R本人自食惡果不足惜忽孽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谢床。 院中可真熱鬧兄一,春花似錦、人聲如沸识腿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)覆履。三九已至蹋盆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間硝全,已是汗流浹背栖雾。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伟众,地道東北人析藕。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像凳厢,于是被迫代替她去往敵國(guó)和親账胧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,504評(píng)論 25 707
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 46,708評(píng)論 22 664
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)先紫、插件治泥、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,024評(píng)論 4 62
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過程并不復(fù)雜遮精,今天將帶大家一窺ios動(dòng)畫全貌居夹。在這里你可以看...
    每天刷兩次牙閱讀 8,465評(píng)論 6 30
  • 近段時(shí)間有很多的決定與選擇败潦,也遇到了許多的困難,列如准脂,桃叔邀請(qǐng)我加盟他的小三餐館劫扒,那日,他帶...
    楊坨閱讀 319評(píng)論 0 0