web-dev-qa-db-fra.com

FloatingActionButton se développe dans une nouvelle activité

Sur la page Android principes de conception des matériaux, l'un des exemples montre un FAB se développant dans un nouveau plein écran. (Sous "Plein écran")

http://www.google.com/design/spec/components/buttons-floating-action-button.html#buttons-floating-action-button-transitions

J'ai essayé d'implémenter le même effet dans mon application, mais avec peu de succès.
J'ai réussi à créer un FAB qui se développe dans une vue en utilisant ce code comme référence: https://Gist.github.com/chris95x8/882b5c5d0aa2096236ba .

Cela a fonctionné, mais je me demandais si je pouvais appliquer le même effet à une transition d'activité. J'ai essayé de le rechercher et de jouer avec lui-même, mais je n'ai rien trouvé qui puisse fonctionner.

Je sais que je pourrais faire du FAB un fragment et non pas une toute nouvelle activité, mais je ne sais pas si c'est ce qui se fait, et si c'est optimal ou pas.

Et donc ma question est, existe-t-il un moyen de mettre en œuvre l'effet de révélation de l'expansion fab comme une transition d'activité, ou est-il censé révéler simplement un nouveau fragment?

30
Asaf

Je développe une application qui développe un FloatingActionButton en un nouveau Activity. Je ne suis pas sûr que si vous aimez mon implémentation, mais veuillez d'abord voir les photos:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

Ainsi, la première image montre MainActivity et la dernière montre SecondActivity, qui est "développée" à partir de FAB.

Maintenant, je veux mentionner que je ne suis pas réellement en expansion un FAB dans un nouveau Activity mais je peux laisser l'utilisateur sentir que le nouvelle page est développée à partir de ce FAB , et je pense que cela suffit pour les développeurs et les utilisateurs.

Voici la mise en œuvre:

Préparation:

  1. Un FloatingActionButton bien sûr,
  2. Visitez https://github.com/kyze8439690/RevealLayout et importez cette bibliothèque dans votre projet. Il est utilisé pour jouer une animation révélatrice. Il a un BakedBezierInterpolator personnalisé pour contrôler l'animation de révélation et lui donner un style matériel.

Étapes:

  1. créez activity_main.xml comme ceci:

    <FrameLayout
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent">
    
        <!--Your main content here-->
    
        <RevealLayout
            Android:id="@+id/reveal_layout"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            Android:visibility="invisible">
    
            <View
                Android:id="@+id/reveal_view"
                Android:layout_width="match_parent"
                Android:layout_height="match_parent"
                Android:visibility="invisible"/>
    
        </RevealLayout>
    
    </FrameLayout>
    
  2. trouver des vues:

    mRevealLayout = (RevealLayout) findViewById(R.id.reveal_layout);
    mRevealView = findViewById(R.id.reveal_view);
    
  3. développer lorsque l'utilisateur clique sur FAB:

    mFab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mFab.setClickable(false); // Avoid naughty guys clicking FAB again and again...
            int[] location = new int[2];
            mFab.getLocationOnScreen(location);
            location[0] += mFab.getWidth() / 2;
            location[1] += mFab.getHeight() / 2;
    
            final Intent intent = new Intent(MainActivity.this, SecondActivity.class);
    
            mRevealView.setVisibility(View.VISIBLE);
            mRevealLayout.setVisibility(View.VISIBLE);
    
            mRevealLayout.show(location[0], location[1]); // Expand from center of FAB. Actually, it just plays reveal animation.
            mFab.postDelayed(new Runnable() {
                @Override
                public void run() {
                    startActivity(intent);
                    /**
                     * Without using R.anim.hold, the screen will flash because of transition
                     * of Activities.
                     */
                    overridePendingTransition(0, R.anim.hold);
                }
            }, 600); // 600 is default duration of reveal animation in RevealLayout
            mFab.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mFab.setClickable(true);
                    mRevealLayout.setVisibility(View.INVISIBLE);
                    mViewToReveal.setVisibility(View.INVISIBLE);
                }
            }, 960); // Or some numbers larger than 600.
        }
    });
    

    Et voici hold.xml dans res/anim:

    <set
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:shareInterpolator="false">
    
        <translate
            Android:duration="960" <!-- Enough-large time is OK -->
            Android:fromXDelta="0%"
            Android:fromYDelta="0%"
            Android:toXDelta="0%"
            Android:toYDelta="0%"/>
    
    </set>
    

C'est tout.

Améliorations:

  1. RevealLayout a un bug (joue l'animation de révélation rectangulaire au lieu de circulaire) pour les appareils sous API 17 (Android 4.2), vous pouvez ajouter ces lignes dans le constructeur de celui-ci:

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }
    
  2. Si votre SecondActivity contient un contenu compliqué, un simple View utilisé comme révélation_view dans le layout.xml n'est pas suffisant/parfait. Vous pouvez inclure la deuxième disposition à l'intérieur de RevealLayout Révéler_layout. Il semble inutile et difficile de contrôler si la deuxième mise en page n'apparaîtra pas à chaque fois. Mais pour moi, ça le sera. Vous pouvez donc apporter d'autres améliorations si vous le souhaitez.

  3. Si vous souhaitez implémenter une animation totalement identique à celle indiquée dans le Material Design Guide, vous pouvez définir la hauteur de disposition de RevealLayout en un nombre spécifique au lieu de match_parent. Une fois l'expansion de l'animation terminée (ou quelque temps après la lecture de l'animation, ce qui devrait faciliter le processus d'animation), vous pouvez alors animer translationY. Le point important est de tromper visuellement les utilisateurs en contrôlant la durée de l'animation.

Enfin, c'est ma propre expérience/tentative et je suis un débutant dans le développement d'applications Android. S'il y a des erreurs/améliorations, veuillez laisser des commentaires/modifier ma réponse Merci.

29
ywwynm

J'ai fait une activité personnalisée, basée sur cette question Transition de révélation circulaire pour une nouvelle activité , qui gère le CircularRevealAnimation et son effet inverse lorsque l'activité se termine:

public class RevealActivity extends AppCompatActivity {

private View revealView;

public static final String REVEAL_X="REVEAL_X";
public static final String REVEAL_Y="REVEAL_Y";

public void showRevealEffect(Bundle savedInstanceState, final View rootView) {

    revealView=rootView;

    if (savedInstanceState == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
        rootView.setVisibility(View.INVISIBLE);

        ViewTreeObserver viewTreeObserver = rootView.getViewTreeObserver();

        if(viewTreeObserver.isAlive()) {
            viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {

                    circularRevealActivity(rootView);

                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                        rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    } else {
                        rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    }

                }
            });
        }

    }
}

@TargetApi(Build.VERSION_CODES.Lollipop)
private void circularRevealActivity(View rootView) {

    int cx = getIntent().getIntExtra(REVEAL_X, 0);
    int cy = getIntent().getIntExtra(REVEAL_Y, 0);

    float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());

    // create the animator for this view (the start radius is zero)
    Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, 0, finalRadius);
    circularReveal.setDuration(400);

    // make the view visible and start the animation
    rootView.setVisibility(View.VISIBLE);
    circularReveal.start();
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case Android.R.id.home: onBackPressed();break;
return super.onOptionsItemSelected(item);

}    

}

@Override
public void onBackPressed() {
    destroyActivity(revealView);
}

private void destroyActivity(View rootView) {
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.Lollipop)
        destroyCircularRevealActivity(rootView);
    else
        finish();
}

@TargetApi(Build.VERSION_CODES.Lollipop)
private void destroyCircularRevealActivity(final View rootView) {
    int cx = getIntent().getIntExtra(REVEAL_X, 0);
    int cy = getIntent().getIntExtra(REVEAL_Y, 0);

    float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());

    // create the animator for this view (the start radius is zero)
    Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, finalRadius, 0);
    circularReveal.setDuration(400);

    circularReveal.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {

        }

        @Override
        public void onAnimationEnd(Animator animator) {
            rootView.setVisibility(View.INVISIBLE);
            finishAfterTransition();
        }

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });

    // make the view visible and start the animation
    rootView.setVisibility(View.VISIBLE);
    circularReveal.start();
}
}

Vous pouvez étendre cela avec votre propre activité et appeler dans votre onCreate la méthode 'showRevealEffect' comme ceci:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.your_activity_layout);

    //your code

    View root= findViewById(R.id.your_root_id);
    showRevealEffect(savedInstanceState, root);

}

Vous devez également utiliser un thème transparent comme celui-ci:

<style name="Theme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="Android:windowIsTranslucent">true</item>
    <item name="Android:windowBackground">@Android:color/transparent</item>
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="colorControlNormal">@Android:color/white</item>
</style>

En fin de compte, pour lancer cette activité, vous devez passer via les coordonnées supplémentaires où l'animation doit commencer:

int[] location = new int[2];

    fab.getLocationOnScreen(location);

    Intent intent = new Intent(this, YourRevealActivity.class);

    intent.putExtra(SearchActivity.REVEAL_X, location[0]);
    intent.putExtra(SearchActivity.REVEAL_Y, location[1]);

    startActivity(intent);
6
Bronx

Quelqu'un a étudié la mise en œuvre de la transition entre les activités de Plaid . Son exemple a été publié via https://github.com/hujiaweibujidao/FabDialogMorph .

En bref, elle transite deux activités avec:

  1. Le FAB comme élément partagé .
  2. La disposition dans l'activité cible avec le même Android:transitionName comme FAB.
  3. Pour lisser l'animation, MorphDrawable (étendu de Drawable) et MorphTransition (étendu de ChangeBounds) sont implémentés et appliqués.
0
Ting Yi Shih