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

Scaling UI

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for jbarr21 jbarr21
July 27, 2018

Scaling UI

Avatar for jbarr21

jbarr21

July 27, 2018
Tweet

More Decks by jbarr21

Other Decks in Technology

Transcript

  1. Before & After <color name="ub__black">#000000</color> <color name="ub__uber_black_100">#09091A</color> <color name="ub__uber_black_95">#151525</color> <color

    name="ub__uber_black_90">#222231</color> <color name="ub__uber_black_80">#3A3A48</color> <color name="ub__uber_black_60">#6B6B76</color> <color name="ub__uber_black_40">#9D9DA3</color> <color name="ub__white">#FFFFFF</color> <color name="ub__uber_white_120">#B2B2BA</color> <color name="ub__uber_white_100">#C0C0C8</color> <color name="ub__uber_white_80">#CDCDD3</color> <color name="ub__uber_white_60">#D9D9DE</color> <color name="ub__uber_white_40">#E6E6E9</color> <color name="ub__uber_white_20">#F2F2F4</color> <color name="ub__uber_white_10">#F9F9F9</color> <color name="ub__uber_blue_120">#1EACC7</color> <color name="ub__uber_blue_100">#1FBAD6</color> <color name="ub__uber_blue_100_semi_transparent">#661FBAD6</color> <color name="ub__uber_blue_80">#4CC8DE</color> <color name="ub__uber_blue_60">#79D6E6</color> <color name="ub__uber_blue_40">#A5E3EF</color> <color name="ub__uber_blue_20">#D2F1F7</color> ...
  2. Lint class ColorResourceUsageDetector : ResourceXmlDetector() { override fun visitAttribute(context: XmlContext,

    attr: Attr) { if (attr.name in COLOR_ATTRIBUTES { if (attr.value.startsWith(PREFIX_COLOR_RES) && attr.value !in WHITELISTED_COLORS) { when { isInResFolder(LAYOUT) && !isColorSelector(attr.value) -> reportError() isInVectorDrawable(attribute) -> reportError() isInResFolder(context, DRAWABLE) && !hasQualifiedVersion(QUALIFIER_V21) && !isColorSelector(context, attributeVal) -> reportError() } } else if (attr.value.startsWith(PREFIX_THEME_ATTR) && isInResFolder(DRAWABLE) && !isInVectorDrawable(attribute) && !isQualifiedVersion(context)) { reportError() } } } }
  3. Font Updates 1. Add new font asset 2. Update font

    name -- <string name="font_path">fonts/Roboto.ttf</string> ++ <string name="font_path">fonts/GoogleSans.ttf</string> 3. ✅ ? src/main/res/value/strings-fonts.xml
  4. ViewPump override fun onCreateView( name: String, context: Context, attrs: AttributeSet):

    View { return ViewPump.get().inflate(InflateRequest.builder() .name(name) .context(context) .attrs(attrs) .fallbackViewCreator(mViewCreator) .build()).view() } ViewPumpLayoutInflater.java
  5. ViewPump override fun onCreateView( name: String, context: Context, attrs: AttributeSet):

    View { return ViewPump.get().inflate(InflateRequest.builder() .name(name) .context(context) .attrs(attrs) .fallbackViewCreator(mViewCreator) .build()).view() } ViewPumpLayoutInflater.java
  6. ViewPump Interceptors class TextUpdatingInterceptor() : Interceptor { override fun intercept(chain:

    Chain): InflateResult { val result: InflateResult = chain.proceed(chain.request()) val view: View = result.view() if (view is TextView) { // You also have access to result.context and result.attrs view.text = "[Prefix] ${view.text}" } return result } }
  7. Calligraphy class CalligraphyInterceptor( val calligraphy: Calligraphy) : Interceptor { override

    fun intercept(chain: Chain): InflateResult { val result: InflateResult = chain.proceed(chain.request()) return result.toBuilder() .view( calligraphy.onViewCreated( result.view(), result.context(), result.attrs())) .build() } }
  8. Reimplement AppCompat class AppCompatInterceptor : Interceptor { override fun intercept(chain:

    Interceptor.Chain): InflateResult { val request = chain.request() val viewNameToInflate = viewNameToInflate(request.name()) return chain.proceed(request.toBuilder().name(viewNameToInflate).build()) } fun viewNameToInflate(requestedViewName: String) = when (requestedViewName) { "Button" -> AppCompatButton::class.java.name "EditText" -> AppCompatEditText::class.java.name "ImageView" -> AppCompatImageView::class.java.name "TextView" -> AppCompatTextView::class.java.name else -> requestedViewName } }
  9. Reimplement AppCompat class AppCompatInterceptor : Interceptor { override fun intercept(chain:

    Interceptor.Chain): InflateResult { val request = chain.request() val viewNameToInflate = viewNameToInflate(request.name()) return chain.proceed(request.toBuilder().name(viewNameToInflate).build()) } fun viewNameToInflate(requestedViewName: String) = when (requestedViewName) { "Button" -> AppCompatButton::class.java.name "EditText" -> AppCompatEditText::class.java.name "ImageView" -> AppCompatImageView::class.java.name "TextView" -> AppCompatTextView::class.java.name else -> requestedViewName } }
  10. Hide Unaccessible Views class AccessibilityEncouragingInterceptor : Interceptor { override fun

    intercept(chain: Interceptor.Chain): InflateResult { val result = chain.proceed(chain.request()) result.view()?.let { if (it.isClickable && it.contentDescription.isNullOrEmpty()) { it.visibility = View.INVISIBLE } } return result } }
  11. Other Example Usages • Geocities-ify an app • Draw over

    views under some conditions • Backport functionality for new attrs • Dynamic string reloading • Track recently inflated views & add data to crash reports
  12. Artist A build tool that codegens a base set of

    Android Views github.com/uber/artist
  13. Artist Trait @AutoService(Trait::class) class VisibilityTrait : Trait { override fun

    generateFor( type: TypeSpec.Builder, initMethod: MethodSpec.Builder, rClass: ClassName, baseType: String) { arrayOf("visible", "invisible", "gone").forEach { type.addMethod(createVisibilityConvenienceMethod(it)) } } ... }
  14. Artist Trait @AutoService(Trait::class) class VisibilityTrait : Trait { ... fun

    createVisibilityConvenienceMethod(type: String): MethodSpec { return MethodSpec.methodBuilder("is${type.capitalize()}") .addModifiers(Modifier.PUBLIC) .returns(TypeName.BOOLEAN) .addStatement( "return getVisibility() == \$T.${type.toUpperCase()}", TypeNames.Android.View) .build() } }
  15. Artist Generated View public class MyTextView extends AppCompatTextView { public

    MyTextView(Context context) { this(context, null); } public MyTextView(Context context, AttributeSet attrs) { this(context, attrs, R.attr.textViewStyle); } public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr, 0); } // protected init method - provided in every stencil public boolean isVisible() { return getVisibility() == View.VISIBLE; } public boolean isGone() { return getVisibility() == View.GONE; } public boolean isInvisible() { return getVisibility() == View.INVISIBLE; } }
  16. @AutoService(ViewStencilProvider.class) class SampleViewStencilProvider : ViewStencilProvider { override fun stencils() =

    setOf( ViewStencil("android.support.v7.widget.AppCompatButton", 3, "buttonStyle"), ViewStencil("android.support.v7.widget.AppCompatEditText", 3, "android.R.attr.editTextStyle", TextInputTrait.class), ViewStencil("android.widget.LinearLayout", 3), ViewStencil("android.support.v7.widget.AppCompatImageView", 3), ViewStencil("android.support.v4.widget.NestedScrollView", 3, ScrollableTrait.class), ViewStencil("android.support.v7.widget.AppCompatTextView", 3, "android.R.attr.textViewStyle") ) override fun globalTraits() = setOf( VisibilityTrait.class::java, ForegroundTrait.class::java, ViewTrait.class::java ) }
  17. Artist Use Cases • Make Rx APIs 1st class citizens:

    myView.clicks() • Add built-in analytics to taps and impressions • Make a bugfix to MyTextView and all other usages will receive it • Easily change all of your parent classes from AppCompatWidget to MaterialWidget
  18. Wrapping Up • Collaborate with Design to create a system

    that works • Use semantic names for colors & styles flexibility across apps • Enforce your design system with Lint & ErrorProne • Use ViewPump to post-process inflated views • Use Artist to easily maintain your base set of views