UI

2017/08/14

Best Practices for User Interface

1 Desinging for Multiple Screes

  1. Android powers devices with several different screen sizes
  2. your apps to be compaatiable with all screes sizes
  3. optimize the UX for each screen configuration

1.1 Supporting Different Screen Sizes

  1. using flexible dimensons for views, RelativeLayout, screen size and orientation quelifiers, alias filters, and nine-patch bitmaps
  2. 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”

  1. “wrap_content”: set to the min size necessary to fit the content within that view
  2. “match_parent”: make the component expand to match the size of its parent view
  3. instead of hard-coded sizes
  4. use nested instances of LinearLayout

1.1.2 Use Relative layout

  1. not a straight
  2. specify your layout int terms of the spacial relationShips between components
  3. RelativeLayout.LayoutParams

1.1.3 Use Size Qualifiers

  1. run-time automatically select the appropriate resource besed on the current device’s configuration
  2. such as Fragment

1.1.4 Use the Smallest-width Qualifier

  1. Android 3.2+, (< Android3.2 use layout-large/main.xml)
  2. layout-sw600dp/mail.xml, smallest-width >= 600dp,two panes
  3. such as Fragment

1.1.5 Use Layout Aliases

  1. single-pane layout : res/layout/main.xml
  2. multi-pane layout : res/layout-large (or layout-sw600dp), be benefit of tablets and TVs
  3. add two-pane layout (res/layout/main_twopanes.xml) to avoid this duplication of the same file for tablets and TVs;
  4. 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

  1. landscape and protrait
  2. different layouts: onepane.xml, onepane_with_bar.xml, twopanes.xml, twopanes_narrow.xml
  3. 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

  1. resolve to stretch or shrink images uniformly
  2. the draw9patch utility of SDK, indicate which areas can and cannot be stretched
  3. android;background="@drawable/button"

1.2 Supporting Different Screen Densities

  1. pixel densities
  2. using density-independent pixels (resloution-independent units of measurements)
  3. providing bitmaps appropriate for each density

1.2.1 Use Density-independent Pixels

  1. avoid to use absolution pixels, as the same number of pixels may correspond to different plysical sizes on different devices
  2. use either dp or sp(text size) units, rather than px

1.2.2 Provide Alternative Bitmaps

  1. low, medium, high, extra-high density
  2. place the image files in the appropriate subdirectory under res/xx
  3. use @drawable/xx.png, the system selects the appropriate bitmap based on the screen’s dpi
  4. palce launcher icons in the res/mipmap-[density]/ floders. see Icon Design Guidelines

1.3 Implementing Adpative UI Flows

  1. run-time detection of active layout, reacting according to current layout, handing screen configuration changes

1.3.1 Determine the Current Layout

  1. signle-pane or dual-pane
  2. different components

1.3.2 React According to Current Layout

  1. different actions to different results
  2. signle-pane or dual-pane

1.3.3 Reuse Fragments in Other Activities

  1. Supporting Tablets and Handsets

1.3.4 Handle Screen Configuration Changes

  1. a rotation change
  2. keep your interface consistent

2 Building a Responsive UI with ConstranitLayout

  1. ConstraintLayout
  2. create large and complex layouts with a flat view hierarchy
  3. similar to RelativeLayout, but more flexible
  4. esay to use with Android studio’s Layout Editor by drag-and-dropping instand of editing the xml
  5. ConstraintLayout Examples project on GitHub
  6. Android新特性介绍,ConstraintLayout完全解析 (guolin blog)

2.1 Constraints Overview

  1. add at least one horizontal and one vertical constraint for the view

2.2 Add ConstraintLayout to your project

  1. add maven.google.com repository declared in module-level build.gradle file

     repositories {
         maven {
             url 'https://maven.google.com'
         }
     }
    
  2. add the library as a dependency

     dependencies {
         complie 'com.android.support.constraint:constraint-layout:1.0.2'
     }
    
  3. Convert a layout
  4. 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

  1. a dedicated space
  2. access to important actions in a predicatable way
  3. support for navigation and view switching(with tabs or drop-down lists)
  4. v7 appcompat support library’s Toolbar

3.1 Setting Up the App Bar

3.1.1 Add a Toolbar to an Activity

  1. add library
  2. extends AppCompatActivity
  3. set the <application> element to use one of appcompat’s NoActionBar, android:theme=@style/Theme.AppCompat.Light.NoActionBar
  4. 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"/>
    
  5. call the setSupportActionBar(toolbar) in the activity’s onCreate()

3.1.2 Use App Bar Utility Methods

  1. getSupportActionBar() to handle the bar

3.2 Adding and Handing Actions

  1. the app bar allows yout to add buttons for user actions

3.2.1 Add Action Buttons

  1. 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" />
    
  2. app:showAsAction attibute specifies whether the action should be shown as a button on the app bar

3.2.2 Respond to Actions

  1. 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

  1. select the Up button, navigate to the parent activity

3.3.1 Declare a Parent Activity

  1. set android:parentActivityName in Android4.1(API 16+)
  2. define a <meta-data>, where name is android.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

  1. setDisplayHomeAsUpEnable() of the bar, not need to catch in onOptionsItemSelected()

     Toolbar childToolbar = (Toolbar) findViewById(R.id.child_toolbar);
     setSupportActionBar(childToolbar);
    	
     ActioBar actionBar = getSupportActionBar();
     actionBar.setDisplayHomeAsUpEnable(true);
    

3.4 Action Views and Action Providers

  1. add two versatile compontents: an action view and an action provider
  2. an action view provides rich functionallity within the app bar, such as SearchView
  3. an action provider is ac action with its own customized layout, such as SharedActionProvider

3.4.1 Add an Action View

  1. 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" />
    
  2. collapseActionView flag indicates how to display the widget when the user is not interacting with it
  3. 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
  1. define a class implements MenuItem.OnActionExpandListener and setOnActionExpandListener()

     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

  1. add an actionProviderClass attribute
  2. 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

  1. quick pop-up message without waiting for the user to respond
  2. not prominent
  3. Snackbar supersedes Toast

4.1 Building and Displaying a Pop-Up Message

  1. automatically go away after a short period
  2. not need to act on

4.1.1 Use a CoordinatorLayout

  1. a Snackbar is attached to a view
  2. dimiss the Snackbar by swiping it away
  3. the layout moves some other UI elements when the Snackbar apper, such as FloatingActionButton

     <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

  1. create a Snackbar object with the message text, Snackbar.make(CoordinatorLayoutId, StringId, duration)
  2. show() to display it not duplicately

     Snackbar.make(findViewById(R.id.coordinator), R.string.email_sent, Snackbar.LENGTH_SHORT)
              .show();
    

4.2 Adding an Action to a Message

  1. add an action to respond to message
  2. implement View.OnClickListener to override onClick()
  3. 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

  1. View

5.1 Creating a Custom View class

  1. view encapsultes a specific set of functionality with an easy to UI, CPU, memory efficiently, and so on
  2. 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

  1. extend view directly
  2. provide a constructor that takes a Context and an AttributeSet object as parameters

     class PieChart extends View {
         public PieChart(Context context, AttributeSet attrs) { 
             super(context, attrs);
         }
     }
    

5.1.2 Define Custom Attributes

  1. 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>
    
  2. 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>
    
  3. retrieve attribute values at runtime
  4. Apply the retrieved attribute values to your view

5.1.3 Apply Custom Attributes

  1. obtainStyleSttributes() to get a TypedArray

    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

  1. attributes controll the behavior and appearance of views, are read after view initialized.
  2. dynamic behavoir, use getter/setter pair for each custom attibute
  3. call invalidate(), requestLayout() to ensure the view behaves reliably

     public boolean isShowText() {
         return showText;
     }
     public void setShowText(String showText) {
         this.showText = showText;
         invalidate();
         requestLayout();
     }
    
  4. support event listener to communicate important events

5.1.5 Design For Accessibility

  1. 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
  2. Making Applications Accessible

5.2 Implementing Custom Drawing

  1. view appearance

5.2.1 Override onDraw()

5.2.2 Create Drawing Objects

  1. what to draw —— Cavans
  2. how to draw —— Paint

5.2.3 Handle Layout Events

  1. what size the view is —— onSizeChanged()
  2. onMeasure()

5.2.4 Draw!

  1. drawText(), specify the typeface by calling setTypeface()
  2. draw promitive shapes, such as drawRect(), drawOval()
  3. draw more complex shapes, such as drawPath()
  4. drawBitmap()
  5. define gradient fills by create LinearGradient, and setShader()

5.3 Making the View Interactive

  1. mimic real-world action
  2. sense subtle behavior or feel

5.3.1 Handle Input Gestures

  1. onTouchEvent()
  2. define interactions in terms of gestures such as tapping, pulling, pushing, flinging, and zooming
  3. implement GestureDetector.OnGestureListener (SimpleOnGestureListener), at onDown() return false, the system assumes you want to ignore the reset of the gesture.

5.3.2 Create Physically Plausible Motion

  1. Scroll is the basis for handling flywheel-style fling gesture
  2. ValueAnimator

5.3.3 Make Your Transitions Smooth

  1. ValueAnimator
  2. change the based view properties to use ViewPropertyAnimator, animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();

5.4 Optimizing the View

  1. run at a consistently high frame rate
  2. avoid common performance problems
  3. use hardware acceleration to make your custom drawing run faster
  4. a well-designed view that responds to gesture and transitions between states

Do Less, Less Frequently

  1. eliminate allocations in onDraw()
  2. eliminate unnecessary call to invalidate()
  3. traversing layouts is very expensive operation, such as requestLayout(), make view hierarchies as shallow as possible
  4. custom ViewGroup to generate a complex UI

6 Creating Backward-Compatible UIs

  1. about Action Bar Tabs
  2. 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 **

  1. wide a userbase (a maiority of users)
  2. visible change (visually impaired and less optimal)

7.1 Developing Accessible Application

  1. easy navigation, set labels and fire events
  2. facilitate a smooth UX

7.1.1 Add Content Descriptions

  1. android:contentDescription to add lables to UI elements
  2. at runtime, use setContentDescription()
  3. avoid the web-developer pitfall of labelling everything with useless information
  4. TalkBack, and enable it in Settings > Accessibility > TalkBack

7.1.2 Design for Focus Navigation

  1. touchScreen, like a D-pad, arrow-keys, or a trackball
  2. support connecting external devices like keyboards via USB or bluetooth
  3. all navigational elements need to be set as focusable, View.setFocusable() or android:focusable
  4. 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

  1. AccessibilityEvent is created when you select an item or change focus
  2. fire events at the appropriate times, generate events by sendAccessibilityEvent(int)
  3. 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

  1. install and enable ana accessibility service: TalkBack (GooglePlay), use a directional controller instand of touch screen

7.2 Developing Accessiblity Services

  1. listen for accesslibility events, like event type and content descriptions

7.2.1 Create Your Accessibility Service

  1. MyAccessibilityService extends AccessibilityService
  2. 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

  1. perform manual and automate testing, use analysis tools and get feedback from users

8 Managing the System UI

  1. system bar display notifications, communication of device status, and device navigation
  2. Apps temporarily dim or hide the bar for a less distracting or a fully immersive experience
  3. user’s needs and expectations to modify the system bars

8.1 Dimming the System Bars

  1. dim the status and navigation bars
  2. touch the bar area of the screen, both bars are visible
  3. the advantage is the bars are still present but their details are obscured

8.1.1 Dim the Status and Navigation Bars

  1. 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

  1. 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

  1. hide the status bar on different versions of Android

8.2.1 Hide the Status Bar on Android 4.0-

  1. android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"
  2. set WindowManager flags, hide and show the status bar easier

     if (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+

  1. setSystemUiVisibility()
  2. onResume(), onWindowFocusChanged() call hide() method
  3. 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

  1. SYSTEM_UI_FLAG_LAYOUT_STABLE to maintain a stable layout
  2. android:fitsStstemWindows="true" to be stable
  3. fitSystemWindows(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+)

  1. hide the navigation bar except the status bar

8.3.1 Hide the Navigation Bar

  1. int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
  2. notice as described above (hide the status)

8.3.2 Make Content Appear Behind the Navigation Bar

  1. notice as described above (hide the status)

8.4 Using Immersive Full-Screen Mode

  1. SYSTEM_UI_FLAG_IMMERSIVE (Android4.4, API 19)
  2. automatically hide again after a few moments
  3. 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

  1. reigster a listener to get notified of system UI visibility changes to adjust app’s UI
  2. 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

  1. 记录到另一篇文章中Material Design

文内导航