├── .gitignore ├── .npmignore ├── README.md ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── reactnativeandroiddesignsupport │ ├── DesignSupportPackage.java │ ├── ReactAppBarLayoutManager.java │ ├── ReactCoordinatorLayoutManager.java │ ├── ReactNestedScrollView.java │ ├── ReactNestedScrollViewHelper.java │ ├── ReactNestedScrollViewManager.java │ ├── ReactTabLayout.java │ ├── ReactTabLayoutManager.java │ ├── ReactTextInputLayoutManager.java │ └── SnackbarModule.java ├── index.js ├── lib ├── AppBarLayoutAndroid.js ├── CoordinatorLayoutAndroid.js ├── NestedScrollViewAndroid.js ├── SnackbarAndroid.js ├── TabLayoutAndroid.js └── TextInputLayoutAndroid.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | android/build 3 | android/build/* 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-android-design-support [![npm version](https://img.shields.io/npm/v/react-native-android-design-support.svg?style=flat-square)](https://www.npmjs.com/package/react-native-android-design-support) 2 | 3 | React Native wrapper for [Android Design Support Library](http://android-developers.blogspot.tw/2015/05/android-design-support-library.html), providing native Material Design to modern and also older Android devices. 4 | 5 | - [x] Snackbar 6 | - [ ] Navigation View 7 | - [ ] Floating Action Button 8 | - [x] TextInputLayout 9 | - [x] TabLayout 10 | - [x] CoordinatorLayout 11 | - [x] AppBarLayout 12 | - [ ] CollapsingToolbarLayout 13 | 14 | ## Installation 15 | 16 | - Run `npm install react-native-android-design-support --save` to install using npm. 17 | 18 | - Add the following two lines to `android/settings.gradle`: 19 | 20 | ```gradle 21 | include ':react-native-android-design-support' 22 | project(':react-native-android-design-support').projectDir = new File(settingsDir, '../node_modules/react-native-android-design-support/android') 23 | ``` 24 | 25 | - Edit `android/app/build.gradle` and add the annoated lines as below: 26 | 27 | ```gradle 28 | ... 29 | 30 | dependencies { 31 | compile fileTree(dir: "libs", include: ["*.jar"]) 32 | compile "com.android.support:appcompat-v7:23.0.1" 33 | compile "com.facebook.react:react-native:0.15.+" 34 | compile project(':react-native-android-design-support') // <- Add this line 35 | } 36 | ``` 37 | 38 | - Edit `MainActivity.java` (usually at `android/app/src/main/java/com//MainActivity.java`) and add the annoated lines as below: 39 | 40 | ```java 41 | import com.facebook.react.LifecycleState; 42 | import com.facebook.react.ReactInstanceManager; 43 | import com.facebook.react.ReactRootView; 44 | import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; 45 | import com.facebook.react.shell.MainReactPackage; 46 | import com.facebook.soloader.SoLoader; 47 | 48 | import com.reactnativeandroiddesignsupport.DesignSupportPackage; // <- Add this line 49 | 50 | public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler { 51 | 52 | ... 53 | 54 | mReactRootView = new ReactRootView(this); 55 | 56 | mReactInstanceManager = ReactInstanceManager.builder() 57 | .setApplication(getApplication()) 58 | .setBundleAssetName("index.android.bundle") 59 | .setJSMainModuleName("index.android") 60 | .addPackage(new MainReactPackage()) 61 | .addPackage(new DesignSupportPackage(this)) // <- Add this line 62 | .setUseDeveloperSupport(BuildConfig.DEBUG) 63 | .setInitialLifecycleState(LifecycleState.RESUMED) 64 | .build(); 65 | 66 | ... 67 | ``` 68 | 69 | > Make sure to update the Android Support Repository to the latest in the Android SDK Manager. 70 | > If it is not updated, the following error might occur while compiling the Android apk: 71 | > 72 | > ``` 73 | > A problem occurred configuring project ':react-native-android-design-support'. 74 | > Could not resolve all dependencies for configuration ':react-native-android-design-support'. 75 | > ``` 76 | 77 | 78 | ## Usage 79 | 80 | ### Snackbar 81 | 82 | ```js 83 | var SnackbarAndroid = require('react-native-android-design-support').SnackbarAndroid; 84 | 85 | // Show an simple Snackbar with text: 86 | SnackbarAndroid.show('Hello World!'); 87 | 88 | // Set the Snackbar duration: 89 | SnackbarAndroid.show('Hello World!', SnackbarAndroid.LONG); 90 | 91 | // Set an action and callback on the Snackbar: 92 | SnackbarAndroid.show('Hi!', SnackbarAndroid.LONG, 'Show another "Hi!"', { yo: 'ya' }, (p) => { SnackbarAndroid.show(`Hi, ${JSON.stringify(p)}`) }); 93 | ``` 94 | 95 | ### TextInputLayout 96 | 97 | ```js 98 | 99 | 100 | 101 | ``` 102 | 103 | #### Props of `TextInputLayoutAndroid` 104 | 105 | - `hint` 106 | - `hintAnimationEnabled` 107 | - `errorEnabled` 108 | - `error` 109 | - `counterEnabled` 110 | - `counterMaxLength` 111 | 112 | 113 | ### TabLayout 114 | 115 | The most common usage of `TabLayoutAndroid` is with `ViewPagerAndroid`: 116 | 117 | ```js 118 | var ViewPagerWithTabExample = React.createClass({ 119 | 120 | componentDidMount: function() { 121 | this.refs.tab.setViewPager(this.refs.viewPager); 122 | }, 123 | 124 | render: function() { 125 | return ( 126 | 127 | 134 | 135 | 136 | This is the first page. 137 | 138 | 139 | This is the second page. 140 | 141 | 142 | This is the third page. 143 | 144 | 145 | 146 | ); 147 | } 148 | }); 149 | ``` 150 | 151 | > Note that the `tabs` attribute must be provided, so that the `TabLayout` can know whether the tab titles or icons to be displayed. 152 | 153 | Also, you can use it directly by just specifying the `tabs`, or giving childrens as it tabs: 154 | 155 | ```js 156 | var TabLayoutAndroid = require('react-native-android-design-support').TabLayoutAndroid; 157 | 158 | ... 159 | 160 | render: function() { 161 | return ( 162 | 165 | 166 | First Tab 167 | 168 | 169 | Second Tab 170 | 171 | 172 | ); 173 | } 174 | 175 | ... 176 | ``` 177 | 178 | > TODO: Handle custom events while using it independently. 179 | 180 | #### Props of `TabLayoutAndroid` 181 | 182 | - `style` 183 | - `tabs`: `Array: { text: String }`: The data of the tabs, required if no chirdren is provided. 184 | - `tabMode`: `String`: The tab mode, `fixed` or `scrollable`. 185 | - `normalColor`: The text color for normal tab. 186 | - `selectedColor`: The text color for selected tab. 187 | - `selectedTabIndicatorColor`: The color for selected tab indicator. 188 | 189 | 190 | ### CoordinatorLayout 191 | 192 | The following is an example of using `` with `` and ``, which shall be the most common usage. 193 | 194 | ```js 195 | ... 196 | 197 | var { 198 | CoordinatorLayoutAndroid, 199 | AppBarLayoutAndroid, 200 | NestedScrollViewAndroid 201 | } = require('react-native-android-design-support'); 202 | 203 | var CoordinatorLayoutBasicExample = React.createClass({ 204 | 205 | componentDidMount: function() { 206 | // Set view behavior for the main content container. This will make 207 | // this.refs.contentContainer move with the 208 | // in this.refs.coordinatorLayout. 209 | this.refs.coordinatorLayout.setAppBarScrollingViewBehavior(this.refs.contentContainer); 210 | }, 211 | 212 | render: function() { 213 | return ( 214 | 215 | 216 | {/* Any elements that are directily in */} 217 | {/* will not use React Native's flexbox position. We need to */} 218 | {/* specify `layoutWidthAndroid` and `layoutHeightAndroid` */} 219 | {/* (= "matchParent" or "wrapContent") instead. */} 220 | 224 | {/* Elements directily in can have an */} 225 | {/* additional scrollFlagsAndroid prop. This must be an array */} 226 | {/* of strings, specifying the scroll flags of this element. */} 227 | {/* see https://goo.gl/RiAyDX for reference. */} 228 | 229 | Hi, I will be collapsed when scrolling down, and show again when scrolled to top. 230 | 231 | 232 | Hi, I will be collapsed when scrolling down, and show again when scrolling up. 233 | 234 | 235 | Hi, I will not be collapsed. 236 | 237 | 238 | 244 | {/* We'll need to use because the */} 245 | {/* default dosen't support CoordinatorLayout. */} 246 | 's 248 | // heignt by ourself. Normally this will be 249 | // React.Dimensions.get('window').height - . 250 | height={React.Dimensions.get('window').height - (18)} 251 | > 252 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas feugiat tortor eget ligula luctus, eget porttitor purus imperdiet. Proin accumsan erat non accumsan convallis. Donec a cursus libero. Proin varius metus vitae nisl ornare tempor. Donec leo libero, scelerisque non lorem nec, dapibus pharetra elit. Maecenas ultrices dui nec euismod convallis. Quisque facilisis elementum luctus. Aenean tempus dui sed elit ultrices egestas. Phasellus consectetur ac ipsum nec eleifend. Donec et leo neque. In auctor, est eu placerat efficitur, tortor mauris congue urna, a rutrum dui ex id purus. Curabitur eget ultrices purus. Curabitur vulputate justo lacus, vitae varius nulla ullamcorper et. Donec consectetur molestie mollis. 253 | Quisque ac pretium nisi. Phasellus non diam vitae velit dictum ultricies. Praesent vel quam enim. Curabitur id turpis nec ante consequat dignissim. Pellentesque ac augue felis. Curabitur pulvinar viverra iaculis. Mauris vitae consequat mi. Fusce feugiat ac risus ac fringilla. Praesent sed diam nec sem porta scelerisque. Maecenas euismod sed tellus sodales iaculis. Vivamus sit amet felis vitae enim auctor sodales. Duis eleifend nec orci id laoreet. Nulla ullamcorper et est non aliquet. Etiam posuere urna eget ipsum malesuada dapibus. Aliquam ligula velit, venenatis id magna ac, luctus dignissim sem. 254 | Suspendisse a nulla imperdiet, blandit nunc nec, consequat massa. Proin fermentum sem sapien, sed fringilla tellus dapibus ut. Aliquam iaculis eu lorem non feugiat. Nulla dignissim erat et mi imperdiet, cursus ultrices velit eleifend. Vestibulum ac aliquam diam, et molestie felis. Vivamus facilisis ex et interdum suscipit. Phasellus dapibus rhoncus placerat. Nunc blandit leo eu volutpat pellentesque. Phasellus semper metus ut risus aliquam, vestibulum vestibulum lectus lacinia. 255 | Phasellus nec sapien sed ipsum consequat faucibus. Cras mollis leo elit, id pellentesque orci condimentum ut. Proin orci massa, feugiat id auctor quis, euismod sit amet purus. Sed et justo porta, volutpat massa vitae, auctor metus. Etiam tincidunt lacinia lectus sit amet iaculis. Proin tempor lectus at libero vestibulum, vitae auctor metus porttitor. Nunc egestas posuere elementum. Donec nec consequat velit. Quisque quis massa libero. Curabitur a sem a nisl sagittis dapibus consectetur eget nulla. Vivamus at ultricies elit, quis gravida ex. 256 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas feugiat tortor eget ligula luctus, eget porttitor purus imperdiet. Proin accumsan erat non accumsan convallis. Donec a cursus libero. Proin varius metus vitae nisl ornare tempor. Donec leo libero, scelerisque non lorem nec, dapibus pharetra elit. Maecenas ultrices dui nec euismod convallis. Quisque facilisis elementum luctus. Aenean tempus dui sed elit ultrices egestas. Phasellus consectetur ac ipsum nec eleifend. Donec et leo neque. In auctor, est eu placerat efficitur, tortor mauris congue urna, a rutrum dui ex id purus. Curabitur eget ultrices purus. Curabitur vulputate justo lacus, vitae varius nulla ullamcorper et. Donec consectetur molestie mollis. 257 | Quisque ac pretium nisi. Phasellus non diam vitae velit dictum ultricies. Praesent vel quam enim. Curabitur id turpis nec ante consequat dignissim. Pellentesque ac augue felis. Curabitur pulvinar viverra iaculis. Mauris vitae consequat mi. Fusce feugiat ac risus ac fringilla. Praesent sed diam nec sem porta scelerisque. Maecenas euismod sed tellus sodales iaculis. Vivamus sit amet felis vitae enim auctor sodales. Duis eleifend nec orci id laoreet. Nulla ullamcorper et est non aliquet. Etiam posuere urna eget ipsum malesuada dapibus. Aliquam ligula velit, venenatis id magna ac, luctus dignissim sem. 258 | Suspendisse a nulla imperdiet, blandit nunc nec, consequat massa. Proin fermentum sem sapien, sed fringilla tellus dapibus ut. Aliquam iaculis eu lorem non feugiat. Nulla dignissim erat et mi imperdiet, cursus ultrices velit eleifend. Vestibulum ac aliquam diam, et molestie felis. Vivamus facilisis ex et interdum suscipit. Phasellus dapibus rhoncus placerat. Nunc blandit leo eu volutpat pellentesque. Phasellus semper metus ut risus aliquam, vestibulum vestibulum lectus lacinia. 259 | Phasellus nec sapien sed ipsum consequat faucibus. Cras mollis leo elit, id pellentesque orci condimentum ut. Proin orci massa, feugiat id auctor quis, euismod sit amet purus. Sed et justo porta, volutpat massa vitae, auctor metus. Etiam tincidunt lacinia lectus sit amet iaculis. Proin tempor lectus at libero vestibulum, vitae auctor metus porttitor. Nunc egestas posuere elementum. Donec nec consequat velit. Quisque quis massa libero. Curabitur a sem a nisl sagittis dapibus consectetur eget nulla. Vivamus at ultricies elit, quis gravida ex. 260 | 261 | 262 | 263 | 264 | ); 265 | }, 266 | }); 267 | 268 | ... 269 | ``` 270 | 271 | And this example includes the usage of `` and ``: 272 | 273 | ```js 274 | ... 275 | 276 | var { 277 | CoordinatorLayoutAndroid, 278 | AppBarLayoutAndroid, 279 | TabLayoutAndroid, 280 | NestedScrollViewAndroid 281 | } = require('react-native-android-design-support'); 282 | 283 | var { ViewPagerAndroid, ToolbarAndroid } = require('react-native'); 284 | 285 | var CoordinatorLayoutWithTabExample = React.createClass({ 286 | 287 | componentDidMount: function() { 288 | this.refs.tab.setViewPager(this.refs.viewPager); 289 | this.refs.coordinatorLayout.setAppBarScrollingViewBehavior(this.refs.viewPager); 290 | }, 291 | 292 | render: function() { 293 | return ( 294 | 295 | 296 | 300 | 305 | 315 | 316 | 317 | 323 | 324 | 327 | This is the first tab. 328 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas feugiat tortor eget ligula luctus, eget porttitor purus imperdiet. Proin accumsan erat non accumsan convallis. Donec a cursus libero. Proin varius metus vitae nisl ornare tempor. Donec leo libero, scelerisque non lorem nec, dapibus pharetra elit. Maecenas ultrices dui nec euismod convallis. Quisque facilisis elementum luctus. Aenean tempus dui sed elit ultrices egestas. Phasellus consectetur ac ipsum nec eleifend. Donec et leo neque. In auctor, est eu placerat efficitur, tortor mauris congue urna, a rutrum dui ex id purus. Curabitur eget ultrices purus. Curabitur vulputate justo lacus, vitae varius nulla ullamcorper et. Donec consectetur molestie mollis. 329 | Quisque ac pretium nisi. Phasellus non diam vitae velit dictum ultricies. Praesent vel quam enim. Curabitur id turpis nec ante consequat dignissim. Pellentesque ac augue felis. Curabitur pulvinar viverra iaculis. Mauris vitae consequat mi. Fusce feugiat ac risus ac fringilla. Praesent sed diam nec sem porta scelerisque. Maecenas euismod sed tellus sodales iaculis. Vivamus sit amet felis vitae enim auctor sodales. Duis eleifend nec orci id laoreet. Nulla ullamcorper et est non aliquet. Etiam posuere urna eget ipsum malesuada dapibus. Aliquam ligula velit, venenatis id magna ac, luctus dignissim sem. 330 | Suspendisse a nulla imperdiet, blandit nunc nec, consequat massa. Proin fermentum sem sapien, sed fringilla tellus dapibus ut. Aliquam iaculis eu lorem non feugiat. Nulla dignissim erat et mi imperdiet, cursus ultrices velit eleifend. Vestibulum ac aliquam diam, et molestie felis. Vivamus facilisis ex et interdum suscipit. Phasellus dapibus rhoncus placerat. Nunc blandit leo eu volutpat pellentesque. Phasellus semper metus ut risus aliquam, vestibulum vestibulum lectus lacinia. 331 | Phasellus nec sapien sed ipsum consequat faucibus. Cras mollis leo elit, id pellentesque orci condimentum ut. Proin orci massa, feugiat id auctor quis, euismod sit amet purus. Sed et justo porta, volutpat massa vitae, auctor metus. Etiam tincidunt lacinia lectus sit amet iaculis. Proin tempor lectus at libero vestibulum, vitae auctor metus porttitor. Nunc egestas posuere elementum. Donec nec consequat velit. Quisque quis massa libero. Curabitur a sem a nisl sagittis dapibus consectetur eget nulla. Vivamus at ultricies elit, quis gravida ex. 332 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas feugiat tortor eget ligula luctus, eget porttitor purus imperdiet. Proin accumsan erat non accumsan convallis. Donec a cursus libero. Proin varius metus vitae nisl ornare tempor. Donec leo libero, scelerisque non lorem nec, dapibus pharetra elit. Maecenas ultrices dui nec euismod convallis. Quisque facilisis elementum luctus. Aenean tempus dui sed elit ultrices egestas. Phasellus consectetur ac ipsum nec eleifend. Donec et leo neque. In auctor, est eu placerat efficitur, tortor mauris congue urna, a rutrum dui ex id purus. Curabitur eget ultrices purus. Curabitur vulputate justo lacus, vitae varius nulla ullamcorper et. Donec consectetur molestie mollis. 333 | Quisque ac pretium nisi. Phasellus non diam vitae velit dictum ultricies. Praesent vel quam enim. Curabitur id turpis nec ante consequat dignissim. Pellentesque ac augue felis. Curabitur pulvinar viverra iaculis. Mauris vitae consequat mi. Fusce feugiat ac risus ac fringilla. Praesent sed diam nec sem porta scelerisque. Maecenas euismod sed tellus sodales iaculis. Vivamus sit amet felis vitae enim auctor sodales. Duis eleifend nec orci id laoreet. Nulla ullamcorper et est non aliquet. Etiam posuere urna eget ipsum malesuada dapibus. Aliquam ligula velit, venenatis id magna ac, luctus dignissim sem. 334 | Suspendisse a nulla imperdiet, blandit nunc nec, consequat massa. Proin fermentum sem sapien, sed fringilla tellus dapibus ut. Aliquam iaculis eu lorem non feugiat. Nulla dignissim erat et mi imperdiet, cursus ultrices velit eleifend. Vestibulum ac aliquam diam, et molestie felis. Vivamus facilisis ex et interdum suscipit. Phasellus dapibus rhoncus placerat. Nunc blandit leo eu volutpat pellentesque. Phasellus semper metus ut risus aliquam, vestibulum vestibulum lectus lacinia. 335 | Phasellus nec sapien sed ipsum consequat faucibus. Cras mollis leo elit, id pellentesque orci condimentum ut. Proin orci massa, feugiat id auctor quis, euismod sit amet purus. Sed et justo porta, volutpat massa vitae, auctor metus. Etiam tincidunt lacinia lectus sit amet iaculis. Proin tempor lectus at libero vestibulum, vitae auctor metus porttitor. Nunc egestas posuere elementum. Donec nec consequat velit. Quisque quis massa libero. Curabitur a sem a nisl sagittis dapibus consectetur eget nulla. Vivamus at ultricies elit, quis gravida ex. 336 | 337 | 338 | 339 | 342 | This is the second tab. 343 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas feugiat tortor eget ligula luctus, eget porttitor purus imperdiet. Proin accumsan erat non accumsan convallis. Donec a cursus libero. Proin varius metus vitae nisl ornare tempor. Donec leo libero, scelerisque non lorem nec, dapibus pharetra elit. Maecenas ultrices dui nec euismod convallis. Quisque facilisis elementum luctus. Aenean tempus dui sed elit ultrices egestas. Phasellus consectetur ac ipsum nec eleifend. Donec et leo neque. In auctor, est eu placerat efficitur, tortor mauris congue urna, a rutrum dui ex id purus. Curabitur eget ultrices purus. Curabitur vulputate justo lacus, vitae varius nulla ullamcorper et. Donec consectetur molestie mollis. 344 | Quisque ac pretium nisi. Phasellus non diam vitae velit dictum ultricies. Praesent vel quam enim. Curabitur id turpis nec ante consequat dignissim. Pellentesque ac augue felis. Curabitur pulvinar viverra iaculis. Mauris vitae consequat mi. Fusce feugiat ac risus ac fringilla. Praesent sed diam nec sem porta scelerisque. Maecenas euismod sed tellus sodales iaculis. Vivamus sit amet felis vitae enim auctor sodales. Duis eleifend nec orci id laoreet. Nulla ullamcorper et est non aliquet. Etiam posuere urna eget ipsum malesuada dapibus. Aliquam ligula velit, venenatis id magna ac, luctus dignissim sem. 345 | Suspendisse a nulla imperdiet, blandit nunc nec, consequat massa. Proin fermentum sem sapien, sed fringilla tellus dapibus ut. Aliquam iaculis eu lorem non feugiat. Nulla dignissim erat et mi imperdiet, cursus ultrices velit eleifend. Vestibulum ac aliquam diam, et molestie felis. Vivamus facilisis ex et interdum suscipit. Phasellus dapibus rhoncus placerat. Nunc blandit leo eu volutpat pellentesque. Phasellus semper metus ut risus aliquam, vestibulum vestibulum lectus lacinia. 346 | Phasellus nec sapien sed ipsum consequat faucibus. Cras mollis leo elit, id pellentesque orci condimentum ut. Proin orci massa, feugiat id auctor quis, euismod sit amet purus. Sed et justo porta, volutpat massa vitae, auctor metus. Etiam tincidunt lacinia lectus sit amet iaculis. Proin tempor lectus at libero vestibulum, vitae auctor metus porttitor. Nunc egestas posuere elementum. Donec nec consequat velit. Quisque quis massa libero. Curabitur a sem a nisl sagittis dapibus consectetur eget nulla. Vivamus at ultricies elit, quis gravida ex. 347 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas feugiat tortor eget ligula luctus, eget porttitor purus imperdiet. Proin accumsan erat non accumsan convallis. Donec a cursus libero. Proin varius metus vitae nisl ornare tempor. Donec leo libero, scelerisque non lorem nec, dapibus pharetra elit. Maecenas ultrices dui nec euismod convallis. Quisque facilisis elementum luctus. Aenean tempus dui sed elit ultrices egestas. Phasellus consectetur ac ipsum nec eleifend. Donec et leo neque. In auctor, est eu placerat efficitur, tortor mauris congue urna, a rutrum dui ex id purus. Curabitur eget ultrices purus. Curabitur vulputate justo lacus, vitae varius nulla ullamcorper et. Donec consectetur molestie mollis. 348 | Quisque ac pretium nisi. Phasellus non diam vitae velit dictum ultricies. Praesent vel quam enim. Curabitur id turpis nec ante consequat dignissim. Pellentesque ac augue felis. Curabitur pulvinar viverra iaculis. Mauris vitae consequat mi. Fusce feugiat ac risus ac fringilla. Praesent sed diam nec sem porta scelerisque. Maecenas euismod sed tellus sodales iaculis. Vivamus sit amet felis vitae enim auctor sodales. Duis eleifend nec orci id laoreet. Nulla ullamcorper et est non aliquet. Etiam posuere urna eget ipsum malesuada dapibus. Aliquam ligula velit, venenatis id magna ac, luctus dignissim sem. 349 | Suspendisse a nulla imperdiet, blandit nunc nec, consequat massa. Proin fermentum sem sapien, sed fringilla tellus dapibus ut. Aliquam iaculis eu lorem non feugiat. Nulla dignissim erat et mi imperdiet, cursus ultrices velit eleifend. Vestibulum ac aliquam diam, et molestie felis. Vivamus facilisis ex et interdum suscipit. Phasellus dapibus rhoncus placerat. Nunc blandit leo eu volutpat pellentesque. Phasellus semper metus ut risus aliquam, vestibulum vestibulum lectus lacinia. 350 | Phasellus nec sapien sed ipsum consequat faucibus. Cras mollis leo elit, id pellentesque orci condimentum ut. Proin orci massa, feugiat id auctor quis, euismod sit amet purus. Sed et justo porta, volutpat massa vitae, auctor metus. Etiam tincidunt lacinia lectus sit amet iaculis. Proin tempor lectus at libero vestibulum, vitae auctor metus porttitor. Nunc egestas posuere elementum. Donec nec consequat velit. Quisque quis massa libero. Curabitur a sem a nisl sagittis dapibus consectetur eget nulla. Vivamus at ultricies elit, quis gravida ex. 351 | 352 | 353 | 354 | This is the third tab. 355 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas feugiat tortor eget ligula luctus, eget porttitor purus imperdiet. Proin accumsan erat non accumsan convallis. Donec a cursus libero. Proin varius metus vitae nisl ornare tempor. Donec leo libero, scelerisque non lorem nec, dapibus pharetra elit. Maecenas ultrices dui nec euismod convallis. Quisque facilisis elementum luctus. Aenean tempus dui sed elit ultrices egestas. Phasellus consectetur ac ipsum nec eleifend. Donec et leo neque. In auctor, est eu placerat efficitur, tortor mauris congue urna, a rutrum dui ex id purus. Curabitur eget ultrices purus. Curabitur vulputate justo lacus, vitae varius nulla ullamcorper et. Donec consectetur molestie mollis. 356 | 357 | 358 | 359 | 360 | ); 361 | }, 362 | }); 363 | 364 | ... 365 | ``` 366 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 22 10 | versionCode 1 11 | versionName "1.0" 12 | ndk { 13 | abiFilters "armeabi-v7a", "x86" 14 | } 15 | } 16 | } 17 | 18 | dependencies { 19 | compile fileTree(include: ['*.jar'], dir: 'libs') 20 | compile 'com.facebook.react:react-native:0.15.+' 21 | compile 'com.android.support:appcompat-v7:23.0.0' 22 | compile 'com.android.support:support-annotations:+' 23 | compile 'com.android.support:design:23.1.1' 24 | } 25 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativeandroiddesignsupport/DesignSupportPackage.java: -------------------------------------------------------------------------------- 1 | package com.reactnativeandroiddesignsupport; 2 | 3 | import android.app.Activity; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.JavaScriptModule; 7 | import com.facebook.react.bridge.NativeModule; 8 | import com.facebook.react.bridge.ReactApplicationContext; 9 | import com.facebook.react.uimanager.ViewManager; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | /** 16 | * The React package. 17 | */ 18 | public class DesignSupportPackage implements ReactPackage { 19 | private Activity mActivity = null; 20 | 21 | public DesignSupportPackage(Activity activity) { 22 | mActivity = activity; 23 | } 24 | 25 | @Override 26 | public List createNativeModules(ReactApplicationContext reactContext) { 27 | List modules = new ArrayList<>(); 28 | 29 | modules.add(new SnackbarModule(reactContext, mActivity)); 30 | return modules; 31 | } 32 | 33 | @Override 34 | public List> createJSModules() { 35 | return Collections.emptyList(); 36 | } 37 | 38 | @Override 39 | public List createViewManagers(ReactApplicationContext reactContext) { 40 | return Arrays.asList( 41 | new ReactTextInputLayoutManager(), 42 | new ReactNestedScrollViewManager(), 43 | new ReactAppBarLayoutManager(), 44 | new ReactCoordinatorLayoutManager(), 45 | new ReactTabLayoutManager() 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativeandroiddesignsupport/ReactAppBarLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.reactnativeandroiddesignsupport; 2 | 3 | import java.util.Map; 4 | import javax.annotation.Nullable; 5 | 6 | import android.view.View; 7 | import android.support.design.widget.CoordinatorLayout; 8 | import android.support.design.widget.AppBarLayout; 9 | 10 | import com.facebook.infer.annotation.Assertions; 11 | import com.facebook.react.bridge.ReadableArray; 12 | import com.facebook.react.bridge.ReadableMap; 13 | import com.facebook.react.bridge.JSApplicationIllegalArgumentException; 14 | import com.facebook.react.common.MapBuilder; 15 | import com.facebook.react.uimanager.ViewGroupManager; 16 | import com.facebook.react.uimanager.ReactProp; 17 | import com.facebook.react.uimanager.ThemedReactContext; 18 | 19 | public class ReactAppBarLayoutManager extends ViewGroupManager { 20 | 21 | @Override 22 | public String getName() { 23 | return "RCTAppBarLayoutAndroid"; 24 | } 25 | 26 | @Override 27 | public AppBarLayout createViewInstance(ThemedReactContext context) { 28 | return new AppBarLayout(context); 29 | } 30 | 31 | public boolean needsCustomLayoutForChildren() { 32 | return true; 33 | } 34 | 35 | public static final int COMMAND_SET_CHILDREN_SCROLL_FLAGS = 1; 36 | 37 | @Override 38 | public Map getCommandsMap() { 39 | return MapBuilder.of( 40 | "setChildrenScrollFlags", 41 | COMMAND_SET_CHILDREN_SCROLL_FLAGS); 42 | } 43 | 44 | @Override 45 | public void receiveCommand(AppBarLayout view, int commandType, @Nullable ReadableArray args) { 46 | Assertions.assertNotNull(view); 47 | Assertions.assertNotNull(args); 48 | 49 | switch (commandType) { 50 | case COMMAND_SET_CHILDREN_SCROLL_FLAGS: { 51 | ReadableArray options = args.getArray(0); 52 | setChildrenScrollFlags(view, options); 53 | return; 54 | } 55 | 56 | default: 57 | throw new JSApplicationIllegalArgumentException(String.format( 58 | "Unsupported command %d received by %s.", 59 | commandType, 60 | getClass().getSimpleName())); 61 | } 62 | } 63 | 64 | @ReactProp(name = "childrenScrollFlags") 65 | public void setScrollFlags(AppBarLayout view, ReadableArray options) { 66 | this.setChildrenScrollFlags(view, options); 67 | } 68 | 69 | private void setChildrenScrollFlags(AppBarLayout view, ReadableArray options) { 70 | try { 71 | int optionSize = options.size(); 72 | for (int i=0; i { 22 | 23 | @Override 24 | public String getName() { 25 | return "RCTCoordinatorLayoutAndroid"; 26 | } 27 | 28 | @Override 29 | public CoordinatorLayout createViewInstance(ThemedReactContext context) { 30 | return new CoordinatorLayout(context); 31 | } 32 | 33 | public boolean needsCustomLayoutForChildren() { 34 | return true; 35 | } 36 | 37 | public static final int COMMAND_SET_VIEW_APPBAR_SCROLLING_VIEW_BEHAVIOR = 1; 38 | public static final int COMMAND_SET_CHILDREN_LAYOUT = 2; 39 | 40 | @Override 41 | public Map getCommandsMap() { 42 | return MapBuilder.of( 43 | "setAppBarScrollingViewBehavior", 44 | COMMAND_SET_VIEW_APPBAR_SCROLLING_VIEW_BEHAVIOR, 45 | "setChildrenLayout", 46 | COMMAND_SET_CHILDREN_LAYOUT 47 | ); 48 | } 49 | 50 | @Override 51 | public void receiveCommand(CoordinatorLayout view, int commandType, @Nullable ReadableArray args) { 52 | Assertions.assertNotNull(view); 53 | Assertions.assertNotNull(args); 54 | 55 | switch (commandType) { 56 | case COMMAND_SET_VIEW_APPBAR_SCROLLING_VIEW_BEHAVIOR: { 57 | setViewAppBarScrollingViewBehavior(view, args.getInt(0)); 58 | return; 59 | } 60 | 61 | case COMMAND_SET_CHILDREN_LAYOUT: { 62 | ReadableArray options = args.getArray(0); 63 | setChildrenLayout(view, options); 64 | return; 65 | } 66 | 67 | default: 68 | throw new JSApplicationIllegalArgumentException(String.format( 69 | "Unsupported command %d received by %s.", 70 | commandType, 71 | getClass().getSimpleName())); 72 | } 73 | } 74 | 75 | private void setViewAppBarScrollingViewBehavior(CoordinatorLayout parent, int viewId) { 76 | try { 77 | View view = parent.getRootView().findViewById(viewId); 78 | 79 | CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) view.getLayoutParams(); 80 | params.setBehavior(new AppBarLayout.ScrollingViewBehavior()); 81 | view.requestLayout(); 82 | 83 | } catch (Exception e) { 84 | // TODO: Handle Exception 85 | } 86 | } 87 | 88 | private void setChildrenLayout(CoordinatorLayout view, ReadableArray options) { 89 | try { 90 | int optionSize = options.size(); 91 | for (int i=0; iNote that {@link ReactScrollView} and {@link ReactHorizontalScrollView} are exposed to JS 43 | * as a single ScrollView component, configured via the {@code horizontal} boolean property. 44 | */ 45 | 46 | // EDITED: 2. Repalce all ReactScrollView in this file with 47 | // ReactNestedScrollView 48 | public class ReactNestedScrollViewManager 49 | extends ViewGroupManager 50 | implements ReactScrollViewCommandHelper.ScrollCommandHandler { 51 | 52 | // EDITED: 3. Change the class name 53 | private static final String REACT_CLASS = "RCTNestedScrollViewAndroid"; 54 | @Override 55 | public String getName() { 56 | return REACT_CLASS; 57 | } 58 | 59 | // EDITED: 4. Stretch the view always 60 | @Override 61 | public ReactNestedScrollView createViewInstance(ThemedReactContext context) { 62 | ReactNestedScrollView sv = new ReactNestedScrollView(context); 63 | sv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 64 | return sv; 65 | } 66 | @ReactProp(name = "showsVerticalScrollIndicator") 67 | public void setShowsVerticalScrollIndicator(ReactNestedScrollView view, boolean value) { 68 | view.setVerticalScrollBarEnabled(value); 69 | } 70 | @ReactProp(name = ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS) 71 | public void setRemoveClippedSubviews(ReactNestedScrollView view, boolean removeClippedSubviews) { 72 | view.setRemoveClippedSubviews(removeClippedSubviews); 73 | } 74 | @Override 75 | public @Nullable Map getCommandsMap() { 76 | return ReactScrollViewCommandHelper.getCommandsMap(); 77 | } 78 | @Override 79 | public void receiveCommand( 80 | ReactNestedScrollView scrollView, 81 | int commandId, 82 | @Nullable ReadableArray args) { 83 | ReactScrollViewCommandHelper.receiveCommand(this, scrollView, commandId, args); 84 | } 85 | @Override 86 | public void scrollTo( 87 | ReactNestedScrollView scrollView, 88 | ReactScrollViewCommandHelper.ScrollToCommandData data) { 89 | scrollView.smoothScrollTo(data.mDestX, data.mDestY); 90 | } 91 | @Override 92 | public void scrollWithoutAnimationTo( 93 | ReactNestedScrollView scrollView, 94 | ReactScrollViewCommandHelper.ScrollToCommandData data) { 95 | scrollView.scrollTo(data.mDestX, data.mDestY); 96 | } 97 | @Override 98 | public @Nullable Map getExportedCustomDirectEventTypeConstants() { 99 | return MapBuilder.builder() 100 | .put(ScrollEvent.EVENT_NAME, MapBuilder.of("registrationName", "onScroll")) 101 | .put("topScrollBeginDrag", MapBuilder.of("registrationName", "onScrollBeginDrag")) 102 | .put("topScrollEndDrag", MapBuilder.of("registrationName", "onScrollEndDrag")) 103 | .put("topScrollAnimationEnd", MapBuilder.of("registrationName", "onScrollAnimationEnd")) 104 | .put("topMomentumScrollBegin", MapBuilder.of("registrationName", "onMomentumScrollBegin")) 105 | .put("topMomentumScrollEnd", MapBuilder.of("registrationName", "onMomentumScrollEnd")) 106 | .build(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativeandroiddesignsupport/ReactTabLayout.java: -------------------------------------------------------------------------------- 1 | package com.reactnativeandroiddesignsupport; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.TabLayout; 5 | 6 | public class ReactTabLayout extends TabLayout { 7 | 8 | public ReactTabLayout(Context context) { 9 | super(context); 10 | } 11 | 12 | public final static int[] getSelectedStateSet() { 13 | return SELECTED_STATE_SET; 14 | } 15 | 16 | public final static int[] getEmptyStateSet() { 17 | return EMPTY_STATE_SET; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativeandroiddesignsupport/ReactTabLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.reactnativeandroiddesignsupport; 2 | 3 | import java.util.Map; 4 | import javax.annotation.Nullable; 5 | 6 | import android.view.View; 7 | import android.support.design.widget.TabLayout; 8 | import android.support.v4.view.ViewPager; 9 | 10 | import com.facebook.infer.annotation.Assertions; 11 | import com.facebook.react.bridge.ReadableArray; 12 | import com.facebook.react.bridge.ReadableMap; 13 | import com.facebook.react.bridge.JSApplicationIllegalArgumentException; 14 | import com.facebook.react.common.MapBuilder; 15 | import com.facebook.react.uimanager.ViewGroupManager; 16 | import com.facebook.react.uimanager.ReactProp; 17 | import com.facebook.react.uimanager.ThemedReactContext; 18 | 19 | import android.util.Log; 20 | 21 | 22 | public class ReactTabLayoutManager extends ViewGroupManager { 23 | public ReadableArray mTabs = null; 24 | 25 | @Override 26 | public String getName() { 27 | return "RCTTabLayoutAndroid"; 28 | } 29 | 30 | @Override 31 | public TabLayout createViewInstance(ThemedReactContext context) { 32 | return new TabLayout(context); 33 | } 34 | 35 | public boolean needsCustomLayoutForChildren() { 36 | return true; 37 | } 38 | 39 | @Override 40 | public void addView(TabLayout view, View child, int index) { 41 | view.addTab(view.newTab().setCustomView(child)); 42 | } 43 | 44 | @Override 45 | public int getChildCount(TabLayout view) { 46 | return view.getTabCount(); 47 | } 48 | 49 | @Override 50 | public View getChildAt(TabLayout view, int index) { 51 | TabLayout.Tab t = view.getTabAt(index); 52 | return t.getCustomView(); 53 | } 54 | 55 | @Override 56 | public void removeViewAt(TabLayout view, int index) { 57 | view.removeTabAt(index); 58 | } 59 | 60 | // @Override 61 | // public void removeAllViews(TabLayout view) { 62 | // view.removeAllTabs(); 63 | // } 64 | 65 | public static final int COMMAND_SET_VIEW_PAGER = 1; 66 | 67 | @Override 68 | public Map getCommandsMap() { 69 | return MapBuilder.of( 70 | "setViewPager", 71 | COMMAND_SET_VIEW_PAGER); 72 | } 73 | 74 | @Override 75 | public void receiveCommand(TabLayout view, int commandType, @Nullable ReadableArray args) { 76 | Assertions.assertNotNull(view); 77 | Assertions.assertNotNull(args); 78 | 79 | switch (commandType) { 80 | case COMMAND_SET_VIEW_PAGER: { 81 | int viewPagerId = args.getInt(0); 82 | ViewPager viewPager = (ViewPager) view.getRootView().findViewById(viewPagerId); 83 | view.setupWithViewPager(viewPager); 84 | 85 | ReadableArray tabs = args.getArray(1); 86 | if (tabs != null) { 87 | view.removeAllTabs(); 88 | this.populateTablayoutWithTabs(view, tabs); 89 | } 90 | 91 | return; 92 | } 93 | 94 | default: 95 | throw new JSApplicationIllegalArgumentException(String.format( 96 | "Unsupported command %d received by %s.", 97 | commandType, 98 | getClass().getSimpleName())); 99 | } 100 | } 101 | 102 | @ReactProp(name = "tabs") 103 | public void setTabs(TabLayout view, ReadableArray tabs) { 104 | view.removeAllTabs(); 105 | this.mTabs = tabs; 106 | this.populateTablayoutWithTabs(view, tabs); 107 | } 108 | 109 | @ReactProp(name = "normalColor", customType = "Color") 110 | public void setNormalColor(TabLayout view, int color) { 111 | int selectedColor = view.getTabTextColors().getColorForState(ReactTabLayout.getSelectedStateSet(), color); 112 | view.setTabTextColors(color, selectedColor); 113 | } 114 | 115 | @ReactProp(name = "selectedColor", customType = "Color") 116 | public void setSelectedColor(TabLayout view, int color) { 117 | int normalColor = view.getTabTextColors().getColorForState(ReactTabLayout.getEmptyStateSet(), color); 118 | view.setTabTextColors(normalColor, color); 119 | } 120 | 121 | @ReactProp(name = "selectedTabIndicatorColor", customType = "Color") 122 | public void setSelectedTabIndicatorColor(TabLayout view, int color) { 123 | view.setSelectedTabIndicatorColor(color); 124 | } 125 | 126 | @ReactProp(name = "tabMode") 127 | public void setTabMode(TabLayout view, String mode) { 128 | if ("fixed".equals(mode)) { 129 | view.setTabMode(TabLayout.MODE_FIXED); 130 | } else if ("scrollable".equals(mode)) { 131 | view.setTabMode(TabLayout.MODE_SCROLLABLE); 132 | } 133 | } 134 | 135 | private void populateTablayoutWithTabs(TabLayout view, ReadableArray tabs) { 136 | try { 137 | int tabSize = tabs.size(); 138 | for (int i=0; i { 14 | 15 | @Override 16 | public String getName() { 17 | return "RCTTextInputLayoutAndroid"; 18 | } 19 | 20 | @Override 21 | public TextInputLayout createViewInstance(ThemedReactContext context) { 22 | TextInputLayout textInputLayout = new TextInputLayout(context); 23 | textInputLayout.setHintAnimationEnabled(true); 24 | return textInputLayout; 25 | } 26 | 27 | public boolean needsCustomLayoutForChildren() { 28 | return true; 29 | } 30 | 31 | @ReactProp(name = "hint") 32 | public void setHint(TextInputLayout view, @Nullable String hintText) { 33 | view.setHint(hintText); 34 | } 35 | 36 | @ReactProp(name = "hintAnimationEnabled") 37 | public void setHintAnimationEnabled(TextInputLayout view, boolean hintAnimationEnabled) { 38 | view.setHintAnimationEnabled(hintAnimationEnabled); 39 | } 40 | 41 | @ReactProp(name = "errorEnabled") 42 | public void setErrorEnabled(TextInputLayout view, boolean errorEnabled) { 43 | view.setErrorEnabled(errorEnabled); 44 | } 45 | 46 | @ReactProp(name = "error") 47 | public void setError(TextInputLayout view, @Nullable String errorMessage) { 48 | view.setError(errorMessage); 49 | } 50 | 51 | @ReactProp(name = "counterEnabled") 52 | public void setCounterEnabled(TextInputLayout view, boolean counterEnabled) { 53 | view.setCounterEnabled(counterEnabled); 54 | } 55 | 56 | @ReactProp(name = "counterMaxLength") 57 | public void setCounterMaxLength(TextInputLayout view, @Nullable int counterMaxLength) { 58 | view.setCounterMaxLength(counterMaxLength); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativeandroiddesignsupport/SnackbarModule.java: -------------------------------------------------------------------------------- 1 | package com.reactnativeandroiddesignsupport; 2 | 3 | import android.content.Context; 4 | import android.app.Activity; 5 | import android.view.View; 6 | import android.view.View.OnClickListener; 7 | 8 | import com.facebook.react.bridge.NativeModule; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.bridge.ReactContext; 11 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 12 | import com.facebook.react.bridge.ReactMethod; 13 | import com.facebook.react.bridge.Callback; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | import android.support.design.widget.Snackbar; 19 | 20 | public class SnackbarModule extends ReactContextBaseJavaModule { 21 | public Activity mActivity = null; 22 | public Context mContext = null; 23 | 24 | @Override 25 | public String getName() { 26 | return "SnackbarAndroid"; 27 | } 28 | 29 | public SnackbarModule(ReactApplicationContext reactContext, Activity activity) { 30 | super(reactContext); 31 | this.mContext = reactContext; 32 | this.mActivity = activity; 33 | } 34 | 35 | @ReactMethod 36 | public void show(String text, int duration, String action, String payload, Callback callback) { 37 | final String fPayload = payload; 38 | final Callback fCallback = callback; 39 | 40 | if (action == "") { 41 | Snackbar 42 | .make(mActivity.findViewById(android.R.id.content), text, duration) 43 | .show(); 44 | } else { 45 | Snackbar 46 | .make(mActivity.findViewById(android.R.id.content), text, duration) 47 | .setAction(action, new OnClickListener() { 48 | @Override 49 | public void onClick(View v) { 50 | fCallback.invoke(fPayload); 51 | }}) 52 | .show(); 53 | } 54 | } 55 | 56 | @Override 57 | public Map getConstants() { 58 | final Map constants = new HashMap<>(); 59 | constants.put("SHORT", Snackbar.LENGTH_SHORT); 60 | constants.put("LONG", Snackbar.LENGTH_LONG); 61 | return constants; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NestedScrollViewAndroid: require('./lib/NestedScrollViewAndroid'), 3 | SnackbarAndroid: require('./lib/SnackbarAndroid'), 4 | TextInputLayoutAndroid: require('./lib/TextInputLayoutAndroid'), 5 | TabLayoutAndroid: require('./lib/TabLayoutAndroid'), 6 | CoordinatorLayoutAndroid: require('./lib/CoordinatorLayoutAndroid'), 7 | AppBarLayoutAndroid: require('./lib/AppBarLayoutAndroid') 8 | }; 9 | -------------------------------------------------------------------------------- /lib/AppBarLayoutAndroid.js: -------------------------------------------------------------------------------- 1 | var React = require('react-native') 2 | var { requireNativeComponent, PropTypes, View, Text, createElement, UIManager, findNodeHandle } = React; 3 | 4 | var AppBarLayoutAndroid = React.createClass({ 5 | propTypes: { 6 | ...View.propTypes, 7 | childrenScrollFlags: PropTypes.array 8 | }, 9 | 10 | getDefaultProps: function() { 11 | return {}; 12 | }, 13 | 14 | componentDidMount: function() { 15 | this._setChildrenScrollFlags(); 16 | }, 17 | 18 | componentDidUpdate: function() { 19 | this._setChildrenScrollFlags(); 20 | }, 21 | 22 | _setChildrenScrollFlags: function() { 23 | var self = this; 24 | var scrollFlags = []; 25 | 26 | React.Children.map(this.props.children, function(child, index) { 27 | if (child.props.scrollFlagsAndroid) scrollFlags.push({ 28 | index: index, 29 | scrollFlags: child.props.scrollFlagsAndroid 30 | }); 31 | }); 32 | 33 | UIManager.dispatchViewManagerCommand( 34 | React.findNodeHandle(this), 35 | UIManager.RCTAppBarLayoutAndroid.Commands.setChildrenScrollFlags, 36 | [scrollFlags], 37 | ); 38 | }, 39 | 40 | render: function() { 41 | return ( 42 | 45 | {this.props.children} 46 | 47 | ); 48 | } 49 | 50 | }); 51 | 52 | var RCTAppBarLayoutAndroid = requireNativeComponent('RCTAppBarLayoutAndroid', AppBarLayoutAndroid, { 53 | nativeOnly: {} 54 | }); 55 | 56 | module.exports = AppBarLayoutAndroid; 57 | -------------------------------------------------------------------------------- /lib/CoordinatorLayoutAndroid.js: -------------------------------------------------------------------------------- 1 | var React = require('react-native') 2 | var { requireNativeComponent, PropTypes, View, UIManager, findNodeHandle } = React; 3 | 4 | var CoordinatorLayoutAndroid = React.createClass({ 5 | propTypes: { 6 | ...View.propTypes 7 | }, 8 | 9 | getDefaultProps: function() { 10 | return {}; 11 | }, 12 | 13 | componentDidMount: function() { 14 | this._setChildrenLayout(); 15 | }, 16 | 17 | componentDidUpdate: function() { 18 | this._setChildrenLayout(); 19 | }, 20 | 21 | setAppBarScrollingViewBehavior: function(element) { 22 | var viewID = findNodeHandle(element); 23 | 24 | UIManager.dispatchViewManagerCommand( 25 | React.findNodeHandle(this), 26 | UIManager.RCTCoordinatorLayoutAndroid.Commands.setAppBarScrollingViewBehavior, 27 | [viewID], 28 | ); 29 | }, 30 | 31 | _setChildrenLayout: function() { 32 | var self = this; 33 | var layout = []; 34 | 35 | React.Children.map(this.props.children, function(child, index) { 36 | layout.push({ 37 | index: index, 38 | layoutWidth: child.props.layoutWidthAndroid || 'matchParent', 39 | layoutHeight: child.props.layoutHeightAndroid || 'matchParent' 40 | }); 41 | }); 42 | 43 | UIManager.dispatchViewManagerCommand( 44 | React.findNodeHandle(this), 45 | UIManager.RCTCoordinatorLayoutAndroid.Commands.setChildrenLayout, 46 | [layout], 47 | ); 48 | }, 49 | 50 | render: function() { 51 | return ( 52 | 58 | {this.props.children} 59 | 60 | ); 61 | } 62 | }); 63 | 64 | var RCTCoordinatorLayoutAndroid = requireNativeComponent('RCTCoordinatorLayoutAndroid', CoordinatorLayoutAndroid, { 65 | nativeOnly: {} 66 | }); 67 | 68 | module.exports = CoordinatorLayoutAndroid; 69 | -------------------------------------------------------------------------------- /lib/NestedScrollViewAndroid.js: -------------------------------------------------------------------------------- 1 | var React = require('react-native') 2 | var { requireNativeComponent, PropTypes, StyleSheet, View, ScrollView } = React; 3 | var ScrollResponder = require('react-native/Libraries/Components/ScrollResponder'); 4 | 5 | var SCROLLVIEW = 'scrollView'; 6 | var INNERVIEW = 'InnerScrollView'; 7 | 8 | var NestedScrollViewAndroid = React.createClass({ 9 | mixins: [ScrollResponder.Mixin], 10 | 11 | propTypes: { 12 | ...ScrollView.propTypes, 13 | }, 14 | 15 | getInitialState: function() { 16 | return this.scrollResponderMixinGetInitialState(); 17 | }, 18 | 19 | setNativeProps: function(props: Object) { 20 | this.refs[SCROLLVIEW].setNativeProps(props); 21 | }, 22 | 23 | getScrollResponder: function(): ReactComponent { 24 | return this; 25 | }, 26 | 27 | getInnerViewNode: function(): any { 28 | return React.findNodeHandle(this.refs[INNERVIEW]); 29 | }, 30 | 31 | scrollTo: function(destY?: number, destX?: number) { 32 | this.getScrollResponder().scrollResponderScrollTo(destX || 0, destY || 0); 33 | }, 34 | 35 | scrollWithoutAnimationTo: function(destY?: number, destX?: number) { 36 | this.getScrollResponder().scrollResponderScrollWithouthAnimationTo( 37 | destX || 0, 38 | destY || 0, 39 | ); 40 | }, 41 | 42 | handleScroll: function(e: Event) { 43 | if (__DEV__) { 44 | if (this.props.onScroll && !this.props.scrollEventThrottle) { 45 | console.log( 46 | 'You specified `onScroll` on a but not ' + 47 | '`scrollEventThrottle`. You will only receive one event. ' + 48 | 'Using `16` you get all the events but be aware that it may ' + 49 | 'cause frame drops, use a bigger number if you don\'t need as ' + 50 | 'much precision.' 51 | ); 52 | } 53 | } 54 | 55 | if (this.props.keyboardDismissMode === 'on-drag') { 56 | dismissKeyboard(); 57 | } 58 | 59 | this.scrollResponderHandleScroll(e); 60 | }, 61 | 62 | _handleContentOnLayout: function(e: Object) { 63 | var { width, height } = e.nativeEvent.layout; 64 | this.props.onContentSizeChange && this.props.onContentSizeChange(width, height); 65 | }, 66 | 67 | render: function() { 68 | var contentContainerStyle = [ 69 | this.props.horizontal && styles.contentContainerHorizontal, 70 | this.props.contentContainerStyle, 71 | ]; 72 | 73 | // if (__DEV__ && this.props.style) { 74 | // var style = flattenStyle(this.props.style); 75 | // var childLayoutProps = ['alignItems', 'justifyContent'] 76 | // .filter((prop) => style && style[prop] !== undefined); 77 | // invariant( 78 | // childLayoutProps.length === 0, 79 | // 'ScrollView child layout (' + JSON.stringify(childLayoutProps) + 80 | // ') must by applied through the contentContainerStyle prop.' 81 | // ); 82 | // } 83 | 84 | var contentSizeChangeProps = {}; 85 | 86 | if (this.props.onContentSizeChange) { 87 | contentSizeChangeProps = { 88 | onLayout: this._handleContentOnLayout, 89 | }; 90 | } 91 | 92 | var contentContainer = 93 | 99 | {this.props.children} 100 | ; 101 | 102 | 103 | var props = { 104 | ...this.props, 105 | style: ([styles.base, this.props.style, this.props.height && { height: this.props.height }]: ?Array), 106 | onTouchStart: this.scrollResponderHandleTouchStart, 107 | onTouchMove: this.scrollResponderHandleTouchMove, 108 | onTouchEnd: this.scrollResponderHandleTouchEnd, 109 | onScrollBeginDrag: this.scrollResponderHandleScrollBeginDrag, 110 | onScrollEndDrag: this.scrollResponderHandleScrollEndDrag, 111 | onMomentumScrollBegin: this.scrollResponderHandleMomentumScrollBegin, 112 | onMomentumScrollEnd: this.scrollResponderHandleMomentumScrollEnd, 113 | onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder, 114 | onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture, 115 | onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder, 116 | onScroll: this.handleScroll, 117 | onResponderGrant: this.scrollResponderHandleResponderGrant, 118 | onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest, 119 | onResponderTerminate: this.scrollResponderHandleTerminate, 120 | onResponderRelease: this.scrollResponderHandleResponderRelease, 121 | onResponderReject: this.scrollResponderHandleResponderReject, 122 | }; 123 | 124 | var onRefreshStart = this.props.onRefreshStart; 125 | 126 | props.onRefreshStart = onRefreshStart 127 | ? function() { onRefreshStart && onRefreshStart(this.endRefreshing); }.bind(this) 128 | : null; 129 | 130 | return ( 131 | 135 | {contentContainer} 136 | 137 | ); 138 | } 139 | }); 140 | 141 | var RCTNestedScrollViewAndroid = requireNativeComponent('RCTNestedScrollViewAndroid', NestedScrollViewAndroid, { 142 | nativeOnly: {} 143 | }); 144 | 145 | var styles = StyleSheet.create({ 146 | base: { 147 | // flex: 1 148 | }, 149 | contentContainerHorizontal: { 150 | alignSelf: 'flex-start', 151 | flexDirection: 'row', 152 | }, 153 | }); 154 | 155 | module.exports = NestedScrollViewAndroid; 156 | -------------------------------------------------------------------------------- /lib/SnackbarAndroid.js: -------------------------------------------------------------------------------- 1 | var RCTSnackbarAndroid = require('react-native').NativeModules.SnackbarAndroid; 2 | 3 | var SnackbarAndroid = { 4 | INDEFINITE: RCTSnackbarAndroid.INDEFINITE, 5 | LONG: RCTSnackbarAndroid.LONG, 6 | SHORT: RCTSnackbarAndroid.SHORT, 7 | 8 | show: function(text, duration, action, payload, callback) { 9 | if (duration === undefined || duration === null) duration = RCTSnackbarAndroid.SHORT; 10 | 11 | if (!action) action = ''; 12 | if (!payload) payload = {}; 13 | var nPayload = JSON.stringify(payload); 14 | var nCallback = function(p) { 15 | callback(JSON.parse(p)); 16 | } 17 | 18 | RCTSnackbarAndroid.show(text, duration, action, nPayload, nCallback); 19 | } 20 | }; 21 | 22 | module.exports = SnackbarAndroid; 23 | -------------------------------------------------------------------------------- /lib/TabLayoutAndroid.js: -------------------------------------------------------------------------------- 1 | var React = require('react-native') 2 | var { requireNativeComponent, PropTypes, View, Text, createElement, UIManager, findNodeHandle } = React; 3 | 4 | var TabLayoutAndroid = React.createClass({ 5 | propTypes: { 6 | ...View.propTypes, 7 | tabs: PropTypes.array, 8 | tabMode: PropTypes.string, 9 | normalColor: PropTypes.string, 10 | selectedColor: PropTypes.string, 11 | selectedTabIndicatorColor: PropTypes.string 12 | }, 13 | 14 | getDefaultProps: function() { 15 | return { 16 | tabMode: 'fixed', 17 | normalColor: '#17171786', 18 | selectedColor: '#171717', 19 | selectedTabIndicatorColor: '#008574' 20 | }; 21 | }, 22 | 23 | setViewPager: function(viewPager) { 24 | var viewPagerID = findNodeHandle(viewPager); 25 | 26 | UIManager.dispatchViewManagerCommand( 27 | React.findNodeHandle(this), 28 | UIManager.RCTTabLayoutAndroid.Commands.setViewPager, 29 | [viewPagerID, this.props.tabs], 30 | ); 31 | }, 32 | 33 | _childrenWithOverridenStyle: function() { 34 | if (!this.props.children) return null; 35 | 36 | var self = this; 37 | return React.Children.map(this.props.children, function(child, i) { 38 | var newProps = { 39 | key: i, 40 | ...child.props, 41 | style: [{ 42 | justifyContent: 'center', 43 | alignItems: 'center' 44 | }, child.props.style, self.props.activeTabStyle], 45 | }; 46 | if (child.type && 47 | child.type.displayName && 48 | (child.type.displayName !== 'RCTView') && 49 | (child.type.displayName !== 'View')) { 50 | console.warn('Each TabLayout child must be a . Was ' + child.type.displayName); 51 | } 52 | return createElement(child.type, newProps); 53 | }); 54 | }, 55 | 56 | render: function() { 57 | return ( 58 | 64 | {this._childrenWithOverridenStyle()} 65 | 66 | ); 67 | } 68 | 69 | }); 70 | 71 | var RCTTabLayoutAndroid = requireNativeComponent('RCTTabLayoutAndroid', TabLayoutAndroid, { 72 | nativeOnly: {} 73 | }); 74 | 75 | module.exports = TabLayoutAndroid; 76 | -------------------------------------------------------------------------------- /lib/TextInputLayoutAndroid.js: -------------------------------------------------------------------------------- 1 | var React = require('react-native') 2 | var { requireNativeComponent, PropTypes, View } = React; 3 | 4 | var TextInputLayoutAndroid = React.createClass({ 5 | propTypes: { 6 | ...View.propTypes, 7 | hint: PropTypes.string, 8 | hintAnimationEnabled: PropTypes.bool, 9 | errorEnabled: PropTypes.bool, 10 | error: PropTypes.string, 11 | counterEnabled: PropTypes.bool, 12 | counterMaxLength: PropTypes.number 13 | }, 14 | 15 | getDefaultProps: function() { 16 | return {}; 17 | }, 18 | 19 | render: function() { 20 | return ( 21 | 25 | {this.props.children} 26 | 27 | ); 28 | } 29 | }); 30 | 31 | var RCTTextInputLayoutAndroid = requireNativeComponent('RCTTextInputLayoutAndroid', TextInputLayoutAndroid, { 32 | nativeOnly: {} 33 | }); 34 | 35 | module.exports = TextInputLayoutAndroid; 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-android-design-support", 3 | "version": "0.0.5", 4 | "description": "React Native wrapper for Android Design Support Library, providing native Material Design to modern and also older Android devices.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Neson/react-native-android-design-support.git" 12 | }, 13 | "keywords": [ 14 | "react-native", 15 | "android", 16 | "material-design" 17 | ], 18 | "author": "Neson ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/Neson/react-native-android-design-support/issues" 22 | }, 23 | "homepage": "https://github.com/Neson/react-native-android-design-support#readme", 24 | "peerDependencies": { 25 | "react-native": ">=0.15.0" 26 | } 27 | } 28 | --------------------------------------------------------------------------------