Best Practices for User Interface
1 Desinging for Multiple Screes
- Android powers devices with several different screen sizes
- your apps to be compaatiable with all screes sizes
- optimize the UX for each screen configuration
1.1 Supporting Different Screen Sizes
- using flexible dimensons for views, RelativeLayout, screen size and orientation quelifiers, alias filters, and nine-patch bitmaps
- how to support different screen sizes by:
- layout can be adequately resized to fit the screen
- providing appropriate UI layout according to screen configuration
- the correct layout is applied to the correct screen
- providing bitmaps that scale correctly
1.1.1 Use “wrap_content” and “match_parent”
- “wrap_content”: set to the min size necessary to fit the content within that view
- “match_parent”: make the component expand to match the size of its parent view
- instead of hard-coded sizes
- use nested instances of LinearLayout
1.1.2 Use Relative layout
- not a straight
- specify your layout int terms of the spacial relationShips between components
RelativeLayout.LayoutParams
1.1.3 Use Size Qualifiers
- run-time automatically select the appropriate resource besed on the current device’s configuration
- such as
Fragment
1.1.4 Use the Smallest-width Qualifier
- Android 3.2+, (< Android3.2 use layout-large/main.xml)
- layout-sw600dp/mail.xml, smallest-width >= 600dp,two panes
- such as
Fragment
1.1.5 Use Layout Aliases
- single-pane layout : res/layout/main.xml
- multi-pane layout : res/layout-large (or layout-sw600dp), be benefit of tablets and TVs
- add two-pane layout (res/layout/main_twopanes.xml) to avoid this duplication of the same file for tablets and TVs;
-
based 3, add values-large(values-sw600dp)/layout.xml, actually not define the layout.
<resources> <item name="main" type="layout"@layout/main_twopanes/> </resources>
1.1.6 Use Orientation Qualifiers
- landscape and protrait
- different layouts:
onepane.xml, onepane_with_bar.xml, twopanes.xml, twopanes_narrow.xml
-
different values :
values-sw600dp, values-sw600dp-land/port, values-large-land/port
<resources> <item name="main_layout" type="layout">@layout/twopanes_narrow</item> <bool name="has_two_panes">true</bool> </resources>
1.1.7 Use Nine-patch Bitmaps
- resolve to stretch or shrink images uniformly
- the draw9patch utility of SDK, indicate which areas can and cannot be stretched
android;background="@drawable/button"
1.2 Supporting Different Screen Densities
- pixel densities
- using density-independent pixels (resloution-independent units of measurements)
- providing bitmaps appropriate for each density
1.2.1 Use Density-independent Pixels
- avoid to use absolution pixels, as the same number of pixels may correspond to different plysical sizes on different devices
- use either
dp
orsp
(text size) units, rather thanpx
1.2.2 Provide Alternative Bitmaps
- low, medium, high, extra-high density
- place the image files in the appropriate subdirectory under res/xx
- use
@drawable/xx.png
, the system selects the appropriate bitmap based on the screen’s dpi - palce launcher icons in the res/mipmap-[density]/ floders. see Icon Design Guidelines
1.3 Implementing Adpative UI Flows
- run-time detection of active layout, reacting according to current layout, handing screen configuration changes
1.3.1 Determine the Current Layout
- signle-pane or dual-pane
- different components
1.3.2 React According to Current Layout
- different actions to different results
- signle-pane or dual-pane
1.3.3 Reuse Fragments in Other Activities
1.3.4 Handle Screen Configuration Changes
- a rotation change
- keep your interface consistent
2 Building a Responsive UI with ConstranitLayout
- ConstraintLayout
- create large and complex layouts with a flat view hierarchy
- similar to
RelativeLayout
, but more flexible - esay to use with Android studio’s Layout Editor by drag-and-dropping instand of editing the xml
- ConstraintLayout Examples project on GitHub
- Android新特性介绍,ConstraintLayout完全解析 (guolin blog)
2.1 Constraints Overview
- add at least one horizontal and one vertical constraint for the view
2.2 Add ConstraintLayout to your project
-
add
maven.google.com
repository declared in module-levelbuild.gradle
filerepositories { maven { url 'https://maven.google.com' } }
-
add the library as a dependency
dependencies { complie 'com.android.support.constraint:constraint-layout:1.0.2' }
- Convert a layout
- Create a new layout
2.3 Add a constraint
2.4 Adjust the constraint bias
2.5 Adjust the view size
2.6 Adjust the view margins
2.7 Control linear groups with a chain
2.8 Automatically create constraints
3 Adding the App Bar
- a dedicated space
- access to important actions in a predicatable way
- support for navigation and view switching(with tabs or drop-down lists)
- v7 appcompat support library’s Toolbar
3.1 Setting Up the App Bar
3.1.1 Add a Toolbar to an Activity
- add library
- extends
AppCompatActivity
- set the
<application>
element to use one of appcompat’sNoActionBar
,android:theme=@style/Theme.AppCompat.Light.NoActionBar
-
add a
Toolbar
to the activity’s layout<android.support.v7.widget.Toolbar android:id="@+id/my_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:elevation="4dp" android:theme="@style/ThemeOverlay.AppCompat.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
- call the
setSupportActionBar(toolbar)
in the activity’s onCreate()
3.1.2 Use App Bar Utility Methods
getSupportActionBar()
to handle the bar
3.2 Adding and Handing Actions
- the app bar allows yout to add buttons for user actions
3.2.1 Add Action Buttons
-
add an
<item>
in your an XML menu res (res/menu/)<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_favor" android:icon="@drawable/ic_favor" android:title="@string/action_favor" app:showAsAction="ifRoom" /> <item android:id="@+id/action_settings" android:icon="@drawable/ic_setttings" android:title="@string/action_settings" app:showAsAction="never" />
-
app:showAsAction
attibute specifies whether the action should be shown as a button on the app bar
3.2.2 Respond to Actions
-
override the activity’s
onOptionsItemSelected()
to respond the actions@Override public boolean onOptionItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_settings: return true; case R.id.action_favor: return true; defalut: // action was not recognized, invoke the superclass to handle it return super.onOptionItemSelected(itme); }
3.3 Adding an Up Action
- select the Up button, navigate to the parent activity
3.3.1 Declare a Parent Activity
- set
android:parentActivityName
in Android4.1(API 16+) -
define a
<meta-data>
, where name isandroid.support.PARENT_ACTIVTIY
and the value is the parent activity’s name<!-- A child of the main activity --> <activity android:name="com.example.myfirstapp.MyChildActivity" android:label="@string/title_activity_child" android:parentActivityName="com.example.myfirstapp.MainActivity" > <!-- Parent activity meta-data to support 4.0 and lower --> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.example.myfirstapp.MainActivity" /> </activity>
3.3.2 Enable the Up Button
-
setDisplayHomeAsUpEnable()
of the bar, not need to catch inonOptionsItemSelected()
Toolbar childToolbar = (Toolbar) findViewById(R.id.child_toolbar); setSupportActionBar(childToolbar); ActioBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnable(true);
3.4 Action Views and Action Providers
- add two versatile compontents: an action view and an action provider
- an action view provides rich functionallity within the app bar, such as
SearchView
- an action provider is ac action with its own customized layout, such as
SharedActionProvider
3.4.1 Add an Action View
-
add
actionViewClass, actionLayout
attributes to the- element
<item android:id="@+id/action_search" android:title="@string/action_search" android:icon="@drawable/ic_search" app:showAsAction="ifRoom|collapseActionView" app:actionViewClass="android.support.v7.widget.SearchView" />
collapseActionView
flag indicates how to display the widget when the user is not interacting with it-
onCreateOptionsMenu()
to config the action@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_activity_actions, menu); MenuItem searchItem = menu.findItem(R.id.action_search); SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); return super.onCreateOptionMenu(menu); }
3.4.1.1 Responding to action View expansion
-
define a class implements
MenuItem.OnActionExpandListener
andsetOnActionExpandListener()
OnActionExpandListener expandListener = new OnActionExpandListener() { @Override public boolean onMenuItemActionCollapse(MenuItem item) { return true; } @Override public boolean onMenuItemActionExpand(MenuItem item) { return true; } }; // onCreateOptionMene MenuItem actionMenuItem = menu.findItem(R.id.actionItem); MenuItemCompat.setOnActionExpandListener(actionMenuItem, expandListener);
3.4.2 Add an Action Provider
- add an
actionProviderClass
attribute -
not need to set icon
<item android:id="@+id/action_share" android:title="@string/share" app:showAsAction="ifRoom" app:actionProviderClass="android.support.v7.widget.ShareActionProvider" />
4 Showing Pop-Up Messages
- quick pop-up message without waiting for the user to respond
- not prominent
- Snackbar supersedes
Toast
4.1 Building and Displaying a Pop-Up Message
- automatically go away after a short period
- not need to act on
4.1.1 Use a CoordinatorLayout
- a
Snackbar
is attached to a view - dimiss the
Snackbar
by swiping it away -
the layout moves some other UI elements when the
Snackbar
apper, such asFloatingActionButton
<android.support.design.widget.CoordinatorLayout android:id="@+id/coordinator" android:layout_width="match_parent" android:layout_height="match_parent" > <!-- Here are the existing layout elements, now wrapped in a CoordinatorLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.design.widget.CoordinatorLayout>
4.1.2 Display a Message
- create a
Snackbar
object with the message text,Snackbar.make(CoordinatorLayoutId, StringId, duration)
-
show()
to display it not duplicatelySnackbar.make(findViewById(R.id.coordinator), R.string.email_sent, Snackbar.LENGTH_SHORT) .show();
4.2 Adding an Action to a Message
- add an action to respond to message
- implement
View.OnClickListener
to overrideonClick()
-
a Snackbar
setAction()
snackbar.setAction(R.string.undo_string, new View.OnClickListener() { @Override public void onClick(View v) { //code to undo the user's last action } });
5 Creating Custom Views
5.1 Creating a Custom View class
- view encapsultes a specific set of functionality with an easy to UI, CPU, memory efficiently, and so on
- a custom view :
- conform to Android standards
- provide styleable attributes that work with Android XML layouts
- send accessibility events
- be compatible withe multiple Android platforms
5.1.1 Subclass a View
- extend view directly
-
provide a constructor that takes a
Context
and anAttributeSet
object as parametersclass PieChart extends View { public PieChart(Context context, AttributeSet attrs) { super(context, attrs); } }
5.1.2 Define Custom Attributes
-
define attributes in a
<declare-styleable>
res element,res/values/attrs.xml
<resources> <declare-styleable name="PieChart"> <attr name="showText" format="boolean" /> <attr name="labelPosition" format="enum"> <enum name="left" value="0"/> <enum name="right" value="1"/> </attr> </declare-styleable> </resources>
-
specify values for the attributes in XML layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews"> <com.example.customviews.charting.PieChart custom:showText="true" custom:labelPosition="left" /> </LinearLayout>
- retrieve attribute values at runtime
- Apply the retrieved attribute values to your view
5.1.3 Apply Custom Attributes
-
obtainStyleSttributes()
to get aTypedArray
public PieChart(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.getTheme().obtainStyleAttributes(attrs, R.styleable.PieChart, 0, 0); String showText = a.getBoolean(R.styleable.PieChart_showText, false); int textPos = a.getInteger(R.styleable.PieChart_lablePostion, 0); a.recycle(); }
5.1.4 Add Properties and Events
- attributes controll the behavior and appearance of views, are read after view initialized.
- dynamic behavoir, use getter/setter pair for each custom attibute
-
call
invalidate(), requestLayout()
to ensure the view behaves reliablypublic boolean isShowText() { return showText; } public void setShowText(String showText) { this.showText = showText; invalidate(); requestLayout(); }
- support event listener to communicate important events
5.1.5 Design For Accessibility
- support users with diablilities:
- lable your input fields using the
android:contentDescription
attribute - send accessibility events by calling
senAccessibilityEvent()
when appropriate - support alternate controllers, such as D-pad and trackball
- lable your input fields using the
- Making Applications Accessible
5.2 Implementing Custom Drawing
- view appearance
5.2.1 Override onDraw()
5.2.2 Create Drawing Objects
5.2.3 Handle Layout Events
- what size the view is ——
onSizeChanged()
onMeasure()
5.2.4 Draw!
drawText()
, specify the typeface by callingsetTypeface()
- draw promitive shapes, such as
drawRect(), drawOval()
- draw more complex shapes, such as
drawPath()
drawBitmap()
- define gradient fills by create
LinearGradient
, andsetShader()
5.3 Making the View Interactive
- mimic real-world action
- sense subtle behavior or feel
5.3.1 Handle Input Gestures
onTouchEvent()
- define interactions in terms of gestures such as tapping, pulling, pushing, flinging, and zooming
- implement
GestureDetector.OnGestureListener
(SimpleOnGestureListener
), atonDown()
return false, the system assumes you want to ignore the reset of the gesture.
5.3.2 Create Physically Plausible Motion
Scroll
is the basis for handling flywheel-style fling gestureValueAnimator
5.3.3 Make Your Transitions Smooth
ValueAnimator
- change the based view properties to use
ViewPropertyAnimator
,animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();
5.4 Optimizing the View
- run at a consistently high frame rate
- avoid common performance problems
- use hardware acceleration to make your custom drawing run faster
- a well-designed view that responds to gesture and transitions between states
Do Less, Less Frequently
- eliminate allocations in
onDraw()
- eliminate unnecessary call to
invalidate()
- traversing layouts is very expensive operation, such as
requestLayout()
, make view hierarchies as shallow as possible - custom
ViewGroup
to generate a complex UI
6 Creating Backward-Compatible UIs
- about
Action Bar Tabs
- There is so useless that not make an introducation.
6.1 Abstracting the New APIs
6.2 Proxying to the New APIs
6.3 Creating an Implementation with Older APIs
6.4 Using the Version-Aware Component
7 Implementing Accessibility **for user with disabilities **
- wide a userbase (a maiority of users)
- visible change (visually impaired and less optimal)
7.1 Developing Accessible Application
- easy navigation, set labels and fire events
- facilitate a smooth UX
7.1.1 Add Content Descriptions
android:contentDescription
to add lables to UI elements- at runtime, use
setContentDescription()
- avoid the web-developer pitfall of labelling everything with useless information
- TalkBack, and enable it in
Settings > Accessibility > TalkBack
7.1.2 Design for Focus Navigation
- touchScreen, like a D-pad, arrow-keys, or a trackball
- support connecting external devices like keyboards via USB or bluetooth
- all navigational elements need to be set as focusable,
View.setFocusable()
orandroid:focusable
-
each UI has 4 attributes,
android:nextFocusUp/Down/Left/Right
to determine the next view<Button android:id="@+id/doSomething" android:focusable="true" android:nextFocusDown=”@id/label” ... /> <TextView android:id="@+id/label" android:focusable=”true” android:text="@string/labelText" android:nextFocusUp=”@id/doSomething” ... />
7.1.3 Fire Accessibility Events
AccessibilityEvent
is created when you select an item or change focus- fire events at the appropriate times, generate events by
sendAccessibilityEvent(int)
-
for example, extend an imageView, write captions by typing on the keyboard when it has focus
public void onTextChanged(String before, String after) { if (AccessibilityManager.getInstance(context).isEnable()) { sendAccessibilityEvent(Accessibility.TYPE_VIEW_TEXT_CHANGED); } }
7.1.4 Test Your Application
- install and enable ana accessibility service:
TalkBack
(GooglePlay), use a directional controller instand of touch screen
7.2 Developing Accessiblity Services
- listen for accesslibility events, like event type and content descriptions
7.2.1 Create Your Accessibility Service
MyAccessibilityService
extendsAccessibilityService
-
specify
android:accessibilityservice
intent<service android:name=".MyAccessibilityService"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> </service>
7.2.2 Configure Your Accessibility Service
7.2.3 Respond to AccessibilityEvents
7.2.4 Query the View Hierarchy for More Context
7.3 Testing Your App’s Accessibility
- perform manual and automate testing, use analysis tools and get feedback from users
8 Managing the System UI
system bar
display notifications, communication of device status, and device navigation- Apps temporarily dim or hide the bar for a less distracting or a fully immersive experience
- user’s needs and expectations to modify the system bars
8.1 Dimming the System Bars
- dim the status and navigation bars
- touch the bar area of the screen, both bars are visible
- the advantage is the bars are still present but their details are obscured
8.1.1 Dim the Status and Navigation Bars
-
SYSTEM_UI_FLAG_LOW_PROFILE
View decorView = getActivity().getWindow().getDecorView(); int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE; decorView.setSystemUiVisibility(uiOptions);
8.1.2 Reveal the Status and Navigation Bars
-
setSytemUiVisibilty()
View decorView = getActivity().getWindow().getDecorView(); // Calling setSystemUiVisibility() with a value of 0 clears // all flags. decorView.setSystemUiVisibility(0);
8.2 Hiding the Status Bar
- hide the status bar on different versions of Android
8.2.1 Hide the Status Bar on Android 4.0-
android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"
-
set
WindowManager
flags, hide and show the status bar easierif (Build.VERSION.SDK_INT < 16) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); }
8.2.2 Hide the Status Bar on Android 4.0+
setSystemUiVisibility()
onResume(), onWindowFocusChanged()
callhide()
method-
content doesn’t resize ad the status bar hides and shows
View decorView = getWindow().getDecorView(); int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN; decorView.setSystemUiVisibility(uiOptions); // Remember that you should never show the action bar if the status bar is hidden, so hide that too if necessary. ActionBar actionBar = getActionBar(); actionBar.hide();
8.2.3 Make Content Appear Behind the Status Bar
SYSTEM_UI_FLAG_LAYOUT_STABLE
to maintain a stable layoutandroid:fitsStstemWindows="true"
to be stablefitSystemWindows(Rect insets)
is calling by the view hierarchy when the content insets for a window hava changed
8.3 Hiding the Navigation Bar (Android 4.0+)
- hide the navigation bar except the status bar
8.3.1 Hide the Navigation Bar
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
- notice as described above (hide the status)
8.3.2 Make Content Appear Behind the Navigation Bar
- notice as described above (hide the status)
8.4 Using Immersive Full-Screen Mode
SYSTEM_UI_FLAG_IMMERSIVE
(Android4.4, API 19)- automatically hide again after a few moments
- immersive mode states
- Non-immersive mode
- Reminder bubble
- Immersive mode
- Sticky flag
8.4.1 Choose an Approach
8.4.2 Use Non-Sticky Immersion
8.4.3 Use Sticky Immersion
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
8.5 Responding to UI Visibility Changes
- reigster a listener to get notified of system UI visibility changes to adjust app’s UI
-
View.OnSystemUiVisibilityChangeListener
View decorView = getWindow().getDecorView(); decorView.setOnSystemUiVisibilityChangeListener(new *View.OnSystemUiVisibilityChangeListener() *) { @Override public void onSystemUiVisibilityChange(int visibility) { //Note System bars will only be "visible" if none of the LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { //System bars are visible. Adjust UI, such as showing the action bar or other navigational controls } } });
9 Creating Apps with Material Design
- 记录到另一篇文章中Material Design