1. Aperçu
Lors du développement de produits Android 11, il est courant d'effectuer diverses personnalisations de Launcher3. Récemment, une exigence fonctionnelle a été demandée pour modifier la forme des icônes afin qu'elles aient des coins arrondis. Dans Launcher3, toutes les applications et les éléments du hotseat sont cosntruits par BubbleTextView, donc la modification des icônes doit également être effectuée dans BubbleTextView.java.
2. Classes concernées pour la modification des icônes du hotseat de Launcher3
/package/app/Launcher3/src/com/android/launcher3/BubbleTextView.java
/packages/apps/Launcher3/src/com/android/launcher3/FastBitmapDrawable.java
3. Analyse et implémentation de la fonctionnalité pour personnaliser les icônes du hotseat de Launcher3
3.1 Analyse du code source de BubbleTextView.java concernant la liaison des icônes
/package/app/Launcher3/src/com/android/launcher3/BubbleTextView.java
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
IconLabelDotView, DraggableView, Reorderable {
private static final int AFFICHER_ESPACE_DE_TRAVAIL = 0;
private static final int AFFICHER_TOUTES LES_APPLICATIONS = 1;
private static final int AFFICHER_DOSSIER = 2;
private static final int[] ETAT_APPUYE = new int[] {android.R.attr.state_pressed};
public void appliquerDepuisElementEspaceTravail(WorkspaceItemInfo info) {
appliquerDepuisElementEspaceTravail(info, false);
}
public void appliquerDepuisElementEspaceTravail(WorkspaceItemInfo info, boolean promettreChangementEtat) {
appliquerIconeEtEtiquette(info);
definirTag(info);
if (promettreChangementEtat || (info.aPromettreIconeUi())) {
appliquerPromesse(promettreChangementEtat);
}
appliquerPointEtat(info, false /* animé */);
}
public void appliquerDepuisInfoApplication(AppInfo info) {
appliquerIconeEtEtiquette(info);
// Nous n'avons pas besoin de vérifier l'info car ce n'est pas un WorkspaceItemInfo
super.definirTag(info);
// Vérifier haute résolution immédiatement
verifierHauteResolution();
if (info instanceof PromiseAppInfo) {
PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
appliquerNiveauProgression(promiseAppInfo.niveau);
}
appliquerPointEtat(info, false /* animé */);
}
@UiThread
protected void appliquerIconeEtEtiquette(ItemInfoWithIcon info) {
boolean utiliserTheme = mAffichage == AFFICHER_ESPACE_DE_TRAVAIL || mAffichage == AFFICHER_DOSSIER
|| mAffichage == AFFICHER_BARRE_TACHE;
FastBitmapDrawable iconeDrawable = info.nouvelleIcone(getContext(), utiliserTheme);
mDotParams.couleur = PaletteIcoul getCouleurRapetisee(iconeDrawable.getCouleurIcoul(), 0.54f);
definirIcoul(iconeDrawable);
appliquerEtiquette(info);
}
packages\apps\Launcher3\src\com\android\launcher3\modele\donnees\ItemInfoWithIcon.java
/** * Le bitmap pour l'icône de l'application */ public BitmapInfo bitmap = BitmapInfo.INFOS_FAIBLE_RESOLUTION;
<br></br> /**
* Retourne un FastBitmapDrawable avec l'icône et le thème appliqués
*/
public FastBitmapDrawable nouvelleIcoul(Context context, boolean appliquerTheme) {
FastBitmapDrawable drawable = appliquerTheme
? bitmap.nouvelleIcoulThematisee(context) : bitmap.nouvelleIcoul(context);
drawable.setEstDesactive(estDesactive());
return drawable;
}
frameworks\libs\systemui\iconloaderlib\src\com\android\launcher3\icoul\BitmapInfo.java
/**
* Crée un drawable pour le BitmapInfo fourni
*/
public FastBitmapDrawable nouvelleIcoul(Context context) {
FastBitmapDrawable drawable = estFaibleRes()
? new PlaceHolderIcoulDrawable(this, context)
: new FastBitmapDrawable(this);
drawable.mAlphaDesactive = Graphiques.getFloat(context, R.attr.alphaIcoulDesactive, 1f);
return drawable;
}
On constate dans le code ci-dessus que l'appel à appliquerDepuisInfoApplication((AppInfo) info) effectue le chargement des données de l'icône. Dans appliquerDepuisInfoApplication((AppInfo) info), appliquerIcoulEtEtiquette(info) est responsable de la liaison et de l'affichage de l'icône et du texte. appliquerIcoulEtEtiquette est la méthode qui gère l'affichage de l'icône. C'est donc ici que nous devons résoudre le problème.
frameworks\libs\systemui\iconloaderlib\src\com\android\launcher3\icoul\FastBitmapDrawable.java
En examinant FastBitmapDrawable.nouvelleIcoul();
public class FastBitmapDrawable extends Drawable {
private static final float ECHELLE_APPUYE = 1.1f;
private static final float DESATURATION_DESACTIVE = 1f;
private static final float LUMINOSITE_DESACTIVE = 0.5f;
public static final DUREE_FEEDBACK_CLIC = 200;
private static FiltreCouleur sFiltreCouleurDesactive;
protected final Paint mPeinture = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTIALIAS_FLAG);
protected Bitmap mBitmap;
protected final int mCouleurIcoul;
private boolean mEstAppuye;
private boolean mEstDesactive;
private float mAlphaDesactive = 1f;
// Animateur et propriétés pour l'échelle du drawable bitmap rapide
private static final Propriete<FastBitmapDrawable, Float> ECHELLE
= new Propriete<FastBitmapDrawable, Float>(Float.TYPE, "echelle") {
@Override
public Float get(FastBitmapDrawable drawableBitmapRapide) {
return drawableBitmapRapide.mEchelle;
}
@Override
public void set(FastBitmapDrawable drawableBitmapRapide, Float valeur) {
drawableBitmapRapide.mEchelle = valeur;
drawableBitmapRapide.invalidateSelf();
}
};
/**
* Retourne un FastBitmapDrawable avec l'icône.
*/
public static FastBitmapDrawable nouvelleIcoul(Context context, ItemInfoWithIcon info) {
FastBitmapDrawable drawable = nouvelleIcoul(context, info.bitmap);
drawable.setEstDesactive(info.estDesactive());
return drawable;
}
/**
* Crée un drawable pour le BitmapInfo fourni
*/
public static FastBitmapDrawable nouvelleIcoul(Context context, BitmapInfo info) {
final FastBitmapDrawable drawable;
if (info instanceof Fabrique) {
drawable = ((Fabrique) info).nouveauDrawable();
} else if (info.estFaibleRes()) {
drawable = new PlaceHolderIcoulDrawable(info, context);
} else {
drawable = new FastBitmapDrawable(info);
}
drawable.mAlphaDesactive = Themes.getFloat(context, R.attr.alphaIcoulDesactive, 1f);
return drawable;
}
}
En appelant les méthodes comme FastBitmapDrawable.nouvelleIcoul(), on constate que c'est finalement FastBitmapDrawable qui traite l'icône. FastBitmapDrawable est en fait une sous-classe de Drawable, et il utilise finalement draw(Canvas canvas) pour dessiner l'icône. En continuant à suivre FastBitmapDrawable, on constate que la méthode drawInternal(Canvas canvas, Rect bounds) est responsable du dessin de l'icône.
public FastBitmapDrawable(ItemInfoWithIcon info) {
this(info.icoulBitmap, info.couleurIcoul);
}
protected FastBitmapDrawable(Bitmap b, int couleurIcoul) {
this(b, couleurIcoul, false);
}
protected FastBitmapDrawable(Bitmap b, int couleurIcoul, boolean estDesactive) {
mBitmap = b;
mCouleurIcoul = couleurIcoul;
setFilterBitmap(true);
setEstDesactive(estDesactive);
}
@Override
public final void draw(Canvas canvas) {
if (mEchelle != 1f) {
int count = canvas.save();
Rect bounds = getBounds();
canvas.scale(mEchelle, mEchelle, bounds.exactCenterX(), bounds.exactCenterY());
drawInternal(canvas, bounds);
canvas.restoreToCount(count);
} else {
drawInternal(canvas, getBounds());
}
}
protected void drawInternal(Canvas canvas, Rect bounds) {
canvas.drawBitmap(mBitmap, null, bounds, mPeinture);
}
Après avoir analysé le code, on voit que la méthode drawInternal(Canvas canvas, Rect bounds) est responsable du dessin de l'icône. C'est ici que nous allons effectuer le traitement pour arrondir les coins de l'icône:
protected void drawInternal(Canvas canvas, Rect bounds) {
- canvas.drawBitmap(mBitmap, null, bounds, mPeinture);
+ // Initialiser le shader bitmap
+ BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+ Paint peinture = new Paint();
+ peinture.setAntiAlias(true);
+ peinture.setDither(true);
+ peinture.setShader(bitmapShader);
+ peinture.setStrokeWidth(16);
+ // Utiliser la peinture pour dessiner le shader bitmap sur le canevas
+ int mLargeur = Math.min(mBitmap.getWidth(), mBitmap.getHeight());
+ canvas.drawRoundRect(new RectF(8, 8, mLargeur-8, mLargeur-8), 40, 40, peinture);
+ //canvas.drawCircle(mLargeur / 2, mLargeur / 2, mLargeur / 2, peinture);
+ //canvas.drawBitmap(mBitmap, null, bounds, mPeinture);
}