Improving android experience for both users and developers

Preview:

DESCRIPTION

Android UI and User Experience has changed dramatically in the recent version(s) and while users generally enjoy the new features, there are still several areas that are left to application-level-DIY-patterns. For developers, this is double challenge, they want to provide users with the bleeding edge UI patterns and at the same time, they have to deal with evolving API, that sometimes changes dramatically. Presentation covers the gotchas developer might face dealing with ever-moving Android API, and how to utilize Java language and the tools it have to make the experience for developer more pleasant. Typical trends in the API will get analyzed and divided into several areas or "patterns", discussing typical scenarios how these components are designed and implemented. This talk will propose several such UI patterns, that will compete to become "de facto" standards and details on the implementation, including possible impact on existing API as we have both end users and developers in mind. The list of patterns/areas discussed in the talk include following : ActionBar ListView TimePicker KineticGestureComponent

Citation preview

IMPROVING ANDROID EXPERIENCE FOR BOTH USERS AND DEVELOPERS

droidcon Berlin 2013, Pavel Lahoda, Actiwerks Ltd.

LET’S HAVE A LITTLE WARMUP

ANDROID.UTIL.LOG

HOW MANY TIMES YOU’VE TYPED THIS ?

prn.log("Index=" + i);

HOW ABOUT THIS ?

public static String cc(int depth) { if(skipCallStackCode == true) { return NA; // short-circuit for production } StackTraceElement[] ste = getStackTrace(null); int depthCount = 0; boolean shallowFlag = true; for(StackTraceElement element : ste) { if(prn.class.getName().equals(element.getClassName()) == true) { // always ignore elements that are above this class in the stack shallowFlag = false; } else { if(shallowFlag == false) { if(depthCount >= depth) { String name = element.getFileName(); if(name != null) { if(name.endsWith(".java")) { name = name.substring(0, name.length()-5); } } else { name ="[?]"; } return name; } else { depthCount++; } } } } return NA_BUG; }

HOW DOES IT WORK ?

GITHUB LINK

WHERE IS THE UX ?

HAVE TIME TO WORK ON AWESOME STUFF !

IMPORTANCE OF HAVING YOUR VIEW FLEXIBLE

ANDROID AND WEB COMMON STUFFUNLIMITED AMOUNT OF DISPLAY SIZES

WEB TRENDS: RESPONSIVE DESIGN

ANDROID TRENDS: LOTS OF WORK

CURRENT ANDROID LAYOUTS ARE NOT FLEXIBLE ENOUGH

ALTERNATIVE:USE PURE JAVA VIEWGROUP

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec); tinyGap = widthSpecSize/100; myComponent.measure(View.MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, View.MeasureSpec.EXACTLY));

// more component’s measuring goes there setMeasuredDimension(widthSpecSize, newHeight);}

@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {

myComponent.layout(x, y, x+myComponent.getMeasuredWidth(), y+titleLabel.getMeasuredHeight());// more component’s layout goes there

}

ONMEASURE() - PLACE FOR YOUR LOGIC

NO NEED TO REBIND COMPONENTS OR CALL SLOW LAYOUT INFLATE CODE

WORKS GREAT FOR ORIENTATION CHANGES

BBC NEWS FOR TABLETS EXAMPLE

INSPIRATION:WEB RESPONSIVE LAYOUT

DEAL WITH MANY DIFFERENT SCREEN SIZES

STANDOUT - FLOATING WINDOWS

ACTION BAR

ONE OF WORST EXAMPLES OF API DESIGN

ACTIONBAR IS NOT A DESCENDANT OF A VIEW

BLOG.PERPETUMDESIGN.COM/2011/08/STRANGE-CASE-OF-DR-ACTION-AND-

MR-BAR.HTML

ACTIONBARSHERLOCK

ACTION BAR PLUS

PROBLEM ?

AB OCCUPIES ~10% OF SCREEN SPACE

FUNCTIONALITY OFTEN REDUCED TO BRANDING

FLY-OUT MENU: A FACEBOOK SOLUTION

NOT CONSISTENT WITH THE REST OF ACTIONBAR FUNCTIONALITY

ACTIONBAR PARTS

ACTIONBAR BEHAVIOR

Touch here showsa menu with options

Touch here moves up in hierarchy

Touch here performs action

Touch here show a menu with options

Any extension should stay consistent with this

DON’T CONFUSE YOUR USERS

OWN APPS’D USE SOME UX FACELIFT

STILL WANT MORE FROM THE ACTIONBAR AREA ?

ACTION BAR PLUSCUSTOM ACTIONBAR IMPLEMENTATION

EMBRACE EXTRA DIMENSION

TWO TYPES OF NAVIGATION

MAKE SURE THERE IS DEAD AREA TO SEPARATE FROM NOTIFICATION BAR

GESTURE

USE MIDDLE AS BONUS CONTENT

SUCH AS STUFF OUTSIDE THE APP

2D JUST ON OVERFLOWACTIONS ARE EITHER TOUCH

OR SWIPE DOWN

ACCESS TO HELP ON ACTIONS

EMBRACE MULTITOUCH

EASY SPLIT VIEW FEATURE

BROADCAST THE AVAILABLE SCREEN

actiwerks.intent.splitviewDESCRIPTION OF THE INTENT APPEARS SOON ON THE OPENINTENTS.ORG

BROADCAST DETAILS

private Intent splitIntent;

splitIntent = new Intent();splitIntent.setAction("actiwerks.intent.splitview");

splitIntent.removeExtra("APP_SPLIT_SIZE");splitIntent.putExtra("APP_SPLIT_SIZE", windowVerticalOffset);getContext().sendBroadcast(splitIntent);

RECEIVE THE BROADCAST

<receiver android:name="AppSplitViewReceiver" ><intent-filter>

<action android:name="actiwerks.intent.splitview" /></intent-filter>

</receiver>

public class AppSplitViewReceiver extends BroadcastReceiver {

@Override public void onReceive(Context context, Intent intent) { if(intent.getAction() != null && intent.getAction().equals("actiwerks.intent.splitview")) { int peekSize = intent.getIntExtra("APP_SPLIT_SIZE", -1); // Handle the intent in the app, resize the window/layout accordingly } }

}

ACTIONBARPLUS IN THE GITHUB

FUN WITH THE LIST VIEW

PROBLEM ?

ARRAYADAPTER API NOT DESIGNEDFOR GENERIC VIEWGROUP

interface ViewAdapterBinder<T, V> {public void bindViewToData(V view, T data);

}

public class ArrayViewGroupAdapter<T, V extends ViewGroup> extends ArrayAdapter<T>

BIND DATA TO VIEW

public View getView(int position, View convertView, ViewGroup parent){ // assign the view we are converting to a local variable V v = null; try { v = (V) convertView; } catch(ClassCastException ccex) {}

// safe to ignore, keep null to force new instance to be created

// first check to see if the view is null. if so, we have to inflate it. // to inflate it basically means to render, or show, the view. if (v == null) { v = getInstanceOfV(); } T data = getItem(position); if (data != null && binder != null) { binder.bindViewToData(v, data); } else { // signal error here prn.log("Can't bind data to view " + position); } // the view must be returned to our activity return v;

}

private V getInstanceOfV() {

ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();

Class<V> type = (Class<V>) superClass.getActualTypeArguments()[1];

try { return type.getDeclaredConstructor(Context.class).newInstance(getContext());

} catch (Exception ex) {// Oops, no default constructorthrow new RuntimeException(ex);

}

}

public class SampleArrayAdapter extends ArrayViewGroupAdapter<SampleData, SampleListItem> {

public SampleArrayAdapter(Context context) { super(context, new ArrayViewGroupAdapter.ViewAdapterBinder<SampleData, SampleListItem>() { @Override public void bindViewToData(SampleListItem view, SampleData data) { view.setTitle(data.getTitle()); view.setDetails(data.getDetails()); } }); }}

A TYPE-SAFE, REFACTORING FRIENDLY SOLUTION

ADVANTAGES FOR THE DEVELOPER AND THE USER

INSTANT LISTS WITH INTROSPECTION

OBJECTFORMS.COM

FLEXIBLE LAYOUT ALLOWS RESIZING

PINCH TO ZOOM GESTURE IN LISTS

TIME AND DATE PICKER

PROBLEM ?

UNLIKE OTHER WIDGETS, TIME & DATE PICKER NEEDS DIALOG

(OR PLENTY OF SPACE)

SOLUTION:SPLIT EDITING INTO SPECIALIZED

TIME & DATE KEYBOARD

DIRECT MANIPULATION GESTURE

“KEYBOARD” FOR DATE SELECTION

NO TOUCH AT ALL

KINETIC GESTURES

SHAKE GESTURE

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{ if (motion == UIEventSubtypeMotionShake) { [self showAlert]; }} //When a gesture is detected (and ended) the showAlert method is called. -(IBAction)showAlert{ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"ShakeGesture Demo" message:@"Shake detected" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show];}

AREA THAT IS NOT GETTING THE LOVE IT DESERVERS

TIM BRAY : SENSPLORE

SAMSUNG GESTURES

HELP US SHAPE THE FUTURE

http://www.kineticgestures.org

TAKEWAY

Q & A

THANK YOU

PAVEL LAHODA

PAVEL@ACTIWERKS.COM @PERPETUMDESIGN