A custom view styling library for Android that generates the obtainStyledAttributes() and TypedArray boilerplate code for you.

Overview

DEPRECATED

This project is no longer maintained. Consider using https://github.com/airbnb/paris

Build Status Android Arsenal Maven Central Barber

Barber is your personal custom view stylist.

  • Simply annotate your fields using the @StyledAttr or @AndroidAttr annotations
  • Call the appropriate Barber.style(this...) variant
  • Let Barber take care of all the boilerplate for you.
  • Profit

This library is heavily influenced by Jake Wharton's Butter Knife library, and was actually suggested to me by the man himself.

Usage

Barber has two main annotations that you use: @StyledAttr and @AndroidAttr. These can be used on fields or methods (e.g. setters). StyledAttr is used for retrieving custom attrs for custom views. @AndroidAttr is used for retrieving values for attributes in the android namespace.

The Barber class has 3 overloaded style() methods, so you can call the appropriate one from whichever constructor you prefer.

Annotated fields or methods cannot be private, and must at least be package accessible. This is because Barber will generate a **$$Barbershop class in the same package as the target class.

StyledAttr

Declare your styled attributes in your attrs.xml, like you normally would. For example:

<declare-styleable name="BarberView">
    <attr name="stripeColor" format="color" />
    <attr name="stripeCount" format="integer" />
    <attr name="animated" format="boolean" />
    <attr name="toggleAnimation" format="reference" />
</declare-styleable>
public class BarberView extends FrameLayout {

    @StyledAttr(value = R.styleable.BarberView_stripeColor, kind = Kind.COLOR)
    public int stripeColor;

    @StyledAttr(R.styleable.BarberView_stripeCount)
    public int stripeCount;

    @StyledAttr(value = R.styleable.BarberView_animated, defaultValue = R.bool.animated_default)
    public boolean isAnimated;

    public BarberView(Context context) {
        super(context);
    }

    public BarberView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BarberView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public BarberView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        Barber.style(this, attrs, R.styleable.BarberView, defStyleAttr, defStyleRes);
    }

    @StyledAttr(R.styleable.BarberView_toggleAnimation)
    public void setToggleAnimationDrawable(Drawable toggleAnimation) {
        // Do something with it
    }
}

By default, Barber will resolve which TypedArray method to use based on the type of the target. That is, if you declare it on an int, then Barber will generate code that calls typedArray.getInt(...).

@StyledAttr(R.styleable.BarberView_stripeCount)
public int stripeCount;

"But wait, sometimes my int is a color!".

If you have a special case, such as colors, then you can specify the kind member of the annotation with the appropriate Kind enum to let Barber know.

@StyledAttr(value = R.styleable.BarberView_stripeColor, kind = Kind.COLOR)
public int stripeColor;

The color example above tells Barber it should use TypedArray's getColor(...) method. This works for other types as well!

@StyledAttr(value = R.styleable.TestView_testDimension, kind = Kind.DIMEN)
public float testDimension;

@StyledAttr(value = R.styleable.TestView_testDimensionPixelSize, kind = Kind.DIMEN_PIXEL_SIZE)
public int testDimensionPixelSize;

And, if you're one of the 10 people that use fraction attributes, you'll be happy to know that those are supported as well.

@StyledAttr(
        value = R.styleable.TestView_testFractionBase,
        kind = Kind.FRACTION,
        base = 2,
        pbase = 2
)
public float testFractionBase;

See the Kind enum for a full list of supported types.

Default values

You can specify resource IDs for default values.

@StyledAttr(value = R.styleable.BarberView_animated, defaultValue = R.bool.animated_default)
public boolean isAnimated;

AndroidAttr

If you want to retrieve the value of an Android attribute, you can use @AndroidAttr to retrieve its value

@AndroidAttr("textAllCaps")
public boolean textAllCaps;

Like StyledAttr, the normal behavior is to return the type of the field/param. These are also subject to the same approach as @StyledAttr regarding special return types. See the AttrSetKind enum for a full list of supported types.

@AndroidAttr(value = "textColor", kind = AttrSetKind.RESOURCE)
public int textColor;

Right now it's just limited to the API of AttributeSet, but I may look into adding a more flexible API layer on top of this for coercing the returned data if people express an interest.

Required attributes

If you want to require an attribute to be specified, you can use the @Required annotation as well.

@Required
@StyledAttr(R.styleable.RequiredTestView_requiredString)
public String requiredString;

Now, if a view is inflated without specifying this attribute, its generated $$Barbershop class will throw an IllegalStateException looking like this:

Missing required attribute 'requiredString' while styling 'io.sweers.barber.sample.testing.RequiredTestView'

NOTE: Due to how AttributeSet's interface works, @Required is not compatible with @AndroidAttr annotations.

Installation

buildscript {
    repositories {
        jcenter() // Also available in maven central
    }
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    apt 'io.sweers.barber:barber-compiler:1.3.1'
    compile 'io.sweers.barber:barber-api:1.3.1'
}

Proguard

If you use Proguard, consumer proguard rules are packaged in the api module AAR.

License

Copyright 2015 Henri Z. Sweers

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Add inheritance support

    Add inheritance support

    This should address #4. I was thinking I could get around this by using parameterized classes, and to my good fortune it turned out Butterknife had a pretty decent solution for this. @emilsjolander would you mind taking a look and see what you think? @denley you as well if you're interested, since you opened the issue.

    • Adopt Butterknife's approach to making all generates classes implement a IBarbershopInterface, and do class lookups of this interface rather than method lookups. This makes this next part possible.
    • Fix inheritance by determining if a generated class has a parent and then extending from the parent instead of implementing the IBarbershopInterface. This way, style() can just call super.style() to ensure that the entire hierarchy of style() methods run.

    ~~This functionally works, but I'm slightly concerned about one thing that I'm going to mull over tomorrow. Right now, it traverses the entire hierarchy. So if you have a parent with a child and grandchild, you conceivably could have the parent getting styled three times (that is, once for the parent's constructor, once for the child's, and one more for the grandchild). I'm not sure how to get around this yet though, if at all.~~ I should write better tests.

    Now, if you have a TestView and ChildTestView that extends it, Barber will generate code that looks like this:

    public class TestView$$Barbershop<T extends io.sweers.barber.sample.TestView> implements Barber.IBarbershop<T> {
      @Override
      public void style(final T target, final AttributeSet set, final int[] attrs, final int defStyleAttr, final int defStyleRes) {
        if (set == null) {
          return;
        }
        TypedArray a = target.getContext().obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes);
        if (a.hasValue(0)) {
          target.testBoolean = a.getBoolean(0, false);
        }
        a.recycle();
      }
    }
    
    public class ChildTestView$$Barbershop<T extends io.sweers.barber.sample.ChildTestView> extends TestView$$Barbershop<T> {
      @Override
      public void style(final T target, final AttributeSet set, final int[] attrs, final int defStyleAttr, final int defStyleRes) {
        super.style(target, set, attrs, defStyleAttr, defStyleRes);
        if (set == null) {
          return;
        }
        TypedArray a = target.getContext().obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes);
        if (a.hasValue(0)) {
          target.coolNumber = a.getInt(0, -1);
        }
        a.recycle();
      }
    }
    
    opened by ZacSweers 19
  • Barber is incompatible with Dagger code generation

    Barber is incompatible with Dagger code generation

    Hi -

    Tracking this, so I wanted to put in an issue. When adding Barber to a project that already uses Dagger (and dagger-compiler) for injection, dagger code generation fails to run.

    My setup is as such;

    apt 'io.sweers.barber:barber-compiler:1.0.1'
    compile 'io.sweers.barber:barber-api:1.0.1'
    compile 'com.squareup.dagger:dagger:1.2.2'
    provided 'com.squareup.dagger:dagger-compiler:1.2.2'
    
    bug help wanted 
    opened by inktomi 15
  • Infer resource kinds from support annotations

    Infer resource kinds from support annotations

    I thought this might be a good feature to have while using the library. Android support annotations provide similar functionality to the kind annotation parameter so instead of duplicating the info, this change-set enables looking at the support annotation (which people should be using anyways) and setting kind to resource type if the support annotation used is one of the @Res types. The one additional special case is the @ColorInt annotation. Which this also checks for and sets kind to COLOR if it's used. What are your thoughts?

    opened by saadfarooq 6
  • Simplify acp

    Simplify acp

    • Remove unnecessary configuration and convention
    • Move most configuration to extension
    • Provide support to get android dependencies from any 'provided' like plugin
    • When depending on project dependencies in apt configuration, also need to add a dependency on the acp configuration. This is not required once the compiler jar is packaged
    opened by kageiit 4
  • Inheritance support

    Inheritance support

    Currently, we can only annotate/style a class that is directly instantiated. It would useful to be able to maintain the extendability of custom View types.

    The problem is that a class' generated Barbershop is not invoked by subclasses. When the parent class calls Barber.style it will find the subclass' Barbershop instead of its own, because target.getClass does not refer to its own class.

    I don't see any clean fix for this, unfortunately. So perhaps it's something to discuss for now rather than fix immediately. It would probably require some change to the API, such as passing the Class as a parameter in the Barber.style method. We can't simply have a Barbershop look for its parent class' Barbershop because it won't know the correct styleable resource to pass.

    enhancement research 
    opened by denley 4
  • Defaults support

    Defaults support

    Resolves #16

    This allows for the use of a new defaultValue option in StyledAttr annotations, where you can set a resource ID to use as a default.

    The generated code will still check Required if the annotation is there. It will not resolve the default value unless there is no resource at that index (that is, it won't unnecessarily resolve it just to pass it into the second param of many of the typedarray getters).

    AndroidAttr doesn't support defaults, because it doesn't really make sense to. It would really have to force the user to only use it on setters to be practical.

    opened by ZacSweers 3
  • Default values

    Default values

    Just an idea, but I think this could work. It's not the prettiest solution, but it would at least give users and avenue to handle dynamic defaults.

    public class MyView extends View {
    
      @StyledAttr(R.styleable.MyView_my_attr)
      int myAttr;
    
      public MyView(Context context, AttributeSet set) {
          Barber.style(this, R.style.MyView, set);
      }
    
      // Annotate a method that resolves the default value
      @AttrDefault(R.styleable.MyView_myattr)
      public int getMyAttrDefault() {
          return 4;
      }
    
      // Alternatively could have a "catchAll = true" value that tells Barber 
      // that this method will handle ALL default value resolution. Need to be 
      // careful here though, as things could get sticky really fast.
      @AttrDefault(catchAll = true)
      public Object resolveDefault(int styleable) {
        switch (styleable) {
          case R.styleable.MyView_my_attr:
            return 4;
        }
      }
    
      // Generated code would look like this
      target.myAttr = a.getInt(0, target.getMyAttrDefault();
    }
    
    enhancement 
    opened by ZacSweers 2
  • Support for retrieving attributes just from context

    Support for retrieving attributes just from context

    Would need another overload for Style, and remove the hard requirement for attributeset not to be null in the generated Barbershop#style(...) method.

    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };
    
    // Want to do this boilerplate
    final TypedArray a = context.obtainStyledAttributes(ATTRS);
    mDivider = a.getDrawable(0);
    a.recycle();
    
    // Probable method signature
    style(Object target, int[] attrs);
    
    feature 
    opened by ZacSweers 1
  • Publish to Maven Central

    Publish to Maven Central

    Pretty straightforward, Bintray makes it easy to sync. Issue is the bintray-release plugin currently doesn't generate some required portions of the pom.xml (tracked in this issue. Once that's resolved, should be quick to make them available on both.

    enhancement 
    opened by ZacSweers 1
  • Explore possibly lower minSdkVersion

    Explore possibly lower minSdkVersion

    Right now the minSdkVersion is 14, but it could possibly support lower. I don't know for sure if I want to support lower versions, but something to look in to.

    research 
    opened by ZacSweers 1
  • Split off the api module into an annotations module

    Split off the api module into an annotations module

    Both the api and the compiler module now depend on the annotations module. This removes the need for the android-23.jar dependency, as well as the dual sourceSets configuration in compiler’s build.gradle.

    I was playing around with adding Butterknife as a dependency to test something and ran into a bunch of issues due to the previous setup. This seems like a saner way of doing things, in line with what other popular repos are doing (like Butterknife, for example.)

    The tests are all still successful & the sample runs and works properly.

    opened by ngsilverman 0
  • Add support for context.getTheme().resolveAttribute(...)

    Add support for context.getTheme().resolveAttribute(...)

    Not high on my priority list right now, but will consider adding if people express an interest.


    TypedValue outValue = new TypedValue();
    context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, outValue, true);
    int primaryTextColor = outValue.data;
    

    Have access to TypedValue means this could support a full range of checks to figure out its type. Doing so automatically might come at the expense of flexibility (e.g. user might want to force a particular type).

    @ResolvedAttr(android.R.attr.textColorPrimary)
    int primaryTextColor;
    

    The default could be to resolve the attribute based on TypedValue's type, with an optional specifier.

    Alternative approach.

    Simpler for code generation, but more work for the user as they would have to figure out how to mix their desired attrs with these. Would need another overload for Style, and remove the hard requirement for attributeset not to be null in the generated Barbershop#style(...) method.

    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };
    
    // Want to do this boilerplate
    final TypedArray a = context.obtainStyledAttributes(ATTRS);
    mDivider = a.getDrawable(0);
    a.recycle();
    
    // Probable method signature
    style(Object target, int[] attrs);
    
    feature 
    opened by ZacSweers 0
Releases(1.3.1)
Owner
Zac Sweers
Mobile @ Slack
Zac Sweers
Kotlin code generation for commercetools platform type-safe product-types, reference expansion and custom fields

Kotlin code generation for commercetools platform type-safe product-types, reference expansion and custom fields

null 8 Dec 15, 2022
A small tool to help you generate android projects that have a base code.

Volt Project A small tool to help you generate android projects that have a base code. Usage Change project in base directory. python volt-gen.py <pac

Victor Varenik 3 Feb 2, 2022
Codegeneration tool for isomorphic server and mobile Go apps with gRPC & Protobuf. Share code between your backend, Android & iOS app! :sun_with_face:

Anakin Codegeneration tool for isomorphic server and mobile Go apps with gRPC & Protobuf. Share code between your backend, Android & iOS app! Descript

Kirill Biakov 17 Jun 25, 2020
:package: Android Parcelables made easy through code generation.

Parceler Have a question? Ask it on StackOverflow. Found an issue? Please report it. In Android, Parcelables are a great way to serialize Java Objects

John Ericksen 3.6k Dec 27, 2022
A code generator to create Android ContentProvider

DatabaseCodeGenerator This project is a code generator written in Java used to generate Android code. Given a database schema JSON definition file, it

Foxykeep 297 Nov 25, 2022
Pure Java code generation tool for generating a fully functional ContentProvider for Android.

RoboCoP RoboCoP is a Java library that can generate a fully-functional ContentProvider from a simple JSON schema file. Get the latest version from our

Rain 246 Dec 29, 2022
A Java Code Generator for Pojo Builders

PojoBuilder - A Code Generator for Pojo Builders Author: Michael Karneim Project Homepage: http://github.com/mkarneim/pojobuilder About The PojoBuilde

Michael Karneim 330 Dec 11, 2022
Annotation Processor for generating Parcelable code

ParcelablePlease An AnnotationProcessor for generating Android Parcelable boilerplate code. See this blog entry for comparison with other parcel libra

Hannes Dorfmann 260 Mar 31, 2022
Tons of extensively featured packages for Angular, VUE and React Projects

rxweb Clean Code. Built with Purpose Contributing to rxweb framework If you are thinking to make rxweb framework better, that's truly great. You can c

null 376 Jan 4, 2023
Android Parcelable models made easy

AutoParcel AutoParcel is an AutoValue extension that enables Parcelable values generation. Just add implements Parcelable to your @AutoValue annotated

Francesco Sardo 1.4k Dec 23, 2022
A tool to generate Android ContentProviders.

Android ContentProvider Generator (acpg) A tool to generate Android ContentProviders. It takes a set of entity (a.k.a "table") definitions as the inpu

Benoit Lubek 623 Dec 13, 2022
A easy way to use android sharepreference

Favor A easy way of using Android SharedPreferences. How to use this library Using Gradle compile 'com.cocosw:favor:0.2.0@aar' Using Maven <depend

Kai Liao 467 Nov 10, 2022
Android 注解自动生成与 Flutter 通信的代码

Android-Flutter-Annotation 用于 Android 端项目,通过使用注解自动生成与 Flutter 通信的代码。 可生成的三种通信渠道有: MethodChannel EventChannel BasicMessageChannel 集成 在项目的 build.gradle

JOYY UED 4 Nov 1, 2021
Android Week View is an android library to display calendars (week view or day view) within the app. It supports custom styling.

Android Week View Android Week View is an android library to display calendars (week view or day view) within the app. It supports custom styling. Fea

Raquib-ul Alam (Kanak) 3.4k Jan 3, 2023
Android Week View is an android library to display calendars (week view or day view) within the app. It supports custom styling.

Android Week View Android Week View is an android library to display calendars (week view or day view) within the app. It supports custom styling. Fea

Raquib-ul Alam (Kanak) 3.4k Jan 3, 2023
👋 A common toolkit (utils) ⚒️ built to help you further reduce Kotlin boilerplate code and improve development efficiency. Do you think 'kotlin-stdlib' or 'android-ktx' is not sweet enough? You need this! 🍭

Toolkit [ ?? Work in progress ⛏ ?? ??️ ?? ] Snapshot version: repositories { maven("https://s01.oss.sonatype.org/content/repositories/snapshots") }

凛 35 Jul 23, 2022
The library that removes all boilerplate code allowing you to display lists with few lines of code.

VsRecyclerView The library that removes all boilerplate code allowing you to display lists with few lines of code. Gradle androidExtensions { expe

Valeriy Shtaits 13 Jun 19, 2021
Make your native android Dialog Fancy and Gify. A library that takes the standard Android Dialog to the next level with a variety of styling options and Gif's. Style your dialog from code.

FancyGifDialog-Android Prerequisites Add this in your root build.gradle file (not your module build.gradle file): allprojects { repositories { ...

Shashank Singhal 522 Jan 2, 2023
Make your native android Toasts Fancy. A library that takes the standard Android toast to the next level with a variety of styling options. Style your toast from code.

FancyToast-Android Prerequisites Add this in your root build.gradle file (not your module build.gradle file): allprojects { repositories { ... ma

Shashank Singhal 1.2k Dec 26, 2022
Make your native android Dialog Fancy. A library that takes the standard Android Dialog to the next level with a variety of styling options. Style your dialog from code.

FancyAlertDialog-Android Prerequisites Add this in your root build.gradle file (not your module build.gradle file): allprojects { repositories { ..

Shashank Singhal 350 Dec 9, 2022