{"id":19260771,"url":"https://github.com/evant/sres","last_synced_at":"2025-04-21T16:32:14.276Z","repository":{"id":14889146,"uuid":"17612833","full_name":"evant/sres","owner":"evant","description":"Super-Duper Android Layout Preprocessor","archived":false,"fork":false,"pushed_at":"2020-06-14T22:52:13.000Z","size":396,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-01T14:38:20.767Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/evant.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-03-11T00:00:17.000Z","updated_at":"2018-11-30T12:27:55.000Z","dependencies_parsed_at":"2022-07-12T15:13:35.614Z","dependency_job_id":null,"html_url":"https://github.com/evant/sres","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evant%2Fsres","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evant%2Fsres/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evant%2Fsres/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evant%2Fsres/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evant","download_url":"https://codeload.github.com/evant/sres/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250091042,"owners_count":21373307,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T19:22:51.249Z","updated_at":"2025-04-21T16:32:13.593Z","avatar_url":"https://github.com/evant.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"sres\n====\n**Warning! This is incomplete and not activly worked on. Instead, I'm planning or released the features of this as seperated, smaller libraries, check out [Holdr](https://github.com/evant/holdr) instead.**\n\nSuper-Duper Android Layout Preprocessor\n\nsres simplifies Android Layout creating and usage by using a more light-weight dsl for creating\nlayyouts and by generating code to help use them.\n\n\n\nInstall\n-------\n\nAdd the following to you're build.gradle:\n```groovy\nbuildscript {\n    dependencies {\n        classpath 'me.tatarka.sres.gradle:gradle-sres:1.0-SNAPSHOT'\n    }\n}\n\napply plugin: 'android'\napply plugin: 'sres'\n\ndependencies {\n    compile project(':sres-android')\n}\n```\n\nUsage\n-----\n\n### Layout Syntax\n\nInstead of putting you layouts in `res`, you put them in `sres`.\n\nHere is a simple layout file with a `LinearLayout` that has a `TextView` and a `Button`.\n\n```\n// sample.sres\nLinearLayout(match, match) {\n    orientation = vertical\n\n    TextView(match, wrap) {\n        @+id/text\n        text = @string/hello_world\n    }\n\n    Button(match, 20dp) {\n        @+id/button\n        text = \"Press Me!\"\n        background = @color/button_color\n    }\n}\n```\n\nAttributes are specified with `key = value` and nesting views is done by simply placing them inside\ntheir parent's block. Since `layout_width` and `layout_height` are required on every view, they get\nspecial treatment inside parenthesis following the view name. Also, since id, is so common, you can\nsimple say `@+id/my_id` instead of `id = @+id/my_id`.\n\nYou may notice there are no namespaces specified. The default namespace `android`. You can also\nspecify another namespace with `namespace:key = value`. Built-in are `tools` and `app` for res-auto.\nYou can still specify your own namespaces with `xmlns:my_namespace = ...`.\n\n### In Code\n\nThis layout will not only create `sample.xml` but also `layout.Sample.java`. The later is a custom\nview you can use to easily get at the children with id's without `findViewById()` calls.\n\nFor example, the above layout will generate:\n\n```java\npublic class Sample extends LinearLayout {\n    public android.widget.TextView text;\n    public android.widget.Button button;\n\n    public ListItem(Context context) {\n        this(context, null);\n    }\n\n    public ListItem(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public ListItem(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    public void onFinishInflate() {\n        super.onFinishInflate();\n        text = ((android.widget.TextView) findViewById(R.id.text));\n        button = ((android.widget.Button) findViewById(R.id.button));\n    }\n}\n```\n\nThis way, in code you can do:\n```java\npublic void onCreate() {\n    setContentView(R.layout.sample);\n    layout.Sample sampleView = (layout.Sample) findViewById(android.R.id.content);\n\n    sampleView.text.setText(\"Hello, World!\");\n    sampleView.button.setOnClickListener(new View.OnClickListener() {\n        @Override\n        public void onClick(View v) {\n            Log.d(\"MyApp\", \"Button Clicked!\");\n        }\n    });\n}\n```\n\nYou can also have the layout inflate your own custom view so that you can customize it further.\n```\n// sample.sres\ncom.example.MyView\u003cLinearLayout(match, match) {\n    ...\n}\n```\n\n```java\n// com.example.MyView.java\npublic class MyView extends layout.Sample {\n    public ListItem(Context context) {\n        this(context, null);\n    }\n\n    public ListItem(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public ListItem(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    public void onFinishInflate() {\n        super.onFinishInflate();\n        // Do what you want\n    }\n}\n```\n\n### Data Binding\n\nYou can bind a model class to a layout to save you even more boilerplate. For example, if you had\na model like:\n\n```java\n// com.example.MyModel.java\npublic class MyModel {\n    public String title;\n\n    private String buttonText = \"Press Me!\";\n\n    public String getButtonText() {\n        return buttonText;\n    }\n}\n```\n\nYou can do:\n\n```\n// sample.sres\nLinearLayout(match, match) {\n    bind:class = com.example.MyModel // This is required for any data-binding\n    orientation = vertical\n\n    TextView(match, wrap) {\n        @+id/text\n        bind:text = title\n    }\n\n    Button(match, 20dp) {\n        @+id/button\n        bind:text = getButtonText()\n    }\n}\n```\n\nYou then populate the view with:\n\n```java\nMyModel myModel = new MyModel();\nsampleView.bind(myModel);\n```\n\n#### Listening For Changes\n\nFor the simplest use case, you don't have to change your model object at all to participate in\ndata-binding. However, if you want your view to update when you change your model, there is a little\ninstrumentation that you must do.\n\n```java\n// com.example.MyModel.java\npublic class MyModel {\n    private Property\u003cString\u003e text = new Property\u003cString\u003e(\"Hello, World!\");\n\n    public String getText() {\n        return text.get();\n    }\n\n    public void setText(String newText) {\n        text.set(newText);\n    }\n}\n```\n\nThat's it!. Your public api for your model class doesn't have to change, and you view doesn't have\nto change. This does, of course mean that you can only bind to methods this way, not public fields.\n\nIt even works for computed properties:\n\n```java\n// com.example.MyModel.java\npublic class MyModel {\n    private IntProperty firstValue = new IntProperty(1);\n    private IntProperty secondValue = new IntProperty(2);\n\n    public void setFirst(int value) {\n        firstValue.set(value);\n    }\n\n    public void setSecond(int value) {\n        secondValue.set(value);\n    }\n\n    public String getSum() {\n        return Integer.toString(firstValue.get() + secondValue.get());\n    }\n}\n```\n\n```\n// sample.xml\nTextView(match, wrap) {\n    bind:class = com.example.MyModel\n    bind:text = getSum() // This will be updated when either firstValue or secondValue changes.\n}\n```\n\nHow does this work? When the model is bound to the view, it will detect all the properties in which\n`get()` is called, and then attach listeners to those properties. Keeping this in mind, you _must_\nalways call `get()` on all properties you expect to use even if they are not used in that particular\ncase.\n\nFor example:\n```java\nBooleanProperty isFirst = new BooleanProperty(true);\nProperty\u003cString\u003e first = new Property\u003cString\u003e(\"first\");\nProperty\u003cString\u003e second = new Property\u003cString\u003e(\"second\");\n\n// Wrong!\npublic String getFirstOrSecond() {\n    if (isFirst.get()) {\n        return first.get();\n    } else {\n        // This will never be resisted for changes because it's not called on the first time.\n        return second.get();\n    }\n}\n\n// Right!\npublic String getFirstOrSecond() {\n    String firstString = first.get();\n    String secondString = second.get(); // get() is always called so were are good.\n\n    if (isFirst.get()) {\n        return firstString;\n    } else {\n        return secondString;\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevant%2Fsres","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevant%2Fsres","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevant%2Fsres/lists"}