7
votes

Affichage à l'échelle d'image dans l'image Android

Existe-t-il un moyen d'utiliser la fonction d'image dans l'image sur une activité qui n'est pas une vidéo pour la montrer à la baisse?

J'ai une activité avec une barre de progression géante et du texte que je voudrais afficher sur la fenêtre PiP pendant que l'utilisateur a navigué sur le Web.

J'ai déjà

@Override
protected void onUserLeaveHint() {


    PictureInPictureParams params = new PictureInPictureParams.Builder()
            .build();
    enterPictureInPictureMode(params);

}

défini pour l'activité dans le manifeste.

et pour démarrer PiP

android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout"

Voici à quoi ressemble mon exemple d'application

 entrez la description de l'image ici a>

J'appuie sur home et cela s'anime brièvement à

entrez la description de l'image ici

puis se redessine rapidement pour devenir p>

 enter image description here

J'espère montrer PiP tel qu'il apparaît réduit dans l'image n ° 2 mais après une animation rapide, il se redessine à ce à quoi il ressemble dans l'image n ° 3.

Est-il possible d'obtenir une vue réduite?

Veuillez garder à l'esprit que ce ne sera pas une application de l'App Store. C'est une application très ciblée sur une tablette dédiée.


0 commentaires

4 Réponses :


3
votes

Peut-être un peu hacky, mais vous pouvez changer le DPI au moment de l'exécution.

Le code suivant utilise onPictureInPictureModeChanged () pour écouter un changement de mode et changer le DPI au prochain redémarrage. p>

public class MyApplication extends Application {

    public static final int MODE_NONE = 0;
    public static final int MODE_FULL = 1;
    public static final int MODE_PIP = 2;

    public int mode = MODE_NONE;
    public int orgDensityDpi = 0;

}

Parce que onUserLeaveHint () - qui démarre le mode PIP - est appelé après onSaveInstanceState () le mode actuel ne peut pas être stocké dans un champ de la classe d'activité. Il doit être stocké ailleurs où il survit à un changement de configuration. Un champ dans la classe d'application est utilisé ici.

public class Activity extends AppCompatActivity {

    private MyApplication mApplication;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mApplication = (MyApplication) getApplicationContext();

        if (mApplication.mode == MyApplication.MODE_NONE) {
            saveDpi();
        } else {
            setDpi();
        }

        setContentView(R.layout.activity);

        ...
    }

    private void saveDpi() {
        Configuration configuration = getResources().getConfiguration();
        mApplication.orgDensityDpi = configuration.densityDpi;
    }

    private void setDpi() {
        Configuration configuration = getResources().getConfiguration();
        DisplayMetrics metrics = getResources().getDisplayMetrics();
        if (mApplication.mode == MyApplication.MODE_PIP) {
            configuration.densityDpi = mApplication.orgDensityDpi / 3;
        } else {
            configuration.densityDpi = mApplication.orgDensityDpi;
        }
        getBaseContext().getResources().updateConfiguration(configuration, metrics);
    }

    @Override
    protected void onUserLeaveHint() {
        PictureInPictureParams params = new PictureInPictureParams.Builder().build();
        enterPictureInPictureMode(params);
    }

    @Override
    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
        if (isInPictureInPictureMode) {
            mApplication.mode = MyApplication.MODE_PIP;
        } else {
            mApplication.mode = MyApplication.MODE_FULL;
        }
    }

}

(Il n'est pas nécessaire d'empêcher les changements de configuration avec android: configChanges .)

Résultat:

result


0 commentaires

2
votes

Pas besoin d'inclure les configChanges dans le manifeste.

  <activity android:name=".PictureIPActivity"
              android:resizeableActivity="true"
              android:launchMode="singleTask"
              android:supportsPictureInPicture="true">

La clé pour redimensionner l'animation est les paramètres dans le fichier AndroidManifest.xml

public class PictureIPActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);       
    setContentView(R.layout.activity_picture_ip);
    findViewById(R.id.tv_hello_world).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(mApplication, "enabling PiP", Toast.LENGTH_SHORT).show();
            enterPiPMode();
        }
    });
}

@Override
protected void onPause() {
    super.onPause();
    enterPiPMode();
}

private void enterPiPMode() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        PictureInPictureParams params = new PictureInPictureParams.Builder()
                .setAspectRatio(getPipRatio()).build();
        enterPictureInPictureMode(params);
    }
}

public Rational getPipRatio() {
 //   anything with aspect ration below 0.5 and above 2.5 (roughly) will be 
 unpractical or unpleasant to use
    DisplayMetrics metrics = getResources().getDisplayMetrics();
    return new Rational(Math.round(metrics.xdpi), Math.round(metrics.ydpi));
}
}


0 commentaires

1
votes

Chaque fois que l ' Activité entre ou sort du mode PIP, elle est détruite et recréée (c'est le comportement que j'ai noté). La différence entre l'animation et le résultat final est que lors de l'entrée en mode PIP, le système s'anime en réduisant l'activité et ses composants d'interface utilisateur.

Lorsque l'activité est recréée, elle utilise la même mise en page que vous avez fournie lors de la création initiale de l'activité. avec les mêmes dimensions, le problème est que la configuration de l ' activité a changé et que l'appareil est entré dans une configuration de taille plus petite, c'est-à-dire dans votre cas de xlarge à petit ou normal.

Maintenant que nous savons que l ' activité est détruite, vous pouvez gérer les changements de taille d'écran comme vous le faites habituellement.

Voici ce que vous pouvez faire:

  1. Fournissez une nouvelle mise en page pour la nouvelle configuration.
  2. Fournissez une nouvelle taille de texte pour les nouvelles configurations.
  3. Fournissez une nouvelle taille de texte au moment de l'exécution lors du rappel onPictureInPictureModeChanged () .

J'ai obtenu le résultat souhaité en ajoutant un nouveau dossier dimens-small . Vous pouvez en choisir un pour vous-même. Ce dimens.xml contiendra le android: textSize = "@ dimen / textSize" pour le petit écran.


Maintenant que cela est fait, voici la raison pour laquelle vous ne l'avez probablement pas ne cherchez pas les loisirs: selon les Docs PIP a >

spécifiez que votre activité gère les changements de configuration de mise en page afin que votre activité ne soit pas relancée lorsque des modifications de mise en page se produisent pendant les transitions en mode PIP.

Et même si j'ai ajouté

android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout"

dans ma balise dans le manifeste mon Activité code> était toujours recréé à la fin de chaque changement de mode.

Ce qui est soit un bogue, soit quelque chose qui manque dans la documentation ou dans le code. Ou peut-être une déclaration peu claire pour les transitions / animations uniquement et non le résultat final réel.


0 commentaires

2
votes

Voici une autre solution qui utilise des fragments pour afficher une interface utilisateur mise à l'échelle. Contrairement à ma solution précédente , cette solution a l'avantage d'être capable d'afficher une interface utilisateur optimisée pour le mode PIP. (Par exemple, certaines vues peuvent être masquées en mode PIP.)

Le code suivant utilise onPictureInPictureModeChanged () pour écouter un changement de mode et change l'interface utilisateur au prochain redémarrage. (Parce que la barre d'outils n'est pas nécessaire en mode PIP, elle est masquée avant que le mode PIP soit entré.)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Hello World!"
        android:textSize="10sp"/>

</RelativeLayout>

Parce que onUserLeaveHint () - qui démarre le mode PIP - est appelé après onSaveInstanceState (), le mode actuel ne peut pas être stocké dans un champ de la classe d'activité. Il doit être stocké ailleurs où il survit à un changement de configuration. Un champ de la classe d'application est utilisé ici.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Hello World!"
        android:textSize="36sp" />

    <TextView
        android:id="@+id/text_detail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/text"
        android:layout_centerHorizontal="true"
        android:text="&#x1F642;"
        android:textSize="28sp" />

</RelativeLayout>

Disposition des fragments pour le mode plein écran:

public class MyApplication extends Application {

    public boolean inPipMode = false;

}

Disposition des fragments pour Mode PIP:

public class Activity extends AppCompatActivity {

    private static final String FRAGMENT_TAG_FULL = "fragment_full";
    private static final String FRAGMENT_TAG_PIP = "fragment_pip";

    private MyApplication mApplication;

    private Toolbar mToolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mApplication = (MyApplication) getApplicationContext();

        setContentView(R.layout.activity);

        mToolbar = findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);

        if (!mApplication.inPipMode) {
            showFullFragment();
        } else {
            showPipFragment();
        }
    }

    @Override
    protected void onUserLeaveHint() {
        mToolbar.setVisibility(View.GONE);
        PictureInPictureParams params = new PictureInPictureParams.Builder().build();
        enterPictureInPictureMode(params);
    }

    @Override
    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
        if (isInPictureInPictureMode) {
            mApplication.inPipMode = true;
        } else {
            mApplication.inPipMode = false;
        }
    }

    private void showFullFragment() {
        Fragment fragment = new FragmentFull();
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.container_content, fragment, FRAGMENT_TAG_FULL)
                .commit();
        mToolbar.setVisibility(View.VISIBLE);
    }

    private void showPipFragment() {
        Fragment fragment = new FragmentPip();
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.container_content, fragment, FRAGMENT_TAG_PIP)
                .commit();
        mToolbar.setVisibility(View.GONE);
    }

}

Résultat:

résultat


0 commentaires