add flutter
51
OnTime_Merchant_live/lib/flexbox-layout/.circleci/config.yml
Executable file
@@ -0,0 +1,51 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/code
|
||||
docker:
|
||||
- image: circleci/android:api-29
|
||||
environment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
MAX_RETRY: 4
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "flexbox/build.gradle" }}
|
||||
- run:
|
||||
name: Download Dependencies
|
||||
command: ./gradlew androidDependencies
|
||||
- run:
|
||||
name: Set up gcloud service key
|
||||
command: |
|
||||
if [ -n "$GCLOUD_SERVICE_KEY" ]; then echo ${GCLOUD_SERVICE_KEY} | base64 --decode > ${HOME}/client-secret.json ;
|
||||
gcloud config set project ${GCLOUD_PROJECT} ;
|
||||
gcloud auth activate-service-account ${GCLOUD_SERVICE_ACCOUNT} --key-file ${HOME}/client-secret.json ;
|
||||
fi
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.gradle
|
||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "flexbox/build.gradle" }}
|
||||
- run:
|
||||
name: Build apks
|
||||
command: ./gradlew build assembleAndroidTest
|
||||
- run:
|
||||
name: Run Firebase Test Lab
|
||||
command: |
|
||||
if [ -n "$GCLOUD_SERVICE_KEY" ]; then set +e ;
|
||||
counter=0 ;
|
||||
result=1 ;
|
||||
while [ $result != 0 -a $counter -lt $MAX_RETRY ]; do
|
||||
gcloud firebase test android run \
|
||||
--type instrumentation \
|
||||
--app demo-playground/build/outputs/apk/debug/demo-playground-debug.apk \
|
||||
--test flexbox/build/outputs/apk/androidTest/debug/flexbox-debug-androidTest.apk \
|
||||
--device-ids hammerhead,sailfish \
|
||||
--os-version-ids 19,21,23,24,25,26 \
|
||||
--locales en --orientations portrait,landscape \
|
||||
--results-bucket android-devrel-ci-flexbox \
|
||||
--timeout 180s ;
|
||||
result=$? ;
|
||||
let counter=counter+1 ;
|
||||
done
|
||||
exit $result ;
|
||||
fi
|
||||
15
OnTime_Merchant_live/lib/flexbox-layout/.github/issue_template.md
vendored
Executable file
@@ -0,0 +1,15 @@
|
||||
- [ ] I have searched [existing issues](https://github.com/google/flexbox-layout/issues) and confirmed this is not a duplicate
|
||||
|
||||
## Issues and steps to reproduce
|
||||
*Please replace this with steps to reproduce your issue.*
|
||||
|
||||
## Expected behavior
|
||||
*Please describe what you expected would happen.*
|
||||
|
||||
## Version of the flexbox library
|
||||
*e.g. 0.2.6, 0.3.0-alpha3*
|
||||
|
||||
## Link to code
|
||||
*Please link to the code we can use to reproduce this issue.*
|
||||
*A complete project we can build/run is preferred, if you can't provide one, please show*
|
||||
*us relevant code*
|
||||
10
OnTime_Merchant_live/lib/flexbox-layout/.github/workflows/gradle-wrapper-validation.yml
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
50
OnTime_Merchant_live/lib/flexbox-layout/.gitignore
vendored
Executable file
@@ -0,0 +1,50 @@
|
||||
*.iml
|
||||
.gradle
|
||||
.idea/
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
!.idea/codeStyleSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.vscode/
|
||||
|
||||
# Taken from Android.gitignore https://github.com/github/gitignore/blob/master/Android.gitignore
|
||||
#
|
||||
# Built application files
|
||||
*.apk
|
||||
*.ap_
|
||||
|
||||
# Files for the Dalvik VM
|
||||
*.dex
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
|
||||
# Generated files
|
||||
bin/
|
||||
gen/
|
||||
out/
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Proguard folder generated by Eclipse
|
||||
proguard/
|
||||
|
||||
# Log Files
|
||||
*.log
|
||||
|
||||
# Android Studio Navigation editor temp files
|
||||
.navigation/
|
||||
|
||||
# Android Studio captures folder
|
||||
captures/
|
||||
|
||||
# Intellij
|
||||
*.iml
|
||||
45
OnTime_Merchant_live/lib/flexbox-layout/CONTRIBUTING.md
Executable file
@@ -0,0 +1,45 @@
|
||||
# How to become a contributor and submit your own code
|
||||
|
||||
## Contributor License Agreements
|
||||
|
||||
We'd love to accept your sample apps and patches! Before we can take them, we
|
||||
have to jump a couple of legal hurdles.
|
||||
|
||||
Please fill out either the individual or corporate Contributor License Agreement (CLA).
|
||||
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then you'll need to sign an [individual CLA]
|
||||
(https://cla.developers.google.com).
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a [corporate CLA]
|
||||
(https://cla.developers.google.com).
|
||||
|
||||
Follow either of the two links above to access the appropriate CLA and
|
||||
instructions for how to sign and return it. Once we receive it, we'll be able to
|
||||
accept your pull requests.
|
||||
|
||||
## Contributing A Patch
|
||||
|
||||
1. Submit an issue describing your proposed change to the repo in question.
|
||||
1. The repo owner will respond to your issue promptly.
|
||||
1. If your proposed change is accepted, and you haven't already done so, sign a
|
||||
Contributor License Agreement (see details above).
|
||||
1. Fork the desired repo, develop and test your code changes.
|
||||
1. Ensure that your code adheres to the existing style in the sample to which
|
||||
you are contributing. Refer to the
|
||||
[Android Code Style Guide]
|
||||
(https://source.android.com/source/code-style.html) for the
|
||||
recommended coding standards for this organization.
|
||||
1. Ensure that your code has an appropriate set of unit tests which all pass.
|
||||
1. Submit a pull request.
|
||||
|
||||
## Code Style
|
||||
|
||||
This repository follows the official Android code style.
|
||||
When you send a patch, please try to follow that.
|
||||
Here are the example steps to follow:
|
||||
|
||||
1. From Android Studio or IntelliJ IDEA, navigate to "Preferences" -> "Editor" -> "Code Style"
|
||||
1. Select "Import Scheme" by clicking the gear icon next to the Scheme pull down
|
||||
1. Choose <root directory of flexbox-layout>/tool/codeStyleSettings.xml
|
||||
1. Create a new scheme by typing a scheme name in the "To" edit box or apply to the current scheme.
|
||||
202
OnTime_Merchant_live/lib/flexbox-layout/LICENSE
Executable file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
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.
|
||||
407
OnTime_Merchant_live/lib/flexbox-layout/README.md
Executable file
@@ -0,0 +1,407 @@
|
||||
# FlexboxLayout
|
||||
[  ](https://circleci.com/gh/google/flexbox-layout/tree/main)
|
||||
|
||||
FlexboxLayout is a library project which brings the similar capabilities of
|
||||
[CSS Flexible Box Layout Module](https://www.w3.org/TR/css-flexbox-1) to Android.
|
||||
|
||||
# Installation
|
||||
Add the following dependency to your `build.gradle` file:
|
||||
|
||||
```
|
||||
dependencies {
|
||||
implementation 'com.google.android.flexbox:flexbox:3.0.0'
|
||||
}
|
||||
```
|
||||
|
||||
**Starting from 3.0.0, the groupId is changed to `com.google.android.flexbox` in preparation to uploading the artifacts to google maven.
|
||||
You can still download the artifacts from jcenter for the past versions with the prior groupId (`com.google.android`), but migrating the library 3.0.0 is recommended.**
|
||||
|
||||
Note that the default values for `alignItems` and `alignContent` for `FlexboxLayout` have been changed from `stretch` to `flex_start` starting from 2.0.0, it may break the existing apps.
|
||||
Please make sure to set `stretch` explicitly if you want to apply the behavior of `stretch`.
|
||||
|
||||
|
||||
Note that starting from 1.1.0, the library is expeced to use with AndroidX. Please migrate to [AndroidX](https://developer.android.com/jetpack/androidx/migrate) if you use 1.1.0 or above.
|
||||
|
||||
Please use 1.0.0 if you haven't migrated to AndroidX.
|
||||
|
||||
|
||||
# Usage
|
||||
There are two ways of using Flexbox in your layout.
|
||||
|
||||
## FlexboxLayout
|
||||
The first one is `FlexboxLayout` that extends the `ViewGroup` like `LinearLayout` and `RelativeLayout`.
|
||||
You can specify the attributes from a layout XML like:
|
||||
```xml
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:flexWrap="wrap"
|
||||
app:alignItems="stretch"
|
||||
app:alignContent="stretch" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview1"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="80dp"
|
||||
app:layout_flexBasisPercent="50%"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview2"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
app:layout_alignSelf="center"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview3"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="80dp"
|
||||
app:layout_alignSelf="flex_end"
|
||||
/>
|
||||
</com.google.android.flexbox.FlexboxLayout>
|
||||
```
|
||||
|
||||
Or from code like:
|
||||
```java
|
||||
FlexboxLayout flexboxLayout = (FlexboxLayout) findViewById(R.id.flexbox_layout);
|
||||
flexboxLayout.setFlexDirection(FlexDirection.ROW);
|
||||
|
||||
View view = flexboxLayout.getChildAt(0);
|
||||
FlexboxLayout.LayoutParams lp = (FlexboxLayout.LayoutParams) view.getLayoutParams();
|
||||
lp.setOrder(-1);
|
||||
lp.setFlexGrow(2);
|
||||
view.setLayoutParams(lp);
|
||||
```
|
||||
|
||||
## FlexboxLayoutManager (within RecyclerView)
|
||||
The second one is `FlexboxLayoutManager` that can be used within `RecyclerView`.
|
||||
|
||||
```java
|
||||
RecyclerView recyclerView = (RecyclerView) context.findViewById(R.id.recyclerview);
|
||||
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(context);
|
||||
layoutManager.setFlexDirection(FlexDirection.COLUMN);
|
||||
layoutManager.setJustifyContent(JustifyContent.FLEX_END);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
```
|
||||
|
||||
or for the attributes for the children of the `FlexboxLayoutManager` you can do like:
|
||||
|
||||
```java
|
||||
mImageView.setImageDrawable(drawable);
|
||||
ViewGroup.LayoutParams lp = mImageView.getLayoutParams();
|
||||
if (lp instanceof FlexboxLayoutManager.LayoutParams) {
|
||||
FlexboxLayoutManager.LayoutParams flexboxLp = (FlexboxLayoutManager.LayoutParams) lp;
|
||||
flexboxLp.setFlexGrow(1.0f);
|
||||
flexboxLp.setAlignSelf(AlignSelf.FLEX_END);
|
||||
}
|
||||
```
|
||||
|
||||
The advantage of using `FlexboxLayoutManager` is that it recycles the views that go off the screen
|
||||
for reuse for the views that are appearing as the user scrolls instead of inflating every individual view,
|
||||
which consumes much less memory especially when the number of items contained in the Flexbox container is large.
|
||||
|
||||

|
||||
|
||||
|
||||
## Supported attributes/features comparison
|
||||
Due to some characteristics of `RecyclerView`, some Flexbox attributes are not available/not implemented
|
||||
to the `FlexboxLayoutManager`.
|
||||
Here is a quick overview of the attributes/features comparison between the two implementations.
|
||||
|
||||
|Attribute / Feature|FlexboxLayout| FlexboxLayoutManager (RecyclerView)|
|
||||
| ------- |:-----------:|:----------------------------------:|
|
||||
|flexDirection|||
|
||||
|flexWrap|| (except `wrap_reverse`)|
|
||||
|justifyContent|||
|
||||
|alignItems|||
|
||||
|alignContent|| - |
|
||||
|layout_order|| - |
|
||||
|layout_flexGrow|||
|
||||
|layout_flexShrink|||
|
||||
|layout_alignSelf|||
|
||||
|layout_flexBasisPercent|||
|
||||
|layout_(min/max)Width|||
|
||||
|layout_(min/max)Height|||
|
||||
|layout_wrapBefore|||
|
||||
|Divider|||
|
||||
|View recycling| - ||
|
||||
|Scrolling| *1 ||
|
||||
|
||||
*1 Partially possible by wrapping it with `ScrollView`. But it isn't likely to work with a large set
|
||||
of views inside the layout. Because it doesn't consider view recycling.
|
||||
|
||||
# Supported attributes
|
||||
|
||||
## Attributes for the FlexboxLayout:
|
||||
|
||||
* __flexDirection__
|
||||
* This attribute determines the direction of the main axis (and the cross axis, perpendicular to the main axis). The direction children items are placed inside the Flexbox layout.
|
||||
Possible values are:
|
||||
* row (default)
|
||||
* row_reverse
|
||||
* column
|
||||
* column_reverse
|
||||
|
||||

|
||||
|
||||
* __flexWrap__
|
||||
* This attribute controls whether the flex container is single-line or multi-line, and the
|
||||
direction of the cross axis. Possible values are:
|
||||
* nowrap (default for FlexboxLayout)
|
||||
* wrap (default for FlexboxLayoutManager)
|
||||
* wrap_reverse (not supported by FlexboxLayoutManager)
|
||||
|
||||

|
||||
|
||||
* __justifyContent__
|
||||
* This attribute controls the alignment along the main axis. Possible values are:
|
||||
* flex_start (default)
|
||||
* flex_end
|
||||
* center
|
||||
* space_between
|
||||
* space_around
|
||||
* space_evenly
|
||||
|
||||

|
||||
|
||||
* __alignItems__
|
||||
* This attribute controls the alignment along the cross axis. Possible values are:
|
||||
* flex_start (default for FlexboxLayout)
|
||||
* flex_end
|
||||
* center
|
||||
* baseline
|
||||
* stretch (default for FlexboxLayoutManager)
|
||||
|
||||

|
||||
|
||||
* __alignContent__
|
||||
* This attribute controls the alignment of the flex lines in the flex container. Possible values
|
||||
are:
|
||||
* flex_start (default)
|
||||
* flex_end
|
||||
* center
|
||||
* space_between
|
||||
* space_around
|
||||
* stretch
|
||||
|
||||

|
||||
|
||||
* __showDividerHorizontal__ (one or more of `none | beginning | middle | end`)
|
||||
* __dividerDrawableHorizontal__ (reference to a drawable)
|
||||
* Puts horizontal dividers between flex lines (or flex items when flexDirection
|
||||
is set to `column` or `column_rebase`).
|
||||
|
||||
* __showDividerVertical__ (one or more of `none | beginning | middle | end`)
|
||||
* __dividerDrawableVertical__ (reference to a drawable)
|
||||
* Puts vertical dividers between flex items (or flex lines when flexDirection
|
||||
is set to `column` or `column_rebase`).
|
||||
|
||||
* __showDivider__ (one or more of `none | beginning | middle | end`)
|
||||
* __dividerDrawable__ (reference to a drawable)
|
||||
* Shorthand for setting both horizontal and vertical dividers. Note that if used with other attributes
|
||||
(such as `justifyContent="space_around"` or `alignContent="space_between"` ... etc) for putting
|
||||
spaces between flex lines or flex items, you may see unexpected spaces. Please avoid using these
|
||||
at the same time.
|
||||
|
||||
Example of putting both vertical and horizontal dividers.
|
||||
|
||||
`res/drawable/divider.xml`
|
||||
```xml
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<size
|
||||
android:width="8dp"
|
||||
android:height="12dp" />
|
||||
<solid android:color="#44A444" />
|
||||
</shape>
|
||||
```
|
||||
|
||||
`res/layout/content_main.xml`
|
||||
```xml
|
||||
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:alignContent="flex_start"
|
||||
app:alignItems="flex_start"
|
||||
app:flexWrap="wrap"
|
||||
app:showDivider="beginning|middle"
|
||||
app:dividerDrawable="@drawable/divider" >
|
||||
|
||||
<TextView
|
||||
style="@style/FlexItem"
|
||||
android:layout_width="220dp"
|
||||
android:layout_height="80dp"
|
||||
android:text="1" />
|
||||
<TextView
|
||||
style="@style/FlexItem"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="80dp"
|
||||
android:text="2" />
|
||||
<TextView
|
||||
style="@style/FlexItem"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="80dp"
|
||||
android:text="3" />
|
||||
<TextView
|
||||
style="@style/FlexItem"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:text="4" />
|
||||
<TextView
|
||||
style="@style/FlexItem"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="80dp"
|
||||
android:text="5" />
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
## Attributes for the children of a FlexboxLayout
|
||||
|
||||
* __layout_order__ (integer)
|
||||
* This attribute can change how the ordering of the children views are laid out.
|
||||
By default, children are displayed and laid out in the same order as they appear in the
|
||||
layout XML. If not specified, `1` is set as a default value.
|
||||
|
||||

|
||||
|
||||
* __layout_flexGrow__ (float)
|
||||
* This attribute determines how much this child will grow if positive free space is
|
||||
distributed relative to the rest of other flex items included in the same flex line.
|
||||
If a flex item has a positive `layout_flexGrow` value, the item will take up the remaining
|
||||
space in the flex line. If multiple flex items in the same flex line have positive `layout_flexGrow`
|
||||
values, the remaining free space is distributed depending on the proportion of their declared
|
||||
`layout_flexGrow` value. (Similar to the `layout_weight` attribute in the `LinearLayout`)
|
||||
If not specified, `0` is set as a default value.
|
||||
|
||||

|
||||
|
||||
* __layout_flexShrink__ (float)
|
||||
* This attribute determines how much this child will shrink if negative free space is
|
||||
distributed relative to the rest of other flex items included in the same flex line.
|
||||
If not specified, `1` is set as a default value.
|
||||
|
||||

|
||||
|
||||
* __layout_alignSelf__
|
||||
* This attribute determines the alignment along the cross axis (perpendicular to the
|
||||
main axis). The alignment in the same direction can be determined by the
|
||||
`alignItems` in the parent, but if this is set to other than
|
||||
`auto`, the cross axis alignment is overridden for this child. Possible values are:
|
||||
* auto (default)
|
||||
* flex_start
|
||||
* flex_end
|
||||
* center
|
||||
* baseline
|
||||
* stretch
|
||||
|
||||

|
||||
|
||||
* __layout_flexBasisPercent__ (fraction)
|
||||
* The initial flex item length in a fraction format relative to its parent.
|
||||
The initial main size of this child view is trying to be expanded as the specified
|
||||
fraction against the parent main size.
|
||||
If this value is set, the length specified from `layout_width`
|
||||
(or `layout_height`) is overridden by the calculated value from this attribute.
|
||||
This attribute is only effective when the parent's length is definite (MeasureSpec mode is
|
||||
`MeasureSpec.EXACTLY`). The default value is `-1`, which means not set.
|
||||
|
||||

|
||||
|
||||
* __layout_minWidth__ / __layout_minHeight__ (dimension)
|
||||
* These attributes impose minimum size constraints for the children of FlexboxLayout.
|
||||
A child view won't shrink less than the value of these attributes (varies based on the
|
||||
`flexDirection` attribute as to which attribute imposes the size constraint along the
|
||||
main axis) regardless of the `layout_flexShrink` attribute.
|
||||
|
||||

|
||||
|
||||
* __layout_maxWidth__ / __layout_maxHeight__ (dimension)
|
||||
* These attributes impose maximum size constraints for the children of FlexboxLayout.
|
||||
A child view won't be expanded more than the value of these attributes (varies based on the
|
||||
`flexDirection` attribute as to which attribute imposes the size constraint along the
|
||||
main axis) regardless of the `layout_flexGrow` attribute.
|
||||
|
||||

|
||||
|
||||
* __layout_wrapBefore__ (boolean)
|
||||
* This attribute forces a flex line wrapping, the default value is `false`.
|
||||
i.e. if this is set to `true` for a
|
||||
flex item, the item will become the first item of a flex line. (A wrapping happens
|
||||
regardless of the flex items being processed in the previous flex line)
|
||||
This attribute is ignored if the `flex_wrap` attribute is set to `nowrap`.
|
||||
The equivalent attribute isn't defined in the original CSS Flexible Box Module
|
||||
specification, but having this attribute is useful for Android developers. For example, to flatten
|
||||
the layouts when building a grid-like layout or for a situation where developers want
|
||||
to put a new flex line to make a semantic difference from the previous one, etc.
|
||||
|
||||

|
||||
|
||||
# Others
|
||||
|
||||
## Known differences from the original CSS specification
|
||||
This library tries to achieve the same capabilities of the original
|
||||
[Flexible Box specification](https://www.w3.org/TR/css-flexbox-1) as much as possible,
|
||||
but due to some reasons such as the way specifying attributes can't be the same between
|
||||
CSS and Android XML, there are some known differences from the original specification.
|
||||
|
||||
(1) There is no [flex-flow](https://www.w3.org/TR/css-flexbox-1/#flex-flow-property)
|
||||
equivalent attribute
|
||||
* Because `flex-flow` is a shorthand for setting the `flex-direction` and `flex-wrap` properties,
|
||||
specifying two attributes from a single attribute is not practical in Android.
|
||||
|
||||
(2) There is no [flex](https://www.w3.org/TR/css-flexbox-1/#flex-property) equivalent attribute
|
||||
* Likewise `flex` is a shorthand for setting the `flex-grow`, `flex-shrink` and `flex-basis`,
|
||||
specifying those attributes from a single attribute is not practical.
|
||||
|
||||
(3) `layout_flexBasisPercent` is introduced instead of
|
||||
[flexBasis](https://www.w3.org/TR/css-flexbox-1/#flex-basis-property)
|
||||
* Both `layout_flexBasisPercent` in this library and `flex-basis` property in the CSS are used to
|
||||
determine the initial length of an individual flex item. The `flex-basis` property accepts width
|
||||
values such as `1em`, `10px`, and `content` as strings as well as percentage values such as
|
||||
`10%` and `30%`. `layout_flexBasisPercent` only accepts percentage values.
|
||||
However, specifying initial fixed width values can be done by specifying width (or height) values in
|
||||
layout_width (or layout_height, varies depending on the `flexDirection`). Also, the same
|
||||
effect can be done by specifying "wrap_content" in layout_width (or layout_height) if
|
||||
developers want to achieve the same effect as 'content'. Thus, `layout_flexBasisPercent` only
|
||||
accepts percentage values, which can't be done through layout_width (or layout_height) for
|
||||
simplicity.
|
||||
|
||||
(4) `layout_wrapBefore` is introduced.
|
||||
* The equivalent attribute doesn't exist in the CSS Flexible Box Module specification,
|
||||
but as explained above, Android developers will benefit by having this attribute for having
|
||||
more control over when a wrapping happens.
|
||||
|
||||
(5) Default values for `alignItems` and `alignContent` are set to `flex_start` instead of `stretch`.
|
||||
* Setting `stretch` for the `alignItems` is expensive because the children of `FlexboxLayout` are measured more than twice. The difference is more obvious when the layout hierarchy is deeply nested.
|
||||
|
||||
## Xamarin Binding
|
||||
Xamarin binding is now available on [NuGet](https://www.nuget.org/packages/FlexboxLayoutXamarinBindingAndroid/) thanks to [@btripp](https://github.com/btripp)
|
||||
|
||||
## Demo apps
|
||||
### Flexbox Playground demo app
|
||||
The `demo-playground` module works as a playground demo app for trying various values for the supported attributes.
|
||||
You can install it by
|
||||
```
|
||||
./gradlew demo-playground:installDebug
|
||||
```
|
||||
|
||||
### Cat gallery demo app
|
||||
The `demo-cat-gallery` module showcases the usage of the FlexboxLayoutManager inside the RecyclerView
|
||||
that handles various sizes of views aligned nicely regardless of the device width like the
|
||||
Google Photo app without loading all the images on the memory.
|
||||
Thus compared to using the {@link FlexboxLayout}, it's much less likely to abuse the memory,
|
||||
which sometimes leads to the OutOfMemoryError.
|
||||
```
|
||||
./gradlew demo-cat-gallery:installDebug
|
||||
```
|
||||
|
||||
## How to make contributions
|
||||
Please read and follow the steps in [CONTRIBUTING.md](/CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
Please see [LICENSE](/LICENSE)
|
||||
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/align-content.gif
Executable file
|
After Width: | Height: | Size: 5.0 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/align-items.gif
Executable file
|
After Width: | Height: | Size: 3.0 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/divider-beginning-middle.png
Executable file
|
After Width: | Height: | Size: 14 KiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/flex-direction.gif
Executable file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/flex-wrap.gif
Executable file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/flexbox-layoutmanager.gif
Executable file
|
After Width: | Height: | Size: 9.4 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/flexbox-visual.png
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/fluid-grid/nexus9-full-width.png
Executable file
|
After Width: | Height: | Size: 55 KiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/fluid-grid/nexus9-half.png
Executable file
|
After Width: | Height: | Size: 98 KiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/fluid-grid/nexus9-one-fourth.png
Executable file
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 97 KiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/justify-content.gif
Executable file
|
After Width: | Height: | Size: 3.2 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/layout_alignSelf.gif
Executable file
|
After Width: | Height: | Size: 4.5 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/layout_flexBasisPercent.gif
Executable file
|
After Width: | Height: | Size: 3.6 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/layout_flexGrow.gif
Executable file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/layout_flexShrink.gif
Executable file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/layout_maxWidth.gif
Executable file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/layout_minWidth.gif
Executable file
|
After Width: | Height: | Size: 3.7 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/layout_order.gif
Executable file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/layout_wrapBefore.gif
Executable file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
OnTime_Merchant_live/lib/flexbox-layout/assets/pngs/check_green_small.png
Executable file
|
After Width: | Height: | Size: 223 B |
75
OnTime_Merchant_live/lib/flexbox-layout/build.gradle
Executable file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
minSdkVersion = 14
|
||||
targetSdkVersion = 30
|
||||
compileSdkVersion = 30
|
||||
|
||||
androidGradlePluginVersion = "4.2.0"
|
||||
androidxAnnotationVersion = "1.2.0"
|
||||
androidxAppCompatVersion = "1.2.0"
|
||||
androidxCoreVersion = "1.3.2"
|
||||
androidxPreferenceVersion = "1.1.1"
|
||||
androidxRecyclerViewVersion = "1.2.0"
|
||||
androidxEspressoVersion = "3.3.0"
|
||||
androidxTestExtVersion = "1.1.2"
|
||||
androidxTestVersion = "1.3.0"
|
||||
junitVersion = "4.13.2"
|
||||
kotlinVersion = "1.4.32"
|
||||
materialVersion = "1.3.0"
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:$androidGradlePluginVersion"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
// This allows to disable pre dexing. If not disabled, it makes each CI build slow
|
||||
// See https://circleci.com/docs/android/#disable-pre-dexing-to-improve-build-performance
|
||||
// http://tools.android.com/tech-docs/new-build-system/tips#TOC-Improving-Build-Server-performance
|
||||
project.ext.preDexLibs = !project.hasProperty('disablePreDex')
|
||||
|
||||
subprojects {
|
||||
project.plugins.whenPluginAdded { plugin ->
|
||||
if ("com.android.build.gradle.AppPlugin" == plugin.class.name) {
|
||||
project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
|
||||
} else if ("com.android.build.gradle.LibraryPlugin" == plugin.class.name) {
|
||||
project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
|
||||
}
|
||||
}
|
||||
}
|
||||
47
OnTime_Merchant_live/lib/flexbox-layout/demo-cat-gallery/build.gradle
Executable file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.flexbox.apps.catgallery"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
shrinkResources true
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ":flexbox")
|
||||
implementation "androidx.appcompat:appcompat:${rootProject.androidxAppCompatVersion}"
|
||||
implementation "androidx.recyclerview:recyclerview:${rootProject.androidxRecyclerViewVersion}"
|
||||
implementation "com.google.android.material:material:${rootProject.materialVersion}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${rootProject.kotlinVersion}"
|
||||
}
|
||||
17
OnTime_Merchant_live/lib/flexbox-layout/demo-cat-gallery/proguard-rules.pro
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/google/home/thagikura/android-sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.flexbox.apps.catgallery">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox.apps.catgallery
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
/**
|
||||
* Adapter class that handles the data set with the {@link RecyclerView.LayoutManager}
|
||||
*/
|
||||
internal class CatAdapter : RecyclerView.Adapter<CatViewHolder>() {
|
||||
|
||||
companion object {
|
||||
private val CAT_IMAGE_IDS = intArrayOf(
|
||||
R.drawable.cat_1,
|
||||
R.drawable.cat_2,
|
||||
R.drawable.cat_3,
|
||||
R.drawable.cat_4,
|
||||
R.drawable.cat_5,
|
||||
R.drawable.cat_6,
|
||||
R.drawable.cat_7,
|
||||
R.drawable.cat_8,
|
||||
R.drawable.cat_9,
|
||||
R.drawable.cat_10,
|
||||
R.drawable.cat_11,
|
||||
R.drawable.cat_12,
|
||||
R.drawable.cat_13,
|
||||
R.drawable.cat_14,
|
||||
R.drawable.cat_15,
|
||||
R.drawable.cat_16,
|
||||
R.drawable.cat_17,
|
||||
R.drawable.cat_18,
|
||||
R.drawable.cat_19
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CatViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.viewholder_cat, parent, false)
|
||||
return CatViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: CatViewHolder, position: Int) {
|
||||
val pos = position % CAT_IMAGE_IDS.size
|
||||
holder.bindTo(CAT_IMAGE_IDS[pos])
|
||||
}
|
||||
|
||||
override fun getItemCount() = CAT_IMAGE_IDS.size * 4
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox.apps.catgallery
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.flexbox.FlexboxLayoutManager
|
||||
|
||||
/**
|
||||
* ViewHolder that represents a cat image.
|
||||
*/
|
||||
internal class CatViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private val imageView: ImageView = itemView.findViewById(R.id.imageview)
|
||||
|
||||
internal fun bindTo(@DrawableRes drawableRes: Int) {
|
||||
imageView.setImageResource(drawableRes)
|
||||
val lp = imageView.layoutParams
|
||||
if (lp is FlexboxLayoutManager.LayoutParams) {
|
||||
lp.flexGrow = 1f
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox.apps.catgallery
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.flexbox.AlignItems
|
||||
import com.google.android.flexbox.FlexDirection
|
||||
import com.google.android.flexbox.FlexWrap
|
||||
import com.google.android.flexbox.FlexboxLayoutManager
|
||||
|
||||
/**
|
||||
* Launcher Activity for the cat gallery demo app that demonstrates the usage of the
|
||||
* {@link FlexboxLayoutManager} that handles various sizes of views aligned nicely regardless of
|
||||
* the device width like the Google Photo app without loading all the images on the memory.
|
||||
* Thus compared to using the {@link FlexboxLayout}, it's much less likely to abuse the memory,
|
||||
* which some times leads to the OutOfMemoryError.
|
||||
*/
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val toolbar: Toolbar = findViewById(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val flexboxLayoutManager = FlexboxLayoutManager(this).apply {
|
||||
flexWrap = FlexWrap.WRAP
|
||||
flexDirection = FlexDirection.ROW
|
||||
alignItems = AlignItems.STRETCH
|
||||
}
|
||||
|
||||
val recyclerView: RecyclerView = findViewById(R.id.recyclerview)
|
||||
recyclerView.apply {
|
||||
layoutManager = flexboxLayoutManager
|
||||
adapter = CatAdapter()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Google Inc. All rights reserved.
|
||||
|
||||
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.
|
||||
-->
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/coordinator_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="com.google.android.flexbox.apps.catgallery.MainActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/app_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/app_bar_height"
|
||||
android:fitsSystemWindows="true"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:id="@+id/toolbar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
app:contentScrim="?attr/colorPrimary"
|
||||
app:layout_scrollFlags="scroll|enterAlwaysCollapsed">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_collapseMode="pin"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_main"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Google Inc. All rights reserved.
|
||||
|
||||
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.
|
||||
-->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:id="@+id/recyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Google Inc. All rights reserved.
|
||||
|
||||
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.
|
||||
-->
|
||||
<ImageView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/imageview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_margin="1dp"
|
||||
android:contentDescription="@string/content_description_cat_view_holder"/>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Google Inc. All rights reserved.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Google Inc. All rights reserved.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Google Inc. All rights reserved.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<dimen name="app_bar_height">180dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Google Inc. All rights reserved.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">Cat Gallery</string>
|
||||
<string name="content_description_cat_view_holder">Cat image</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Google Inc. All rights reserved.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
|
||||
|
||||
</resources>
|
||||
55
OnTime_Merchant_live/lib/flexbox-layout/demo-playground/build.gradle
Executable file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.apps.flexbox"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
shrinkResources true
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":flexbox")
|
||||
implementation "androidx.annotation:annotation:${rootProject.androidxAnnotationVersion}"
|
||||
implementation "androidx.appcompat:appcompat:${rootProject.androidxAppCompatVersion}"
|
||||
implementation "androidx.preference:preference:${rootProject.androidxPreferenceVersion}"
|
||||
implementation "com.google.android.material:material:${rootProject.materialVersion}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||
|
||||
testImplementation "junit:junit:${rootProject.ext.junitVersion}"
|
||||
|
||||
androidTestImplementation "androidx.annotation:annotation:${rootProject.androidxAnnotationVersion}"
|
||||
androidTestImplementation "androidx.test:runner:${rootProject.androidxTestVersion}"
|
||||
androidTestImplementation "androidx.test:rules:${rootProject.androidxTestVersion}"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:${rootProject.androidxEspressoVersion}"
|
||||
}
|
||||
33
OnTime_Merchant_live/lib/flexbox-layout/demo-playground/proguard-rules.pro
vendored
Executable file
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# Copyright 2016 Google Inc. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.flexbox.test
|
||||
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.RadioGroup
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions.*
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.filters.FlakyTest
|
||||
import androidx.test.filters.MediumTest
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import com.google.android.apps.flexbox.R
|
||||
import com.google.android.flexbox.*
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.core.Is.`is`
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Integration tests for [MainActivity].
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
class MainActivityTest {
|
||||
|
||||
@JvmField
|
||||
@Rule
|
||||
var activityRule = ActivityTestRule(MainActivity::class.java)
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testAddFlexItem() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<FlexboxLayout>(R.id.flexbox_layout)
|
||||
assertNotNull(flexboxLayout)
|
||||
val beforeCount = flexboxLayout.childCount
|
||||
onView(withId(R.id.add_fab)).perform(click())
|
||||
|
||||
assertThat(flexboxLayout.childCount, `is`(beforeCount + 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testRemoveFlexItem() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<FlexboxLayout>(R.id.flexbox_layout)
|
||||
assertNotNull(flexboxLayout)
|
||||
val beforeCount = flexboxLayout.childCount
|
||||
onView(withId(R.id.remove_fab)).perform(click())
|
||||
|
||||
assertThat(flexboxLayout.childCount, `is`(beforeCount - 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testConfigurationChange() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<FlexboxLayout>(R.id.flexbox_layout)
|
||||
assertNotNull(flexboxLayout)
|
||||
onView(withId(R.id.add_fab)).perform(click())
|
||||
onView(withId(R.id.add_fab)).perform(click())
|
||||
onView(withId(R.id.add_fab)).perform(click())
|
||||
val beforeCount = flexboxLayout.childCount
|
||||
|
||||
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
|
||||
// Verify the flex items are restored across the configuration change.
|
||||
assertThat(flexboxLayout.childCount, `is`(beforeCount))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testFlexDirectionSpinner() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<FlexboxLayout>(R.id.flexbox_layout)
|
||||
assertNotNull(flexboxLayout)
|
||||
val navigationView = activity.findViewById<NavigationView>(R.id.nav_view)
|
||||
assertNotNull(navigationView)
|
||||
val menu = navigationView.menu
|
||||
val spinner = menu.findItem(R.id.menu_item_flex_direction).actionView as Spinner
|
||||
val spinnerAdapter = spinner.adapter as ArrayAdapter<CharSequence>
|
||||
|
||||
val columnPosition = spinnerAdapter.getPosition(activity.getString(R.string.column))
|
||||
activity.runOnUiThread { spinner.setSelection(columnPosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.flexDirection, `is`(FlexDirection.COLUMN))
|
||||
|
||||
val rowReversePosition = spinnerAdapter.getPosition(activity.getString(R.string.row_reverse))
|
||||
activity.runOnUiThread { spinner.setSelection(rowReversePosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.flexDirection, `is`(FlexDirection.ROW_REVERSE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testFlexWrapSpinner() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<FlexboxLayout>(R.id.flexbox_layout)
|
||||
assertNotNull(flexboxLayout)
|
||||
val navigationView = activity.findViewById<NavigationView>(R.id.nav_view)
|
||||
assertNotNull(navigationView)
|
||||
val menu = navigationView.menu
|
||||
val spinner = menu.findItem(R.id.menu_item_flex_wrap).actionView as Spinner
|
||||
val spinnerAdapter = spinner.adapter as ArrayAdapter<CharSequence>
|
||||
|
||||
val wrapReversePosition = spinnerAdapter.getPosition(activity.getString(R.string.wrap_reverse))
|
||||
activity.runOnUiThread { spinner.setSelection(wrapReversePosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.flexWrap, `is`(FlexWrap.WRAP_REVERSE))
|
||||
|
||||
val noWrapPosition = spinnerAdapter.getPosition(activity.getString(R.string.nowrap))
|
||||
activity.runOnUiThread { spinner.setSelection(noWrapPosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.flexWrap, `is`(FlexWrap.NOWRAP))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testJustifyContentSpinner() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<View>(R.id.flexbox_layout) as FlexboxLayout
|
||||
assertNotNull(flexboxLayout)
|
||||
val navigationView = activity.findViewById<View>(R.id.nav_view) as NavigationView
|
||||
assertNotNull(navigationView)
|
||||
val menu = navigationView.menu
|
||||
val spinner = menu.findItem(R.id.menu_item_justify_content).actionView as Spinner
|
||||
val spinnerAdapter = spinner.adapter as ArrayAdapter<CharSequence>
|
||||
|
||||
val spaceBetweenPosition = spinnerAdapter.getPosition(activity.getString(R.string.space_between))
|
||||
activity.runOnUiThread { spinner.setSelection(spaceBetweenPosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.justifyContent, `is`(JustifyContent.SPACE_BETWEEN))
|
||||
|
||||
val centerPosition = spinnerAdapter.getPosition(activity.getString(R.string.center))
|
||||
activity.runOnUiThread { spinner.setSelection(centerPosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.justifyContent, `is`(JustifyContent.CENTER))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testAlignItemsSpinner() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<FlexboxLayout>(R.id.flexbox_layout)
|
||||
assertNotNull(flexboxLayout)
|
||||
val navigationView = activity.findViewById<NavigationView>(R.id.nav_view)
|
||||
assertNotNull(navigationView)
|
||||
val menu = navigationView.menu
|
||||
val spinner = menu.findItem(R.id.menu_item_align_items).actionView as Spinner
|
||||
val spinnerAdapter = spinner.adapter as ArrayAdapter<CharSequence>
|
||||
|
||||
val baselinePosition = spinnerAdapter.getPosition(activity.getString(R.string.baseline))
|
||||
activity.runOnUiThread { spinner.setSelection(baselinePosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.alignItems, `is`(AlignItems.BASELINE))
|
||||
|
||||
val flexEndPosition = spinnerAdapter.getPosition(activity.getString(R.string.flex_end))
|
||||
activity.runOnUiThread { spinner.setSelection(flexEndPosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.alignItems, `is`(AlignItems.FLEX_END))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testAlignContentSpinner() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<FlexboxLayout>(R.id.flexbox_layout)
|
||||
assertNotNull(flexboxLayout)
|
||||
val navigationView = activity.findViewById<NavigationView>(R.id.nav_view)
|
||||
assertNotNull(navigationView)
|
||||
val menu = navigationView.menu
|
||||
val spinner = menu.findItem(R.id.menu_item_align_content).actionView as Spinner
|
||||
val spinnerAdapter = spinner.adapter as ArrayAdapter<CharSequence>
|
||||
|
||||
val spaceAroundPosition = spinnerAdapter.getPosition(activity.getString(R.string.space_around))
|
||||
activity.runOnUiThread { spinner.setSelection(spaceAroundPosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.alignContent, `is`(AlignContent.SPACE_AROUND))
|
||||
|
||||
val stretchPosition = spinnerAdapter.getPosition(activity.getString(R.string.stretch))
|
||||
activity.runOnUiThread { spinner.setSelection(stretchPosition) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(flexboxLayout.alignContent, `is`(AlignContent.STRETCH))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testEditFragment_changeOrder() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<View>(R.id.flexbox_layout) as FlexboxLayout
|
||||
assertNotNull(flexboxLayout)
|
||||
onView(withId(R.id.textview1)).perform(click())
|
||||
onView(withId(R.id.edit_text_order)).perform(replaceText("3"), closeSoftKeyboard())
|
||||
onView(withId(R.id.button_ok)).perform(click())
|
||||
val first = flexboxLayout.getReorderedChildAt(0) as TextView
|
||||
val second = flexboxLayout.getReorderedChildAt(1) as TextView
|
||||
val third = flexboxLayout.getReorderedChildAt(2) as TextView
|
||||
|
||||
assertThat(first.text.toString(), `is`("2"))
|
||||
assertThat(second.text.toString(), `is`("3"))
|
||||
assertThat(third.text.toString(), `is`("1"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testEditFragment_changeFlexGrow() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<View>(R.id.flexbox_layout) as FlexboxLayout
|
||||
assertNotNull(flexboxLayout)
|
||||
onView(withId(R.id.textview1)).perform(click())
|
||||
onView(withId(R.id.edit_text_flex_grow)).perform(replaceText("1"), closeSoftKeyboard())
|
||||
onView(withId(R.id.button_ok)).perform(click())
|
||||
val first = activity.findViewById<View>(R.id.textview1) as TextView
|
||||
val second = activity.findViewById<View>(R.id.textview2) as TextView
|
||||
val third = activity.findViewById<View>(R.id.textview3) as TextView
|
||||
assertNotNull(first)
|
||||
assertNotNull(second)
|
||||
assertNotNull(third)
|
||||
|
||||
assertThat(first.width, `is`(flexboxLayout.width - second.width - third.width))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testEditFragment_changeFlexGrowFloat() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<View>(R.id.flexbox_layout) as FlexboxLayout
|
||||
assertNotNull(flexboxLayout)
|
||||
onView(withId(R.id.textview1)).perform(click())
|
||||
onView(withId(R.id.edit_text_flex_grow)).perform(replaceText("1.0"), closeSoftKeyboard())
|
||||
onView(withId(R.id.button_ok)).perform(click())
|
||||
val first = activity.findViewById<View>(R.id.textview1) as TextView
|
||||
val second = activity.findViewById<View>(R.id.textview2) as TextView
|
||||
val third = activity.findViewById<View>(R.id.textview3) as TextView
|
||||
assertNotNull(first)
|
||||
assertNotNull(second)
|
||||
assertNotNull(third)
|
||||
|
||||
assertThat(first.width, `is`(flexboxLayout.width - second.width - third.width))
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testEditFragment_changeFlexBasisPercent() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<View>(R.id.flexbox_layout) as FlexboxLayout
|
||||
assertNotNull(flexboxLayout)
|
||||
onView(withId(R.id.textview1)).perform(click())
|
||||
onView(withId(R.id.edit_text_flex_basis_percent))
|
||||
.perform(replaceText("50"), closeSoftKeyboard())
|
||||
onView(withId(R.id.button_ok)).perform(click())
|
||||
val first = activity.findViewById<TextView>(R.id.textview1)
|
||||
val second = activity.findViewById<TextView>(R.id.textview2)
|
||||
val third = activity.findViewById<TextView>(R.id.textview3)
|
||||
assertNotNull(first)
|
||||
assertNotNull(second)
|
||||
assertNotNull(third)
|
||||
|
||||
assertTrue(first.width - 1 <= flexboxLayout.width / 2 || flexboxLayout.width / 2 <= first.width + 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest
|
||||
fun testSwitchRecyclerViewFragment() {
|
||||
val activity = activityRule.activity
|
||||
val flexboxLayout = activity.findViewById<FlexboxLayout>(R.id.flexbox_layout)
|
||||
assertNotNull(flexboxLayout)
|
||||
val navigationView = activity.findViewById<NavigationView>(R.id.nav_view)
|
||||
assertNotNull(navigationView)
|
||||
assertNull(activity.findViewById(R.id.recyclerview))
|
||||
assertNotNull(activity.findViewById(R.id.flexbox_layout))
|
||||
|
||||
val radioGroup = navigationView.getHeaderView(0)
|
||||
.findViewById<RadioGroup>(R.id.radiogroup_container_implementation)
|
||||
activity.runOnUiThread { radioGroup.check(R.id.radiobutton_recyclerview) }
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertNotNull(activity.findViewById(R.id.recyclerview))
|
||||
assertNull(activity.findViewById(R.id.flexbox_layout))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2016 Google Inc. All rights reserved.
|
||||
|
||||
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.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.apps.flexbox">
|
||||
|
||||
<uses-sdk tools:overrideLibrary="android.support.v14.preference" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name="com.google.android.flexbox.MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.google.android.flexbox.SettingsActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.content.Context
|
||||
|
||||
/**
|
||||
* Convert pixel to dp. Preserve the negative value as it's used for representing
|
||||
* MATCH_PARENT(-1) and WRAP_CONTENT(-2).
|
||||
* Ignore the round error that might happen in dividing the pixel by the density.
|
||||
*
|
||||
* @param pixel the value in pixel
|
||||
*
|
||||
* @return the converted value in dp
|
||||
*/
|
||||
fun Context.pixelToDp(pixel: Int): Int {
|
||||
val displayMetrics = this.resources.displayMetrics
|
||||
return if (pixel < 0) pixel else Math.round(pixel / displayMetrics.density)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert dp to pixel. Preserve the negative value as it's used for representing
|
||||
* MATCH_PARENT(-1) and WRAP_CONTENT(-2).
|
||||
*
|
||||
* @param dp the value in dp
|
||||
*
|
||||
* @return the converted value in pixel
|
||||
*/
|
||||
fun Context.dpToPixel(dp: Int): Int {
|
||||
val displayMetrics = this.resources.displayMetrics
|
||||
return if (dp < 0) dp else Math.round(dp * displayMetrics.density)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.apps.flexbox.R
|
||||
|
||||
/**
|
||||
* [RecyclerView.Adapter] implementation for [FlexItemViewHolder].
|
||||
*/
|
||||
internal class FlexItemAdapter(private val activity: AppCompatActivity,
|
||||
private val flexContainer: FlexContainer)
|
||||
: RecyclerView.Adapter<FlexItemViewHolder>() {
|
||||
|
||||
private val layoutParams = mutableListOf<FlexboxLayoutManager.LayoutParams>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FlexItemViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.viewholder_flex_item, parent, false)
|
||||
|
||||
return FlexItemViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FlexItemViewHolder, position: Int) {
|
||||
val adapterPosition = holder.adapterPosition
|
||||
// TODO: More optimized set the click listener inside the view holder
|
||||
holder.itemView.setOnClickListener(FlexItemClickListener(activity,
|
||||
FlexItemChangedListenerImplRecyclerView(flexContainer, this),
|
||||
adapterPosition))
|
||||
holder.bindTo(layoutParams[position])
|
||||
}
|
||||
|
||||
fun addItem(lp: FlexboxLayoutManager.LayoutParams) {
|
||||
layoutParams.add(lp)
|
||||
notifyItemInserted(layoutParams.size - 1)
|
||||
}
|
||||
|
||||
fun removeItem(position: Int) {
|
||||
if (position < 0 || position >= layoutParams.size) {
|
||||
return
|
||||
}
|
||||
layoutParams.removeAt(position)
|
||||
notifyItemRemoved(layoutParams.size)
|
||||
notifyItemRangeChanged(position, layoutParams.size)
|
||||
}
|
||||
|
||||
val items get() = layoutParams
|
||||
|
||||
override fun getItemCount() = layoutParams.size
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
/**
|
||||
* A listener that listens to the change of a flex item
|
||||
*/
|
||||
internal interface FlexItemChangedListener {
|
||||
|
||||
fun onFlexItemChanged(flexItem: FlexItem, viewIndex: Int)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.view.ViewGroup
|
||||
|
||||
/**
|
||||
* Default implementation for the [FlexItemChangedListener].
|
||||
*/
|
||||
internal class FlexItemChangedListenerImpl(private val flexContainer: FlexContainer) : FlexItemChangedListener {
|
||||
|
||||
override fun onFlexItemChanged(flexItem: FlexItem, viewIndex: Int) {
|
||||
val view = flexContainer.getFlexItemAt(viewIndex)
|
||||
view.layoutParams = flexItem as ViewGroup.LayoutParams
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
/**
|
||||
* Implementation for the [FlexItemChangedListener].
|
||||
* It expects RecyclerView as the underlying flex container implementation.
|
||||
*/
|
||||
internal class FlexItemChangedListenerImplRecyclerView(private val flexContainer: FlexContainer,
|
||||
private val adapter: RecyclerView.Adapter<*>) : FlexItemChangedListener {
|
||||
|
||||
override fun onFlexItemChanged(flexItem: FlexItem, viewIndex: Int) {
|
||||
val view = flexContainer.getFlexItemAt(viewIndex)
|
||||
view.layoutParams = flexItem as ViewGroup.LayoutParams
|
||||
adapter.notifyDataSetChanged()
|
||||
// TODO: An Exception is thrown if notifyItemChanged(int) is used.
|
||||
// Investigate that, but using LinearLayoutManager also produces the same Exception
|
||||
// java.lang.IllegalArgumentException: Called attach on a child which is not detached:
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
/**
|
||||
* Implementation of the [android.view.View.OnClickListener] when a flex item is clicked in
|
||||
* the Flexbox Playground demo app.
|
||||
*/
|
||||
internal class FlexItemClickListener(private val activity: AppCompatActivity, private val flexItemChangedListener: FlexItemChangedListener,
|
||||
private val viewIndex: Int) : View.OnClickListener {
|
||||
|
||||
override fun onClick(v: View) =
|
||||
FlexItemEditFragment.newInstance(v.layoutParams as FlexItem, viewIndex).apply {
|
||||
setFlexItemChangedListener(flexItemChangedListener)
|
||||
}.show(activity.supportFragmentManager, EDIT_DIALOG_TAG)
|
||||
|
||||
companion object {
|
||||
|
||||
private const val EDIT_DIALOG_TAG = "edit_dialog_tag"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.*
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.google.android.apps.flexbox.R
|
||||
import com.google.android.flexbox.validators.*
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
|
||||
/**
|
||||
* DialogFragment that changes the properties for a flex item.
|
||||
*/
|
||||
internal class FlexItemEditFragment : DialogFragment() {
|
||||
|
||||
private lateinit var alignSelfAuto: String
|
||||
|
||||
private lateinit var alignSelfFlexStart: String
|
||||
|
||||
private lateinit var alignSelfFlexEnd: String
|
||||
|
||||
private lateinit var alignSelfCenter: String
|
||||
|
||||
private lateinit var alignSelfBaseline: String
|
||||
|
||||
private lateinit var alignSelfStretch: String
|
||||
|
||||
private var viewIndex: Int = 0
|
||||
|
||||
private lateinit var flexItem: FlexItem
|
||||
|
||||
/**
|
||||
* Instance of a [FlexItem] being edited. At first it's created as another instance from
|
||||
* the [flexItem] because otherwise changes before clicking the ok button will be
|
||||
* reflected if the [flexItem] is changed directly.
|
||||
*/
|
||||
private lateinit var flexItemInEdit: FlexItem
|
||||
|
||||
private var flexItemChangedListener: FlexItemChangedListener? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
setStyle(STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog)
|
||||
} else {
|
||||
setStyle(STYLE_NORMAL, android.R.style.Theme_Dialog)
|
||||
}
|
||||
arguments?.let {
|
||||
flexItem = it.getParcelable(FLEX_ITEM_KEY)!!
|
||||
viewIndex = it.getInt(VIEW_INDEX_KEY)
|
||||
}
|
||||
flexItemInEdit = createNewFlexItem(flexItem)
|
||||
|
||||
activity?.let {
|
||||
alignSelfAuto = it.getString(R.string.auto)
|
||||
alignSelfFlexStart = it.getString(R.string.flex_start)
|
||||
alignSelfFlexEnd = it.getString(R.string.flex_end)
|
||||
alignSelfCenter = it.getString(R.string.center)
|
||||
alignSelfBaseline = it.getString(R.string.baseline)
|
||||
alignSelfStretch = it.getString(R.string.stretch)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_flex_item_edit, container, false)
|
||||
dialog?.setTitle((viewIndex + 1).toString())
|
||||
|
||||
val context = activity ?: return view
|
||||
val orderTextInput: TextInputLayout = view.findViewById(R.id.input_layout_order)
|
||||
val orderEdit: EditText = view.findViewById(R.id.edit_text_order)
|
||||
orderEdit.setText(flexItem.order.toString())
|
||||
orderEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, orderTextInput, IntegerInputValidator(),
|
||||
R.string.must_be_integer))
|
||||
if (flexItem is FlexboxLayoutManager.LayoutParams) {
|
||||
// Order is not enabled in FlexboxLayoutManager
|
||||
orderEdit.isEnabled = false
|
||||
}
|
||||
|
||||
val flexGrowInput: TextInputLayout = view .findViewById(R.id.input_layout_flex_grow)
|
||||
val flexGrowEdit: EditText = view.findViewById(R.id.edit_text_flex_grow)
|
||||
flexGrowEdit.setText(flexItem.flexGrow.toString())
|
||||
flexGrowEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, flexGrowInput, NonNegativeDecimalInputValidator(),
|
||||
R.string.must_be_non_negative_float))
|
||||
|
||||
val flexShrinkInput: TextInputLayout = view.findViewById(R.id.input_layout_flex_shrink)
|
||||
val flexShrinkEdit: EditText = view.findViewById(R.id.edit_text_flex_shrink)
|
||||
flexShrinkEdit.setText(flexItem.flexShrink.toString())
|
||||
flexShrinkEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, flexShrinkInput, NonNegativeDecimalInputValidator(),
|
||||
R.string.must_be_non_negative_float))
|
||||
|
||||
val flexBasisPercentInput: TextInputLayout =
|
||||
view.findViewById(R.id.input_layout_flex_basis_percent)
|
||||
val flexBasisPercentEdit: EditText = view.findViewById(R.id.edit_text_flex_basis_percent)
|
||||
if (flexItem.flexBasisPercent != FlexboxLayout.LayoutParams.FLEX_BASIS_PERCENT_DEFAULT) {
|
||||
flexBasisPercentEdit
|
||||
.setText(Math.round(flexItem.flexBasisPercent * 100).toString())
|
||||
} else {
|
||||
flexBasisPercentEdit.setText(flexItem.flexBasisPercent.toInt().toString())
|
||||
}
|
||||
flexBasisPercentEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, flexBasisPercentInput, FlexBasisPercentInputValidator(),
|
||||
R.string.must_be_minus_one_or_non_negative_integer))
|
||||
|
||||
val widthInput: TextInputLayout = view.findViewById(R.id.input_layout_width)
|
||||
val widthEdit: EditText = view.findViewById(R.id.edit_text_width)
|
||||
widthEdit.setText(context.pixelToDp(flexItem.width).toString())
|
||||
widthEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, widthInput, DimensionInputValidator(),
|
||||
R.string.must_be_minus_one_or_minus_two_or_non_negative_integer))
|
||||
|
||||
val heightInput: TextInputLayout = view.findViewById(R.id.input_layout_height)
|
||||
val heightEdit: EditText= view.findViewById(R.id.edit_text_height)
|
||||
heightEdit.setText(context.pixelToDp(flexItem.height).toString())
|
||||
heightEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, heightInput, DimensionInputValidator(),
|
||||
R.string.must_be_minus_one_or_minus_two_or_non_negative_integer))
|
||||
|
||||
val minWidthInput: TextInputLayout = view.findViewById(R.id.input_layout_min_width)
|
||||
val minWidthEdit: EditText = view.findViewById(R.id.edit_text_min_width)
|
||||
minWidthEdit.setText(context.pixelToDp(flexItem.minWidth).toString())
|
||||
minWidthEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, minWidthInput, FixedDimensionInputValidator(),
|
||||
R.string.must_be_non_negative_integer))
|
||||
|
||||
val minHeightInput: TextInputLayout = view.findViewById(R.id.input_layout_min_height)
|
||||
val minHeightEdit: EditText = view.findViewById(R.id.edit_text_min_height)
|
||||
minHeightEdit.setText(context.pixelToDp(flexItem.minHeight).toString())
|
||||
minHeightEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, minHeightInput, FixedDimensionInputValidator(),
|
||||
R.string.must_be_non_negative_integer))
|
||||
|
||||
val maxWidthInput: TextInputLayout = view.findViewById(R.id.input_layout_max_width)
|
||||
val maxWidthEdit: EditText = view.findViewById(R.id.edit_text_max_width)
|
||||
maxWidthEdit.setText(context.pixelToDp(flexItem.maxWidth).toString())
|
||||
maxWidthEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, maxWidthInput, FixedDimensionInputValidator(),
|
||||
R.string.must_be_non_negative_integer))
|
||||
|
||||
val maxHeightInput: TextInputLayout = view.findViewById(R.id.input_layout_max_height)
|
||||
val maxHeightEdit: EditText = view.findViewById(R.id.edit_text_max_height)
|
||||
maxHeightEdit.setText(context.pixelToDp(flexItem.maxHeight).toString())
|
||||
maxHeightEdit.addTextChangedListener(
|
||||
FlexEditTextWatcher(context, maxHeightInput, FixedDimensionInputValidator(),
|
||||
R.string.must_be_non_negative_integer))
|
||||
|
||||
setNextFocusesOnEnterDown(orderEdit, flexGrowEdit, flexShrinkEdit, flexBasisPercentEdit,
|
||||
widthEdit, heightEdit, minWidthEdit, minHeightEdit, maxWidthEdit, maxHeightEdit)
|
||||
|
||||
val alignSelfSpinner: Spinner = view.findViewById(R.id.spinner_align_self)
|
||||
val arrayAdapter = ArrayAdapter.createFromResource(requireActivity(),
|
||||
R.array.array_align_self, R.layout.spinner_item)
|
||||
alignSelfSpinner.adapter = arrayAdapter
|
||||
alignSelfSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, ignored: View?, position: Int,
|
||||
id: Long) {
|
||||
flexItemInEdit.alignSelf = when (parent.getItemAtPosition(position).toString()) {
|
||||
alignSelfAuto -> AlignSelf.AUTO
|
||||
alignSelfFlexStart -> AlignItems.FLEX_START
|
||||
alignSelfFlexEnd -> AlignItems.FLEX_END
|
||||
alignSelfCenter -> AlignItems.CENTER
|
||||
alignSelfBaseline -> AlignItems.BASELINE
|
||||
alignSelfStretch -> AlignItems.STRETCH
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// No op
|
||||
}
|
||||
}
|
||||
|
||||
val wrapBeforeCheckBox: CheckBox = view.findViewById(R.id.checkbox_wrap_before)
|
||||
wrapBeforeCheckBox.isChecked = flexItem.isWrapBefore
|
||||
wrapBeforeCheckBox.setOnCheckedChangeListener { _, isChecked ->
|
||||
flexItemInEdit.isWrapBefore = isChecked }
|
||||
val alignSelfPosition = arrayAdapter
|
||||
.getPosition(alignSelfAsString(flexItem.alignSelf))
|
||||
alignSelfSpinner.setSelection(alignSelfPosition)
|
||||
|
||||
view.findViewById<Button>(R.id.button_cancel).setOnClickListener {
|
||||
copyFlexItemValues(flexItem, flexItemInEdit)
|
||||
dismiss()
|
||||
}
|
||||
val okButton: Button = view.findViewById(R.id.button_ok)
|
||||
okButton.setOnClickListener(View.OnClickListener {
|
||||
if (orderTextInput.isErrorEnabled || flexGrowInput.isErrorEnabled ||
|
||||
flexBasisPercentInput.isErrorEnabled || widthInput.isErrorEnabled ||
|
||||
heightInput.isErrorEnabled || minWidthInput.isErrorEnabled ||
|
||||
minHeightInput.isErrorEnabled || maxWidthInput.isErrorEnabled ||
|
||||
maxHeightInput.isErrorEnabled) {
|
||||
Toast.makeText(activity, R.string.invalid_values_exist, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
return@OnClickListener
|
||||
}
|
||||
if (flexItemChangedListener != null) {
|
||||
copyFlexItemValues(flexItemInEdit, flexItem)
|
||||
flexItemChangedListener!!.onFlexItemChanged(flexItem, viewIndex)
|
||||
}
|
||||
dismiss()
|
||||
})
|
||||
return view
|
||||
}
|
||||
|
||||
fun setFlexItemChangedListener(flexItemChangedListener: FlexItemChangedListener) {
|
||||
this.flexItemChangedListener = flexItemChangedListener
|
||||
}
|
||||
|
||||
private fun setNextFocusesOnEnterDown(vararg textViews: TextView) {
|
||||
// This can be done by setting android:nextFocus* as in
|
||||
// https://developer.android.com/training/keyboard-input/navigation.html
|
||||
// But it requires API level 11 as a minimum sdk version. To support the lower level
|
||||
// devices,
|
||||
// doing it programmatically.
|
||||
for (i in textViews.indices) {
|
||||
textViews[i].setOnEditorActionListener { v, actionId, event ->
|
||||
if (actionId == EditorInfo.IME_ACTION_NEXT ||
|
||||
actionId == EditorInfo.IME_ACTION_DONE ||
|
||||
actionId == EditorInfo.IME_NULL
|
||||
&& event.action == KeyEvent.ACTION_DOWN
|
||||
&& event.keyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
if (i + 1 < textViews.size) {
|
||||
textViews[i + 1].requestFocus()
|
||||
} else if (i == textViews.size - 1) {
|
||||
val inputMethodManager = activity?.getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE) as InputMethodManager?
|
||||
inputMethodManager?.hideSoftInputFromWindow(v.windowToken, 0)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// Suppress the key focus change by KeyEvent.ACTION_UP of the enter key
|
||||
textViews[i].setOnKeyListener { _, keyCode, event -> keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_UP }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun alignSelfAsString(alignSelf: Int): String {
|
||||
return when (alignSelf) {
|
||||
AlignSelf.AUTO -> alignSelfAuto
|
||||
AlignItems.FLEX_START -> alignSelfFlexStart
|
||||
AlignItems.FLEX_END -> alignSelfFlexEnd
|
||||
AlignItems.CENTER -> alignSelfCenter
|
||||
AlignItems.BASELINE -> alignSelfBaseline
|
||||
AlignItems.STRETCH -> alignSelfStretch
|
||||
else -> alignSelfAuto
|
||||
}
|
||||
}
|
||||
|
||||
private inner class FlexEditTextWatcher internal constructor(val context: Context,
|
||||
val textInputLayout: TextInputLayout,
|
||||
val inputValidator: InputValidator,
|
||||
val errorMessageId: Int) : TextWatcher {
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
if (inputValidator.isValidInput(s)) {
|
||||
textInputLayout.isErrorEnabled = false
|
||||
textInputLayout.error = ""
|
||||
} else {
|
||||
textInputLayout.isErrorEnabled = true
|
||||
textInputLayout.error = activity?.resources?.getString(errorMessageId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
if (textInputLayout.isErrorEnabled || editable.isEmpty() ||
|
||||
!inputValidator.isValidInput(editable.toString())) {
|
||||
return
|
||||
}
|
||||
val value = editable.toString().toFloatOrNull() ?: return
|
||||
when (textInputLayout.id) {
|
||||
R.id.input_layout_order -> if (flexItemInEdit !is FlexboxLayoutManager.LayoutParams) {
|
||||
flexItemInEdit.order = value.toInt()
|
||||
} else return
|
||||
R.id.input_layout_flex_grow -> flexItemInEdit.flexGrow = value
|
||||
R.id.input_layout_flex_shrink -> flexItemInEdit.flexShrink = value
|
||||
R.id.input_layout_width -> flexItemInEdit.width = context.dpToPixel(value.toInt())
|
||||
R.id.input_layout_height -> flexItemInEdit.height = context.dpToPixel(value.toInt())
|
||||
R.id.input_layout_flex_basis_percent -> if (value != FlexboxLayout.LayoutParams.FLEX_BASIS_PERCENT_DEFAULT) {
|
||||
flexItemInEdit.flexBasisPercent = value.toInt() / 100.0f
|
||||
} else {
|
||||
flexItemInEdit.flexBasisPercent = FlexItem.FLEX_BASIS_PERCENT_DEFAULT
|
||||
}
|
||||
R.id.input_layout_min_width -> flexItemInEdit.minWidth = context.dpToPixel(value.toInt())
|
||||
R.id.input_layout_min_height -> flexItemInEdit.minHeight = context.dpToPixel(value.toInt())
|
||||
R.id.input_layout_max_width -> flexItemInEdit.maxWidth = context.dpToPixel(value.toInt())
|
||||
R.id.input_layout_max_height -> flexItemInEdit.maxHeight = context.dpToPixel(value.toInt())
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNewFlexItem(item: FlexItem): FlexItem {
|
||||
if (item is FlexboxLayout.LayoutParams) {
|
||||
val newItem = FlexboxLayout.LayoutParams(item.getWidth(), item.getHeight())
|
||||
copyFlexItemValues(item, newItem)
|
||||
return newItem
|
||||
} else if (item is FlexboxLayoutManager.LayoutParams) {
|
||||
val newItem = FlexboxLayoutManager.LayoutParams(item.getWidth(), item.getHeight())
|
||||
copyFlexItemValues(item, newItem)
|
||||
return newItem
|
||||
}
|
||||
throw IllegalArgumentException("Unknown FlexItem: $item")
|
||||
}
|
||||
|
||||
private fun copyFlexItemValues(from: FlexItem, to: FlexItem) {
|
||||
if (from !is FlexboxLayoutManager.LayoutParams) {
|
||||
to.order = from.order
|
||||
}
|
||||
to.flexGrow = from.flexGrow
|
||||
to.flexShrink = from.flexShrink
|
||||
to.flexBasisPercent = from.flexBasisPercent
|
||||
to.height = from.height
|
||||
to.width = from.width
|
||||
to.maxHeight = from.maxHeight
|
||||
to.minHeight = from.minHeight
|
||||
to.maxWidth = from.maxWidth
|
||||
to.minWidth = from.minWidth
|
||||
to.alignSelf = from.alignSelf
|
||||
to.isWrapBefore = from.isWrapBefore
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val FLEX_ITEM_KEY = "flex_item"
|
||||
|
||||
private const val VIEW_INDEX_KEY = "view_index"
|
||||
|
||||
fun newInstance(flexItem: FlexItem, viewIndex: Int) = FlexItemEditFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(FLEX_ITEM_KEY, flexItem)
|
||||
putInt(VIEW_INDEX_KEY, viewIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.apps.flexbox.R
|
||||
|
||||
/**
|
||||
* ViewHolder implementation for a flex item.
|
||||
*/
|
||||
class FlexItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private val textView: TextView = itemView.findViewById(R.id.textview)
|
||||
|
||||
fun bindTo(params: RecyclerView.LayoutParams) {
|
||||
val adapterPosition = adapterPosition
|
||||
textView.apply {
|
||||
text = (adapterPosition + 1).toString()
|
||||
setBackgroundResource(R.drawable.flex_item_background)
|
||||
gravity = Gravity.CENTER
|
||||
layoutParams = params
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.apps.flexbox.R
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Fragment that contains the [FlexboxLayout] as the playground.
|
||||
*/
|
||||
class FlexboxLayoutFragment : Fragment() {
|
||||
|
||||
private lateinit var flexContainer: FlexboxLayout
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_flexboxlayout, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val activity = activity as MainActivity
|
||||
flexContainer = view.findViewById(R.id.flexbox_layout)
|
||||
|
||||
val fragmentHelper = FragmentHelper(activity, flexContainer)
|
||||
fragmentHelper.initializeViews()
|
||||
if (savedInstanceState != null) {
|
||||
val flexItems = savedInstanceState
|
||||
.getParcelableArrayList<FlexItem>(FLEX_ITEMS_KEY)!!
|
||||
flexContainer.removeAllViews()
|
||||
for (i in flexItems.indices) {
|
||||
val flexItem = flexItems[i]
|
||||
val textView = createBaseFlexItemTextView(activity, i)
|
||||
textView.layoutParams = flexItem as FlexboxLayout.LayoutParams
|
||||
flexContainer.addView(textView)
|
||||
}
|
||||
}
|
||||
for (i in 0 until flexContainer.flexItemCount) {
|
||||
flexContainer.getFlexItemAt(i).setOnClickListener(
|
||||
FlexItemClickListener(activity,
|
||||
FlexItemChangedListenerImpl(flexContainer), i))
|
||||
}
|
||||
|
||||
val addFab: FloatingActionButton = activity.findViewById(R.id.add_fab)
|
||||
addFab.setOnClickListener {
|
||||
val viewIndex = flexContainer.flexItemCount
|
||||
// index starts from 0. New View's index is N if N views ([0, 1, 2, ... N-1])
|
||||
// exist.
|
||||
val textView = createBaseFlexItemTextView(activity, viewIndex)
|
||||
val lp = FlexboxLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
fragmentHelper.setFlexItemAttributes(lp)
|
||||
textView.layoutParams = lp
|
||||
textView.setOnClickListener(FlexItemClickListener(activity,
|
||||
FlexItemChangedListenerImpl(flexContainer), viewIndex))
|
||||
flexContainer.addView(textView)
|
||||
}
|
||||
val removeFab: FloatingActionButton = activity.findViewById(R.id.remove_fab)
|
||||
removeFab.setOnClickListener(View.OnClickListener {
|
||||
if (flexContainer.flexItemCount == 0) {
|
||||
return@OnClickListener
|
||||
}
|
||||
flexContainer.removeViewAt(flexContainer.flexItemCount - 1)
|
||||
})
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
val flexItems = (0 until flexContainer.flexItemCount)
|
||||
.map { flexContainer.getFlexItemAt(it) }
|
||||
.mapTo(ArrayList()) { it.layoutParams as FlexItem }
|
||||
outState.putParcelableArrayList(FLEX_ITEMS_KEY, flexItems)
|
||||
}
|
||||
|
||||
private fun createBaseFlexItemTextView(context: Context, index: Int): TextView {
|
||||
return TextView(context).apply {
|
||||
setBackgroundResource(R.drawable.flex_item_background)
|
||||
text = (index + 1).toString()
|
||||
gravity = Gravity.CENTER
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val FLEX_ITEMS_KEY = "flex_items_key"
|
||||
|
||||
fun newInstance(): FlexboxLayoutFragment {
|
||||
return FlexboxLayoutFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Spinner
|
||||
import android.widget.Toast
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.apps.flexbox.R
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
|
||||
/**
|
||||
* Helper class that has the common logic for initializing the Fragment for the play ground demo
|
||||
* such as [FlexboxLayoutFragment] and a Fragment that uses RecyclerView in it.
|
||||
*/
|
||||
internal class FragmentHelper(private val activity: MainActivity, private val flexContainer: FlexContainer) {
|
||||
|
||||
private lateinit var ROW: String
|
||||
|
||||
private lateinit var COLUMN: String
|
||||
|
||||
private lateinit var ROW_REVERSE: String
|
||||
|
||||
private lateinit var COLUMN_REVERSE: String
|
||||
|
||||
private lateinit var NOWRAP: String
|
||||
|
||||
private lateinit var WRAP: String
|
||||
|
||||
private lateinit var WRAP_REVERSE: String
|
||||
|
||||
private lateinit var FLEX_START: String
|
||||
|
||||
private lateinit var FLEX_END: String
|
||||
|
||||
private lateinit var CENTER: String
|
||||
|
||||
private lateinit var BASELINE: String
|
||||
|
||||
private lateinit var STRETCH: String
|
||||
|
||||
private lateinit var SPACE_BETWEEN: String
|
||||
|
||||
private lateinit var SPACE_AROUND: String
|
||||
|
||||
private lateinit var SPACE_EVENLY: String
|
||||
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
fun initializeViews() {
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
initializeStringResources()
|
||||
val navigationView: NavigationView = activity.findViewById(R.id.nav_view)
|
||||
navigationView.setNavigationItemSelectedListener(activity)
|
||||
val navigationMenu = navigationView.menu
|
||||
initializeFlexDirectionSpinner(navigationMenu)
|
||||
initializeFlexWrapSpinner(navigationMenu)
|
||||
initializeJustifyContentSpinner(navigationMenu)
|
||||
initializeAlignItemsSpinner(navigationMenu)
|
||||
initializeAlignContentSpinner(navigationMenu)
|
||||
}
|
||||
|
||||
private fun initializeStringResources() {
|
||||
ROW = activity.getString(R.string.row)
|
||||
COLUMN = activity.getString(R.string.column)
|
||||
ROW_REVERSE = activity.getString(R.string.row_reverse)
|
||||
COLUMN_REVERSE = activity.getString(R.string.column_reverse)
|
||||
NOWRAP = activity.getString(R.string.nowrap)
|
||||
WRAP = activity.getString(R.string.wrap)
|
||||
WRAP_REVERSE = activity.getString(R.string.wrap_reverse)
|
||||
FLEX_START = activity.getString(R.string.flex_start)
|
||||
FLEX_END = activity.getString(R.string.flex_end)
|
||||
CENTER = activity.getString(R.string.center)
|
||||
BASELINE = activity.getString(R.string.baseline)
|
||||
STRETCH = activity.getString(R.string.stretch)
|
||||
SPACE_BETWEEN = activity.getString(R.string.space_between)
|
||||
SPACE_AROUND = activity.getString(R.string.space_around)
|
||||
SPACE_EVENLY = activity.getString(R.string.space_evenly)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the attributes for a [FlexItem] based on the stored default values in
|
||||
* the SharedPreferences.
|
||||
|
||||
* @param flexItem the FlexItem instance
|
||||
* *
|
||||
* @return a FlexItem instance, which attributes from the SharedPreferences are updated
|
||||
*/
|
||||
fun setFlexItemAttributes(flexItem: FlexItem): FlexItem {
|
||||
flexItem.width = activity.dpToPixel(readPreferenceAsInteger(activity.getString(R.string.new_width_key), DEFAULT_WIDTH))
|
||||
flexItem.height = activity.dpToPixel(readPreferenceAsInteger(activity.getString(R.string.new_height_key), DEFAULT_HEIGHT))
|
||||
// Order is not supported in the FlexboxLayoutManager
|
||||
if (flexItem !is FlexboxLayoutManager.LayoutParams) {
|
||||
flexItem.order = readPreferenceAsInteger(activity.getString(R.string.new_flex_item_order_key), "1")
|
||||
}
|
||||
flexItem.flexGrow = readPreferenceAsFloat(activity.getString(R.string.new_flex_grow_key), "0.0")
|
||||
flexItem.flexShrink = readPreferenceAsFloat(activity.getString(R.string.new_flex_shrink_key), "1.0")
|
||||
val flexBasisPercent = readPreferenceAsInteger(
|
||||
activity.getString(R.string.new_flex_basis_percent_key), "-1")
|
||||
flexItem.flexBasisPercent = if (flexBasisPercent == -1) -1f else (flexBasisPercent / 100.0).toFloat()
|
||||
return flexItem
|
||||
}
|
||||
|
||||
private fun readPreferenceAsInteger(key: String, defValue: String): Int {
|
||||
return if (sharedPreferences.contains(key)) {
|
||||
sharedPreferences.getString(key, defValue)?.toIntOrNull() ?: defValue.toInt()
|
||||
} else {
|
||||
defValue.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
private fun readPreferenceAsFloat(key: String, defValue: String): Float {
|
||||
return if (sharedPreferences.contains(key)) {
|
||||
sharedPreferences.getString(key, defValue)?.toFloatOrNull() ?: defValue.toFloat()
|
||||
} else {
|
||||
defValue.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeSpinner(currentValue: Int, menuItemId: Int, navigationMenu: Menu,
|
||||
arrayResourceId: Int, listener: AdapterView.OnItemSelectedListener,
|
||||
converter: ValueToStringConverter) {
|
||||
val spinner = navigationMenu.findItem(menuItemId).actionView as Spinner
|
||||
val adapter = ArrayAdapter.createFromResource(activity,
|
||||
arrayResourceId, R.layout.spinner_item)
|
||||
spinner.adapter = adapter
|
||||
spinner.onItemSelectedListener = listener
|
||||
val selectedAsString = converter.asString(currentValue)
|
||||
val position = adapter.getPosition(selectedAsString)
|
||||
spinner.setSelection(position)
|
||||
}
|
||||
|
||||
private fun initializeFlexDirectionSpinner(navigationMenu: Menu) {
|
||||
initializeSpinner(flexContainer.flexDirection, R.id.menu_item_flex_direction,
|
||||
navigationMenu, R.array.array_flex_direction,
|
||||
object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, ignored: View?, position: Int,
|
||||
id: Long) {
|
||||
flexContainer.flexDirection = when (parent.getItemAtPosition(position).toString()) {
|
||||
ROW -> FlexDirection.ROW
|
||||
ROW_REVERSE -> FlexDirection.ROW_REVERSE
|
||||
COLUMN -> FlexDirection.COLUMN
|
||||
COLUMN_REVERSE -> FlexDirection.COLUMN_REVERSE
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// No op
|
||||
}
|
||||
}, object : ValueToStringConverter {
|
||||
override fun asString(value: Int): String {
|
||||
return when (value) {
|
||||
FlexDirection.ROW -> ROW
|
||||
FlexDirection.ROW_REVERSE -> ROW_REVERSE
|
||||
FlexDirection.COLUMN -> COLUMN
|
||||
FlexDirection.COLUMN_REVERSE -> COLUMN_REVERSE
|
||||
else -> ROW
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initializeFlexWrapSpinner(navigationMenu: Menu) {
|
||||
initializeSpinner(flexContainer.flexWrap, R.id.menu_item_flex_wrap,
|
||||
navigationMenu, R.array.array_flex_wrap,
|
||||
object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, ignored: View?, position: Int,
|
||||
id: Long) {
|
||||
flexContainer.flexWrap = when (parent.getItemAtPosition(position).toString()) {
|
||||
NOWRAP -> FlexWrap.NOWRAP
|
||||
WRAP -> FlexWrap.WRAP
|
||||
WRAP_REVERSE -> if (flexContainer is FlexboxLayoutManager) {
|
||||
Toast.makeText(activity,
|
||||
R.string.wrap_reverse_not_supported,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
} else {
|
||||
FlexWrap.WRAP_REVERSE
|
||||
}
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// No op
|
||||
}
|
||||
}, object : ValueToStringConverter {
|
||||
override fun asString(value: Int): String {
|
||||
return when (value) {
|
||||
FlexWrap.NOWRAP -> NOWRAP
|
||||
FlexWrap.WRAP -> WRAP
|
||||
FlexWrap.WRAP_REVERSE -> WRAP_REVERSE
|
||||
else -> NOWRAP
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initializeJustifyContentSpinner(navigationMenu: Menu) {
|
||||
initializeSpinner(flexContainer.justifyContent, R.id.menu_item_justify_content,
|
||||
navigationMenu, R.array.array_justify_content,
|
||||
object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, ignored: View?, position: Int,
|
||||
id: Long) {
|
||||
flexContainer.justifyContent = when (parent.getItemAtPosition(position).toString()) {
|
||||
FLEX_START -> JustifyContent.FLEX_START
|
||||
FLEX_END -> JustifyContent.FLEX_END
|
||||
CENTER -> JustifyContent.CENTER
|
||||
SPACE_BETWEEN -> JustifyContent.SPACE_BETWEEN
|
||||
SPACE_AROUND -> JustifyContent.SPACE_AROUND
|
||||
SPACE_EVENLY -> JustifyContent.SPACE_EVENLY
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// No op
|
||||
}
|
||||
}, object : ValueToStringConverter {
|
||||
override fun asString(value: Int): String {
|
||||
return when (value) {
|
||||
JustifyContent.FLEX_START -> FLEX_START
|
||||
JustifyContent.FLEX_END -> FLEX_END
|
||||
JustifyContent.CENTER -> CENTER
|
||||
JustifyContent.SPACE_AROUND -> SPACE_AROUND
|
||||
JustifyContent.SPACE_BETWEEN -> SPACE_BETWEEN
|
||||
JustifyContent.SPACE_EVENLY -> SPACE_EVENLY
|
||||
else -> FLEX_START
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initializeAlignItemsSpinner(navigationMenu: Menu) {
|
||||
initializeSpinner(flexContainer.alignItems, R.id.menu_item_align_items,
|
||||
navigationMenu, R.array.array_align_items,
|
||||
object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, ignored: View?, position: Int,
|
||||
id: Long) {
|
||||
flexContainer.alignItems = when (parent.getItemAtPosition(position).toString()) {
|
||||
FLEX_START -> AlignItems.FLEX_START
|
||||
FLEX_END -> AlignItems.FLEX_END
|
||||
CENTER -> AlignItems.CENTER
|
||||
BASELINE -> AlignItems.BASELINE
|
||||
STRETCH -> AlignItems.STRETCH
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// No op
|
||||
}
|
||||
}, object : ValueToStringConverter {
|
||||
override fun asString(value: Int): String {
|
||||
return when (value) {
|
||||
AlignItems.FLEX_START -> FLEX_START
|
||||
AlignItems.FLEX_END -> FLEX_END
|
||||
AlignItems.CENTER -> CENTER
|
||||
AlignItems.BASELINE -> BASELINE
|
||||
AlignItems.STRETCH -> STRETCH
|
||||
else -> STRETCH
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initializeAlignContentSpinner(navigationMenu: Menu) {
|
||||
initializeSpinner(flexContainer.alignContent, R.id.menu_item_align_content,
|
||||
navigationMenu, R.array.array_align_content,
|
||||
object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, ignored: View?, position: Int,
|
||||
id: Long) {
|
||||
if (flexContainer is FlexboxLayoutManager) {
|
||||
Toast.makeText(activity, R.string.align_content_not_supported,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
flexContainer.alignContent = when (parent.getItemAtPosition(position).toString()) {
|
||||
FLEX_START -> AlignContent.FLEX_START
|
||||
FLEX_END -> AlignContent.FLEX_END
|
||||
CENTER -> AlignContent.CENTER
|
||||
SPACE_BETWEEN -> AlignContent.SPACE_BETWEEN
|
||||
SPACE_AROUND -> AlignContent.SPACE_AROUND
|
||||
STRETCH -> AlignContent.STRETCH
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// No op
|
||||
}
|
||||
}, object : ValueToStringConverter {
|
||||
override fun asString(value: Int): String {
|
||||
when (value) {
|
||||
AlignContent.FLEX_START -> return FLEX_START
|
||||
AlignContent.FLEX_END -> return FLEX_END
|
||||
AlignContent.CENTER -> return CENTER
|
||||
AlignContent.SPACE_BETWEEN -> return SPACE_BETWEEN
|
||||
AlignContent.SPACE_AROUND -> return SPACE_AROUND
|
||||
AlignContent.STRETCH -> return STRETCH
|
||||
else -> return STRETCH
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Converter for converting an int value for Flexbox properties to a String.
|
||||
*/
|
||||
private interface ValueToStringConverter {
|
||||
|
||||
fun asString(value: Int): String
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val DEFAULT_WIDTH = "120"
|
||||
|
||||
private const val DEFAULT_HEIGHT = "80"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.RadioGroup
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.google.android.apps.flexbox.R
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
|
||||
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val toolbar: Toolbar = findViewById(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
val drawer: DrawerLayout = findViewById(R.id.drawer_layout)
|
||||
val toggle = ActionBarDrawerToggle(
|
||||
this, drawer, toolbar, R.string.navigation_drawer_open,
|
||||
R.string.navigation_drawer_close)
|
||||
drawer.addDrawerListener(toggle)
|
||||
toggle.syncState()
|
||||
|
||||
val navigationView: NavigationView = findViewById(R.id.nav_view)
|
||||
val radioGroup: RadioGroup = navigationView.getHeaderView(0)
|
||||
.findViewById(R.id.radiogroup_container_implementation)
|
||||
val fragmentManager = supportFragmentManager
|
||||
|
||||
radioGroup.setOnCheckedChangeListener { _, checkedId ->
|
||||
if (checkedId == R.id.radiobutton_viewgroup) {
|
||||
replaceToFlexboxLayoutFragment(fragmentManager)
|
||||
} else {
|
||||
replaceToRecyclerViewFragment(fragmentManager)
|
||||
}
|
||||
}
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
replaceToFlexboxLayoutFragment(fragmentManager)
|
||||
}
|
||||
}
|
||||
|
||||
private fun replaceToFlexboxLayoutFragment(fragmentManager: FragmentManager) {
|
||||
var fragment: FlexboxLayoutFragment? = fragmentManager.findFragmentByTag(FLEXBOXLAYOUT_FRAGMENT) as FlexboxLayoutFragment?
|
||||
if (fragment == null) {
|
||||
fragment = FlexboxLayoutFragment.newInstance()
|
||||
}
|
||||
fragmentManager.beginTransaction()
|
||||
.replace(R.id.container, fragment, FLEXBOXLAYOUT_FRAGMENT).commit()
|
||||
}
|
||||
|
||||
private fun replaceToRecyclerViewFragment(fragmentManager: FragmentManager) {
|
||||
var fragment: RecyclerViewFragment? = fragmentManager.findFragmentByTag(RECYCLERVIEW_FRAGMENT) as RecyclerViewFragment?
|
||||
if (fragment == null) {
|
||||
fragment = RecyclerViewFragment.newInstance()
|
||||
}
|
||||
fragmentManager.beginTransaction()
|
||||
.replace(R.id.container, fragment, RECYCLERVIEW_FRAGMENT).commit()
|
||||
}
|
||||
|
||||
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val id = item.itemId
|
||||
|
||||
if (id == R.id.action_settings) {
|
||||
val intent = Intent(this, SettingsActivity::class.java)
|
||||
startActivity(intent)
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val FLEXBOXLAYOUT_FRAGMENT = "flexboxlayout_fragment"
|
||||
|
||||
private const val RECYCLERVIEW_FRAGMENT = "recyclerview_fragment"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.apps.flexbox.R
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
|
||||
/**
|
||||
* Fragment that contains the [RecyclerView] and the [FlexboxLayoutManager] as its
|
||||
* LayoutManager for the flexbox playground.
|
||||
*/
|
||||
internal class RecyclerViewFragment : Fragment() {
|
||||
|
||||
private lateinit var adapter: FlexItemAdapter
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_recyclerview, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val recyclerView: RecyclerView = view.findViewById(R.id.recyclerview)
|
||||
val activity = activity as MainActivity
|
||||
val flexboxLayoutManager = FlexboxLayoutManager(activity)
|
||||
recyclerView.layoutManager = flexboxLayoutManager
|
||||
adapter = FlexItemAdapter(activity, flexboxLayoutManager)
|
||||
recyclerView.adapter = adapter
|
||||
if (savedInstanceState != null) {
|
||||
val layoutParams : List<FlexboxLayoutManager.LayoutParams>? = savedInstanceState
|
||||
.getParcelableArrayList(FLEX_ITEMS_KEY)
|
||||
layoutParams?.let {
|
||||
for (i in layoutParams.indices) {
|
||||
adapter.addItem(layoutParams[i])
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
val fragmentHelper = FragmentHelper(activity, flexboxLayoutManager)
|
||||
fragmentHelper.initializeViews()
|
||||
|
||||
val addFab: FloatingActionButton = activity.findViewById(R.id.add_fab)
|
||||
addFab.setOnClickListener {
|
||||
val lp = FlexboxLayoutManager.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
fragmentHelper.setFlexItemAttributes(lp)
|
||||
adapter.addItem(lp)
|
||||
}
|
||||
val removeFab: FloatingActionButton = activity.findViewById(R.id.remove_fab)
|
||||
removeFab.setOnClickListener(View.OnClickListener {
|
||||
if (adapter.itemCount == 0) {
|
||||
return@OnClickListener
|
||||
}
|
||||
adapter.removeItem(adapter.itemCount - 1)
|
||||
})
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putParcelableArrayList(FLEX_ITEMS_KEY, ArrayList(adapter.items))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val FLEX_ITEMS_KEY = "flex_items"
|
||||
|
||||
fun newInstance(): RecyclerViewFragment {
|
||||
return RecyclerViewFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.google.android.apps.flexbox.R
|
||||
import com.google.android.flexbox.validators.DimensionInputValidator
|
||||
import com.google.android.flexbox.validators.FlexBasisPercentInputValidator
|
||||
import com.google.android.flexbox.validators.IntegerInputValidator
|
||||
import com.google.android.flexbox.validators.NonNegativeDecimalInputValidator
|
||||
|
||||
internal class SettingsActivity : FragmentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Display the fragment as the main content.
|
||||
supportFragmentManager.beginTransaction().replace(android.R.id.content,
|
||||
SettingsFragment()).commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragment for settings.
|
||||
*/
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, s: String?) {
|
||||
addPreferencesFromResource(R.xml.new_flex_item_preferences)
|
||||
|
||||
val orderPreference = findPreference(
|
||||
getString(R.string.new_flex_item_order_key)) as EditTextPreference?
|
||||
orderPreference?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
val validator = IntegerInputValidator()
|
||||
if (!validator.isValidInput(newValue.toString())) {
|
||||
Toast.makeText(activity,
|
||||
R.string.must_be_integer,
|
||||
Toast.LENGTH_LONG).show()
|
||||
return@OnPreferenceChangeListener false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
val flexGrowPreference = findPreference(
|
||||
getString(R.string.new_flex_grow_key)) as EditTextPreference?
|
||||
flexGrowPreference?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
val validator = NonNegativeDecimalInputValidator()
|
||||
if (!validator.isValidInput(newValue.toString())) {
|
||||
Toast.makeText(activity,
|
||||
R.string.must_be_non_negative_float,
|
||||
Toast.LENGTH_LONG).show()
|
||||
return@OnPreferenceChangeListener false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
val flexShrinkPreference = findPreference(
|
||||
getString(R.string.new_flex_shrink_key)) as EditTextPreference?
|
||||
flexShrinkPreference?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
val validator = NonNegativeDecimalInputValidator()
|
||||
if (!validator.isValidInput(newValue.toString())) {
|
||||
Toast.makeText(activity,
|
||||
R.string.must_be_non_negative_float,
|
||||
Toast.LENGTH_LONG).show()
|
||||
return@OnPreferenceChangeListener false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
val flexBasisPercentPreference = findPreference(
|
||||
getString(R.string.new_flex_basis_percent_key)) as EditTextPreference?
|
||||
flexBasisPercentPreference?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
val validator = FlexBasisPercentInputValidator()
|
||||
if (!validator.isValidInput(newValue.toString())) {
|
||||
Toast.makeText(activity,
|
||||
R.string.must_be_minus_one_or_non_negative_integer,
|
||||
Toast.LENGTH_LONG).show()
|
||||
return@OnPreferenceChangeListener false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
val widthPreference = findPreference(
|
||||
getString(R.string.new_width_key)) as EditTextPreference?
|
||||
widthPreference?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
val validator = DimensionInputValidator()
|
||||
if (!validator.isValidInput(newValue.toString())) {
|
||||
Toast.makeText(activity,
|
||||
R.string.must_be_minus_one_or_minus_two_or_non_negative_integer,
|
||||
Toast.LENGTH_LONG).show()
|
||||
return@OnPreferenceChangeListener false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
val heightPreference = findPreference(
|
||||
getString(R.string.new_height_key)) as EditTextPreference?
|
||||
heightPreference?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
val validator = DimensionInputValidator()
|
||||
if (!validator.isValidInput(newValue.toString())) {
|
||||
Toast.makeText(activity,
|
||||
R.string.must_be_minus_one_or_minus_two_or_non_negative_integer,
|
||||
Toast.LENGTH_LONG).show()
|
||||
return@OnPreferenceChangeListener false
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox.validators
|
||||
|
||||
import android.text.TextUtils
|
||||
|
||||
/**
|
||||
* Validator for dimension values including match_parent and wrap_content.
|
||||
*/
|
||||
class DimensionInputValidator : InputValidator {
|
||||
|
||||
override fun isValidInput(charSequence: CharSequence): Boolean {
|
||||
// -1 represents match_parent, -2 represents wrap_content
|
||||
return !charSequence.isEmpty() && (TextUtils.isDigitsOnly(charSequence) ||
|
||||
charSequence.toString() == "-1" ||
|
||||
charSequence.toString() == "-2")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox.validators
|
||||
|
||||
import android.text.TextUtils
|
||||
|
||||
/**
|
||||
* Validator for dimension values.
|
||||
*/
|
||||
class FixedDimensionInputValidator : InputValidator {
|
||||
|
||||
override fun isValidInput(charSequence: CharSequence): Boolean {
|
||||
return !charSequence.isEmpty() && TextUtils.isDigitsOnly(charSequence)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox.validators
|
||||
|
||||
import android.text.TextUtils
|
||||
|
||||
/**
|
||||
* Validator for the flex basis percent attribute.
|
||||
*/
|
||||
class FlexBasisPercentInputValidator : InputValidator {
|
||||
|
||||
override fun isValidInput(charSequence: CharSequence): Boolean {
|
||||
// -1 represents not set
|
||||
return !charSequence.isEmpty() && (TextUtils.isDigitsOnly(charSequence) || charSequence.toString() == "-1")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox.validators
|
||||
|
||||
/**
|
||||
* Interface to verify a given input.
|
||||
*/
|
||||
interface InputValidator {
|
||||
|
||||
/**
|
||||
* Verifies if the given input is valid.
|
||||
|
||||
* @param charSequence the input to be verified
|
||||
* *
|
||||
* @return `true` if charSequence is valid, `false` otherwise
|
||||
*/
|
||||
fun isValidInput(charSequence: CharSequence): Boolean
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox.validators
|
||||
|
||||
/**
|
||||
* Validator for the integers.
|
||||
*/
|
||||
class IntegerInputValidator : InputValidator {
|
||||
|
||||
override fun isValidInput(charSequence: CharSequence): Boolean {
|
||||
return charSequence.toString().toIntOrNull() != null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.flexbox.validators
|
||||
|
||||
/**
|
||||
* Validator for non negative integers.
|
||||
*/
|
||||
class NonNegativeDecimalInputValidator : InputValidator {
|
||||
|
||||
override fun isValidInput(charSequence: CharSequence): Boolean {
|
||||
return charSequence.toString().toFloatOrNull() ?: -1f >= 0
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 127 B |
|
After Width: | Height: | Size: 104 B |
|
After Width: | Height: | Size: 88 B |
|
After Width: | Height: | Size: 82 B |
|
After Width: | Height: | Size: 97 B |
|
After Width: | Height: | Size: 90 B |
|
After Width: | Height: | Size: 97 B |
|
After Width: | Height: | Size: 94 B |
|
After Width: | Height: | Size: 102 B |
|
After Width: | Height: | Size: 97 B |