vous présente :
Spécialiste Android
1
Android2EE est référencé en tant qu’organisme de formation, vous pouvez faire prendre en charge tout ou partie
du montant de cette formation par votre OPCA. Cette formation Initiation avancée à Android est éligible au titre
du DIF et CIF.
Lieu : Paris, Toulouse, Lyon
Durée : 3 jours
Prix : 1680 €
Lieu : Paris, Toulouse, Lyon
Durée : 5 jours
Prix : 2980 €
Lieu : Paris, Toulouse, Lyon
Durée : 3 jours
Prix : 1780 €
Android2ee.com
Formations:
http://www.android2ee.com/fr/formations-android/inter-entreprises
http://www.android2ee.com/fr/formations-android/formation-complete
Open Ressources
http://www.android2ee.com/fr/videos/devoxx-android-a-quick-course-1
http://www.android2ee.com/fr/livre-en-consultation/introduction-openbook
http://www.android2ee.com/tutoriaux/appwidget.html
mathias.seguy@android2ee.com
https://github.com/MathiasSeguy-Android2EE
http://fr.linkedin.com/pub/mathias-seguy/37/a71/a59
https://plus.google.com/115788770291974884100/about
https://plus.google.com/b/109641731378552898326/109641731378552898326/about
http://fr.slideshare.net/Android2EE
https://play.google.com/store/apps/developer?id=ANDROID2EE
http://fr.twitter.com/#%21/android2ee
@deprectaed use linkedIn
http://mathias-seguy.developpez.com/homepage/index.php
http://blog.developpez.com/android2ee-mathias-seguy/
To GDG
Vous souhaitez que je vienne faire une conférence dans votre Android
User Group.
Contactez moi, je suis preneur.
4
5
From Google I/O And Devoxx
Ce chapitre est un extrait des « meilleurs » proTips donnés par les équipes Google lors des GoogleI/O, notamment :
•Android ProTips, Reto Meier, Google I/O 2011
•Making Good Apps Great , Reto Meier, Google I/O 2012
•Google I_O 2013 - Android Protips_ Making Apps Work Like Magic , Reto Meier, Google I/O 2013
•Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
Merci à eux pour ces présentations.
6
Statics as Temporaries
Utiliser des variables statiques pour vos variables
temporaires
public boolean pointInArea(int x, int y, Area area) {
Point testPoint = new Point(x, y);
return area.intersect(testPoint);
}
static final Point tmpPoint = new Point();
public boolean pointInArea(int x, int y, Area area) {
tmpPoint.x = x;
tmpPoint.y = y;
return area.intersect(tmpPoint.yPoint);
}
Autoboxing creates Objects
Utiliser les types primitifs, l’AutoBoxing créé des
objets!
Est équivalent à
float x = 5;
Float y = x;
doSomething(x);
void doSomething(Float z) {}
float x = 5;
Float y = new Float(x);
doSomething(new Float(x));
void doSomething(Float z) {}
Recycle those Bitmaps
Les ressources sont limitées,
recyclez vos Bitmaps le plus tôt
possible (n’attendez pas la
méthode finalize())
Vous pensez que cela aide:
Mais en fait vous souhaitez:
// done using this one, clear
reference
myBitmap = null;
// done using this one, recycle it
myBitmap.recycle();
From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
7
Use ViewStub
La ViewStub permet le lazy loading de layouts.
<ViewStub
android:id="@+id/stub_import"
android:inflatedId="@+id/panel_import"
android:layout="@layout/progress_overlay"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
findViewById(R.id.stub_import).setVisibility(View.VISIBLE);
// or
View importPanel = ((ViewStub)
findViewById(R.id.stub_import)).inflate();id/stub_import
id/panel_import
From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
8
Use merge et include
<!-- The merge tag must be the root tag -->
<merge
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Content -->
</merge>
<!– The include tag is the one to use-->
<LinearLayout …>
<include layout="@layout/subLayout" />
<LinearLayout> .. </LinearLayout>
</LinearLayout>
Without merge With merge
From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
9
Compounds Drawables
Utiliser les balises android:drawable*** pour mettre des
images à droite, à gauche … de vos composants héritant
de TextView (TextView, Button, EditText,…)
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
android:drawableLeft="@drawable/icon" />
Balises
android:drawableBottom
android:drawableEnd
android:drawablePadding
android:drawableRight
android:drawableStart
android:drawableTop
From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
10
ListView and ArrayAdpater : Use ViewHolder and convertView
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(DATA[position]);
holder.icon.setImageBitmap((position & 1) ==? mIcon1 : mIcon2);
return convertView;
}
static class ViewHolder {
TextView text;
ImageView icon;
}
From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
11
UUID : Unique User ID
L’ UUID Pattern permet détecter l'utilisateur plutôt que les installations. Si vous avez besoin d’identifier de manière unique l'utilisateur qui
possède l’application, ce pattern est à utiliser.
private static String uniqueID = null;
private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
public synchronized static String id(Context context) {
if (uniqueID == null) {
SharedPreferences sharedPrefs =
context.getSharedPreferences(PREF_UNIQUE_ID, Context.MODE_PRIVATE
);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
if (uniqueID == null) {
uniqueID = UUID.randomUUID().toString();
Editor editor = sharedPrefs.edit();
editor.putString(PREF_UNIQUE_ID, uniqueID);
editor.commit();
}
}
return uniqueID;
}
Android ProTips, Reto Meier, Google I/O 2011
12
Know your Network and Battery context
<receiver android:name="DeviceStateReceiever" >
<action android:name="android.intent.action.ACTION_DOCK_EVENT" />
<action android:name="android.intent.action.ACTION_BATTERY_LOW" />
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.net.wifi.STATE_CHANGE" />
</receiver>
Android ProTips, Reto Meier, Google I/O 2011
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork.isConnectedOrConnecting();
boolean isMobile = activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE;
13
KeyBoard and EditText : Customize KeyBoard and set Action
Quand vous définissez un champ de type EditText vous devez définir :
• Son type de clavier 7
• L’action IME
• (et le Hint)
<EditText
android:id="@+id/editEmailInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/compose_email_hint"
android:imeOptions="actionSend|flagNoEnterAction"
android:inputType="textShortMessage|
textAutoCorrect|
textCapSentences" />
Android ProTips, Reto Meier, Google I/O 2011
EditText.OnEditorActionListener myActionListener = new
EditText.OnEditorActionListener() {
@Override
public boolean onEditorAction(EditText v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
// do here your stuff f
return true;
}
return false;
}
};
14
Always be asynchronous
Rendez tout asynchrone en utilisant les
Handler, Asynctask, IntentService (le système
des Intents quoi), AsyncQueryHandler (le
GetContentResolver en un sens), Loader
(abstractClass) and CursorLoader (HoneyComb
only).
public class TutoActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
SimpleCursorAdapter mAdapter;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// …
// Create an empty adapter we will use to display the loaded data.
mAdapter = new SimpleCursorAdapter(this,android.R.layout.simple_list_item_2, null,
new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
listView.setAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
// Callbacks
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri baseUri = MyContentProvider.CONTENT_URI;
return new CursorLoader(this, baseUri, null, null, null, null);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
Android ProTips, Reto Meier, Google I/O 2011
15
Use the big cookies strategy when possible
Préférez transférer un gros paquet de données au travers d’internet quand c’est possible plutôt que plein de petits paquets.
Making Good Apps Great, Reto Meier, Google I/O 2012
int prefetchCacheSize = DEFAULT_PREFETCH_CACHE;
switch (activeNetwork.getType()) {
case ConnectivityManager.TYPE_WIFI:
prefetchCacheSize = MAX_PREFETCH_CACHE;
break;
case ConnectivityManager.TYPE_MOBILE): {
switch (telephonyManager.getNetworkType()) {
case TelephonyManager.NETWORK_TYPE_LTE:
case TelephonyManager.NETWORK_TYPE_HSPAP:
prefetchCacheSize *= 4; break;
case TelephonyManager.NETWORK_TYPE_EDGE:
case TelephonyManager.NETWORK_TYPE_GPRS:
prefetchCacheSize /= 2; break;
default: break;
} break;
}
default: break;
}
Use AndroidBeam
Parce que c'est tout simple d'échanger des données entre deux devices.
Send Receive
16
Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013
public void onResume() {
super.onResume();
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
nfcAdapter.setNdefPushMessageCallback(new CreateNdefMessageCallback() {
public NdefMessage createNdefMessage(NfcEvent event) {
NdefMessage message = createMessage();
return message;
}
}, this);
// ...}
private void createMessage() {
String mimeType = "application/com.myapp.nfcbeam";
byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII"));
String payLoad = ""; // TODO Contextualized payload.;
NdefMessage nfcMessage = new NdefMessage(new NdefRecord[] {
new NdefRecord(ndefRecord.TNF_MIME_MEDIA, mimeByes, new
byte[], payload.getBytes()),
NdefRecord.createApplicationRecord("com.myapp.nfcbeam") });
}
private void extractPayload(Intent beamIntent) {
Parcelable[] messages = beamIntent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefAdapter message = (NdefMessage)message[0];
NdefRecord record = message.getRecords()[0];
String payload = new String(record.getPayload());
navigateTo(payload);
}
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/com.myapp.nfcbeam"/>
</intent-filter>
Location
Utiliser le ServiceGooglePlay, il connait vos utilisateurs mieux que vous.
17
Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013
private void connectLBS() {
int gpsExists = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (gpsExists == ConnectionResult.SUCCESS) {
mLocationClient = new LocationClient(this, this, this);
mlocationClient.connect();
}
}
@Override
public void onConnected(Bundle connectionHint) {
requestUpdates(mlocationClient);
}
private void requestUpdates(LocationClient mlocationClient) {
LocationRequest request = LocationRequest.create();
request.setInterval(minTime);
request.setPriority(lowPowerMoreImportantThanAccuracy ?
LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY :
LocationRequest.PRIORITY_HIGH_ACCURACY);
mlocationClient.requestLocationUpdates(request, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
updateLocation(location);
}
});
}
Activity Recognition
Adapter vos applications au contexte utilisateur (devant la télé, en voiture, en vélo, à pied...).
18
Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013
Intent intent = new Intent(this, ActivityRecognitionIntentService.class);
intent.setAction(MyActivity.ACTION_STRING);
PendingIntent pi =
PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURR
ENT);
mActivityRecognitionClient.requestActivityUpdates(interval, pi);
@Override
protected void onHandleIntent(Intent intent) {
if (intent.getAction() == MyActivity.ACTION_STRING) {
if (ActivityRecognitionResult.hasResult(intent)) {
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
DetectedActivity detectedActivity = result.getMostProbableActivity();
int activityType = detectedActivity.getType();
if (activityType == DetectedActivity.STILL)
setUpdateSpeed(PAUSED);
else if (activityType == DetectedActivity.IN_VEHICLE)
setUpdateSpeed(FASTER);
else
setUpdateSpeed(REGULAR);
}
}
}
TextToSpeech : TTS
De parlez à vos utilisateurs, parfois c'est super.
Initialize Use
19
Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013
private void initTextToSpeech() {
Intent intent = new Intent(Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(intent, TTS_DATA_CHECK);
}
protected void onActivityResult(int request, int result, Intent data) {
if (request == TTS_DATA_CHECK &&
result == Engine.CHECK_VOICE_DATA_PASS) {
tts = new TextToSpeech(this, new OnInitListener() {
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS)
ttsIsInit = true;
}
});
} else
startActivity(new Intent(Engine.ACTION_INSTALL_TTS_DATA);
}
private void say(String text) {
if (tts != null && ttisIsInit)
tts.speak(text, TextToSpeech.QUEUE_ADD, null);
}
Speech Recognition
D'écouter vos utilisateurs, parfois c'est encore plus super.
Record Consume
20
Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013
private void requestVoiceInput() {
Intent intent = new
Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
getString(R.String.voice_input_prompt);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE,
Locale.ENGLISH);
startActivityForResult(intent, VOICE_RECOGNITION);
}
@Override
protected void onActivityResult(int request, int result, Intent data)
{
if (request == VOICE_RECOGNITION && result ==
RESULT_OK) {
ArrayList<String> results =
data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
String mostLikelyResult = results[0];
useSpeechInput(mostLikelyResult);
}
}
21
22
From Google I/O 2012
Ce chapitre est un extrait de la conférence des Google I/O 2012 :
•Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
Qui est exceptionnel sur la lutte contre la fragmentation, un très grand moment, merci à eux.
23
Be Lazy
Utiliser une classe abstraite qui sera instanciée par une factory et renverra l’implémentation correspondant à le version du système.
Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
public abstract class VersionedLoremIpsum {
public abstract String doLorem();
public abstract int doIpsum();
}
public class EclairLoremIpsum
extends VersionedLoremIpsum {
public String doLorem() {
// do lorem, Eclair-style
}
public abstract int doIpsum() {
// deliver ipsum, a là Eclair
}
}
public class FroyoLoremIpsum
extends EclairLoremIpsum {
public String doLorem() {
String l = super.doLorem();
// additional processing
return l;
}
public abstract int doIpsum() {
...
Activity
VersionedLoremIpsum li;
li=VLIFactory.get()
VLIFactory
public static VersionedLoremIpsum get(
int sdk = Build.VERSION.SDK_INT;
if (sdk <= Build.VERSION_CODES.ECLAIR) {
li = new EclairLoremIpsum();
} else if (sdk <=
Build.VERSION_CODES.FROYO) {
li = new FroyoLoremIpsum();
} else {
li = new GingerbreadLoremIpsum();
}
)
24
Resources are your best friend, use them.
Utilisez des booléens pour savoir quelle est la version du système. Ils s’utilisent dans le code et dans le Manifest.xml.
Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
Manifest
<receiver android:name="MyPreICSAppWidget"
android:enabled="@bool/preICS">
...
</receiver>
<receiver android:name="MyPostICSAppWidget"
android:enabled="@bool/postICS">
...
</receiver>
SomeWhere in java
Resources res = getResources();
boolean postICS =
res.getBoolean(R.bool.postICS);
if (postICS) {
// do something cool and cutting-edge
} else {
// do something old-school but elegant!
}
Resvalues-v14bools.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="postICS">true</bool>
<bool name="preICS">false</bool>
</resources>
Resvaluesbools.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="postICS">false</bool>
<bool name="preICS">true</bool>
</resources>
25
Construisez vos Layouts dynamiquement en fonction de la version système.
Utilisez merge et include pour construire des IHM qui s’adaptent à la version de manière transparente.
Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
Reslayoutmain.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...
<!-- non multi-versioned stuff here -->
<include layout="@layout/desserts" />
<!-- more non multi-versioned stuff here -->
</LinearLayout>
Reslayoutdesserts.xml
<?xml ... ?>
<merge xmlns:android="...">
<CheckBox ... />
<CheckBox ... />
<CheckBox ... />
...
</merge>
Reslayout-v11desserts.xml
<?xml ... ?>
<merge xmlns:android="...">
<Switch ... />
<Switch ... />
<Switch ... />
...
</merge>
26
Hériter du thème de la version.
Restez cohérent avec le thème du SDK de l’appareil cible, conservez une application cohérente avec l’appareil.
Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
MyTheme
resvaluestheme.xml
<style name=" MyBaseTheme" parent="@android:style/Theme" />
<style name="MyTheme" parent="MyBaseTheme">
<style name="MyButtonStyle" parent="@android:style/Widget.Button">
resvalues-v11theme.xml
<style name=" MyBaseTheme" parent="@android:style/Theme.Holo" />
<style name="MyButtonStyle" parent="@android:style/Widget.Holo.Button">
MyBaseTheme
Theme Theme.Holo
27
Utiliser les Fragments et l’ActionBar sur toutes vos versions.
Ayez une application identique quelque soit la version du système et utilisez le même fichier de layout.
Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
public class SpeakerDetailFragmentHC extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.speaker_detail, container, false);
return view;
}
public class SpeakerDetailFragment extends android.support.v4.app.Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.speaker_detail, container, false);
return view;
}
public class BaseActivityHC extends Activity{...}
public class BaseActivityLegacy extends android.support.v4.app.FragmentActivity {...}android.support.v4.app.ActionBarActivity
private void pushInboxNotifications() {
Notification notification = new Notification.Builder(this)
.setContentTitle("10 New emails for me")
.setContentText("subject")
.setSmallIcon(android.R.drawable.ic_menu_agenda)
.setStyle(Notification.InboxStyle)
.addLine("Line 1, i can add more if i want")
.addAction(R.drawable.icon,R.string.notification_message,new PendingIntent(....))
.setSound(aSound)
.build();
NotificationManager
notificationManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notification);
}
28
Utiliser les notifications PreJB, PostJB.
Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
if version >= JellyBean
use Notification.Builder
else
use NotificationCompat.Builder
29
L’ordre des boutons a changé depuis ICS.
Avant ICS le bouton OK est à gauche, le bouton non ou cancel à droite.
Après ICS le bouton OK est à droite, le bouton non ou cancel à gauche.
Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
<uses-sdk
android:maxSdkVersion="16"
android:minSdkVersion="8"
android:targetSdkVersion="17" />
30
Min et target SDK importe.
Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
31
32
MinSDK=11 ?o? Utiliser le parrallel pattern.
Deux types d'animation incompatibles : Avant (TweenAnimation) Après (ObjectAnimator, ViewProperty) HoneyComb.
CodeDependent Chet Haase http://graphics-geek.blogspot.fr/.
Be short.
Le temps d'animation par défaut est de 300 ms.
Cela dépend de l'objet à animer (une vue, une activité ce n'est pas un bouton).
Be Amazing... but once.
Les animations longues, belles et délirantes deviennent ennuyeuses au bout de 3.
Remplacer les par des animations efficaces au bout de 3.
Si en développement votre animation ne vous pourrit pas la vie alors elle ne pourrira pas celle de vos utilisateurs.
Thanks Chet.
Depuis HoneyComb, les animations sont améliorées, simplifiées et super flexibles.
Dev.Bytes nous poste des tutos de 3 minutes pour nous les apprendre.
Merci à Chet Haase pour tout ça.
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:id="@+id/container"/>
33
MinSDK=16.
Ajoutez cette balise à vos Layouts.
Dès qu'un changement est détecté, il sera automatiquement animé pour le plaisir de vos utilisateurs.
Dev.Bytes Chet Haase LayoutTransChanging https://www.youtube.com/watch?v=55wLsaWpQ4g.
// Note that this assumes a LayoutTransition is set on the container, which is the
// case here because the container has the attribute "animateLayoutChanges" set to true
// in the layout file. You can also call setLayoutTransition(new LayoutTransition()) in
// code to set a LayoutTransition on any container.
LayoutTransition transition = container.getLayoutTransition();
// New capability as of Jellybean; monitor the container for *all* layout changes
// (not just add/remove/visibility changes) and animate these changes as well.
transition.enableTransitionType(LayoutTransition.CHANGING);
// Create bitmap to be re-used, based on the size of one of the bitmaps
mBitmapOptions = new BitmapFactory.Options();
//Set for asking only the width and height of the bitmap, not the bitmap itself
mBitmapOptions.inJustDecodeBounds = true;
//With that option, bitMap is not load, but its size is set in mBitmapOptions.outWidth
and mBitmapOptions.outHeight
BitmapFactory.decodeResource(getResources(), R.drawable.a, mBitmapOptions);
//Then create bitmap object (still not displaying any drawable)
mCurrentBitmap = Bitmap.createBitmap(mBitmapOptions.outWidth,
mBitmapOptions.outHeight, Bitmap.Config.ARGB_8888);
//Then change the inJustDecode for asking the bitmap itself
mBitmapOptions.inJustDecodeBounds = false;
//Then define where the loaded bitmap will be drop in the
mBitmapOptions.inSampleSize = 1;
//and finally load it (as mBitmapOptions.inBitmap = mCurrentBitmap, the loaded bitmap
will be in mCurrentBitmap)
BitmapFactory.decodeResource(getResources(), R.drawable.a, mBitmapOptions);
imageview.setImageBitmap(mCurrentBitmap);
34
MinSDK=15.
Créer un objet BitMap et réutilisé le pour charger les images suivantes.
Dev.Bytes Chet Haase Bitmap Allocation https://www.youtube.com/watch?v=rsQet4nBVi8
mCurrentIndex = (mCurrentIndex + 1) % imageIDs.length;
BitmapFactory.Options bitmapOptions = null;
if (reuseBitmapAllocation) {
}
long startTime = System.currentTimeMillis();
mCurrentBitmap = BitmapFactory.decodeResource(getResources(),
imageIDs[mCurrentIndex], bitmapOptions);
imageview.setImageBitmap(mCurrentBitmap);
Premier chargement Chargements Suivants
mBitmapOptions.inBitmap = mCurrentBitmap;
// Re-use the bitmap by using BitmapOptions.inBitmap
bitmapOptions = mBitmapOptions;
bitmapOptions.inBitmap = mCurrentBitmap;
Allocation Mémoire et Garbage optimisés
Rapidité de chargement accrue
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.jellybean_statue);
originalImageView.setImageBitmap(bitmap);
for (int i = 2; i < 10; ++i) {
addScaledImageView(bitmap, i, container);
}
35
MinSDK=15.
Créer un objet BitMap et réutilisé le pour charger les images suivantes.
Dev.Bytes Chet Haase LayoutTransChanging
private Bitmap addScaledImageView(Bitmap original, int sampleSize, LinearLayout container) {
// inSampleSize tells the loader how much to scale the final image, which it does at
// load time by simply reading less pixels for every pixel value in the final bitmap.
// Note that it only scales by powers of two, so a value of two results in a bitmap
// 1/2 the size of the original and a value of four results in a bitmap 1/4 the original
// size. Intermediate values are rounded down, so a value of three results in a bitmap 1/2
// the original size.
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
Bitmap scaledBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.jellybean_statue, bitmapOptions);
return scaledBitmap;
}
Chargement normal
bitmapOptions.inSampleSize = sampleSize;
Chargement réduit
static class MyView extends View {
Bitmap mBitmap;
Paint paint = new Paint();
int mShapeX, mShapeY;
int mShapeW, mShapeH;
public MyView(Context context, AttributeSet attrs, int defStyle) {...; setupShape();}
public MyView(Context context, AttributeSet attrs) {...; setupShape();}
public MyView(Context context) {{...; setupShape();}
private void setupShape() {Initialisation des objets graphiques...}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//Initialisation des tailles des composants et des positions
mShapeX = (w - mBitmap.getWidth()) / 2;
}
36
MinSDK=14.
Animer vos vues. Vous pouvez en redéfinir une ou bien le mettre en place directement dans votre activité.
Dev.Bytes Chet Haase Bouncer https://www.youtube.com/watch?v=vCTcmPIKgpM
Initialisation de votre custom View
Dev.Bytes Chet Haase Bouncer https://www.youtube.com/watch?v=vCTcmPIKgpM
37
MinSDK=14.
Mise en place de l'animation.
1. Définir et lancer l'animation.
void startAnimation() {
// This variation uses an ObjectAnimator. The ObjectAnimator automatically animate the target object for
us, so we no longer need to listen for frame updates and do that work ourselves.
ObjectAnimator anim = getObjectAnimator();
anim.setRepeatCount(ValueAnimator.INFINITE);
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.setInterpolator(new AccelerateInterpolator());
anim.start();}
ObjectAnimator getObjectAnimator() {
}
public void setShapeY(int shapeY) {
//Set the new value
mShapeY = shapeY;
//Calculate the dirty area and ask to redraw it
int minY = mShapeY;int maxY = mShapeY + mShapeH;minY = Math.min(mShapeY, minY);
maxY = Math.max(mShapeY + mShapeH, maxY);
//ask to redraw the rectangle area
invalidate(mShapeX, minY, mShapeX + mShapeW, maxY);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, mShapeX, mShapeY, paint);}
return ObjectAnimator.ofInt(this, "shapeY", 0, (getHeight() - mShapeH));
2. Construire l'ObjectAnimator
3. Mettre a jour les données graphiques
Calculer la zone a redessiner
Demander le redraw
4. Dessiner l'objet
// Create the AnimationDrawable in which we will store all frames of the animation
final AnimationDrawable animationDrawable = new AnimationDrawable();
for (int i = 0; i < 10; ++i) {
animationDrawable.addFrame(createDrawableForFrameNumber(i), 300);
}
// Run until we say stop
animationDrawable.setOneShot(false);
imageview.setImageDrawable(animationDrawable);
animationDrawable.start();
38
MinSDK=14.
Animer vos vues. Vous pouvez en redéfinir une ou bien le mettre en place directement dans votre activité.
Dev.Bytes Chet Haase KeyframeAnimation https://www.youtube.com/watch?v=V3ksidLf7vA
AnimationDrawable
private BitmapDrawable createDrawableForFrameNumber(int frameNumber) {
Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.GRAY);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextSize(80);
paint.setColor(Color.BLACK);
canvas.drawText("Frame " + frameNumber, 40, 220, paint);
return new BitmapDrawable(getResources(), bitmap);
}
Building a Bitmap from scratch
39
From Google I/O 2013
By Chet Haase and Romain Guy
Google I/o 2013 Romain Guy - Chet Haase "A Moving Experience" http://graphics-geek.blogspot.fr/2013/06/devbytes-animating-listview-deletion.html
mListView.setEnabled(false);
v.animate().setDuration(duration).alpha(endAlpha).translationX(endX).
withEndAction(new Runnable() {
@Override
public void run() {
// Restore animated values
v.setAlpha(1);
v.setTranslationX(0);
}});
0.Lancement de l'animation dans View.OnTouchListener# MotionEvent.ACTION_UP
animateRemoval(mListView, v);
Google I/o 2013 Romain Guy - Chet Haase "A Moving Experience" http://graphics-geek.blogspot.fr/2013/06/devbytes-animating-listview-deletion.html
40
From Google I/O 2013
By Chet Haase and Romain Guy
private void animateRemoval(final ListView listview, View viewToRemove) {
observer.removeOnPreDrawListener(this);
for (int i = 0; i < listview.getChildCount(); ++i) {
child.setTranslationY(delta);
if (firstAnimation) {
child.animate().withEndAction(new Runnable() {
public void run() {
mBackgroundContainer.hideBackground();
mSwiping = false;
mListView.setEnabled(true);
}
});
mItemIdTopMap.clear();
return true;}});
final ViewTreeObserver observer = listview.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
child.animate().setDuration(MOVE_DURATION).translationY(0);
1.Récupération du ViewTreeObserver
2.Ajout du Listener PreDraw
3.Mise en place de l'animation dans le preDraw
41
And of course thanks to
Merci pour votre attention.
android2ee.com.
#android2ee
mathias.seguy@android2ee.com
42
MyTorchMyLightMySensor My Google
Public Profile

ProTips DroidCon Paris 2013

  • 1.
  • 2.
    Android2EE est référencéen tant qu’organisme de formation, vous pouvez faire prendre en charge tout ou partie du montant de cette formation par votre OPCA. Cette formation Initiation avancée à Android est éligible au titre du DIF et CIF. Lieu : Paris, Toulouse, Lyon Durée : 3 jours Prix : 1680 € Lieu : Paris, Toulouse, Lyon Durée : 5 jours Prix : 2980 € Lieu : Paris, Toulouse, Lyon Durée : 3 jours Prix : 1780 €
  • 3.
    Android2ee.com Formations: http://www.android2ee.com/fr/formations-android/inter-entreprises http://www.android2ee.com/fr/formations-android/formation-complete Open Ressources http://www.android2ee.com/fr/videos/devoxx-android-a-quick-course-1 http://www.android2ee.com/fr/livre-en-consultation/introduction-openbook http://www.android2ee.com/tutoriaux/appwidget.html mathias.seguy@android2ee.com https://github.com/MathiasSeguy-Android2EE http://fr.linkedin.com/pub/mathias-seguy/37/a71/a59 https://plus.google.com/115788770291974884100/about https://plus.google.com/b/109641731378552898326/109641731378552898326/about http://fr.slideshare.net/Android2EE https://play.google.com/store/apps/developer?id=ANDROID2EE http://fr.twitter.com/#%21/android2ee @deprectaed uselinkedIn http://mathias-seguy.developpez.com/homepage/index.php http://blog.developpez.com/android2ee-mathias-seguy/ To GDG Vous souhaitez que je vienne faire une conférence dans votre Android User Group. Contactez moi, je suis preneur.
  • 4.
  • 5.
    5 From Google I/OAnd Devoxx Ce chapitre est un extrait des « meilleurs » proTips donnés par les équipes Google lors des GoogleI/O, notamment : •Android ProTips, Reto Meier, Google I/O 2011 •Making Good Apps Great , Reto Meier, Google I/O 2012 •Google I_O 2013 - Android Protips_ Making Apps Work Like Magic , Reto Meier, Google I/O 2013 •Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010 Merci à eux pour ces présentations.
  • 6.
    6 Statics as Temporaries Utiliserdes variables statiques pour vos variables temporaires public boolean pointInArea(int x, int y, Area area) { Point testPoint = new Point(x, y); return area.intersect(testPoint); } static final Point tmpPoint = new Point(); public boolean pointInArea(int x, int y, Area area) { tmpPoint.x = x; tmpPoint.y = y; return area.intersect(tmpPoint.yPoint); } Autoboxing creates Objects Utiliser les types primitifs, l’AutoBoxing créé des objets! Est équivalent à float x = 5; Float y = x; doSomething(x); void doSomething(Float z) {} float x = 5; Float y = new Float(x); doSomething(new Float(x)); void doSomething(Float z) {} Recycle those Bitmaps Les ressources sont limitées, recyclez vos Bitmaps le plus tôt possible (n’attendez pas la méthode finalize()) Vous pensez que cela aide: Mais en fait vous souhaitez: // done using this one, clear reference myBitmap = null; // done using this one, recycle it myBitmap.recycle(); From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
  • 7.
    7 Use ViewStub La ViewStubpermet le lazy loading de layouts. <ViewStub android:id="@+id/stub_import" android:inflatedId="@+id/panel_import" android:layout="@layout/progress_overlay" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" /> findViewById(R.id.stub_import).setVisibility(View.VISIBLE); // or View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();id/stub_import id/panel_import From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
  • 8.
    8 Use merge etinclude <!-- The merge tag must be the root tag --> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Content --> </merge> <!– The include tag is the one to use--> <LinearLayout …> <include layout="@layout/subLayout" /> <LinearLayout> .. </LinearLayout> </LinearLayout> Without merge With merge From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
  • 9.
    9 Compounds Drawables Utiliser lesbalises android:drawable*** pour mettre des images à droite, à gauche … de vos composants héritant de TextView (TextView, Button, EditText,…) <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" android:drawableLeft="@drawable/icon" /> Balises android:drawableBottom android:drawableEnd android:drawablePadding android:drawableRight android:drawableStart android:drawableTop From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
  • 10.
    10 ListView and ArrayAdpater: Use ViewHolder and convertView public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item_icon_text, parent, false); holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text); holder.icon = (ImageView) convertView.findViewById(R.id.icon); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.text.setText(DATA[position]); holder.icon.setImageBitmap((position & 1) ==? mIcon1 : mIcon2); return convertView; } static class ViewHolder { TextView text; ImageView icon; } From Android Graphics, Animations and Tips & Tricks, Romain Guy & Chet Haase, Devoxx 2010
  • 11.
    11 UUID : UniqueUser ID L’ UUID Pattern permet détecter l'utilisateur plutôt que les installations. Si vous avez besoin d’identifier de manière unique l'utilisateur qui possède l’application, ce pattern est à utiliser. private static String uniqueID = null; private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID"; public synchronized static String id(Context context) { if (uniqueID == null) { SharedPreferences sharedPrefs = context.getSharedPreferences(PREF_UNIQUE_ID, Context.MODE_PRIVATE ); uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null); if (uniqueID == null) { uniqueID = UUID.randomUUID().toString(); Editor editor = sharedPrefs.edit(); editor.putString(PREF_UNIQUE_ID, uniqueID); editor.commit(); } } return uniqueID; } Android ProTips, Reto Meier, Google I/O 2011
  • 12.
    12 Know your Networkand Battery context <receiver android:name="DeviceStateReceiever" > <action android:name="android.intent.action.ACTION_DOCK_EVENT" /> <action android:name="android.intent.action.ACTION_BATTERY_LOW" /> <action android:name="android.intent.action.ACTION_POWER_CONNECTED" /> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <action android:name="android.net.wifi.STATE_CHANGE" /> </receiver> Android ProTips, Reto Meier, Google I/O 2011 ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); boolean isConnected = activeNetwork.isConnectedOrConnecting(); boolean isMobile = activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE;
  • 13.
    13 KeyBoard and EditText: Customize KeyBoard and set Action Quand vous définissez un champ de type EditText vous devez définir : • Son type de clavier 7 • L’action IME • (et le Hint) <EditText android:id="@+id/editEmailInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/compose_email_hint" android:imeOptions="actionSend|flagNoEnterAction" android:inputType="textShortMessage| textAutoCorrect| textCapSentences" /> Android ProTips, Reto Meier, Google I/O 2011 EditText.OnEditorActionListener myActionListener = new EditText.OnEditorActionListener() { @Override public boolean onEditorAction(EditText v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_DONE) { // do here your stuff f return true; } return false; } };
  • 14.
    14 Always be asynchronous Rendeztout asynchrone en utilisant les Handler, Asynctask, IntentService (le système des Intents quoi), AsyncQueryHandler (le GetContentResolver en un sens), Loader (abstractClass) and CursorLoader (HoneyComb only). public class TutoActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> { SimpleCursorAdapter mAdapter; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // … // Create an empty adapter we will use to display the loaded data. mAdapter = new SimpleCursorAdapter(this,android.R.layout.simple_list_item_2, null, new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, new int[] { android.R.id.text1, android.R.id.text2 }, 0); listView.setAdapter(mAdapter); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); } // Callbacks public Loader<Cursor> onCreateLoader(int id, Bundle args) { Uri baseUri = MyContentProvider.CONTENT_URI; return new CursorLoader(this, baseUri, null, null, null, null); } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { mAdapter.swapCursor(data); } public void onLoaderReset(Loader<Cursor> loader) { mAdapter.swapCursor(null); } } Android ProTips, Reto Meier, Google I/O 2011
  • 15.
    15 Use the bigcookies strategy when possible Préférez transférer un gros paquet de données au travers d’internet quand c’est possible plutôt que plein de petits paquets. Making Good Apps Great, Reto Meier, Google I/O 2012 int prefetchCacheSize = DEFAULT_PREFETCH_CACHE; switch (activeNetwork.getType()) { case ConnectivityManager.TYPE_WIFI: prefetchCacheSize = MAX_PREFETCH_CACHE; break; case ConnectivityManager.TYPE_MOBILE): { switch (telephonyManager.getNetworkType()) { case TelephonyManager.NETWORK_TYPE_LTE: case TelephonyManager.NETWORK_TYPE_HSPAP: prefetchCacheSize *= 4; break; case TelephonyManager.NETWORK_TYPE_EDGE: case TelephonyManager.NETWORK_TYPE_GPRS: prefetchCacheSize /= 2; break; default: break; } break; } default: break; }
  • 16.
    Use AndroidBeam Parce quec'est tout simple d'échanger des données entre deux devices. Send Receive 16 Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013 public void onResume() { super.onResume(); NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); nfcAdapter.setNdefPushMessageCallback(new CreateNdefMessageCallback() { public NdefMessage createNdefMessage(NfcEvent event) { NdefMessage message = createMessage(); return message; } }, this); // ...} private void createMessage() { String mimeType = "application/com.myapp.nfcbeam"; byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII")); String payLoad = ""; // TODO Contextualized payload.; NdefMessage nfcMessage = new NdefMessage(new NdefRecord[] { new NdefRecord(ndefRecord.TNF_MIME_MEDIA, mimeByes, new byte[], payload.getBytes()), NdefRecord.createApplicationRecord("com.myapp.nfcbeam") }); } private void extractPayload(Intent beamIntent) { Parcelable[] messages = beamIntent.getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES); NdefAdapter message = (NdefMessage)message[0]; NdefRecord record = message.getRecords()[0]; String payload = new String(record.getPayload()); navigateTo(payload); } <intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/com.myapp.nfcbeam"/> </intent-filter>
  • 17.
    Location Utiliser le ServiceGooglePlay,il connait vos utilisateurs mieux que vous. 17 Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013 private void connectLBS() { int gpsExists = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); if (gpsExists == ConnectionResult.SUCCESS) { mLocationClient = new LocationClient(this, this, this); mlocationClient.connect(); } } @Override public void onConnected(Bundle connectionHint) { requestUpdates(mlocationClient); } private void requestUpdates(LocationClient mlocationClient) { LocationRequest request = LocationRequest.create(); request.setInterval(minTime); request.setPriority(lowPowerMoreImportantThanAccuracy ? LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY : LocationRequest.PRIORITY_HIGH_ACCURACY); mlocationClient.requestLocationUpdates(request, new LocationListener() { @Override public void onLocationChanged(Location location) { updateLocation(location); } }); }
  • 18.
    Activity Recognition Adapter vosapplications au contexte utilisateur (devant la télé, en voiture, en vélo, à pied...). 18 Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013 Intent intent = new Intent(this, ActivityRecognitionIntentService.class); intent.setAction(MyActivity.ACTION_STRING); PendingIntent pi = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURR ENT); mActivityRecognitionClient.requestActivityUpdates(interval, pi); @Override protected void onHandleIntent(Intent intent) { if (intent.getAction() == MyActivity.ACTION_STRING) { if (ActivityRecognitionResult.hasResult(intent)) { ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent); DetectedActivity detectedActivity = result.getMostProbableActivity(); int activityType = detectedActivity.getType(); if (activityType == DetectedActivity.STILL) setUpdateSpeed(PAUSED); else if (activityType == DetectedActivity.IN_VEHICLE) setUpdateSpeed(FASTER); else setUpdateSpeed(REGULAR); } } }
  • 19.
    TextToSpeech : TTS Deparlez à vos utilisateurs, parfois c'est super. Initialize Use 19 Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013 private void initTextToSpeech() { Intent intent = new Intent(Engine.ACTION_CHECK_TTS_DATA); startActivityForResult(intent, TTS_DATA_CHECK); } protected void onActivityResult(int request, int result, Intent data) { if (request == TTS_DATA_CHECK && result == Engine.CHECK_VOICE_DATA_PASS) { tts = new TextToSpeech(this, new OnInitListener() { public void onInit(int status) { if (status == TextToSpeech.SUCCESS) ttsIsInit = true; } }); } else startActivity(new Intent(Engine.ACTION_INSTALL_TTS_DATA); } private void say(String text) { if (tts != null && ttisIsInit) tts.speak(text, TextToSpeech.QUEUE_ADD, null); }
  • 20.
    Speech Recognition D'écouter vosutilisateurs, parfois c'est encore plus super. Record Consume 20 Android Protips_ Making Apps Work Like Magic, Reto Meier, Google I/O 2013 private void requestVoiceInput() { Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.String.voice_input_prompt); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ENGLISH); startActivityForResult(intent, VOICE_RECOGNITION); } @Override protected void onActivityResult(int request, int result, Intent data) { if (request == VOICE_RECOGNITION && result == RESULT_OK) { ArrayList<String> results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); String mostLikelyResult = results[0]; useSpeechInput(mostLikelyResult); } }
  • 21.
  • 22.
    22 From Google I/O2012 Ce chapitre est un extrait de la conférence des Google I/O 2012 : •Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012 Qui est exceptionnel sur la lutte contre la fragmentation, un très grand moment, merci à eux.
  • 23.
    23 Be Lazy Utiliser uneclasse abstraite qui sera instanciée par une factory et renverra l’implémentation correspondant à le version du système. Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012 public abstract class VersionedLoremIpsum { public abstract String doLorem(); public abstract int doIpsum(); } public class EclairLoremIpsum extends VersionedLoremIpsum { public String doLorem() { // do lorem, Eclair-style } public abstract int doIpsum() { // deliver ipsum, a là Eclair } } public class FroyoLoremIpsum extends EclairLoremIpsum { public String doLorem() { String l = super.doLorem(); // additional processing return l; } public abstract int doIpsum() { ... Activity VersionedLoremIpsum li; li=VLIFactory.get() VLIFactory public static VersionedLoremIpsum get( int sdk = Build.VERSION.SDK_INT; if (sdk <= Build.VERSION_CODES.ECLAIR) { li = new EclairLoremIpsum(); } else if (sdk <= Build.VERSION_CODES.FROYO) { li = new FroyoLoremIpsum(); } else { li = new GingerbreadLoremIpsum(); } )
  • 24.
    24 Resources are yourbest friend, use them. Utilisez des booléens pour savoir quelle est la version du système. Ils s’utilisent dans le code et dans le Manifest.xml. Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012 Manifest <receiver android:name="MyPreICSAppWidget" android:enabled="@bool/preICS"> ... </receiver> <receiver android:name="MyPostICSAppWidget" android:enabled="@bool/postICS"> ... </receiver> SomeWhere in java Resources res = getResources(); boolean postICS = res.getBoolean(R.bool.postICS); if (postICS) { // do something cool and cutting-edge } else { // do something old-school but elegant! } Resvalues-v14bools.xml <?xml version="1.0" encoding="utf-8"?> <resources> <bool name="postICS">true</bool> <bool name="preICS">false</bool> </resources> Resvaluesbools.xml <?xml version="1.0" encoding="utf-8"?> <resources> <bool name="postICS">false</bool> <bool name="preICS">true</bool> </resources>
  • 25.
    25 Construisez vos Layoutsdynamiquement en fonction de la version système. Utilisez merge et include pour construire des IHM qui s’adaptent à la version de manière transparente. Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012 Reslayoutmain.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout ... <!-- non multi-versioned stuff here --> <include layout="@layout/desserts" /> <!-- more non multi-versioned stuff here --> </LinearLayout> Reslayoutdesserts.xml <?xml ... ?> <merge xmlns:android="..."> <CheckBox ... /> <CheckBox ... /> <CheckBox ... /> ... </merge> Reslayout-v11desserts.xml <?xml ... ?> <merge xmlns:android="..."> <Switch ... /> <Switch ... /> <Switch ... /> ... </merge>
  • 26.
    26 Hériter du thèmede la version. Restez cohérent avec le thème du SDK de l’appareil cible, conservez une application cohérente avec l’appareil. Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012 MyTheme resvaluestheme.xml <style name=" MyBaseTheme" parent="@android:style/Theme" /> <style name="MyTheme" parent="MyBaseTheme"> <style name="MyButtonStyle" parent="@android:style/Widget.Button"> resvalues-v11theme.xml <style name=" MyBaseTheme" parent="@android:style/Theme.Holo" /> <style name="MyButtonStyle" parent="@android:style/Widget.Holo.Button"> MyBaseTheme Theme Theme.Holo
  • 27.
    27 Utiliser les Fragmentset l’ActionBar sur toutes vos versions. Ayez une application identique quelque soit la version du système et utilisez le même fichier de layout. Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012 public class SpeakerDetailFragmentHC extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.speaker_detail, container, false); return view; } public class SpeakerDetailFragment extends android.support.v4.app.Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.speaker_detail, container, false); return view; } public class BaseActivityHC extends Activity{...} public class BaseActivityLegacy extends android.support.v4.app.FragmentActivity {...}android.support.v4.app.ActionBarActivity
  • 28.
    private void pushInboxNotifications(){ Notification notification = new Notification.Builder(this) .setContentTitle("10 New emails for me") .setContentText("subject") .setSmallIcon(android.R.drawable.ic_menu_agenda) .setStyle(Notification.InboxStyle) .addLine("Line 1, i can add more if i want") .addAction(R.drawable.icon,R.string.notification_message,new PendingIntent(....)) .setSound(aSound) .build(); NotificationManager notificationManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(0, notification); } 28 Utiliser les notifications PreJB, PostJB. Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012 if version >= JellyBean use Notification.Builder else use NotificationCompat.Builder
  • 29.
    29 L’ordre des boutonsa changé depuis ICS. Avant ICS le bouton OK est à gauche, le bouton non ou cancel à droite. Après ICS le bouton OK est à droite, le bouton non ou cancel à gauche. Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
  • 30.
    <uses-sdk android:maxSdkVersion="16" android:minSdkVersion="8" android:targetSdkVersion="17" /> 30 Min ettarget SDK importe. Multiversioning Android, Bruno Oliveira & Adam Powell , Google I/O 2012
  • 31.
  • 32.
    32 MinSDK=11 ?o? Utiliserle parrallel pattern. Deux types d'animation incompatibles : Avant (TweenAnimation) Après (ObjectAnimator, ViewProperty) HoneyComb. CodeDependent Chet Haase http://graphics-geek.blogspot.fr/. Be short. Le temps d'animation par défaut est de 300 ms. Cela dépend de l'objet à animer (une vue, une activité ce n'est pas un bouton). Be Amazing... but once. Les animations longues, belles et délirantes deviennent ennuyeuses au bout de 3. Remplacer les par des animations efficaces au bout de 3. Si en développement votre animation ne vous pourrit pas la vie alors elle ne pourrira pas celle de vos utilisateurs. Thanks Chet. Depuis HoneyComb, les animations sont améliorées, simplifiées et super flexibles. Dev.Bytes nous poste des tutos de 3 minutes pour nous les apprendre. Merci à Chet Haase pour tout ça.
  • 33.
    <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:animateLayoutChanges="true" android:id="@+id/container"/> 33 MinSDK=16. Ajoutez cette baliseà vos Layouts. Dès qu'un changement est détecté, il sera automatiquement animé pour le plaisir de vos utilisateurs. Dev.Bytes Chet Haase LayoutTransChanging https://www.youtube.com/watch?v=55wLsaWpQ4g. // Note that this assumes a LayoutTransition is set on the container, which is the // case here because the container has the attribute "animateLayoutChanges" set to true // in the layout file. You can also call setLayoutTransition(new LayoutTransition()) in // code to set a LayoutTransition on any container. LayoutTransition transition = container.getLayoutTransition(); // New capability as of Jellybean; monitor the container for *all* layout changes // (not just add/remove/visibility changes) and animate these changes as well. transition.enableTransitionType(LayoutTransition.CHANGING);
  • 34.
    // Create bitmapto be re-used, based on the size of one of the bitmaps mBitmapOptions = new BitmapFactory.Options(); //Set for asking only the width and height of the bitmap, not the bitmap itself mBitmapOptions.inJustDecodeBounds = true; //With that option, bitMap is not load, but its size is set in mBitmapOptions.outWidth and mBitmapOptions.outHeight BitmapFactory.decodeResource(getResources(), R.drawable.a, mBitmapOptions); //Then create bitmap object (still not displaying any drawable) mCurrentBitmap = Bitmap.createBitmap(mBitmapOptions.outWidth, mBitmapOptions.outHeight, Bitmap.Config.ARGB_8888); //Then change the inJustDecode for asking the bitmap itself mBitmapOptions.inJustDecodeBounds = false; //Then define where the loaded bitmap will be drop in the mBitmapOptions.inSampleSize = 1; //and finally load it (as mBitmapOptions.inBitmap = mCurrentBitmap, the loaded bitmap will be in mCurrentBitmap) BitmapFactory.decodeResource(getResources(), R.drawable.a, mBitmapOptions); imageview.setImageBitmap(mCurrentBitmap); 34 MinSDK=15. Créer un objet BitMap et réutilisé le pour charger les images suivantes. Dev.Bytes Chet Haase Bitmap Allocation https://www.youtube.com/watch?v=rsQet4nBVi8 mCurrentIndex = (mCurrentIndex + 1) % imageIDs.length; BitmapFactory.Options bitmapOptions = null; if (reuseBitmapAllocation) { } long startTime = System.currentTimeMillis(); mCurrentBitmap = BitmapFactory.decodeResource(getResources(), imageIDs[mCurrentIndex], bitmapOptions); imageview.setImageBitmap(mCurrentBitmap); Premier chargement Chargements Suivants mBitmapOptions.inBitmap = mCurrentBitmap; // Re-use the bitmap by using BitmapOptions.inBitmap bitmapOptions = mBitmapOptions; bitmapOptions.inBitmap = mCurrentBitmap; Allocation Mémoire et Garbage optimisés Rapidité de chargement accrue
  • 35.
    Bitmap bitmap =BitmapFactory.decodeResource(getResources(),R.drawable.jellybean_statue); originalImageView.setImageBitmap(bitmap); for (int i = 2; i < 10; ++i) { addScaledImageView(bitmap, i, container); } 35 MinSDK=15. Créer un objet BitMap et réutilisé le pour charger les images suivantes. Dev.Bytes Chet Haase LayoutTransChanging private Bitmap addScaledImageView(Bitmap original, int sampleSize, LinearLayout container) { // inSampleSize tells the loader how much to scale the final image, which it does at // load time by simply reading less pixels for every pixel value in the final bitmap. // Note that it only scales by powers of two, so a value of two results in a bitmap // 1/2 the size of the original and a value of four results in a bitmap 1/4 the original // size. Intermediate values are rounded down, so a value of three results in a bitmap 1/2 // the original size. BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); Bitmap scaledBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.jellybean_statue, bitmapOptions); return scaledBitmap; } Chargement normal bitmapOptions.inSampleSize = sampleSize; Chargement réduit
  • 36.
    static class MyViewextends View { Bitmap mBitmap; Paint paint = new Paint(); int mShapeX, mShapeY; int mShapeW, mShapeH; public MyView(Context context, AttributeSet attrs, int defStyle) {...; setupShape();} public MyView(Context context, AttributeSet attrs) {...; setupShape();} public MyView(Context context) {{...; setupShape();} private void setupShape() {Initialisation des objets graphiques...} @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { //Initialisation des tailles des composants et des positions mShapeX = (w - mBitmap.getWidth()) / 2; } 36 MinSDK=14. Animer vos vues. Vous pouvez en redéfinir une ou bien le mettre en place directement dans votre activité. Dev.Bytes Chet Haase Bouncer https://www.youtube.com/watch?v=vCTcmPIKgpM Initialisation de votre custom View
  • 37.
    Dev.Bytes Chet HaaseBouncer https://www.youtube.com/watch?v=vCTcmPIKgpM 37 MinSDK=14. Mise en place de l'animation. 1. Définir et lancer l'animation. void startAnimation() { // This variation uses an ObjectAnimator. The ObjectAnimator automatically animate the target object for us, so we no longer need to listen for frame updates and do that work ourselves. ObjectAnimator anim = getObjectAnimator(); anim.setRepeatCount(ValueAnimator.INFINITE); anim.setRepeatMode(ValueAnimator.REVERSE); anim.setInterpolator(new AccelerateInterpolator()); anim.start();} ObjectAnimator getObjectAnimator() { } public void setShapeY(int shapeY) { //Set the new value mShapeY = shapeY; //Calculate the dirty area and ask to redraw it int minY = mShapeY;int maxY = mShapeY + mShapeH;minY = Math.min(mShapeY, minY); maxY = Math.max(mShapeY + mShapeH, maxY); //ask to redraw the rectangle area invalidate(mShapeX, minY, mShapeX + mShapeW, maxY); } @Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(mBitmap, mShapeX, mShapeY, paint);} return ObjectAnimator.ofInt(this, "shapeY", 0, (getHeight() - mShapeH)); 2. Construire l'ObjectAnimator 3. Mettre a jour les données graphiques Calculer la zone a redessiner Demander le redraw 4. Dessiner l'objet
  • 38.
    // Create theAnimationDrawable in which we will store all frames of the animation final AnimationDrawable animationDrawable = new AnimationDrawable(); for (int i = 0; i < 10; ++i) { animationDrawable.addFrame(createDrawableForFrameNumber(i), 300); } // Run until we say stop animationDrawable.setOneShot(false); imageview.setImageDrawable(animationDrawable); animationDrawable.start(); 38 MinSDK=14. Animer vos vues. Vous pouvez en redéfinir une ou bien le mettre en place directement dans votre activité. Dev.Bytes Chet Haase KeyframeAnimation https://www.youtube.com/watch?v=V3ksidLf7vA AnimationDrawable private BitmapDrawable createDrawableForFrameNumber(int frameNumber) { Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.drawColor(Color.GRAY); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setTextSize(80); paint.setColor(Color.BLACK); canvas.drawText("Frame " + frameNumber, 40, 220, paint); return new BitmapDrawable(getResources(), bitmap); } Building a Bitmap from scratch
  • 39.
    39 From Google I/O2013 By Chet Haase and Romain Guy Google I/o 2013 Romain Guy - Chet Haase "A Moving Experience" http://graphics-geek.blogspot.fr/2013/06/devbytes-animating-listview-deletion.html mListView.setEnabled(false); v.animate().setDuration(duration).alpha(endAlpha).translationX(endX). withEndAction(new Runnable() { @Override public void run() { // Restore animated values v.setAlpha(1); v.setTranslationX(0); }}); 0.Lancement de l'animation dans View.OnTouchListener# MotionEvent.ACTION_UP animateRemoval(mListView, v);
  • 40.
    Google I/o 2013Romain Guy - Chet Haase "A Moving Experience" http://graphics-geek.blogspot.fr/2013/06/devbytes-animating-listview-deletion.html 40 From Google I/O 2013 By Chet Haase and Romain Guy private void animateRemoval(final ListView listview, View viewToRemove) { observer.removeOnPreDrawListener(this); for (int i = 0; i < listview.getChildCount(); ++i) { child.setTranslationY(delta); if (firstAnimation) { child.animate().withEndAction(new Runnable() { public void run() { mBackgroundContainer.hideBackground(); mSwiping = false; mListView.setEnabled(true); } }); mItemIdTopMap.clear(); return true;}}); final ViewTreeObserver observer = listview.getViewTreeObserver(); observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { public boolean onPreDraw() { child.animate().setDuration(MOVE_DURATION).translationY(0); 1.Récupération du ViewTreeObserver 2.Ajout du Listener PreDraw 3.Mise en place de l'animation dans le preDraw
  • 41.
  • 42.
    Merci pour votreattention. android2ee.com. #android2ee mathias.seguy@android2ee.com 42 MyTorchMyLightMySensor My Google Public Profile