Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Crafting Custom Android Views

Crafting Custom Android Views

The Android framework is bundled with a large set of UI widgets (aka Views) that may be used to create basic UIs. Using the provided widgets is often a good starting point but one may rapidly be confronted to the lack of possibilities. Fortunately, some options do exist and mainly consist on mastering the View class and its hierarchy.

In this talk, we will focus on pushing the Android UI framework to the next level. We will demonstrate Android lets you go beyond your imagination by improving the existing widgets, creating compound controls and/or crafting completely new Views. If you want to build insanely innovative and advanced UI on Android and want to delight your users, this talk is definitely for you!

Cyril Mottier

April 25, 2013
Tweet

More Decks by Cyril Mottier

Other Decks in Programming

Transcript

  1. The Android SDK is bundled with several built-in Views TextView

    ImageView Button View ViewGroup LinearLayout RelativeLayout ProgressBar ViewStub
  2. 1 Factorize Optimize Innovate Extend Avoid creating the same snippets

    of code over and over again over over over over over ove
  3. static class SavedState extends BaseSavedState { int progress; SavedState(Parcelable superState)

    { super(superState); } private SavedState(Parcel in) { super(in); progress = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(progress); } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; }
  4. static class SavedState extends BaseSavedState { int progress; SavedState(Parcelable superState)

    { super(superState); } private SavedState(Parcel in) { super(in); progress = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(progress); } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; }
  5. @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState

    ss = new SavedState(superState); ss.progress = mProgress; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setProgress(ss.progress); }
  6. @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState

    ss = new SavedState(superState); ss.progress = mProgress; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setProgress(ss.progress); }
  7. @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case

    MotionEvent.ACTION_DOWN: // The first pointer went down (i.e. stop // animations and start a new drag gesture) break; case MotionEvent.ACTION_MOVE: // A pointer has moved (i.e. move the object // accordingly) break; case MotionEvent.ACTION_UP: // The last pointer went up (i.e. compute the // object’s velocity and start animating it) break; case MotionEvent.ACTION_CANCEL: // The gesture was intercepted by a parent break; } return true; }
  8. @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case

    MotionEvent.ACTION_DOWN: // The first pointer went down (i.e. stop // animations and start a new drag gesture) break; case MotionEvent.ACTION_MOVE: // A pointer has moved (i.e. move the object // accordingly) break; case MotionEvent.ACTION_UP: // The last pointer went up (i.e. compute the // object’s velocity and start animating it) break; case MotionEvent.ACTION_CANCEL: // The gesture was intercepted by a parent break; } return true; }
  9. @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case

    MotionEvent.ACTION_DOWN: // The first pointer went down (i.e. stop // animations and start a new drag gesture) break; case MotionEvent.ACTION_MOVE: // A pointer has moved (i.e. move the object // accordingly) break; case MotionEvent.ACTION_UP: // The last pointer went up (i.e. compute the // object’s velocity and start animating it) break; case MotionEvent.ACTION_CANCEL: // The gesture was intercepted by a parent break; } return true; }
  10. @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case

    MotionEvent.ACTION_DOWN: // The first pointer went down (i.e. stop // animations and start a new drag gesture) break; case MotionEvent.ACTION_MOVE: // A pointer has moved (i.e. move the object // accordingly) break; case MotionEvent.ACTION_UP: // The last pointer went up (i.e. compute the // object’s velocity and start animating it) break; case MotionEvent.ACTION_CANCEL: // The gesture was intercepted by a parent break; } return true; }
  11. @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case

    MotionEvent.ACTION_DOWN: // The first pointer went down (i.e. stop // animations and start a new drag gesture) break; case MotionEvent.ACTION_MOVE: // A pointer has moved (i.e. move the object // accordingly) break; case MotionEvent.ACTION_UP: // The last pointer went up (i.e. compute the // object’s velocity and start animating it) break; case MotionEvent.ACTION_CANCEL: // The gesture was intercepted by a parent break; } return true; }
  12. @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case

    MotionEvent.ACTION_DOWN: // The first pointer went down (i.e. stop // animations and start a new drag gesture) break; case MotionEvent.ACTION_MOVE: // A pointer has moved (i.e. move the object // accordingly) break; case MotionEvent.ACTION_UP: // The last pointer went up (i.e. compute the // object’s velocity and start animating it) break; case MotionEvent.ACTION_CANCEL: // The gesture was intercepted by a parent break; } return true; }
  13. 1Track velocity @Override public boolean onTouchEvent(MotionEvent event) { if (mVelocityTracker

    == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); switch (event.getAction()) { case MotionEvent.ACTION_UP: mVelocityTracker.computeCurrentVelocity(1000); if (mVelocityTracker.getXVelocity() > mThreshold) { // Animate the object } // no break; case MotionEvent.ACTION_CANCEL: mVelocityTracker.recycle(); mVelocityTracker = null; break; } return true; }
  14. 1Track velocity @Override public boolean onTouchEvent(MotionEvent event) { if (mVelocityTracker

    == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); switch (event.getAction()) { case MotionEvent.ACTION_UP: mVelocityTracker.computeCurrentVelocity(1000); if (mVelocityTracker.getXVelocity() > mThreshold) { // Animate the object } // no break; case MotionEvent.ACTION_CANCEL: mVelocityTracker.recycle(); mVelocityTracker = null; break; } return true; }
  15. 1Track velocity @Override public boolean onTouchEvent(MotionEvent event) { if (mVelocityTracker

    == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); switch (event.getAction()) { case MotionEvent.ACTION_UP: mVelocityTracker.computeCurrentVelocity(1000); if (mVelocityTracker.getXVelocity() > mThreshold) { // Animate the object } // no break; case MotionEvent.ACTION_CANCEL: mVelocityTracker.recycle(); mVelocityTracker = null; break; } return true; }
  16. 1Track velocity @Override public boolean onTouchEvent(MotionEvent event) { if (mVelocityTracker

    == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); switch (event.getAction()) { case MotionEvent.ACTION_UP: mVelocityTracker.computeCurrentVelocity(1000); if (mVelocityTracker.getXVelocity() > mThreshold) { // Animate the object } // no break; case MotionEvent.ACTION_CANCEL: mVelocityTracker.recycle(); mVelocityTracker = null; break; } return true; }
  17. 1Track velocity @Override public boolean onTouchEvent(MotionEvent event) { if (mVelocityTracker

    == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); switch (event.getAction()) { case MotionEvent.ACTION_UP: mVelocityTracker.computeCurrentVelocity(1000); if (mVelocityTracker.getXVelocity() > mThreshold) { // Animate the object } // no break; case MotionEvent.ACTION_CANCEL: mVelocityTracker.recycle(); mVelocityTracker = null; break; } return true; }
  18. 2 Scroll/fling to state of rest private final Runnable mScrollRunnable

    = new Runnable() { @Override public void run() { if (mOverScroller.computeScrollOffset()) { final int x = mOverScroller.getCurrX(); final int y = mOverScroller.getCurrY(); // Move object to (x, y). postOnAnimation(this); } else { // animation is over } } }; public void fling() { mOverScroller.fling( mStartX, mStartY, // start x/y mVX, mVY, // velocity x/y mMinX, mMaxX, // min/max x mMinY, mMaxY); // min/max y postOnAnimation(mScrollRunnable); }
  19. 2 Scroll/fling to state of rest private final Runnable mScrollRunnable

    = new Runnable() { @Override public void run() { if (mOverScroller.computeScrollOffset()) { final int x = mOverScroller.getCurrX(); final int y = mOverScroller.getCurrY(); // Move object to (x, y). postOnAnimation(this); } else { // animation is over } } }; public void fling() { mOverScroller.fling( mStartX, mStartY, // start x/y mVX, mVY, // velocity x/y mMinX, mMaxX, // min/max x mMinY, mMaxY); // min/max y postOnAnimation(mScrollRunnable); }
  20. 2 Scroll/fling to state of rest private final Runnable mScrollRunnable

    = new Runnable() { @Override public void run() { if (mOverScroller.computeScrollOffset()) { final int x = mOverScroller.getCurrX(); final int y = mOverScroller.getCurrY(); // Move object to (x, y). postOnAnimation(this); } else { // animation is over } } }; public void fling() { mOverScroller.fling( mStartX, mStartY, // start x/y mVX, mVY, // velocity x/y mMinX, mMaxX, // min/max x mMinY, mMaxY); // min/max y postOnAnimation(mScrollRunnable); }
  21. 2 Scroll/fling to state of rest private final Runnable mScrollRunnable

    = new Runnable() { @Override public void run() { if (mOverScroller.computeScrollOffset()) { final int x = mOverScroller.getCurrX(); final int y = mOverScroller.getCurrY(); // Move object to (x, y). postOnAnimation(this); } else { // animation is over } } }; public void fling() { mOverScroller.fling( mStartX, mStartY, // start x/y mVX, mVY, // velocity x/y mMinX, mMaxX, // min/max x mMinY, mMaxY); // min/max y postOnAnimation(mScrollRunnable); }
  22. 2 Scroll/fling to state of rest private final Runnable mScrollRunnable

    = new Runnable() { @Override public void run() { if (mOverScroller.computeScrollOffset()) { final int x = mOverScroller.getCurrX(); final int y = mOverScroller.getCurrY(); // Move object to (x, y). postOnAnimation(this); } else { // animation is over } } }; public void fling() { mOverScroller.fling( mStartX, mStartY, // start x/y mVX, mVY, // velocity x/y mMinX, mMaxX, // min/max x mMinY, mMaxY); // min/max y postOnAnimation(mScrollRunnable); }
  23. 2 Scroll/fling to state of rest private final Runnable mScrollRunnable

    = new Runnable() { @Override public void run() { if (mOverScroller.computeScrollOffset()) { final int x = mOverScroller.getCurrX(); final int y = mOverScroller.getCurrY(); // Move object to (x, y). postOnAnimation(this); } else { // animation is over } } }; public void fling() { mOverScroller.fling( mStartX, mStartY, // start x/y mVX, mVY, // velocity x/y mMinX, mMaxX, // min/max x mMinY, mMaxY); // min/max y postOnAnimation(mScrollRunnable); }
  24. @Override public void draw(Canvas canvas) { super.draw(canvas); if (!mTopEdgeEffect.isFinished()) {

    canvas.save(); canvas.translate(getPaddingLeft(), 0); final int w = getWidth() - getPaddingLeft() - getPaddingRight(); mTopEdgeEffect.setSize(w, getHeight()); if (mTopEdgeEffect.draw(canvas)) { postInvalidateOnAnimation(); } canvas.restore(); } }
  25. @Override public void draw(Canvas canvas) { super.draw(canvas); if (!mTopEdgeEffect.isFinished()) {

    canvas.save(); canvas.translate(getPaddingLeft(), 0); final int w = getWidth() - getPaddingLeft() - getPaddingRight(); mTopEdgeEffect.setSize(w, getHeight()); if (mTopEdgeEffect.draw(canvas)) { postInvalidateOnAnimation(); } canvas.restore(); } }
  26. @Override public void draw(Canvas canvas) { super.draw(canvas); if (!mTopEdgeEffect.isFinished()) {

    canvas.save(); canvas.translate(getPaddingLeft(), 0); final int w = getWidth() - getPaddingLeft() - getPaddingRight(); mTopEdgeEffect.setSize(w, getHeight()); if (mTopEdgeEffect.draw(canvas)) { postInvalidateOnAnimation(); } canvas.restore(); } }
  27. @Override public void draw(Canvas canvas) { super.draw(canvas); if (!mTopEdgeEffect.isFinished()) {

    canvas.save(); canvas.translate(getPaddingLeft(), 0); final int w = getWidth() - getPaddingLeft() - getPaddingRight(); mTopEdgeEffect.setSize(w, getHeight()); if (mTopEdgeEffect.draw(canvas)) { postInvalidateOnAnimation(); } canvas.restore(); } }
  28. @Override public void draw(Canvas canvas) { super.draw(canvas); if (!mTopEdgeEffect.isFinished()) {

    canvas.save(); canvas.translate(getPaddingLeft(), 0); final int w = getWidth() - getPaddingLeft() - getPaddingRight(); mTopEdgeEffect.setSize(w, getHeight()); if (mTopEdgeEffect.draw(canvas)) { postInvalidateOnAnimation(); } canvas.restore(); } }
  29. @Override public void draw(Canvas canvas) { super.draw(canvas); if (!mTopEdgeEffect.isFinished()) {

    canvas.save(); canvas.translate(getPaddingLeft(), 0); final int w = getWidth() - getPaddingLeft() - getPaddingRight(); mTopEdgeEffect.setSize(w, getHeight()); if (mTopEdgeEffect.draw(canvas)) { postInvalidateOnAnimation(); } canvas.restore(); } }
  30. private void init(Context context, AttributeSet attrs, int defStyle) { mViewConfiguration

    = ViewConfiguration.get(context); final int maxFlingV = mViewConfiguration .getScaledMaximumFlingVelocity(); final int touchSlop = mViewConfiguration .getScaledTouchSlop(); // ... }
  31. private void init(Context context, AttributeSet attrs, int defStyle) { mViewConfiguration

    = ViewConfiguration.get(context); final int maxFlingV = mViewConfiguration .getScaledMaximumFlingVelocity(); final int touchSlop = mViewConfiguration .getScaledTouchSlop(); // ... }
  32. private void init(Context context, AttributeSet attrs, int defStyle) { mViewConfiguration

    = ViewConfiguration.get(context); final int maxFlingV = mViewConfiguration .getScaledMaximumFlingVelocity(); final int touchSlop = mViewConfiguration .getScaledTouchSlop(); // ... }