In addition to XML, Java logic can be defined.
(similar to Swing)
Most tutorials about defining UI focus on coding XML.
<RelativeLayout specifications for screens are based on wrap_content, match_parent,
Layout Height wrap_content keeps it smaller than fill-parent.
Layout width fill_parent or weight (percentage of screen)
Property Layout Weight specifies how "greedy" a widget is for space. Default 0.
Property Layout Gravity aligns content of widget within a parent.
Property gravity="center" aligns content within itself.
TODO: RTF (Right-To-Left) language layout.
TESTING EXE: TODO: This program scans the XML and identifies what to look for while testing. Especially helpful for verification of localization.
Android's IMF (Input Method Framework) provides soft keyboards appropriate to field types:
inputType | |
---|---|
text | |
number | |
phone | |
datetime | |
date | |
time |
Email addresses are still text:
<TableRow> <TextView android:text="Email address:" /> <EditText android:inputType="text|textEmailAddress" /> </TableRow>
Notice the field name after the | character.
<TextView android:text="Signed decimal number:" /> <EditText android:inputType="number|numberSigned|numberDecimal" />
SlidingDrawer container in a FrameLayout.
WEBSITE: DroidDraw can be used to design rudimentary screens.
ListView in Android supports header and footer views -- views that do not belong to the underlying adapter but otherwise show up in the list and scroll along with the contents. However, they only work if you have not yet set your own adapter and are therefore not terribly flexible.
LIB: CommonsWare Android Components: SackOfViewsAdapter is another way of approaching this. You provide the Views that make up the rows, and the adapter feeds them to Android as if they were newly created.
The SackOfViewsAdapter is designed to be subclassed, mostly to determine how isEnabled() behaves, so you can control which of those views are selectable and which simply scroll with the list.
ReorderingCursorAdapter maintains its own internal sequence information and maps everything from list positions to their corresponding current positions in the Cursor.
LIB: Mark Murphy's TouchListView: A Drag-and-Drop Capable ListView
LIB: Android Components: EndlessAdapter a wrapper for an existing ListAdapter that adds "endless list" capability. When the user scrolls to the bottom of the list, if there is more data for this list to be retrieved, your code gets invoked in a background thread to fetch the new rows, which then get seamlessly attached to the bottom of the list.
LIB: CommonsWare Android Components: WakefulIntentService
LIB: CWAC ViewSwiper: Gesture-Driven ViewFlipper Replacement
LIB: CommonsWare Android Components: ParcelHelper
LIB: CommonsWare Android Components: AdapterWrapper
LIB: CommonsWare Android Components: MergeAdapter accepts a mix of Adapters and Views and presents them as one contiguous whole to whatever ListView it is poured into. This is good for multiple data sources, or a handful of ordinary Views to mix in with lists of data.<
The API Demo application from
\android-sdk\docs\resources\samples\ApiDemos\src\com\example\android\apis\...
Category | App | Lower Level | Notes |
---|---|---|---|
App | Activity | - | |
Alarm | Alarm Controller | ||
Alarm Service | |||
Device Admin | - | ||
Dialog | - | ||
Intents | Get Music | ||
Launcher Shortcuts | - | ||
Menu | Inflate from XML | ||
Notification | IncomingMessage | ||
Notifying Service Controller | |||
NotifyWithText | |||
Status Bar | |||
Preferences | 1. Preferences from XML | ||
2. Launching Preferences | |||
3. Preference Dependencies | |||
4. Default values | |||
5. Preferences from code | |||
6. Advanced preferences | |||
Search | Invoke Search | ||
Query Search Results | |||
Service | Foreground Service Controller | ||
Local Service Binding | |||
Local Service Controller | |||
Messenger Service | |||
Remote Service Binding | |||
Remote Service Controller | |||
Service Start Arguments Controller | |||
Text-To-Speech | - | ||
Voice Recognition | - | ||
Content | Assets | Read Asset | |
Resources | - | ||
Storage | External Storage | ||
Graphics | ??? | - | |
Media | Media Player | - | |
Video View | - | ||
OS | Morse Code | - | |
Sensors | - | ||
SMS Messaging | - | ||
Text | Linkify | - | |
LogTextBox | - | ||
Marquee | - | ||
Views | Animation | - | |
Auto Complete | - | ||
Buttons | - | ||
Chronometer | - | ||
Controls | - | ||
Custom | - | ||
Date WIdgets | - | ||
Expandable Lists | - | ||
Focus | - | ||
Gallery | - | ||
Gallery | - | ||
Grid | - | ||
ImageButton | - | ||
ImageSwitcher | - | ||
ImageView | - | ||
Layout Animation | - | ||
Layouts | - | ||
Lists | - | ||
Progress Bar | - | ||
Radio Group | - | ||
Rating Bar | - | ||
ScrollBars | - | ||
Seek Bar | - | ||
Spinner | - | ||
Tabs | - | ||
Text Switcher | - | ||
Visibility | - | ||
WebView | - |
stopForeground() is called automatically when a service exits.
startForeground() elevates a service to the foreground. Controls what appears in the status bar by passing a notification class instance.
A notification signals the user that an event has taken place, by displaying an icon in the status bar rather than taking focus and interrupting the user.
LIB: MrTips is a 2-Class library to enable display of help tips in apps.
Designing for accessibility from the start makes the development effort a lot easier for everyone. End-users with disabilitites have it tough enough without us adding to their frustration.
TOOL: Pencil Project is a free and opensource Firefox plug-in tool for making diagrams and GUI prototyping.
Complex apps usually have various related screens. Address data inputs have several related elements, such as continent > country > state > city > town > street address, etc..
Selecting an item on a tree view of topics yields a list. Selecting a list item shows the content associated with the selected item.
On the smallest screen, different screens within an activity can be selected when the user presses the Menu/Options button and selecting another screen. If there are more than 6 choices, the sixth is a More icon to see more options.
On next larger screens, a TabHost always displays an icon for each of alternate screen of the activity.
On even larger screens, all the different screens represented in the tab may be presented on different parts of the screen are always visible.
Traditionally in HTML, JavaScript selects among different CSS files befitting the screen size recognized.
APP:
MyTest from Motorola
to detect device density
dynamically at runtime.
DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); String screen_density = Integer.toString(metrics.densityDpi); Log.e(TAG, "Screen density (metrics.densityDpi) is " + screen_density); String logical_density = Float.toString(metrics.density); Log.e(TAG, "Logical density (metrics.density) is " + logical_density);
To take action based on screen size, this code defines a new Configuration object instance (myConfig), obtains its defaults.
Configuration myConfig = new Configuration(); myConfig.setToDefaults();
Alternative 1, to do something for each size using a switch statement. Among default properties is a myConfig.screenLayout word made up of several bits (1's and 0's). The & in Java selects words which contain 1 bits also in the same position within bit-mask Configuration.SCREENLAYOUT_SIZE_MASK.
myConfig = getResources().getConfiguration(); switch (myConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) { case Configuration.SCREENLAYOUT_SIZE_LARGE: { Log.e(thisAppName, "Screen size is large"); // break; } case Configuration.SCREENLAYOUT_SIZE_NORMAL: { Log.e(thisAppName, "Screen size is normal"); // break; } case Configuration.SCREENLAYOUT_SIZE_SMALL: { Log.e(thisAppName, "Screen size is small"); // break; } case Configuration.SCREENLAYOUT_SIZE_UNDEFINED: { Log.e(thisAppName, "Screen size is undefined"); // break; } // else? }
TODO: Add X-LARGE to code above.
Alternative 2, to do something only for a specific size:
if(getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_LARGE) ==Configuration.SCREENLAYOUT_SIZE_LARGE) { // process large }else{ // process smaller }
To get the aspect ratio of the device:
switch (myConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) { case Configuration.SCREENLAYOUT_LONG_NO: { Log.e(TAG, "The screen is not long"); break; } case Configuration.SCREENLAYOUT_LONG_YES: { Log.e(TAG, "The screen is long"); break; } case Configuration.SCREENLAYOUT_LONG_UNDEFINED: { Log.e(TAG, "The aspect ratio is not defined"); break; } }
New with Honeycomb (API 11):
REF:
Framework: Action Bar
and
REF:
ActionBar Class
SAMPLE APP: Action Bar Compatibility
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // setup Action Bar for tabs final ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); // remove the activity title to make space for tabs actionBar.setDisplayShowTitleEnabled(false); // instantiate fragment for the tab Fragment artistsFragment = new ArtistsFragment(); // add a new tab and set its title text and tab listener actionBar.addTab(actionBar.newTab().setText(R.string.tab_artists) .setTabListener(new TabListener(artistsFragment))); Fragment albumsFragment = new AlbumsFragment(); actionBar.addTab(actionBar.newTab().setText(R.string.tab_albums) .setTabListener(new TabListener(albumsFragment))); }
Dianne Hackborn wrote
the code and presents it in
Android 3.0 Fragments API posted 03 February 2011.
EBOOK: Wei-Ming Lee's description
ARTICLE: icecreamfragments
ARTICLE: Example by Donn Felker
APP: iosched used during Google's I/O 2010 conference was written with a single codebase that works on both smart phones and tablets.
On a smaller form-factor phone, selecting an item in fragment 1 (a list)
starts Activity 2 showing fragment 2 (to show details for the item selected from the list).
On a larger form factor tablet, fragments 1 and 2 (headers and detail) are both shown at the same time
on the same screen.
Selecting an item shown in fragment 1 updates fragment 2.
Fragments are driven from main.xml within res/layout.
<fragment class="com.agilevent.repairshop.VehiclesListFragment" android:id="@+id/vehicles" android:layout_width="fill_parent" android:layout_height="fill_parent" />
LIB: The compatibility library: android-support-v4.jar from within android-sdk/extras/android/compatibility needs to be imported into the libs folder of every project. Use the V4 library because it supports both phones and tablets (most devices on the market) with the minimum API of 4. Introduced with Android Honeycomb for tablets only, v13 contains v4, but its minimum API must be 13.
Right click on the jar, Build Path > Add to Build Path. This makes the jar show up under Referenced Libraries in the Package Explorer.
bit.ly/MapFragmentSupport fixes SDK code to map fragment to inherit MapActivity() instead of Activity(). The downside is that this applies to all activities. This is not fixed as of Ice Cream Sandwich.
DOWNLOAD: Action Bar Sherlock by @jakewharton extends the compatibility library. The library will automatically use the native action bar when available or will automatically wrap a custom implementation around your layouts. This allows you to easily develop an application with an action bar for every version of Android back through 1.6.
To display preferences as headers and details together on side-by-side panels on tablets with clusters of preferences, Android 3.0 code overrides onBuildHeaders() to call loadHeadersFromResource() which loads headers in a separate XML resource file instead of overriding onCreate() to call addPreferencesFromResource(). To ensure backward compatibility, some developers created a separate activity to handle preferences (such as in the OI Preferences app).
Prior to Honeycomb, developers coded editor.commit() after changing preferences with SharedPreferences.Editor to write (persist) preference information to (a persistent) XML file. To avoid a hidden source of slow file system access disk I/O burdening the main application thread, a new thread is started for background execution.
import android.content.SharedPreferences; ... static class CommitAsyncStrategy extends AbstractPrefsPersistStrategy { void persistAsync(final SharedPreferences.Editor editor) { (new Thread() { @Override public void run() { editor.commit(); } }).start(); }
This need to spawn a new thread became unnecessary when Honeycomb introduced the asynchronous editor.apply() method that is asynchrous by internal design.
import android.content.SharedPreferences.Editor; public class ApplyStrategy extends AbstractPrefsPersistStrategy { @Override void persistAsync(Editor editor) { editor.apply(); } }Both new and old (commit and apply) functions can exist in the same single code base by using code to detect what version is being run, as shown by Mark Murphy's Tuning Android Applications book and CPU-Java/PerfsPersist sample app
private static AbstractPrefsPersistStrategy initImpl(){ int sdk=new Integer(Build.VERSION.SDK).intValue(); // deprecated if (sdk<Build.VERSION_CODES.HONEYCOMB) { // sdk<11 return(new CommitAsyncStrategy()); // do old editor.commit() within async coding } else { return(new ApplyStrategy()); // do new editor.apply() without extra async coding. } }
With this code, older versions won't choke (issue VerifyError) on the new Honeycomb-specific code because it is in a separate class in the separate ApplyStrategy().java file.
To take advantage of Java's conditional class loading strategy, both the new ApplyStrategy() and old CommitAsyncStrategy() class definitions extend AbstractPrefsPersistStrategy which is called from onCreate() to initiate processing.
private static final String KEY="counter"; ... SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(this); int counter=prefs.getInt(KEY, 0); ((TextView)findViewById(R.id.value)).setText(String.valueOf(counter)); AbstractPrefsPersistStrategy.persist(prefs.edit().putInt(KEY, counter+1));
In the above code, the getInt() method of the prefs.object obtains an index for the KEY named "counter". Just the number is displayed on the screen when the app is executed.
The persist method is then invoked.
abstract public class AbstractPrefsPersistStrategy { abstract void persistAsync(SharedPreferences.Editor editor); private static final AbstractPrefsPersistStrategy INSTANCE=initImpl(); public static void persist(SharedPreferences.Editor editor) { INSTANCE.persistAsync(editor); } private static AbstractPrefsPersistStrategy initImpl() { // where branching between old and new }
Since INSTANCE was defined as a static variable containing "initImpl()", INSTANCE.persistAsync(editor); is evaluated to initImpl().persistAsync(editor).
initImpl() is where branching between old and new occurs.
Simularly, the Java compiler knows to ignore (not include in binary code generated) source code which definitely won't be executed, such as code within an entry expression that always evaluates false. The compiler knows that by definition static final variables won't ever change within the program. So this approach also won't bloat the size of compiled java code.
This conditional class loading is also used in
Contacts Spinners within the tree/master section of Mark Murphy's Advanced Android
As of Android 2.0, android.provider.Contacts is deprecated in favor of
android.provider.ContactsContract.
TIP: For flexibility, to avoid typing the same specs in, create a New > Android XML File style.xml file in the values folder
so changes can be done in one place. Within this files,
android:gravity="center" and android:textSize="30dp" is copied to the title style file and replaced with
style="@style/title" (not android:style=).
Just as web page CSS, define title, textSize, etc.
LIB: ViewFlow UI widget provides a horizontally scrollable ViewGroup with items populated from an Adapter.
Reading the code snippet below from the bottom up, a label inside a TextView is obtained from a layout after it has been inflated by the inflate method of the inflater object of the inflater class of type LayoutInflater. The view representing a row container is obtained by the getLayoutInflator() from the Activity.
LayoutInflater inflater=getLayoutInflater(); View row=inflater.inflate(R.layout.row, parent, false); TextView label=(TextView)row.findViewById(R.id.label); label.setText(items[position]);
“Inflation” refers to the act of converting an XML layout specification into the actual tree of View objects the XML represents. This involves, for each element: creating an instance of the specified View class, walk the attributes, convert them into property setter calls, iterate over all child elements.
Designing for Responsiveness
what seems fast to the user
View-derived classes are also called widgets or controls.
LIB: Time wheels, carousels, spinners, and other slick GUI control widgets are built using the $300-$1,000/year droidUX, which also improves performance by caching and recycling views.
Hierarchies?
For those who have programmed Java Swing:
Activities in Android refers almost to a (J)Frame in Swing.
Activity groups?
Views in Android refers to (J)Components in Swing.
TextViews in Android refers to a (J)Labels in Swing.
EditTexts in Android refers to a (J)TextFields in Swing.
Buttons in Android refers to a (J)Buttons in Swing.
HTC OpenSense SDK for matching the UI of HTC phones. But do we really need more fragmentation? HTC also has an SDK for 3D.
Selection Widgets???
Rotating icons from CodeProject.Fancy Lists???
http://developer.android.com/guide/topics/ui/drag-drop.html
Resort lists.
LIB: $150/$500/developer GraphWidget displays a horizontal time scrolling time-line for visualizing real-time (live) input data.
LIB: $400/developer Artfulbits library to display charts.
LIB: $339/developer TeeChart, with a APP: demo rated 3.5, from Steema (in Spain), also provides libraries for .NET, Java, ActiveX / COM, PHP and Delphi VCL controls of charts, guages, and maps for Real-time, Business, Financial, and Scientific apps.
After Google purchased in 2010 Admob for mobile ads, click-to-call ads began to appear.
Ads on mobile screens typically are at the bottom.
The equivalent of right-clicking an item on a PC is to "Tap and hold" on an item on an Android device.
For and example task:: Edit (Update/Change), Complete, Postpone, Delete.
The default app menu comes with an orange color.
Use the PopupWindow class to make it look prettier with advice from Menus My Way.
See Android.com's Screen Support explanation
TOOL:
Asset Studio
generates icons shown by the
Launcher, Menus, Action bar, Tabs, Notifications.
It also merges your app inside
device frames of various devices
(currently limited to Nexus and Xoom).
To change the app's icon, replace the graphics files, all named icon.png in these "drawable" folders containing static graphic resources (JPG, etc.) stored according to density:
Folder | Resolution (Density) | Icon pixels | Default Icon |
---|---|---|---|
drawable-hdpi This is the normal size used by 75% of users | high | 72x72 | |
drawable-ldpi | low | 36x36 | |
drawable-mdpi | medium | 40x40 |
NOTE: Emphirically Honeycomb notification icons are best at 64x64, and 48x48.
If a different graphic file name is desired, change the AndroidManifest.xml file
<application android:icon="@drawable/icon"
The .png file type is assumed.
Specify each clickable button in the Manifest.xml:
<Button android:id="@+id/button_1" android:text="Button 1" android:onClick="fireIntent"> </Button>
The value "fireIntent" is the method in your app's activity java code:
public void fireIntent(View view) { Intent intent = null; switch (view.getId()) { case R.id.Button01: //TODO get an intent startActivity(intent); break; } }
This topic is illustrated by
39:00 into Class 2, Part 2
of Marko's Bootcamp Mar. 8, 2011,
and also in his
WEBSITE:
ebook.
Using a graphics manipulation tool such as Adobe Photoshop,
The four corners are unscaled.
The four edges are scaled in one axis.
The middle is scaled in both axes and transparent to provide a selection around a rectangle.
TOOL: Draw 9-Patch WYSIWYG graphics editor (Java program) is used to create NinePatch images. Identify the dividing line for patches by selecting pixels at the edge.
WEBSITE:
android9patch.blogspot.com provides 9-Patch images on an app:
APP:
$4.88 9-Patch viewer app
The OHA had the Ascender company create the 3 "Droid" fonts Android recognizes in manifest files: android:typeface="serif", "sans" (sans-serif), and "monospace".
FreeType fonts used by the OS are in folder /system/fonts. Some developers put alternate fonts in /sdcard/fonts/, and switch between them from the Android Recovery Terminal.
To use that sans-serif TrueType font on Windows Mobile 7, I copy the segoeuil.TTF and segoeui.TTF files from XDA. here. There are 4 fonts installed with Visual Studio 2010 into C:\WIndows\Fonts: Segoe WP, Segoe WP N, Segoe WP N SemiLight Regular, and Segoe WP SemiLight Regular. On Vista, run the ttf executable (as an administrator).
Undeterred by its 70KB size, I code my app to use it within onCreate(): into my app's assets/fonts/ folder.
TextView tv=(TextView)findViewById(R.id.custom); Typeface face=Typeface.createFromAsset(getAssets(),"fonts/???.ttf"); tv.setTypeface(face); File font=new File(Environment.getExternalStorageDirectory(),"MyFont.ttf"); if( font.exists() ){ tv=(TextView)findViewById(R.id.file); face=Typeface.createFromFile(font); tv.setTypeface(face); }else{ // Default uses default "sans" font instead if anything is wrong. findViewById(R.id.filerow).setVisibility(view.GONE); }
TESTING: In addition to providing the code above, Mark Murphy warns that a custom font may not supply a correct elipsis character or the complete set of UTF-8 international glyphs.
To obtain the current user selection by Android in FONT_SCALE in System.Settings
Notifications are an important part of the user interface. When you miss a call or an incoming SMS message arrives, a notification appears briefly at the top of the screen. To see the history of notifications, swipe down from the top edge of the screen.
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.cancel(getIntent().getExtras().getInt("NotifID"));
The calling activity:
//---get the notification ID for the notification; // passed in by the MainActivity--- int notifID = getIntent().getExtras().getInt("NotifID"); //---PendingIntent to launch activity if the user selects // the notification--- Intent i = new Intent("net.learn2develop.AlarmDetails"); i.putExtra("NotifID", notifID); PendingIntent detailsIntent = PendingIntent.getActivity(this, 0, i, 0); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); Notification notif = new Notification( R.drawable.icon, "Time's up!", System.currentTimeMillis()); CharSequence from = "AlarmManager - Time's up!"; CharSequence message = "This is your alert, courtesy of the AlarmManager"; notif.setLatestEventInfo(this, from, message, detailsIntent); //---100ms delay, vibrate for 250ms, pause for 100 ms and // then vibrate for 500ms--- notif.vibrate = new long[] { 100, 250, 100, 500}; nm.notify(notifID, notif);
Auto-stereoscopic (glasses-free) 3D displays are all the rage in 2011.
Unfortunately, each device manufacturer has it's own SDK.
DOWNLOAD: LG Real3D SDK for implementing Real3D applications and content for LG Optimus 3D devices.
DOWNLOAD: Samsung SDK
Generally, we avoid dialogs. They limit user flexibility in navigation. Mark says the more complex the thing in the dialog, the less likely it is that it should be in a dialog in the first place
Haptics non-verbal touch feedback fulfills a need for tactile satisfaction.
Some device manufacturers add rumble effects to their devices not invokable via standard Google vibe() function, which is not consistently implemented across actuators (vibe motors) on various devices.
<uses-permission android:name="android.permission.VIBRATE" />
private Launcher m_launcher;
try{ m_launcher = new Launcher(this); }catch (RuntimeException e){ Log.e(“My App”, e.getMessage()); }
public void btnPlayEffectClicked(View view) { try{ // Strength is from 1 to 100. m_launcher.play(Launcher.BOUNCE_100); // m_launcher.play(Launcher.DOUBLE_STRONG_CLICK_100); // ramp: // staccato: }catch(RuntimeException e) { mTxtOut.append("Error: " + e.getMessage()); }
try{ m_launcher.stop(); }catch (RuntimeException e) {}
QUESTION: What are the "standard" meanings of the MOTIV haptics:
Effect | Meaning | Note |
---|---|---|
CONFIRM FIELD | single short soft | - |
CONFIRM SCREEN | single short medium | - |
WARNING | single long hard | - |
PREPARE | single short soft then short hard | - |
ALERT | single short hard then short soft | - |
ERROR | single long hard then short low | - |
CAUTION | triple long hard | - |
WAKEUP | 4 long hard | - |
etc. | ? | ? |
“Integrated with Immersion MOTIV Haptic Effects” to your app description page.