├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── LICENSE
├── README.md
├── arts
├── rendering_1.gif
├── rendering_2.gif
├── rendering_3.gif
├── rendering_4.gif
├── rendering_5.gif
└── rendering_6.gif
├── build.gradle
├── example
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── sevenheaven
│ │ └── uilibrarysample
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── sevenheaven
│ │ │ └── uilibrarysample
│ │ │ ├── ExampleDetailActivity.java
│ │ │ ├── ExampleListActivity.java
│ │ │ └── ExampleListAdapter.java
│ └── res
│ │ ├── drawable-nodpi
│ │ ├── test0.png
│ │ ├── test1.png
│ │ └── test2.png
│ │ ├── layout
│ │ ├── activity_example_detail.xml
│ │ ├── activity_example_list.xml
│ │ ├── activity_main.xml
│ │ └── item_examplelist.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── sevenheaven
│ └── uilibrarysample
│ └── ExampleUnitTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── uilibrary
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── sevenheaven
│ └── uilibrary
│ └── ApplicationTest.java
├── main
├── AndroidManifest.xml
├── java
│ └── com
│ │ └── sevenheaven
│ │ └── uilibrary
│ │ ├── drawables
│ │ ├── GroupedAvatarDrawable.java
│ │ ├── MaskDrawable.java
│ │ ├── RoundEdgeDrawable.java
│ │ └── progressive
│ │ │ ├── LoadingDrawable.java
│ │ │ ├── ProgressiveDrawable.java
│ │ │ └── providers
│ │ │ ├── AppStoreStyleProgressProvider.java
│ │ │ └── PathProgressProvider.java
│ │ ├── shapes
│ │ ├── ArcShape.java
│ │ └── PolygonShape.java
│ │ ├── utils
│ │ ├── GeomUtil.java
│ │ └── PathMeasurement.java
│ │ └── views
│ │ ├── AnimatedImageView.java
│ │ ├── FitWidthImageView.java
│ │ ├── GroupedAvatarImageView.java
│ │ └── PageIndicator.java
└── res
│ └── values
│ ├── attrs.xml
│ └── strings.xml
└── test
└── java
└── com
└── sevenheaven
└── uilibrary
└── ExampleUnitTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Android
39 |
40 |
41 | Android > Lint > Correctness
42 |
43 |
44 | Android > Lint > Internationalization
45 |
46 |
47 | Android > Lint > Security
48 |
49 |
50 | CorrectnessLintAndroid
51 |
52 |
53 | Gradle
54 |
55 |
56 | LintAndroid
57 |
58 |
59 | Probable bugsGradle
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 7heaven
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](LICENSE)
2 | # UILibrary
3 |
4 | ---
5 |
6 | 把平时写的用到项目里面的一些UI相关的View、Drawable等整理了一下,持续更新
7 |
8 | 主要有以下一些效果:
9 |
10 | **跟着Path运动的ProgressBar效果**
11 |
12 | 
13 |
14 | **QQ群头像效果的控件**
15 |
16 | 
17 |
18 | **苹果app下载的进度效果**
19 |
20 | 
21 |
22 | **任意形状的遮罩**
23 |
24 | 
25 |
26 | **带圆角的任意正多边形和星型**
27 |
28 | 
29 |
30 | **带动画效果切换ScaleType的ImageView**
31 |
32 | 
33 |
34 |
--------------------------------------------------------------------------------
/arts/rendering_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/arts/rendering_1.gif
--------------------------------------------------------------------------------
/arts/rendering_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/arts/rendering_2.gif
--------------------------------------------------------------------------------
/arts/rendering_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/arts/rendering_3.gif
--------------------------------------------------------------------------------
/arts/rendering_4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/arts/rendering_4.gif
--------------------------------------------------------------------------------
/arts/rendering_5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/arts/rendering_5.gif
--------------------------------------------------------------------------------
/arts/rendering_6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/arts/rendering_6.gif
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.1.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/example/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "24.0.1"
6 |
7 | defaultConfig {
8 | applicationId "com.sevenheaven.uilibrarysample"
9 | minSdkVersion 11
10 | targetSdkVersion 24
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | compile 'com.android.support:appcompat-v7:24.2.0'
25 | compile 'com.android.support:recyclerview-v7:24.2.0'
26 |
27 | compile project(':uilibrary')
28 | }
29 |
--------------------------------------------------------------------------------
/example/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/7heaven/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/example/src/androidTest/java/com/sevenheaven/uilibrarysample/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrarysample;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/example/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example/src/main/java/com/sevenheaven/uilibrarysample/ExampleDetailActivity.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrarysample;
2 |
3 | import android.app.Activity;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.FrameLayout;
11 | import android.widget.SeekBar;
12 |
13 | /**
14 | * Created by 7heaven on 16/8/25.
15 | */
16 | public class ExampleDetailActivity extends Activity {
17 |
18 | public interface DetailContentProvider{
19 | T provideInstance();
20 |
21 | void onGestureMove(float x, float y, int action);
22 |
23 | void destroy();
24 | }
25 |
26 | public static DetailContentProvider mContentProvider;
27 |
28 | private FrameLayout mRootLayout;
29 |
30 | private SeekBar xAxisSeekBar;
31 | private SeekBar yAxisSeekBar;
32 |
33 | private int mScreenWidth;
34 | private int mScreenHeight;
35 | public static final int PROGRESS_INIT = -2;
36 | public static final int PROGRESS_DONE = -1;
37 |
38 | public static final String PROVIDER_EXTRA = "";
39 |
40 | @Override
41 | protected void onCreate(Bundle savedInstanceState){
42 | super.onCreate(savedInstanceState);
43 | setContentView(R.layout.activity_example_detail);
44 |
45 | mScreenWidth = getResources().getDisplayMetrics().widthPixels;
46 | mScreenHeight = getResources().getDisplayMetrics().heightPixels;
47 |
48 | mRootLayout = (FrameLayout) findViewById(R.id.root_layout);
49 | xAxisSeekBar = (SeekBar) findViewById(R.id.x_axis_seekbar);
50 | yAxisSeekBar = (SeekBar) findViewById(R.id.y_axis_seekbar);
51 |
52 | if(mContentProvider != null){
53 | Object providedInstance = mContentProvider.provideInstance();
54 |
55 | if(providedInstance instanceof View){
56 | View instance = (View) providedInstance;
57 | ViewGroup.LayoutParams params = instance.getLayoutParams();
58 | if(params == null){
59 | params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
60 | instance.setLayoutParams(params);
61 | }
62 |
63 | mRootLayout.addView(instance);
64 |
65 | }else if(providedInstance instanceof Drawable){
66 | Drawable instance = (Drawable) providedInstance;
67 |
68 | View view = new View(this);
69 | ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
70 |
71 | if(Build.VERSION.SDK_INT > 16){
72 | view.setBackground(instance);
73 | }else{
74 | view.setBackgroundDrawable(instance);
75 | }
76 |
77 | mRootLayout.addView(view);
78 | }
79 |
80 | xAxisSeekBar.setMax(100);
81 | yAxisSeekBar.setMax(100);
82 |
83 | xAxisSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
84 | @Override
85 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
86 | mContentProvider.onGestureMove((float) progress / 100.0F, (float) yAxisSeekBar.getProgress() / 100.0F, MotionEvent.ACTION_MOVE);
87 | }
88 |
89 | @Override
90 | public void onStartTrackingTouch(SeekBar seekBar) {
91 | mContentProvider.onGestureMove(PROGRESS_INIT, PROGRESS_INIT, MotionEvent.ACTION_DOWN);
92 | mContentProvider.onGestureMove(0, (float) yAxisSeekBar.getProgress() / 100.0F, MotionEvent.ACTION_DOWN);
93 | }
94 |
95 | @Override
96 | public void onStopTrackingTouch(SeekBar seekBar) {
97 | mContentProvider.onGestureMove(PROGRESS_DONE, PROGRESS_DONE, MotionEvent.ACTION_UP);
98 | }
99 | });
100 |
101 | yAxisSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
102 | @Override
103 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
104 | mContentProvider.onGestureMove((float) xAxisSeekBar.getProgress() / 100.0F, (float) progress / 100.0F, MotionEvent.ACTION_MOVE);
105 | }
106 |
107 | @Override
108 | public void onStartTrackingTouch(SeekBar seekBar) {
109 | mContentProvider.onGestureMove(PROGRESS_INIT, PROGRESS_INIT, MotionEvent.ACTION_DOWN);
110 | mContentProvider.onGestureMove((float) xAxisSeekBar.getProgress() / 100.0F, 0, MotionEvent.ACTION_DOWN);
111 | }
112 |
113 | @Override
114 | public void onStopTrackingTouch(SeekBar seekBar) {
115 | mContentProvider.onGestureMove(PROGRESS_DONE, PROGRESS_DONE, MotionEvent.ACTION_UP);
116 | }
117 | });
118 | }
119 | }
120 |
121 | // @Override
122 | // public boolean onTouchEvent(MotionEvent event){
123 | // if(mContentProvider != null) {
124 | // int action = event.getActionMasked();
125 | // switch (action) {
126 | // case MotionEvent.ACTION_DOWN:
127 | // mContentProvider.onGestureMove(PROGRESS_INIT, PROGRESS_INIT, action);
128 | // mContentProvider.onGestureMove(0, 0, action);
129 | // break;
130 | // case MotionEvent.ACTION_MOVE:
131 | // mContentProvider.onGestureMove(event.getX() / mScreenWidth, event.getY() / mScreenHeight, action);
132 | // break;
133 | // case MotionEvent.ACTION_UP:
134 | // mContentProvider.onGestureMove(PROGRESS_DONE, PROGRESS_DONE, action);
135 | // break;
136 | // }
137 | // return true;
138 | // }else{
139 | // return super.onTouchEvent(event);
140 | // }
141 | // }
142 |
143 | @Override
144 | protected void onDestroy(){
145 | super.onDestroy();
146 | if(mRootLayout.getChildCount() > 0){
147 | mRootLayout.removeAllViews();
148 | }
149 | if(mContentProvider != null){
150 | mContentProvider.destroy();
151 | mContentProvider = null;
152 | }
153 | }
154 | }
155 |
156 |
--------------------------------------------------------------------------------
/example/src/main/java/com/sevenheaven/uilibrarysample/ExampleListActivity.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrarysample;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.ColorFilter;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.graphics.PixelFormat;
11 | import android.graphics.Rect;
12 | import android.graphics.RectF;
13 | import android.graphics.drawable.Drawable;
14 | import android.graphics.drawable.LayerDrawable;
15 | import android.graphics.drawable.ShapeDrawable;
16 | import android.os.Bundle;
17 | import android.support.v4.content.ContextCompat;
18 | import android.support.v7.widget.LinearLayoutManager;
19 | import android.support.v7.widget.RecyclerView;
20 | import android.view.ContextThemeWrapper;
21 | import android.view.Gravity;
22 | import android.view.ViewGroup;
23 | import android.widget.ImageView;
24 | import android.widget.TextView;
25 |
26 | import java.util.ArrayList;
27 | import java.util.List;
28 |
29 | import com.sevenheaven.uilibrary.drawables.GroupedAvatarDrawable;
30 | import com.sevenheaven.uilibrary.drawables.MaskDrawable;
31 | import com.sevenheaven.uilibrary.drawables.progressive.ProgressiveDrawable;
32 | import com.sevenheaven.uilibrary.drawables.progressive.providers.AppStoreStyleProgressProvider;
33 | import com.sevenheaven.uilibrary.drawables.progressive.providers.PathProgressProvider;
34 | import com.sevenheaven.uilibrary.shapes.ArcShape;
35 | import com.sevenheaven.uilibrary.shapes.PolygonShape;
36 | import com.sevenheaven.uilibrary.views.AnimatedImageView;
37 | import com.sevenheaven.uilibrarysample.ExampleListAdapter.ExampleItem;
38 |
39 | /**
40 | * Created by 7heaven on 16/8/25.
41 | */
42 | public class ExampleListActivity extends Activity {
43 |
44 | private RecyclerView mRecyclerView;
45 | private ExampleListAdapter mAdapter;
46 | private List mExampleItemList;
47 |
48 | @Override
49 | protected void onCreate(Bundle savedInstanceState){
50 | super.onCreate(savedInstanceState);
51 | setContentView(R.layout.activity_example_list);
52 |
53 | mExampleItemList = new ArrayList<>();
54 | mAdapter = new ExampleListAdapter(mExampleItemList);
55 |
56 | mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
57 | mRecyclerView.setAdapter(mAdapter);
58 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
59 | }
60 |
61 | @Override
62 | protected void onResume(){
63 | super.onResume();
64 |
65 | mExampleItemList.clear();
66 | addGroupedAvatarDrawbleToList();
67 | addAppStoreStyleDrawableToList();
68 | addPathProgressDrawableToList();
69 | addMaskDrawableToList();
70 | addPolygonShapeInteractToList();
71 | addAnimatedImageViewToList();
72 | addArcShapeToList();
73 | }
74 |
75 | private void addArcShapeToList(){
76 | final ArcShape arcShape = new ArcShape(0, 0, 0);
77 | final ShapeDrawable shapeDrawable = new ShapeDrawable(arcShape);
78 | shapeDrawable.getPaint().setStyle(Paint.Style.STROKE);
79 | shapeDrawable.getPaint().setColor(0xFF0099CC);
80 | shapeDrawable.setPadding(30, 30, 30, 30);
81 |
82 | mExampleItemList.add(new ExampleItem("ArcShape", new ExampleDetailActivity.DetailContentProvider() {
83 | @Override
84 | public Object provideInstance() {
85 | return shapeDrawable;
86 | }
87 |
88 | @Override
89 | public void onGestureMove(float x, float y, int action) {
90 | if(x >= 0 && x <= 1){
91 | arcShape.setEndAngle(360 * x);
92 | shapeDrawable.invalidateSelf();
93 | }
94 |
95 | if(y >= 0 && y <= 1){
96 | arcShape.setStrokeWidth((int) (y * 100));
97 | shapeDrawable.invalidateSelf();
98 | }
99 | }
100 |
101 | @Override
102 | public void destroy() {
103 |
104 | }
105 | }));
106 | }
107 |
108 | private void addAnimatedImageViewToList(){
109 | final AnimatedImageView imageView = new AnimatedImageView(this);
110 | ViewGroup.MarginLayoutParams layoutParams = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
111 | layoutParams.setMargins(50, 50, 50, 50);
112 | imageView.setLayoutParams(layoutParams);
113 | imageView.setImageResource(R.drawable.test2);
114 |
115 | final ImageView.ScaleType[] allScaleTypes = new ImageView.ScaleType[]{
116 | ImageView.ScaleType.CENTER,
117 | ImageView.ScaleType.CENTER_CROP,
118 | ImageView.ScaleType.CENTER_INSIDE,
119 | ImageView.ScaleType.FIT_CENTER,
120 | ImageView.ScaleType.FIT_END,
121 | ImageView.ScaleType.FIT_START,
122 | ImageView.ScaleType.FIT_XY
123 | };
124 |
125 | mExampleItemList.add(new ExampleItem("AnimatedImageView", new ExampleDetailActivity.DetailContentProvider() {
126 | @Override
127 | public Object provideInstance() {
128 | return imageView;
129 | }
130 |
131 | @Override
132 | public void onGestureMove(float x, float y, int action) {
133 | if(x >= 0 && x <= 1){
134 | int index = (int) (x * allScaleTypes.length);
135 | if(index < allScaleTypes.length) imageView.setScaleTypeAnimated(allScaleTypes[index]);
136 | }
137 | }
138 |
139 | @Override
140 | public void destroy(){
141 | }
142 | }));
143 | }
144 |
145 | /**
146 | * 任意正多边形
147 | */
148 | private void addPolygonShapeInteractToList(){
149 | final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
150 |
151 | final int padding = 30;
152 | final PolygonShape shape = new PolygonShape(6, 0.2F, true, 0.5F);
153 | final Drawable contentDrawable = new Drawable() {
154 | @Override
155 | public void draw(Canvas canvas) {
156 | canvas.save();
157 | canvas.translate(padding, padding);
158 | paint.setStyle(Paint.Style.STROKE);
159 | paint.setStrokeWidth(10);
160 | paint.setColor(0xFF32ADFF);
161 |
162 | shape.draw(canvas, paint);
163 |
164 | canvas.restore();
165 | }
166 |
167 | @Override
168 | public void setAlpha(int i) {
169 |
170 | }
171 |
172 | @Override
173 | public void setColorFilter(ColorFilter colorFilter) {
174 |
175 | }
176 |
177 | @Override
178 | public int getOpacity() {
179 | return PixelFormat.TRANSPARENT;
180 | }
181 |
182 | @Override
183 | public void onBoundsChange(Rect bounds){
184 | shape.resize(bounds.width() - padding - padding, bounds.height() - padding - padding);
185 | }
186 | };
187 | mExampleItemList.add(new ExampleItem("PolygonShapeInteract", new ExampleDetailActivity.DetailContentProvider() {
188 | @Override
189 | public Object provideInstance() {
190 | return contentDrawable;
191 | }
192 |
193 | @Override
194 | public void onGestureMove(float x, float y, int action) {
195 | if(x >= 0 && x <= 1 && y >= 0 && y <= 1){
196 | shape.setCornerRadius(x / 2);
197 | shape.setVertexCount((int) (y * 10) + 3);
198 | contentDrawable.invalidateSelf();
199 | }
200 | }
201 |
202 | @Override
203 | public void destroy(){
204 |
205 | }
206 | }));
207 | }
208 |
209 | /**
210 | * 任意形状的遮罩
211 | */
212 | private void addMaskDrawableToList(){
213 | final Drawable contentDrawable = ContextCompat.getDrawable(this, R.drawable.test1);
214 | final PolygonShape shape = new PolygonShape(6, 0.2F);
215 | final MaskDrawable drawable = new MaskDrawable(contentDrawable, shape);
216 | mExampleItemList.add(new ExampleItem("MaskDrawable+PolygonShape", new ExampleDetailActivity.DetailContentProvider() {
217 | @Override
218 | public Object provideInstance() {
219 | return drawable;
220 | }
221 |
222 | @Override
223 | public void onGestureMove(float x, float y, int action) {
224 | if(x >= 0 && x <= 1){
225 | shape.setVertexCount((int) (x * 6) + 3);
226 | drawable.recreateContent();
227 | }
228 | }
229 |
230 | @Override
231 | public void destroy(){
232 |
233 | }
234 | }));
235 | }
236 |
237 |
238 | /**
239 | * 任意Path作为进度条
240 | */
241 | private void addPathProgressDrawableToList(){
242 | Paint paint = new Paint();
243 | paint.setTextSize(100);
244 |
245 | //取得"Android N"字符串的轮廓
246 | Path animatePath = new Path();
247 | String text = "Android N";
248 | paint.getTextPath(text, 0, text.length(), 0, paint.getTextSize(), animatePath);
249 | PathProgressProvider.PathDesc animateDesc = new PathProgressProvider.PathDesc(animatePath, Gravity.CENTER, true, false, false);
250 |
251 | RectF animatePathBounds = new RectF();
252 | animatePath.computeBounds(animatePathBounds, false);
253 |
254 | Path progressPath = new Path();
255 | progressPath.addCircle(100, 100, animatePathBounds.width() / 2 + 50, Path.Direction.CW);
256 | progressPath.addCircle(100, 100, animatePathBounds.width() / 2 + 80, Path.Direction.CW);
257 | PathProgressProvider.PathDesc pathDesc = new PathProgressProvider.PathDesc(progressPath, Gravity.CENTER, true, true, false);
258 |
259 | ProgressiveDrawable.DrawContentProvider pathProgressProvider = new PathProgressProvider(pathDesc, animateDesc){
260 | @Override
261 | protected void updateProgressPaint(Paint paint){
262 | paint.setStyle(Paint.Style.STROKE);
263 | paint.setStrokeWidth(5);
264 | paint.setStrokeCap(Paint.Cap.ROUND);
265 | paint.setColor(0xFF44AEFF);
266 | }
267 |
268 | @Override
269 | protected void updateAnimationPaint(Paint paint){
270 | paint.setStyle(Paint.Style.STROKE);
271 | paint.setStrokeWidth(2);
272 | paint.setColor(0xFF44AEFF);
273 | }
274 | };
275 |
276 | final ProgressiveDrawable drawable = new ProgressiveDrawable(pathProgressProvider);
277 | mExampleItemList.add(new ExampleItem("PathProgressDrawable", new ExampleDetailActivity.DetailContentProvider() {
278 | @Override
279 | public Object provideInstance() {
280 | return drawable;
281 | }
282 |
283 | @Override
284 | public void onGestureMove(float x, float y, int action) {
285 | if(x >= -1 && x <= 1){
286 | drawable.setProgress(x);
287 | }
288 | }
289 |
290 | @Override
291 | public void destroy(){
292 |
293 | }
294 | }));
295 | }
296 |
297 | /**
298 | * 苹果桌面图标下载风格的进度条
299 | */
300 | private void addAppStoreStyleDrawableToList(){
301 | ProgressiveDrawable.DrawContentProvider appStoreProvider = new AppStoreStyleProgressProvider(0x99000000);
302 |
303 | final ProgressiveDrawable drawable = new ProgressiveDrawable(appStoreProvider);
304 | final Drawable backgroundDrawable = ContextCompat.getDrawable(this, R.drawable.test1);
305 | final LayerDrawable contentDrawalbe = new LayerDrawable(new Drawable[]{backgroundDrawable, drawable});
306 | mExampleItemList.add(new ExampleItem("AppStoreStyleDrawable", new ExampleDetailActivity.DetailContentProvider() {
307 | @Override
308 | public Object provideInstance() {
309 | return contentDrawalbe;
310 | }
311 |
312 | @Override
313 | public void onGestureMove(float x, float y, int action) {
314 | if(x >= -1 && x <= 1){
315 | drawable.setProgress(x);
316 | }
317 | }
318 |
319 | @Override
320 | public void destroy(){
321 |
322 | }
323 | }));
324 | }
325 |
326 | /**
327 | *
328 | */
329 | private void addGroupedAvatarDrawbleToList(){
330 | final Bitmap[] avatars = new Bitmap[]{BitmapFactory.decodeResource(getResources(), R.drawable.test0),
331 | BitmapFactory.decodeResource(getResources(), R.drawable.test1),
332 | BitmapFactory.decodeResource(getResources(), R.drawable.test2),
333 | BitmapFactory.decodeResource(getResources(), R.drawable.test0),
334 | BitmapFactory.decodeResource(getResources(), R.drawable.test1),
335 | BitmapFactory.decodeResource(getResources(), R.drawable.test2),
336 | BitmapFactory.decodeResource(getResources(), R.drawable.test0),
337 | BitmapFactory.decodeResource(getResources(), R.drawable.test1),
338 | BitmapFactory.decodeResource(getResources(), R.drawable.test2)};
339 | final GroupedAvatarDrawable avatarDrawable = new GroupedAvatarDrawable();
340 | avatarDrawable.setStrokeWidth(20);
341 | mExampleItemList.add(new ExampleItem("GroupedAvatarDrawable", new ExampleDetailActivity.DetailContentProvider() {
342 | @Override
343 | public GroupedAvatarDrawable provideInstance() {
344 | return avatarDrawable;
345 | }
346 |
347 | @Override
348 | public void onGestureMove(float x, float y, int action) {
349 | if(x >= 0 && x <= 1){
350 | int index = (int) (x * avatars.length);
351 | int count = index < avatars.length ? index : avatars.length;
352 | Bitmap[] input = new Bitmap[count];
353 | for(int i = 0; i < count; i++){
354 | input[i] = avatars[i];
355 | }
356 |
357 | avatarDrawable.setAvatars(input);
358 | }
359 | }
360 |
361 | @Override
362 | public void destroy(){
363 |
364 | }
365 | }));
366 | }
367 | }
368 |
--------------------------------------------------------------------------------
/example/src/main/java/com/sevenheaven/uilibrarysample/ExampleListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrarysample;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.TextView;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * Created by 7heaven on 16/8/25.
14 | */
15 | public class ExampleListAdapter extends RecyclerView.Adapter {
16 |
17 | private List mExampleItemList;
18 |
19 | public ExampleListAdapter(List exampleItemList){
20 | mExampleItemList = exampleItemList;
21 | }
22 |
23 | @Override
24 | public int getItemCount(){
25 | return mExampleItemList.size();
26 | }
27 |
28 | @Override
29 | public ExampleListViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
30 | return new ExampleListViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_examplelist, parent, false));
31 | }
32 |
33 | @Override
34 | public void onBindViewHolder(ExampleListViewHolder viewHolder, int position){
35 | final ExampleItem item = mExampleItemList.get(position);
36 | viewHolder.title.setText(item.title);
37 | if(item.provider != null){
38 | viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
39 | @Override
40 | public void onClick(View view) {
41 | Intent intent = new Intent(view.getContext(), ExampleDetailActivity.class);
42 | ExampleDetailActivity.mContentProvider = item.provider;
43 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
44 | view.getContext().startActivity(intent);
45 | }
46 | });
47 | }
48 | }
49 |
50 | public static class ExampleListViewHolder extends RecyclerView.ViewHolder{
51 | TextView title;
52 |
53 | public ExampleListViewHolder(View itemView){
54 | super(itemView);
55 |
56 | title = (TextView) itemView.findViewById(R.id.title);
57 | }
58 | }
59 |
60 | public static class ExampleItem{
61 | String title;
62 | ExampleDetailActivity.DetailContentProvider provider;
63 |
64 | public ExampleItem(String title, ExampleDetailActivity.DetailContentProvider provider){
65 | this.title = title;
66 | this.provider = provider;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/test0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/example/src/main/res/drawable-nodpi/test0.png
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/test1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/example/src/main/res/drawable-nodpi/test1.png
--------------------------------------------------------------------------------
/example/src/main/res/drawable-nodpi/test2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/example/src/main/res/drawable-nodpi/test2.png
--------------------------------------------------------------------------------
/example/src/main/res/layout/activity_example_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 |
16 |
21 |
27 |
32 |
33 |
34 |
40 |
46 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/activity_example_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
--------------------------------------------------------------------------------
/example/src/main/res/layout/item_examplelist.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/example/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/example/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/example/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/example/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/example/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/example/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/example/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/example/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | UILibrarySample
3 |
4 |
--------------------------------------------------------------------------------
/example/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/example/src/test/java/com/sevenheaven/uilibrarysample/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrarysample;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7heaven/UILibrary/daa674438a66702320a451aeee18624fa08dc6a1/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':example', ':uilibrary'
2 |
--------------------------------------------------------------------------------
/uilibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/uilibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "24.0.1"
6 |
7 | defaultConfig {
8 | minSdkVersion 11
9 | targetSdkVersion 24
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | testCompile 'junit:junit:4.12'
24 | compile 'com.android.support:appcompat-v7:24.2.0'
25 | }
26 |
--------------------------------------------------------------------------------
/uilibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/7heaven/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/uilibrary/src/androidTest/java/com/sevenheaven/uilibrary/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/uilibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/drawables/GroupedAvatarDrawable.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.drawables;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapShader;
5 | import android.graphics.Canvas;
6 | import android.graphics.ColorFilter;
7 | import android.graphics.Matrix;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.graphics.PixelFormat;
11 | import android.graphics.PorterDuff;
12 | import android.graphics.PorterDuffXfermode;
13 | import android.graphics.Rect;
14 | import android.graphics.RectF;
15 | import android.graphics.Shader;
16 | import android.graphics.drawable.Drawable;
17 |
18 | import com.sevenheaven.uilibrary.utils.GeomUtil;
19 |
20 | /**
21 | * Drawable that draw a group of images as a circle
22 | *
23 | * Created by 7heaven on 16/8/13.
24 | */
25 | public class GroupedAvatarDrawable extends Drawable {
26 |
27 | /**
28 | * Cached bitmaps for bounds change recalculation
29 | */
30 | private Bitmap[] mInputBitmaps;
31 | private Bitmap mSourceBitmap;
32 |
33 | private int mRadiusStrokeWidth;
34 |
35 | private static final float SINGLE_ITEM_OVERLAY_FACTOR = 0.08F;
36 |
37 | private int mCenterX;
38 | private int mCenterY;
39 |
40 | private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
41 |
42 | public GroupedAvatarDrawable(){
43 | mRadiusStrokeWidth = 10;
44 | }
45 |
46 | @Override
47 | public int getOpacity(){
48 | return PixelFormat.TRANSPARENT;
49 | }
50 |
51 | @Override
52 | public void setAlpha(int alpha){
53 | mPaint.setAlpha(alpha);
54 | }
55 |
56 | @Override
57 | public void setColorFilter(ColorFilter filter){
58 | mPaint.setColorFilter(filter);
59 | }
60 |
61 | /**
62 | * set avatar bitmaps for drawing
63 | * @param inputBitmaps
64 | */
65 | public void setAvatars(Bitmap... inputBitmaps){
66 | mInputBitmaps = inputBitmaps;
67 |
68 | if(getBounds().width() > 0 && getBounds().height() > 0){
69 | mCenterX = getBounds().width() / 2;
70 | mCenterY = getBounds().height() / 2;
71 | generateSourceBitmap(getBounds());
72 |
73 | invalidateSelf();
74 | }
75 | }
76 |
77 | /**
78 | * set stroke width for for single image
79 | * @param strokeWidth
80 | */
81 | public void setStrokeWidth(int strokeWidth){
82 | if(mRadiusStrokeWidth != strokeWidth){
83 | mRadiusStrokeWidth = strokeWidth;
84 |
85 | generateSourceBitmap(getBounds());
86 |
87 | invalidateSelf();
88 | }
89 | }
90 |
91 | /**
92 | * Generate source bitmap for drawable drawing
93 | *
94 | * @param bounds
95 | */
96 | private void generateSourceBitmap(Rect bounds){
97 | if(mInputBitmaps != null && mInputBitmaps.length > 0){
98 | final int bitmapCount = mInputBitmaps.length;
99 |
100 | mSourceBitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888);
101 | Canvas canvas = new Canvas(mSourceBitmap);
102 | Paint drawPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
103 | drawPaint.setFilterBitmap(true);
104 |
105 | float fitBoundsRadius = (bounds.width() > bounds.height() ? bounds.height() : bounds.width()) / 2;
106 | if(bitmapCount > 1){
107 | /**
108 | * To ensure when bitmapCount == 3, each item's radius will equals to fitBoundsRadius / 2
109 | * and radius of single item will decrease when bitmapCount increase
110 | */
111 | float scaleFactor = bitmapCount < 7 ? (1.0F + SINGLE_ITEM_OVERLAY_FACTOR) - (bitmapCount - 2) * SINGLE_ITEM_OVERLAY_FACTOR : 1.0F * (5.0F / bitmapCount);
112 | float singleRadius = (fitBoundsRadius * scaleFactor) / 2 - mRadiusStrokeWidth;
113 | float outerCircleRadius = fitBoundsRadius - singleRadius - mRadiusStrokeWidth;
114 |
115 | double step = Math.PI * 2.0 / bitmapCount;
116 |
117 | double startAngle;
118 | if((bitmapCount & 1) == 0){
119 | startAngle = -Math.PI * 0.5 - (bitmapCount == 2 ? Math.PI * 0.25 : step / 2);
120 | }else{
121 | startAngle = -Math.PI * 0.5F;
122 | }
123 |
124 | float[] centerPoint = new float[2];
125 | for(int i = 0; i < bitmapCount; i++){
126 | GeomUtil.pointOnCircumference(mCenterX, mCenterY, startAngle + i * step, outerCircleRadius, centerPoint);
127 |
128 | if(mInputBitmaps[i] != null && !mInputBitmaps[i].isRecycled()) drawBitmapWithStroke(canvas, mInputBitmaps[i], (int) centerPoint[0], (int) centerPoint[1], singleRadius, drawPaint);
129 | }
130 |
131 | if(bitmapCount > 2){
132 | float outerClipRadius = outerCircleRadius * 2;
133 | RectF outerClipBounds = new RectF();
134 | outerClipBounds.left = mCenterX - outerClipRadius;
135 | outerClipBounds.top = mCenterY - outerClipRadius;
136 | outerClipBounds.right = mCenterX + outerClipRadius;
137 | outerClipBounds.bottom = mCenterY + outerClipRadius;
138 |
139 | final int savedCount = canvas.save();
140 | Path clipPath = new Path();
141 | clipPath.moveTo(mCenterX, mCenterY);
142 | GeomUtil.pointOnCircumference(mCenterX, mCenterY, startAngle + (mInputBitmaps.length - 1) * step, outerClipRadius, centerPoint);
143 | clipPath.arcTo(outerClipBounds, (float) Math.toDegrees((startAngle + (mInputBitmaps.length - 1) * step)), (float) Math.toDegrees(step));
144 | clipPath.close();
145 | canvas.clipPath(clipPath);
146 |
147 | GeomUtil.pointOnCircumference(mCenterX, mCenterY, startAngle, outerCircleRadius, centerPoint);
148 | drawBitmapWithStroke(canvas, mInputBitmaps[0], (int) centerPoint[0], (int) centerPoint[1], singleRadius, drawPaint);
149 |
150 |
151 | canvas.restoreToCount(savedCount);
152 | }
153 | }else{
154 | drawBitmapWithStroke(canvas, mInputBitmaps[0], mCenterX, mCenterY, fitBoundsRadius - mRadiusStrokeWidth, drawPaint);
155 | }
156 | }
157 | }
158 |
159 | /**
160 | * Draw single bitmap with the giving centerX, centerY and radius
161 | * @param canvas
162 | * @param bitmap
163 | * @param centerX
164 | * @param centerY
165 | * @param radius
166 | * @param drawPaint
167 | */
168 | private void drawBitmapWithStroke(Canvas canvas, Bitmap bitmap, int centerX, int centerY, float radius, Paint drawPaint){
169 | drawPaint.setStyle(Paint.Style.FILL);
170 |
171 | float strokeRadius = radius + mRadiusStrokeWidth;
172 | drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
173 | canvas.drawCircle(centerX, centerY, strokeRadius, drawPaint);
174 | drawPaint.setXfermode(null);
175 |
176 | final int halfBitmapWidth = bitmap.getWidth() / 2;
177 | final int halfBitmapHeight = bitmap.getHeight() / 2;
178 |
179 | BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
180 | Matrix shaderMatrix = new Matrix();
181 | float minSize = bitmap.getWidth() > bitmap.getHeight() ? bitmap.getHeight() : bitmap.getWidth();
182 | float scale = radius * 2 / minSize;
183 | shaderMatrix.setScale(scale, scale);
184 | shaderMatrix.postTranslate(centerX - (halfBitmapWidth * scale), centerY - (halfBitmapHeight * scale));
185 | bitmapShader.setLocalMatrix(shaderMatrix);
186 |
187 | drawPaint.setShader(bitmapShader);
188 | canvas.drawCircle(centerX, centerY, radius, drawPaint);
189 | drawPaint.setShader(null);
190 | }
191 |
192 | @Override
193 | protected void onBoundsChange(Rect bounds){
194 | mCenterX = bounds.width() / 2;
195 | mCenterY = bounds.height() / 2;
196 |
197 | if(bounds.width() > 0 && bounds.height() > 0) generateSourceBitmap(bounds);
198 | }
199 |
200 | @Override
201 | public void draw(Canvas canvas){
202 | if(mSourceBitmap != null && !mSourceBitmap.isRecycled()){
203 | canvas.drawBitmap(mSourceBitmap, 0, 0, mPaint);
204 | }
205 | }
206 |
207 | public void recycle(){
208 | if(mInputBitmaps != null){
209 | for(int i = 0; i < mInputBitmaps.length; i++){
210 | Bitmap bitmap = mInputBitmaps[i];
211 | if(bitmap != null && !bitmap.isRecycled()){
212 | bitmap.recycle();
213 | }
214 | }
215 |
216 | mInputBitmaps = null;
217 | }
218 |
219 | if(mSourceBitmap != null && !mSourceBitmap.isRecycled()){
220 | mSourceBitmap.recycle();
221 | mSourceBitmap = null;
222 | }
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/drawables/MaskDrawable.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.drawables;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapShader;
5 | import android.graphics.Canvas;
6 | import android.graphics.ColorFilter;
7 | import android.graphics.Paint;
8 | import android.graphics.PixelFormat;
9 | import android.graphics.Rect;
10 | import android.graphics.Shader;
11 | import android.graphics.drawable.Drawable;
12 | import android.graphics.drawable.shapes.Shape;
13 |
14 | /**
15 | * Convenient way to create Drawble with shape-cliped effect
16 | *
17 | * Created by 7heaven on 16/8/25.
18 | */
19 | public class MaskDrawable extends Drawable {
20 |
21 | private Bitmap mBitmap;
22 | private Canvas mAssociateCanvas;
23 |
24 | private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
25 |
26 | private Drawable mDrawingContent;
27 | private Shape mMask;
28 |
29 | public MaskDrawable(Drawable content, Shape mask){
30 | mDrawingContent = content;
31 | mMask = mask;
32 |
33 | mPaint.setStyle(Paint.Style.FILL);
34 | mPaint.setColor(0xFFFFFFFF);
35 | }
36 |
37 | public void setContent(Drawable content){
38 | mDrawingContent = content;
39 | mDrawingContent.setBounds(getBounds());
40 | recreateBitmap();
41 |
42 | invalidateSelf();
43 | }
44 |
45 | public void setMask(Shape mask){
46 | mMask = mask;
47 | mMask.resize(getBounds().width(), getBounds().height());
48 | recreateBitmap();
49 |
50 | invalidateSelf();
51 | }
52 |
53 | public void recreateContent(){
54 | recreateBitmap();
55 |
56 | invalidateSelf();
57 | }
58 |
59 | @Override
60 | public void onBoundsChange(Rect bounds){
61 | if(mDrawingContent != null){
62 | mDrawingContent.setBounds(0, 0, bounds.width(), bounds.height());
63 | }
64 | if(mMask != null){
65 | mMask.resize(bounds.width(), bounds.height());
66 | }
67 |
68 | recreateBitmap();
69 | }
70 |
71 | private void recreateBitmap(){
72 | if(mDrawingContent != null && mMask != null){
73 | if(mBitmap != null && !mBitmap.isRecycled()){
74 | mBitmap.recycle();
75 | mBitmap = null;
76 | }
77 | mBitmap = Bitmap.createBitmap(getBounds().width() == 0 ? 1 : getBounds().width(), getBounds().height() == 0 ? 1 : getBounds().height(), Bitmap.Config.ARGB_8888);
78 | mAssociateCanvas = new Canvas(mBitmap);
79 |
80 | mDrawingContent.draw(mAssociateCanvas);
81 |
82 | mPaint.setShader(new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
83 | }
84 | }
85 |
86 | @Override
87 | public void draw(Canvas canvas){
88 | if(mMask != null){
89 | canvas.save();
90 | canvas.translate(-getBounds().left, -getBounds().top);
91 |
92 | mMask.draw(canvas, mPaint);
93 | canvas.restore();
94 | }
95 | }
96 |
97 | @Override
98 | public void setAlpha(int alpha) {
99 | mPaint.setAlpha(alpha);
100 |
101 | invalidateSelf();
102 | }
103 |
104 | @Override
105 | public void setColorFilter(ColorFilter cf) {
106 | mPaint.setColorFilter(cf);
107 |
108 | invalidateSelf();
109 | }
110 |
111 | @Override
112 | public int getOpacity() {
113 | return PixelFormat.TRANSLUCENT;
114 | }
115 |
116 | public void recycle(){
117 | mPaint.setShader(null);
118 |
119 | if(mBitmap != null && !mBitmap.isRecycled()){
120 | mBitmap.recycle();
121 | mBitmap = null;
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/drawables/RoundEdgeDrawable.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.drawables;
2 |
3 | import android.graphics.Rect;
4 | import android.graphics.drawable.GradientDrawable;
5 | import android.support.annotation.IntDef;
6 |
7 | import java.lang.annotation.Retention;
8 | import java.lang.annotation.RetentionPolicy;
9 |
10 | /**
11 | * Created by 7heaven on 16/8/8.
12 | */
13 | public class RoundEdgeDrawable extends GradientDrawable {
14 |
15 | @IntDef({EdgeMark.TOP_LEFT, EdgeMark.TOP_RIGHT, EdgeMark.BOTTOM_RIGHT, EdgeMark.BOTTOM_LEFT})
16 | @Retention(RetentionPolicy.SOURCE)
17 | public @interface EdgeMark{
18 | int TOP_LEFT = 0x1;
19 | int TOP_RIGHT = 0x2;
20 | int BOTTOM_RIGHT = 0x4;
21 | int BOTTOM_LEFT = 0x8;
22 | }
23 |
24 | private int mEdgeMark;
25 |
26 | public RoundEdgeDrawable(){
27 | super();
28 | mEdgeMark = 0x0;
29 | }
30 |
31 | public RoundEdgeDrawable(GradientDrawable.Orientation orientation, int[] colors){
32 | super(orientation, colors);
33 | mEdgeMark = 0x0;
34 | }
35 |
36 | public RoundEdgeDrawable(GradientDrawable.Orientation orientation, int[] colors, @EdgeMark int edgeMark){
37 | super(orientation, colors);
38 | mEdgeMark = edgeMark;
39 | }
40 |
41 | public void setEdgeMark(@EdgeMark int edgeMark){
42 | if(mEdgeMark != edgeMark){
43 | mEdgeMark = edgeMark;
44 | recalculateRadius(getBounds());
45 | }
46 | }
47 |
48 | @Override
49 | public void setCornerRadius(float radius){
50 |
51 | }
52 |
53 | @Override
54 | public void setCornerRadii(float[] radii){
55 |
56 | }
57 |
58 | private void recalculateRadius(Rect bounds){
59 | int verticalHalf = bounds.height() / 2;
60 | int horizontalHalf = bounds.width() / 2;
61 |
62 | float calculatedRadius = verticalHalf > horizontalHalf ? horizontalHalf : verticalHalf;
63 | if(mEdgeMark == 0x0 || (mEdgeMark & (EdgeMark.TOP_LEFT | EdgeMark.TOP_RIGHT | EdgeMark.BOTTOM_LEFT | EdgeMark.BOTTOM_RIGHT)) > 0){
64 | super.setCornerRadius(calculatedRadius);
65 | }else{
66 | float topLeftRadius = 0;
67 | float topRightRadius = 0;
68 | float bottomRightRadius = 0;
69 | float bottomLeftRadius = 0;
70 |
71 | if((mEdgeMark & EdgeMark.TOP_LEFT) > 0){
72 | topLeftRadius = calculatedRadius;
73 | }
74 |
75 | if((mEdgeMark & EdgeMark.TOP_RIGHT) > 0){
76 | topRightRadius = calculatedRadius;
77 | }
78 |
79 | if((mEdgeMark & EdgeMark.BOTTOM_LEFT) > 0){
80 | bottomLeftRadius = calculatedRadius;
81 | }
82 |
83 | if((mEdgeMark & EdgeMark.BOTTOM_RIGHT) > 0){
84 | bottomRightRadius = calculatedRadius;
85 | }
86 |
87 | super.setCornerRadii(new float[]{topLeftRadius,
88 | topLeftRadius,
89 | topRightRadius,
90 | topRightRadius,
91 | bottomRightRadius,
92 | bottomRightRadius,
93 | bottomLeftRadius,
94 | bottomLeftRadius});
95 | }
96 | }
97 |
98 | @Override
99 | protected void onBoundsChange(Rect bounds){
100 |
101 | recalculateRadius(bounds);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/drawables/progressive/LoadingDrawable.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.drawables.progressive;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ValueAnimator;
5 |
6 | /**
7 | * Created by 7heaven on 16/8/8.
8 | */
9 | public abstract class LoadingDrawable extends ProgressiveDrawable {
10 |
11 | private Animator.AnimatorListener startAnimationListener = new Animator.AnimatorListener(){
12 | @Override
13 | public void onAnimationStart(Animator animator) {
14 |
15 | }
16 |
17 | @Override
18 | public void onAnimationEnd(Animator animator) {
19 | performProgressiveAnimation();
20 |
21 | mAnimator.removeListener(this);
22 | }
23 |
24 | @Override
25 | public void onAnimationCancel(Animator animator) {
26 |
27 | }
28 |
29 | @Override
30 | public void onAnimationRepeat(Animator animator) {
31 |
32 | }
33 | };
34 |
35 | private Animator.AnimatorListener progressiveAnimationListener = new Animator.AnimatorListener(){
36 | @Override
37 | public void onAnimationStart(Animator animator) {
38 |
39 | }
40 |
41 | @Override
42 | public void onAnimationEnd(Animator animator) {
43 | }
44 |
45 | @Override
46 | public void onAnimationCancel(Animator animator) {
47 | }
48 |
49 | @Override
50 | public void onAnimationRepeat(Animator animator) {
51 |
52 | }
53 | };
54 |
55 | private ValueAnimator mProgressiveAnimator;
56 |
57 | public LoadingDrawable(DrawContentProvider contentProvider){
58 | super(contentProvider);
59 | }
60 |
61 | public void setInvertLoadingAnimation(boolean invert){
62 |
63 | }
64 |
65 | public void startLoading(){
66 | mAnimator.addListener(startAnimationListener);
67 | performStartAnimation();
68 | }
69 |
70 | public void stopLoading(){
71 | mProgressiveAnimator.end();
72 |
73 | performEndAnimation();
74 | }
75 |
76 | // TODO implement progressive animation to make a circling animation for progress content drawing
77 | private void performProgressiveAnimation(){
78 |
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/drawables/progressive/ProgressiveDrawable.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.drawables.progressive;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ValueAnimator;
5 | import android.graphics.Canvas;
6 | import android.graphics.ColorFilter;
7 | import android.graphics.PixelFormat;
8 | import android.graphics.Rect;
9 | import android.graphics.drawable.Drawable;
10 | import android.support.annotation.FloatRange;
11 | import android.support.annotation.IntDef;
12 | import android.support.annotation.NonNull;
13 | import android.view.animation.DecelerateInterpolator;
14 | import android.view.animation.Interpolator;
15 |
16 | import java.lang.annotation.Retention;
17 | import java.lang.annotation.RetentionPolicy;
18 |
19 | /**
20 | * Drawable for progress content drawing
21 | *
22 | * Created by 7heaven on 16/8/5.
23 | */
24 | public class ProgressiveDrawable extends Drawable{
25 |
26 | public static float PROGRESS_IDLE = -1;
27 |
28 | private float mProgress = 0;
29 | private float mAnimatedValue = 0;
30 |
31 | ValueAnimator mAnimator;
32 |
33 | private long mShowDuration = -1;
34 | private long mDismissDuration = -1;
35 | private static final long DEFAULT_ANIMATION_DURATION = 400L;
36 |
37 | private Interpolator mShowInterpolator;
38 | private Interpolator mDismissInterpolator;
39 | private static final Interpolator DEFAULT_ANIMATION_INTERPOLATOR = new DecelerateInterpolator();
40 |
41 | @AnimationType int mCurrentAnimationType;
42 |
43 | private DrawContentProvider mDrawContentProvider;
44 |
45 | /**
46 | * Class for provide progressive and animation content change
47 | */
48 | public static abstract class DrawContentProvider{
49 |
50 | private ProgressiveDrawable mHost;
51 |
52 | /**
53 | * Call when the host's bound changed
54 | * @param bounds
55 | */
56 | protected abstract void onBoundsChange(Rect bounds);
57 |
58 | /**
59 | * Draw progressive content and animatable content if need.
60 | * @param canvas the canvas which the content should be draw on.
61 | * @param progress current progress
62 | */
63 | protected abstract void drawProgress(Canvas canvas, @FloatRange(from=0.0F, to=1.0F) float progress);
64 |
65 | /**
66 | * Update the value for animation
67 | *
68 | * @param type AnimationType
69 | * @param value animated value
70 | */
71 | protected abstract void animationUpdate(@AnimationType int type, @FloatRange(from=0.0F, to=1.0F) float value);
72 |
73 | final protected void invalidateContent(){
74 | if(mHost != null){
75 | mHost.invalidateSelf();
76 | }
77 | }
78 |
79 | void setHost(ProgressiveDrawable hostDrawable){
80 | if(hostDrawable == null){
81 | mHost = null;
82 | }else{
83 | mHost = hostDrawable;
84 | }
85 | }
86 | }
87 |
88 | @IntDef({AnimationType.SHOW_ANIMATION, AnimationType.DISMISS_ANIMATION, AnimationType.IDLE})
89 | @Retention(RetentionPolicy.SOURCE)
90 | public @interface AnimationType{
91 | int SHOW_ANIMATION = 0;
92 | int DISMISS_ANIMATION = 1;
93 | int IDLE = 2;
94 | }
95 |
96 | public ProgressiveDrawable(@NonNull DrawContentProvider contentProvider){
97 | if(contentProvider == null) throw new IllegalArgumentException("content provider must not be null!");
98 |
99 | mDrawContentProvider = contentProvider;
100 | mDrawContentProvider.setHost(this);
101 |
102 | mAnimator = ValueAnimator.ofFloat(0, 1);
103 | mAnimator.setDuration(DEFAULT_ANIMATION_DURATION);
104 | mAnimator.setInterpolator(DEFAULT_ANIMATION_INTERPOLATOR);
105 | mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
106 | @Override
107 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
108 | mAnimatedValue = (float) valueAnimator.getAnimatedValue();
109 |
110 | mDrawContentProvider.animationUpdate(mCurrentAnimationType, mAnimatedValue);
111 | }
112 | });
113 | mAnimator.addListener(new Animator.AnimatorListener() {
114 | @Override
115 | public void onAnimationStart(Animator animator) {
116 |
117 | }
118 |
119 | @Override
120 | public void onAnimationEnd(Animator animator) {
121 | mCurrentAnimationType = AnimationType.IDLE;
122 | if(mProgress > 0 && mProgress < 1){
123 | mAnimatedValue = 1;
124 | mDrawContentProvider.animationUpdate(mCurrentAnimationType, mAnimatedValue);
125 | }
126 | }
127 |
128 | @Override
129 | public void onAnimationCancel(Animator animator) {
130 |
131 | }
132 |
133 | @Override
134 | public void onAnimationRepeat(Animator animator) {
135 |
136 | }
137 | });
138 | }
139 |
140 | public void setDrawContentProvider(DrawContentProvider provider){
141 | mDrawContentProvider = provider;
142 |
143 | if(mDrawContentProvider != null){
144 | mDrawContentProvider.onBoundsChange(getBounds());
145 | invalidateSelf();
146 | }
147 | }
148 |
149 | public DrawContentProvider getDrawContentProvider(){
150 | return mDrawContentProvider;
151 | }
152 |
153 | /**
154 | * Set the interpolator for progress bar init animation
155 | * @param interpolator
156 | */
157 | public void setShowAnimationInterpolator(Interpolator interpolator){
158 | mShowInterpolator = interpolator;
159 | }
160 |
161 | /**
162 | * Set the interpolator for progress bar dismiss animation
163 | * @param interpolator
164 | */
165 | public void setDismissAnimationInterpolator(Interpolator interpolator){
166 | mDismissInterpolator = interpolator;
167 | }
168 |
169 | /**
170 | * Set the duration for progress bar init animation
171 | * @param duration
172 | */
173 | public void setShowAnimationDuration(long duration){
174 | mShowDuration = duration;
175 | }
176 |
177 | /**
178 | * Set the duration for progress bar dismiss duration
179 | * @param duration
180 | */
181 | public void setDismissAnimationDuration(long duration){
182 | mDismissDuration = duration;
183 | }
184 |
185 | /**
186 | * Set the progress
187 | * @param progress
188 | */
189 | public void setProgress(float progress){
190 | if(progress == 0){
191 | performStartAnimation();
192 | }else if(progress == PROGRESS_IDLE){
193 | performEndAnimation();
194 | }else if(mAnimatedValue != 1 && !mAnimator.isRunning()){
195 | mAnimatedValue = 1;
196 | mDrawContentProvider.animationUpdate(AnimationType.IDLE, mAnimatedValue);
197 | }
198 |
199 | mProgress = progress;
200 |
201 | invalidateSelf();
202 | }
203 |
204 | void performStartAnimation(){
205 | if(mAnimator.isRunning()){
206 | mAnimator.end();
207 | mAnimator.cancel();
208 | }
209 |
210 | if(mShowInterpolator != null) mAnimator.setInterpolator(mShowInterpolator);
211 | if(mShowDuration != -1) mAnimator.setDuration(mShowDuration);
212 |
213 | if(mCurrentAnimationType == AnimationType.DISMISS_ANIMATION) mAnimator.setFloatValues(1 - mAnimatedValue, 1);
214 | mCurrentAnimationType = AnimationType.SHOW_ANIMATION;
215 |
216 | mAnimator.start();
217 | }
218 |
219 | void performEndAnimation(){
220 | if(mAnimator.isRunning()){
221 | mAnimator.end();
222 | mAnimator.cancel();
223 | }
224 | if(mDismissInterpolator != null) mAnimator.setInterpolator(mDismissInterpolator);
225 | if(mDismissDuration != -1) mAnimator.setDuration(mDismissDuration);
226 |
227 | if(mCurrentAnimationType == AnimationType.SHOW_ANIMATION) mAnimator.setFloatValues(1 - mAnimatedValue, 1);
228 | mCurrentAnimationType = AnimationType.DISMISS_ANIMATION;
229 |
230 | mAnimator.setFloatValues(1 - mAnimatedValue, 1);
231 |
232 | mAnimator.start();
233 | }
234 |
235 | @Override
236 | public void setAlpha(int alpha){
237 |
238 | }
239 |
240 | @Override
241 | public void setColorFilter(ColorFilter filter){
242 |
243 | }
244 |
245 | @Override
246 | public int getOpacity(){
247 | return PixelFormat.TRANSPARENT;
248 | }
249 |
250 | @Override
251 | protected void onBoundsChange(Rect bounds){
252 | mDrawContentProvider.onBoundsChange(bounds);
253 | }
254 |
255 | @Override
256 | public void draw(Canvas canvas){
257 | mDrawContentProvider.drawProgress(canvas, mProgress);
258 | }
259 |
260 | }
261 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/drawables/progressive/providers/AppStoreStyleProgressProvider.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.drawables.progressive.providers;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.Path;
6 | import android.graphics.Rect;
7 | import android.graphics.RectF;
8 | import android.support.annotation.FloatRange;
9 |
10 | import com.sevenheaven.uilibrary.drawables.progressive.ProgressiveDrawable;
11 |
12 | /**
13 | * Created by 7heaven on 16/8/8.
14 | */
15 | public class AppStoreStyleProgressProvider extends ProgressiveDrawable.DrawContentProvider {
16 |
17 | private Path path = new Path();
18 | private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
19 | private RectF mOval = new RectF();
20 | private RectF mOuterBound = new RectF();
21 |
22 | private int mRadius;
23 | private int mProgressRadiusToFit;
24 | private int mFitRadius;
25 |
26 | private int mCenterX;
27 | private int mCenterY;
28 |
29 | private int mColor;
30 | private int mAlpha;
31 |
32 | public AppStoreStyleProgressProvider(int color){
33 | mColor = color;
34 | mAlpha = (color & 0xFF000000) >>> 24;
35 | mPaint.setColor(mColor);
36 | }
37 |
38 | @Override
39 | protected void onBoundsChange(Rect bounds){
40 | mCenterX = bounds.width() / 2;
41 | mCenterY = bounds.height() / 2;
42 |
43 | int size = mCenterX > mCenterY ? mCenterY : mCenterX;
44 |
45 | mFitRadius = (int) (size * 0.8F);
46 |
47 | //calculate the distance between outer radius and inner progress circle radius
48 | mProgressRadiusToFit = (int) (mFitRadius * 0.15F);
49 |
50 | mOuterBound.left = 0;
51 | mOuterBound.top = 0;
52 | mOuterBound.right = bounds.width();
53 | mOuterBound.bottom = bounds.height();
54 | }
55 |
56 | private void updateRadius(int radius){
57 | mRadius = radius;
58 |
59 | int progressRadius = mRadius < mProgressRadiusToFit ? 0 : mRadius - mProgressRadiusToFit;
60 |
61 | mOval.left = mCenterX - progressRadius;
62 | mOval.right = mCenterX + progressRadius;
63 | mOval.top = mCenterY - progressRadius;
64 | mOval.bottom = mCenterY + progressRadius;
65 |
66 | invalidateContent();
67 | }
68 |
69 | @Override
70 | protected void animationUpdate(@ProgressiveDrawable.AnimationType int type, @FloatRange(from=0.0F, to=1.0F) float value){
71 | switch(type){
72 | case ProgressiveDrawable.AnimationType.SHOW_ANIMATION:
73 | case ProgressiveDrawable.AnimationType.IDLE:
74 | int realAlpha = (int) (value * mAlpha);
75 | mPaint.setColor((realAlpha << 24) | (mColor & 0xFFFFFF));
76 |
77 | updateRadius((int) (value * mFitRadius));
78 | break;
79 | case ProgressiveDrawable.AnimationType.DISMISS_ANIMATION:
80 | updateRadius((int) (((value * 1.5) + 1) * mFitRadius));
81 | break;
82 | }
83 | }
84 |
85 | @Override
86 | protected void drawProgress(Canvas canvas, @FloatRange(from=0.0F, to=1.0F) float progress){
87 | path.reset();
88 |
89 | //add outer rect
90 | path.addRect(mOuterBound, Path.Direction.CCW);
91 |
92 | //punch a hole
93 | path.addCircle(mCenterX, mCenterY, mRadius, Path.Direction.CW);
94 |
95 | canvas.drawPath(path, mPaint);
96 |
97 | if(progress >= 0){
98 | float angle = (1.0F - progress) * 360;
99 | float startAngle = -90 + (360 - angle);
100 | canvas.drawArc(mOval, startAngle, angle, true, mPaint);
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/drawables/progressive/providers/PathProgressProvider.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.drawables.progressive.providers;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Matrix;
5 | import android.graphics.Paint;
6 | import android.graphics.Path;
7 | import android.graphics.Rect;
8 | import android.graphics.RectF;
9 | import android.view.Gravity;
10 |
11 | import com.sevenheaven.uilibrary.drawables.progressive.ProgressiveDrawable;
12 | import com.sevenheaven.uilibrary.utils.GeomUtil;
13 | import com.sevenheaven.uilibrary.utils.PathMeasurement;
14 |
15 | /**
16 | * Draw content progressively with the provided path
17 | *
18 | * Created by 7heaven on 16/8/8.
19 | */
20 | public class PathProgressProvider extends ProgressiveDrawable.DrawContentProvider {
21 |
22 | private Path[] mDrawingProgressPaths;
23 | private Path[] mDrawingAnimationPaths;
24 | private Path mDrawingProgressConcatPath;
25 | private Path mDrawingAnimationConcatPath;
26 |
27 | private PathMeasurement mProgressPathMeasurement;
28 | private PathMeasurement mAnimationPathMeasurement;
29 |
30 | private Paint mPaint;
31 |
32 | /**
33 | * Path description
34 | */
35 | public static class PathDesc{
36 | Path mPath;
37 |
38 | final int mGravity;
39 |
40 | RectF mBounds;
41 | Rect mBoundsInt;
42 |
43 | final boolean mSubPathsProgressiveAsync;
44 | final boolean mKeepAspect;
45 | final boolean mScaleForBounds;
46 |
47 | private PathMeasurement mAssociatePathMeasurement;
48 |
49 | public PathDesc(Path path, int gravity, boolean subPathsProgressiveAsync){
50 | this(path, gravity, subPathsProgressiveAsync, true, true);
51 | }
52 |
53 | /**
54 | * Constructor for create a new PathDesc
55 | *
56 | * @param path main path
57 | * @param gravity gravity for path in the ProgressiveDrawble
58 | * @param subPathsProgressiveAsync indicate if path should draw progressively based on each contour's length or the entire length
59 | * @param keepAspect indicate if path should keep aspect when scaleForBounds == true
60 | * @param scaleForBounds indicate if path should scale when drawable bounds change
61 | */
62 | public PathDesc(Path path, int gravity, boolean subPathsProgressiveAsync, boolean keepAspect, boolean scaleForBounds){
63 | mPath = path;
64 | mBounds = new RectF();
65 | mBoundsInt = new Rect();
66 |
67 | mGravity = gravity;
68 | mKeepAspect = keepAspect;
69 | mScaleForBounds = scaleForBounds;
70 | mSubPathsProgressiveAsync = subPathsProgressiveAsync;
71 |
72 | computeBounds();
73 | }
74 |
75 | void computeBounds(){
76 | mPath.computeBounds(mBounds, false);
77 | mBoundsInt.left = (int) mBounds.left;
78 | mBoundsInt.top = (int) mBounds.top;
79 | mBoundsInt.right = (int) mBounds.right;
80 | mBoundsInt.bottom = (int) mBounds.bottom;
81 | }
82 | }
83 |
84 | private PathDesc mProgressPathDesc;
85 | private PathDesc mAnimationPathDesc;
86 |
87 | private Rect pathDescOutRect;
88 |
89 | public PathProgressProvider(PathDesc progressPathDesc, PathDesc animationPathDesc){
90 | mProgressPathDesc = progressPathDesc;
91 | mAnimationPathDesc = animationPathDesc;
92 |
93 | if(mProgressPathDesc != null){
94 | mProgressPathMeasurement = new PathMeasurement(mProgressPathDesc.mPath);
95 | mProgressPathDesc.mAssociatePathMeasurement = mProgressPathMeasurement;
96 | }
97 | if(mAnimationPathDesc != null){
98 | mAnimationPathMeasurement = new PathMeasurement(mAnimationPathDesc.mPath);
99 | mAnimationPathDesc.mAssociatePathMeasurement = mAnimationPathMeasurement;
100 | }
101 |
102 | mDrawingProgressConcatPath = new Path();
103 | mDrawingAnimationConcatPath = new Path();
104 |
105 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
106 | }
107 |
108 | private void transformPath(PathDesc pathDesc, Rect targetBounds){
109 | pathDesc.computeBounds();
110 |
111 | float scaleX = 1;
112 | float scaleY = 1;
113 |
114 | if(pathDesc.mScaleForBounds){
115 | if(pathDesc.mKeepAspect){
116 | float oRatio = pathDesc.mBounds.width() / pathDesc.mBounds.height();
117 | float tRatio = (float) targetBounds.width() / (float) targetBounds.height();
118 |
119 | if(oRatio > tRatio){
120 | float scale = (float) targetBounds.width() / pathDesc.mBounds.width();
121 |
122 | scaleX = scale;
123 | scaleY = scale;
124 | }else{
125 | float scale = (float) targetBounds.height() / pathDesc.mBounds.height();
126 |
127 | scaleX = scale;
128 | scaleY = scale;
129 | }
130 | }else{
131 | scaleX = (float) targetBounds.width() / pathDesc.mBounds.width();
132 | scaleY = (float) targetBounds.height() / pathDesc.mBounds.height();
133 | }
134 | }
135 |
136 | int w = (int) (pathDesc.mBounds.width() * scaleX);
137 | int h = (int) (pathDesc.mBounds.height() * scaleY);
138 |
139 | if(pathDescOutRect == null) pathDescOutRect = new Rect();
140 |
141 | Gravity.apply(pathDesc.mGravity, w, h, targetBounds, pathDescOutRect);
142 |
143 | Matrix transformMatrix = new Matrix();
144 | GeomUtil.getTransformationMatrix(pathDesc.mBoundsInt, pathDescOutRect, transformMatrix);
145 | pathDesc.mPath.transform(transformMatrix);
146 |
147 | pathDesc.computeBounds();
148 |
149 | if(pathDesc.mAssociatePathMeasurement != null){
150 | pathDesc.mAssociatePathMeasurement.setPath(pathDesc.mPath);
151 | }
152 | }
153 |
154 | @Override
155 | protected void onBoundsChange(Rect bounds){
156 | if(mAnimationPathDesc != null) transformPath(mAnimationPathDesc, bounds);
157 | if(mProgressPathDesc != null) transformPath(mProgressPathDesc, bounds);
158 | }
159 |
160 | /**
161 | * call when path's position changed
162 | * @param distance
163 | * @param pos
164 | * @param tan
165 | */
166 | protected void onPathPositionUpdate(Path invokedPath, float distance, float[] pos, float[] tan){}
167 |
168 | protected void updateProgressPaint(Paint paint){
169 | paint.setColor(0xFF0099CC);
170 | }
171 | protected void updateAnimationPaint(Paint paint){
172 | paint.setColor(0xFFCC9900);
173 | }
174 |
175 | @Override
176 | protected void animationUpdate(@ProgressiveDrawable.AnimationType int type, float value){
177 | if(mAnimationPathMeasurement != null){
178 | switch(type){
179 | case ProgressiveDrawable.AnimationType.IDLE:
180 | case ProgressiveDrawable.AnimationType.SHOW_ANIMATION:
181 | mDrawingAnimationPaths = mAnimationPathMeasurement.updatePhare(value, mAnimationPathDesc.mSubPathsProgressiveAsync);
182 | break;
183 | case ProgressiveDrawable.AnimationType.DISMISS_ANIMATION:
184 | mDrawingAnimationPaths = mAnimationPathMeasurement.updatePhare(1 - value, mAnimationPathDesc.mSubPathsProgressiveAsync);
185 | break;
186 | }
187 |
188 | invalidateContent();
189 | }
190 | }
191 |
192 | @Override
193 | protected void drawProgress(Canvas canvas, float progress){
194 | //draw the animation start/end path
195 | if(mDrawingAnimationPaths != null){
196 | updateAnimationPaint(mPaint);
197 | mDrawingAnimationConcatPath.reset();
198 |
199 | for(int i = 0; i < mDrawingAnimationPaths.length; i++){
200 | Path path = mDrawingAnimationPaths[i];
201 | if(path != null) mDrawingAnimationConcatPath.addPath(mDrawingAnimationPaths[i]);
202 | }
203 |
204 | canvas.drawPath(mDrawingAnimationConcatPath, mPaint);
205 | }
206 |
207 | //draw the progressive path
208 | if(mProgressPathMeasurement != null){
209 |
210 | mDrawingProgressPaths = mProgressPathMeasurement.updatePhare(progress, mProgressPathDesc.mSubPathsProgressiveAsync);
211 |
212 | if(mDrawingProgressPaths != null){
213 | updateProgressPaint(mPaint);
214 | mDrawingProgressConcatPath.reset();
215 |
216 | for(int i = 0; i < mDrawingProgressPaths.length; i++){
217 | Path path = mDrawingProgressPaths[i];
218 | if(path != null) mDrawingProgressConcatPath.addPath(path);
219 | }
220 |
221 | canvas.drawPath(mDrawingProgressConcatPath, mPaint);
222 | }
223 | }
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/shapes/ArcShape.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.shapes;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.Path;
6 | import android.graphics.RectF;
7 | import android.graphics.drawable.shapes.Shape;
8 |
9 | import com.sevenheaven.uilibrary.utils.GeomUtil;
10 |
11 | /**
12 | * Created by 7heaven on 2017/2/3.
13 | */
14 |
15 | public class ArcShape extends Shape {
16 |
17 | private float mStartAngle;
18 | private float mEndAngle;
19 |
20 | private int mStrokeWidth;
21 |
22 | private Path mComputedPath;
23 |
24 | public ArcShape(float startAngle, float endAngle, int strokeWidth){
25 | mStartAngle = startAngle;
26 | mEndAngle = endAngle;
27 |
28 | mStrokeWidth = strokeWidth;
29 |
30 | recomputeShape();
31 | }
32 |
33 | public void setStartAngle(float startAngle){
34 | if(startAngle != mStartAngle){
35 | mStartAngle = startAngle;
36 |
37 | recomputeShape();
38 | }
39 | }
40 |
41 | public void setEndAngle(float endAngle){
42 | if(endAngle != mEndAngle){
43 | mEndAngle = endAngle;
44 |
45 | recomputeShape();
46 | }
47 | }
48 |
49 | public void setStrokeWidth(int strokeWidth){
50 | if(strokeWidth != mStrokeWidth){
51 | mStrokeWidth = strokeWidth;
52 |
53 | recomputeShape();
54 | }
55 | }
56 |
57 | private void recomputeShape(){
58 | float centerX = getWidth() / 2;
59 | float centerY = getHeight() / 2;
60 |
61 | int halfStrokeWidth = mStrokeWidth / 2;
62 |
63 | float radius = (centerX > centerY ? centerY : centerX) - halfStrokeWidth;
64 |
65 | float innerRadius = radius - halfStrokeWidth;
66 | float outerRadius = radius + halfStrokeWidth;
67 |
68 | if(mComputedPath == null) mComputedPath = new Path();
69 | mComputedPath.reset();
70 |
71 | RectF oval = new RectF();
72 | ovalSet(centerX, centerY, outerRadius, oval);
73 |
74 | mComputedPath.arcTo(oval, mStartAngle, mEndAngle - mStartAngle, true);
75 |
76 | float[] endPoint = GeomUtil.pointOnCircumference((int) centerX, (int) centerY, Math.toRadians(mEndAngle), radius);
77 | ovalSet(endPoint[0], endPoint[1], halfStrokeWidth, oval);
78 |
79 | mComputedPath.arcTo(oval, mEndAngle, 180, false);
80 |
81 | ovalSet(centerX, centerY, innerRadius, oval);
82 |
83 | mComputedPath.arcTo(oval, mEndAngle, mStartAngle - mEndAngle, false);
84 |
85 | float[] startPoint = GeomUtil.pointOnCircumference((int) centerX, (int) centerY, Math.toRadians(mStartAngle), radius);
86 | ovalSet(startPoint[0], startPoint[1], halfStrokeWidth, oval);
87 |
88 | mComputedPath.arcTo(oval, mStartAngle - 180, 180, false);
89 |
90 | mComputedPath.close();
91 | }
92 |
93 | private void ovalSet(float centerX, float centerY, float radius, RectF outOval){
94 | outOval.left = centerX - radius;
95 | outOval.top = centerY - radius;
96 | outOval.right = centerX + radius;
97 | outOval.bottom = centerY + radius;
98 | }
99 |
100 | @Override
101 | protected void onResize(float width, float height){
102 |
103 | recomputeShape();
104 | }
105 |
106 | @Override
107 | public void draw(Canvas canvas, Paint paint){
108 | if(mComputedPath != null) canvas.drawPath(mComputedPath, paint);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/shapes/PolygonShape.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.shapes;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.Path;
6 | import android.graphics.PointF;
7 | import android.graphics.RectF;
8 | import android.graphics.drawable.shapes.Shape;
9 | import android.support.annotation.FloatRange;
10 | import android.support.annotation.IntRange;
11 |
12 | import com.sevenheaven.uilibrary.utils.GeomUtil;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | /**
18 | * Shape for create a regular polygon or star with round corner
19 | *
20 | * Created by 7heaven on 16/4/25.
21 | */
22 | public class PolygonShape extends Shape {
23 |
24 | private int mVertexCount;
25 | private float mCornerRadius;
26 | private boolean mAsStar;
27 |
28 | private List mControlPoints;
29 |
30 | private float mStarRatio = 0.65F;
31 | private static final double startAngle = -(Math.PI / 2);
32 |
33 | private Path starPath;
34 |
35 | public PolygonShape(int vertexCount, float cornerRadius){
36 | this(vertexCount, cornerRadius, false, -1);
37 | }
38 |
39 | public PolygonShape(int vertexCount, float cornerRadius, boolean star){
40 | this(vertexCount, cornerRadius, star, -1);
41 | }
42 |
43 | public PolygonShape(@IntRange(from=0, to=Integer.MAX_VALUE) int vertexCount, @FloatRange(from=0, to=0.5) float cornerRadius, boolean star, float starRatio){
44 | if(cornerRadius > 0.5F || cornerRadius < 0) throw new IllegalArgumentException("corners range is (0 - 0.5)");
45 | this.mVertexCount = vertexCount;
46 | this.mCornerRadius = cornerRadius;
47 | this.mAsStar = star;
48 | this.mStarRatio = starRatio == -1 ? this.mStarRatio : starRatio;
49 |
50 | starPath = new Path();
51 |
52 | mControlPoints = new ArrayList<>();
53 |
54 | }
55 |
56 | public void setVertexCount(int vertexCount){
57 | if(this.mVertexCount != vertexCount){
58 | this.mVertexCount = vertexCount;
59 |
60 | calculateOutline();
61 | }
62 | }
63 |
64 | public void setCornerRadius(@FloatRange(from=0, to=0.5) float cornerRadius){
65 | if(this.mCornerRadius != cornerRadius){
66 | this.mCornerRadius = cornerRadius;
67 |
68 | calculateOutline();
69 | }
70 | }
71 |
72 | public void setAsStar(boolean asStar){
73 | if(this.mAsStar != asStar){
74 | this.mAsStar = asStar;
75 |
76 | calculateOutline();
77 | }
78 | }
79 |
80 | @Override
81 | public void onResize(float width, float height){
82 | calculateOutline();
83 | }
84 |
85 |
86 | @Override
87 | public void draw(Canvas canvas, Paint paint){
88 |
89 | if(canvas != null) canvas.drawPath(starPath, paint);
90 | }
91 |
92 | private void calculateOutline(){
93 | starPath.reset();
94 | mControlPoints.clear();
95 |
96 | RectF bound = new RectF(0, 0, getWidth(), getHeight());
97 | int radius = (int) ((bound.width() > bound.height() ? bound.height() : bound.width()) * 0.5F);
98 | int centerX = (int) (bound.width() * 0.5F);
99 | int centerY = (int) (bound.height() * 0.5F);
100 |
101 | double angle = startAngle;
102 | double angleStep = Math.PI * 2 / mVertexCount;
103 | double halfStep = angleStep * 0.5F;
104 | float[] positions;
105 |
106 | //check if corner radius is zero, to prevent unnecessary calculation
107 | if(mCornerRadius == 0){
108 | positions = GeomUtil.pointOnCircumference(centerX, centerY, angle, radius);
109 | starPath.moveTo(positions[0], positions[1]);
110 | if(mAsStar){
111 | positions = GeomUtil.pointOnCircumference(centerX, centerY, angle + halfStep, radius * mStarRatio);
112 | starPath.lineTo(positions[0], positions[1]);
113 | }
114 | for(int i = 0; i < mVertexCount - 1; i++){
115 | angle += angleStep;
116 | positions = GeomUtil.pointOnCircumference(centerX, centerY, angle, radius);
117 | starPath.lineTo(positions[0], positions[1]);
118 |
119 | if(mAsStar){
120 | positions = GeomUtil.pointOnCircumference(centerX, centerY, angle + halfStep, radius * mStarRatio);
121 | starPath.lineTo(positions[0], positions[1]);
122 | }
123 | }
124 |
125 | starPath.close();
126 | }else{
127 |
128 | /**
129 | *
130 | * /\ \
131 | * \/ <----- ) When drawing a round corner Polygon, each corner contains a curve and a straight line,
132 | * / Curve represent the round corner and connect to the next corner using the straight line,
133 | * the Curve is a Bezier curve, the control point of the bezier curve is the vertex of the
134 | * corner, and the start point and the end point are on the straight lines representing this
135 | * corner
136 | */
137 |
138 | float[] startP = GeomUtil.pointOnCircumference(centerX, centerY, angle - (mAsStar ? halfStep : angleStep), mAsStar ? radius * mStarRatio : radius);
139 | float[] centerP = GeomUtil.pointOnCircumference(centerX, centerY, angle, radius);
140 | float[] endP = GeomUtil.pointOnCircumference(centerX, centerY, angle + (mAsStar ? halfStep : angleStep), mAsStar ? radius * mStarRatio : radius);
141 |
142 | float[] bezierStart = GeomUtil.pointOnLine(centerP[0], centerP[1], startP[0], startP[1], this.mCornerRadius);
143 | float[] bezierEnd = GeomUtil.pointOnLine(centerP[0], centerP[1], endP[0], endP[1], this.mCornerRadius);
144 |
145 | float[] nextStart = GeomUtil.pointOnLine(endP[0], endP[1], centerP[0], centerP[1], this.mCornerRadius);
146 |
147 | starPath.moveTo(bezierStart[0], bezierStart[1]);
148 | starPath.quadTo(centerP[0], centerP[1], bezierEnd[0], bezierEnd[1]);
149 | starPath.lineTo(nextStart[0], nextStart[1]);
150 |
151 | int cpIndex = 0;
152 |
153 | mControlPoints.add(new PointF(bezierStart[0], bezierStart[1]));
154 | mControlPoints.add(new PointF(centerP[0], centerP[1]));
155 | mControlPoints.add(new PointF(bezierEnd[0], bezierEnd[1]));
156 |
157 | if(mAsStar){
158 | cpIndex += 3;
159 | centerP = endP.clone();
160 | endP = GeomUtil.pointOnCircumference(centerX, centerY, angle + angleStep, radius);
161 |
162 | bezierEnd = GeomUtil.pointOnLine(centerP[0], centerP[1], endP[0], endP[1], this.mCornerRadius);
163 |
164 | mControlPoints.add(new PointF(nextStart[0], nextStart[1]));
165 |
166 | nextStart = GeomUtil.pointOnLine(endP[0], endP[1], centerP[0], centerP[1], this.mCornerRadius);
167 |
168 | starPath.quadTo(centerP[0], centerP[1], bezierEnd[0], bezierEnd[1]);
169 | starPath.lineTo(nextStart[0], nextStart[1]);
170 |
171 | mControlPoints.add(new PointF(centerP[0], centerP[1]));
172 | mControlPoints.add(new PointF(bezierEnd[0], bezierEnd[1]));
173 | }
174 |
175 | for(int i = 0; i < mVertexCount - 1; i++){
176 | cpIndex += 3;
177 |
178 | angle += angleStep;
179 | centerP = endP.clone();
180 | endP = GeomUtil.pointOnCircumference(centerX, centerY, angle + (mAsStar ? halfStep : angleStep), mAsStar ? radius * mStarRatio : radius);
181 |
182 | bezierEnd = GeomUtil.pointOnLine(centerP[0], centerP[1], endP[0], endP[1], this.mCornerRadius);
183 |
184 | mControlPoints.add(new PointF(nextStart[0], nextStart[1]));
185 |
186 | nextStart = GeomUtil.pointOnLine(endP[0], endP[1], centerP[0], centerP[1], this.mCornerRadius);
187 |
188 | starPath.quadTo(centerP[0], centerP[1], bezierEnd[0], bezierEnd[1]);
189 | starPath.lineTo(nextStart[0], nextStart[1]);
190 |
191 |
192 | mControlPoints.add(new PointF(centerP[0], centerP[1]));
193 | mControlPoints.add(new PointF(bezierEnd[0], bezierEnd[1]));
194 |
195 | if(mAsStar){
196 | cpIndex += 3;
197 | centerP = endP;
198 | endP = GeomUtil.pointOnCircumference(centerX, centerY, angle + angleStep, radius);
199 |
200 | bezierEnd = GeomUtil.pointOnLine(centerP[0], centerP[1], endP[0], endP[1], this.mCornerRadius);
201 |
202 | mControlPoints.add(new PointF(nextStart[0], nextStart[1]));
203 |
204 | nextStart = GeomUtil.pointOnLine(endP[0], endP[1], centerP[0], centerP[1], this.mCornerRadius);
205 |
206 | starPath.quadTo(centerP[0], centerP[1], bezierEnd[0], bezierEnd[1]);
207 | starPath.lineTo(nextStart[0], nextStart[1]);
208 |
209 | mControlPoints.add(new PointF(centerP[0], centerP[1]));
210 | mControlPoints.add(new PointF(bezierEnd[0], bezierEnd[1]));
211 | }
212 | }
213 |
214 | starPath.close();
215 | }
216 | }
217 |
218 | public PointF[] getControlPoints(){
219 | return mControlPoints.toArray(new PointF[mControlPoints.size()]);
220 | }
221 |
222 | public Path getOutline(){
223 | return starPath;
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/utils/GeomUtil.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.utils;
2 |
3 | import android.graphics.Matrix;
4 | import android.graphics.Rect;
5 |
6 | /**
7 | * Utility for geometry calculation
8 | *
9 | * Created by 7heaven on 16/8/5.
10 | */
11 | public class GeomUtil {
12 |
13 | /**
14 | * Get the position on line(x0,y0-x1,y1) based on ratio
15 | *
16 | * @param x0
17 | * @param y0
18 | * @param x1
19 | * @param y1
20 | * @param ratio
21 | * @return position on line based on ratio float[]{x, y}
22 | */
23 | public static float[] pointOnLine(float x0, float y0, float x1, float y1, float ratio){
24 | float[] result = new float[2];
25 |
26 | pointOnLine(x0, y0, x1, y1, ratio, result);
27 |
28 | return result;
29 | }
30 |
31 | public static void pointOnLine(float x0, float y0, float x1, float y1, float ratio, float[] position){
32 | if(position != null && position.length == 2){
33 | float dx = x1 - x0;
34 | float dy = y1 - y0;
35 |
36 | dx *= ratio;
37 | dy *= ratio;
38 |
39 | position[0] = x0 + dx;
40 | position[1] = y0 + dy;
41 | }
42 | }
43 |
44 | /**
45 | * Calculate point on circumference for circle(centerX, centerY, radius) with angle in radians
46 | *
47 | * @param centerX x of the center of the circle
48 | * @param centerY y of the center of the circle
49 | * @param angle angle in radians
50 | * @param radius radius of the circle
51 | * @return point on circumference float[]{x, y}
52 | */
53 | public static float[] pointOnCircumference(int centerX, int centerY, double angle, double radius){
54 | float[] result = new float[2];
55 |
56 | pointOnCircumference(centerX, centerY, angle, radius, result);
57 |
58 | return result;
59 | }
60 |
61 | public static void pointOnCircumference(int centerX, int centerY, double angle, double radius, float[] outPoint){
62 | if(outPoint != null && outPoint.length == 2){
63 | float x = (float) (radius * Math.cos(angle) + centerX);
64 | float y = (float) (radius * Math.sin(angle) + centerY);
65 |
66 | outPoint[0] = x;
67 | outPoint[1] = y;
68 | }
69 | }
70 |
71 | /**
72 | * Get the transformation matrix from the original rectangle to the target rectangle
73 | *
74 | * @param originalRect
75 | * @param targetRect
76 | * @param outMatrix
77 | */
78 | public static void getTransformationMatrix(Rect originalRect, Rect targetRect, Matrix outMatrix){
79 | if(outMatrix != null){
80 | int transformX = targetRect.left - originalRect.left;
81 | int transformY = targetRect.top - originalRect.top;
82 |
83 | float scaleX = (float) targetRect.width() / (float) originalRect.width();
84 | float scaleY = (float) targetRect.height() / (float) originalRect.height();
85 |
86 | outMatrix.setScale(scaleX, scaleY, originalRect.left, originalRect.top);
87 | outMatrix.postTranslate(transformX, transformY);
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/utils/PathMeasurement.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.utils;
2 |
3 | import android.graphics.Path;
4 | import android.graphics.PathMeasure;
5 | import android.support.annotation.FloatRange;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | *
12 | * Convenient way for Path calculation when Path contain multiple contours
13 | *
14 | * Created by 7heaven on 16/8/8.
15 | */
16 | public class PathMeasurement {
17 |
18 | /**
19 | * Main path for measurement
20 | */
21 | private Path mPath;
22 |
23 | /**
24 | * Sub paths and stored parameters for calculation
25 | */
26 | private Path[] mSubPaths;
27 | private Path[] mSubPathsOutputStore;
28 | private Path[] mSubPathsOutput;
29 | private float[] mSubPathLengths;
30 | private float[] mSubPathPercentageRanges;
31 |
32 | /**
33 | * Total length of the main path
34 | */
35 | private float mTotalLength;
36 |
37 | /**
38 | * PathMeasure instance for all calculations
39 | */
40 | private PathMeasure mPathMeasure;
41 |
42 | public PathMeasurement(Path path){
43 | mPath = path;
44 |
45 | mPathMeasure = new PathMeasure();
46 |
47 | initPathCalculation();
48 | }
49 |
50 | public void setPath(Path path){
51 | mPath = path;
52 |
53 | initPathCalculation();
54 | }
55 |
56 | private void initPathCalculation(){
57 | if(mPath != null){
58 | //ensure PathMeasure position at the first contour
59 | mPathMeasure.setPath(mPath, false);
60 | mTotalLength = 0;
61 |
62 | List subPathLengths = new ArrayList<>();
63 | List subPaths = new ArrayList<>();
64 |
65 | do{
66 | Path path = new Path();
67 | float sublength = mPathMeasure.getLength();
68 | mTotalLength += sublength;
69 | mPathMeasure.getSegment(0, sublength, path, true);
70 | subPathLengths.add(sublength);
71 | subPaths.add(path);
72 | }while(mPathMeasure.nextContour());
73 |
74 | final int totalContour = subPathLengths.size();
75 |
76 | mSubPathsOutputStore = new Path[totalContour];
77 | mSubPaths = new Path[totalContour];
78 | mSubPathLengths = new float[totalContour];
79 | mSubPathsOutput = new Path[totalContour];
80 | mSubPathPercentageRanges = new float[totalContour * 3];
81 |
82 | float percentageStep = 0;
83 | for(int i = 0; i < totalContour; i++){
84 | mSubPaths[i] = subPaths.get(i);
85 | mSubPathsOutputStore[i] = new Path();
86 | mSubPathLengths[i] = subPathLengths.get(i);
87 |
88 | float singlePercentage = mSubPathLengths[i] / mTotalLength;
89 |
90 | //sub path start position
91 | mSubPathPercentageRanges[i * 3] = percentageStep;
92 | //sub path end position
93 | mSubPathPercentageRanges[i * 3 + 1] = percentageStep + singlePercentage;
94 | mSubPathPercentageRanges[i * 3 + 2] = mTotalLength / mSubPathLengths[i];
95 |
96 | percentageStep += singlePercentage;
97 | }
98 |
99 |
100 | mPathMeasure.setPath(mPath, false);
101 | }
102 | }
103 |
104 | /**
105 | * Get all the sub paths of the main Path
106 | * @return
107 | */
108 | public Path[] getAllSubPaths(){
109 | return mSubPaths;
110 | }
111 |
112 | /**
113 | * Get position and tangent values based on percentage mapping to the entire Path
114 | * @param percentage
115 | * @param pos
116 | * @param tan
117 | * @return
118 | */
119 | public float getPosTan(@FloatRange(from=0, to=1.0) float percentage, float[] pos, float[] tan){
120 | return getPosTan(percentage, pos, tan, false, 0);
121 | }
122 |
123 | /**
124 | * Get position and tangent values based on percentage, this is useful when Path contains more than one contour
125 | * @param percentage
126 | * @param pos
127 | * @param tan
128 | * @param async indicate whether percentage should apply to the entire Path length or apply to each sub path
129 | * @param contourNum this parameter will work only when asyn == true, indicate which contour should be the target to return position and tangent
130 | * @return
131 | */
132 | public float getPosTan(@FloatRange(from=0, to=1.0) float percentage, float[] pos, float[] tan, final boolean async, int contourNum){
133 | float distance = percentage * mTotalLength;
134 |
135 | if(pos != null && tan != null && pos.length == 2 && tan.length == 2){
136 | mPathMeasure.setPath(mPath, false);
137 |
138 | int i = 0;
139 | do{
140 | if(async){
141 | if(i == contourNum){
142 | mPathMeasure.getPosTan(mSubPathLengths[i] * percentage, pos, tan);
143 | break;
144 | }
145 | }else{
146 | float subPathStart = mSubPathPercentageRanges[i * 3];
147 | float subPathEnd = mSubPathPercentageRanges[i * 3 + 1];
148 | float multiples = mSubPathPercentageRanges[i * 3 + 2];
149 |
150 | if(subPathStart < percentage && subPathEnd >= percentage){
151 | float subDistance = subPathEnd >= percentage ? (percentage - subPathStart) * multiples * mSubPathLengths[i] : mSubPathLengths[i];
152 |
153 | mPathMeasure.getPosTan(subDistance, pos, tan);
154 | }else if(subPathStart >= percentage){
155 | break;
156 | }
157 | }
158 |
159 | i++;
160 | }while(i < mSubPathsOutputStore.length && mPathMeasure.nextContour());
161 |
162 | mPathMeasure.setPath(mPath, false);
163 | }
164 |
165 | return distance;
166 | }
167 |
168 | /**
169 | * Return sub paths based on percentage, notice that the return array with always be same length which is the sub-path count of the main path,
170 | * so for those sub-paths not covered by percentage will return null;
171 | * @param percentage
172 | * @param async indicate whether percentage should apply to the entire Path length or apply to each sub path
173 | * @return
174 | */
175 | public Path[] updatePhare(@FloatRange(from=0, to=1.0) float percentage, final boolean async){
176 | if(mSubPathsOutputStore != null){
177 |
178 | mPathMeasure.setPath(mPath, false);
179 |
180 | int i = 0;
181 | do{
182 | mSubPathsOutputStore[i].reset();
183 |
184 | if(async){
185 | boolean success = mPathMeasure.getSegment(0, mSubPathLengths[i] * percentage, mSubPathsOutputStore[i], true);
186 | if(success){
187 | // On KITKAT and earlier releases, the resulting path may not display on a hardware-accelerated Canvas. A simple workaround is to add a single operation to this path
188 | // @see https://developer.android.com/reference/android/graphics/PathMeasure.html#getSegment(float, float, android.graphics.Path, boolean)
189 | mSubPathsOutputStore[i].rLineTo(0, 0);
190 | mSubPathsOutput[i] = mSubPathsOutputStore[i];
191 | }else{
192 | mSubPathsOutput[i] = null;
193 | }
194 | }else{
195 | float subPathStart = mSubPathPercentageRanges[i * 3];
196 | float subPathEnd = mSubPathPercentageRanges[i * 3 + 1];
197 | float multiples = mSubPathPercentageRanges[i * 3 + 2];
198 |
199 | if(subPathStart >= percentage){
200 | mSubPathsOutput[i] = null;
201 | }else{
202 |
203 | float endD = subPathEnd >= percentage ? (percentage - subPathStart) * multiples * mSubPathLengths[i] : mSubPathLengths[i];
204 |
205 | boolean success = mPathMeasure.getSegment(0, endD, mSubPathsOutputStore[i], true);
206 | if(success){
207 | // On KITKAT and earlier releases, the resulting path may not display on a hardware-accelerated Canvas. A simple workaround is to add a single operation to this path
208 | // @see https://developer.android.com/reference/android/graphics/PathMeasure.html#getSegment(float, float, android.graphics.Path, boolean)
209 | mSubPathsOutputStore[i].rLineTo(0, 0);
210 | mSubPathsOutput[i] = mSubPathsOutputStore[i];
211 | }else{
212 | mSubPathsOutput[i] = null;
213 | }
214 | }
215 | }
216 |
217 | i++;
218 |
219 | }while(i < mSubPathsOutputStore.length && mPathMeasure.nextContour());
220 |
221 | mPathMeasure.setPath(mPath, false);
222 |
223 | return mSubPathsOutput;
224 | }
225 |
226 | return null;
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/views/AnimatedImageView.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.views;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.graphics.Matrix;
7 | import android.graphics.drawable.Drawable;
8 | import android.os.Build;
9 | import android.util.AttributeSet;
10 | import android.view.animation.DecelerateInterpolator;
11 | import android.widget.ImageView;
12 |
13 | /**
14 | * A ImageView that animated scaleType change
15 | *
16 | * Created by 7heaven on 16/7/30.
17 | */
18 | public class AnimatedImageView extends ImageView {
19 |
20 | private Matrix mProgressiveMatrix;
21 | private float[] mInitialMatrixValues;
22 | private float[] mProgressingMatrixValues;
23 | private float[] mTargetMatrixValues;
24 |
25 | private ValueAnimator mAnimator;
26 |
27 | private boolean mAnimationMark = false;
28 | private boolean mIgnoreMatrixAnimation = true;
29 |
30 | public AnimatedImageView(Context context){
31 | this(context, null);
32 | }
33 |
34 | public AnimatedImageView(Context context, AttributeSet attrs){
35 | this(context, attrs, 0);
36 | }
37 |
38 | public AnimatedImageView(Context context, AttributeSet attrs, int defStyle){
39 | super(context, attrs, defStyle);
40 |
41 | Matrix targetMatrix = getImageMatrix();
42 | mProgressiveMatrix = new Matrix(targetMatrix);
43 | mInitialMatrixValues = new float[9];
44 | mProgressingMatrixValues = new float[9];
45 | mTargetMatrixValues = new float[9];
46 |
47 | mAnimator = ValueAnimator.ofFloat(0, 1);
48 | mAnimator.setDuration(500);
49 | mAnimator.setInterpolator(new DecelerateInterpolator());
50 | mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
51 | @Override
52 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
53 | float value = (float) valueAnimator.getAnimatedValue();
54 | updateProgressiveMatrix(value);
55 | }
56 | });
57 | }
58 |
59 | @Override
60 | public void setScaleType(ScaleType scaleType){
61 | super.setScaleType(scaleType);
62 |
63 | mIgnoreMatrixAnimation = true;
64 | }
65 |
66 | /**
67 | * setScaleType with animated transformation
68 | * @param scaleType
69 | */
70 | public void setScaleTypeAnimated(ScaleType scaleType){
71 | super.setScaleType(scaleType);
72 |
73 | mIgnoreMatrixAnimation = false;
74 | }
75 |
76 | @Override
77 | protected void onLayout(boolean changed, int left, int top, int right, int bottom){
78 | Matrix targetMatrix = getImageMatrix();
79 |
80 | if(targetMatrix != null){
81 | if(!targetMatrix.equals(mProgressiveMatrix)){
82 | if(mIgnoreMatrixAnimation){
83 | mProgressiveMatrix = new Matrix(targetMatrix);
84 | }else{
85 | mAnimationMark = true;
86 | }
87 | }
88 | }
89 | }
90 |
91 | private void startProgressiveMatrixTransform(){
92 | if(mAnimator.isRunning()) mAnimator.end();
93 |
94 | Matrix targetMatrix = getImageMatrix();
95 | if(targetMatrix != null){
96 | targetMatrix.getValues(mTargetMatrixValues);
97 | }
98 |
99 | if(mProgressiveMatrix != null){
100 | mProgressiveMatrix.getValues(mInitialMatrixValues);
101 | }
102 |
103 | for(int i = 0; i < 9; i++){
104 | mProgressingMatrixValues[i] = 0;
105 | }
106 |
107 | mAnimator.start();
108 | }
109 |
110 | private void updateProgressiveMatrix(float progress){
111 | for(int i = 0; i < 9; i++){
112 | float dValue = mTargetMatrixValues[i] - mInitialMatrixValues[i];
113 | mProgressingMatrixValues[i] = dValue * progress + mInitialMatrixValues[i];
114 | }
115 |
116 | mProgressiveMatrix.setValues(mProgressingMatrixValues);
117 |
118 | invalidate();
119 | }
120 |
121 | @Override
122 | public void onDraw(Canvas canvas){
123 | if(mAnimationMark){
124 | startProgressiveMatrixTransform();
125 | mAnimationMark = false;
126 | }
127 |
128 | Drawable drawable = getDrawable();
129 | if(drawable != null){
130 | int saveCount = canvas.getSaveCount();
131 | canvas.save();
132 | boolean cropToPadding = Build.VERSION.SDK_INT > 16 && getCropToPadding();
133 | if(cropToPadding){
134 | final int scrollX = getScrollX();
135 | final int scrollY = getScrollY();
136 |
137 | canvas.clipRect(scrollX + getPaddingLeft(), scrollY + getPaddingTop(),
138 | scrollX + getRight() - getLeft() - getPaddingRight(),
139 | scrollY + getBottom() - getTop() - getPaddingBottom());
140 | }
141 |
142 | canvas.translate(getPaddingLeft(), getPaddingTop());
143 |
144 | if(mProgressiveMatrix != null){
145 | canvas.concat(mProgressiveMatrix);
146 | }
147 |
148 | drawable.draw(canvas);
149 | canvas.restoreToCount(saveCount);
150 | }
151 | }
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/views/FitWidthImageView.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.views;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Matrix;
6 | import android.graphics.drawable.Drawable;
7 | import android.util.AttributeSet;
8 | import android.widget.ImageView;
9 |
10 | /**
11 | * TODO reconstrution
12 | * Created by 7heaven on 16/8/9.
13 | */
14 | public class FitWidthImageView extends ImageView {
15 |
16 | private int width;
17 | private int height;
18 |
19 | private int intrinsicWidth;
20 | private int intrinsicHeight;
21 |
22 | private float scaleRate;
23 |
24 | public FitWidthImageView(Context context) {
25 | this(context, null);
26 | }
27 |
28 | public FitWidthImageView(Context context, AttributeSet attrs) {
29 | this(context, attrs, 0);
30 | }
31 |
32 | public FitWidthImageView(Context context, AttributeSet attrs, int defStyle) {
33 | super(context, attrs, defStyle);
34 | setScaleType(ScaleType.MATRIX);
35 | }
36 |
37 | @Override
38 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
39 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
40 |
41 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
42 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
43 |
44 |
45 | width = getMeasuredWidth();
46 |
47 | if(heightMode == MeasureSpec.EXACTLY){
48 | height = heightSize;
49 | }else{
50 | height = heightSize - getPaddingTop() - getPaddingBottom();
51 | }
52 |
53 | float rate = (float) width / (float) intrinsicWidth;
54 | height = (int) ((float) intrinsicHeight * rate);
55 | setMeasuredDimension(width, height);
56 |
57 | scaleToFit(false);
58 | }
59 |
60 |
61 |
62 | @Override
63 | protected void onSizeChanged(int w, int h, int oldw, int oldh){
64 | super.onSizeChanged(w, h, oldw, oldh);
65 |
66 | width = w;
67 | height = h;
68 | }
69 |
70 | @Override
71 | public void setImageDrawable(Drawable drawable) {
72 | free();
73 | if (drawable != null) {
74 | intrinsicWidth = drawable.getIntrinsicWidth();
75 | intrinsicHeight = drawable.getIntrinsicHeight();
76 | super.setImageDrawable(drawable);
77 | scaleToFit();
78 | } else {
79 | super.setImageDrawable(null);
80 | }
81 | }
82 |
83 | @Override
84 | public void setImageResource(int resourceId) {
85 | free();
86 | super.setImageResource(resourceId);
87 |
88 | Drawable d = this.getDrawable();
89 |
90 | if (d != null) {
91 | intrinsicWidth = d.getIntrinsicWidth();
92 | intrinsicHeight = d.getIntrinsicHeight();
93 | scaleToFit();
94 | }
95 | }
96 |
97 | @Override
98 | public void setImageBitmap(Bitmap bitmap) {
99 | free();
100 | if (bitmap != null) {
101 | intrinsicWidth = bitmap.getWidth();
102 | intrinsicHeight = bitmap.getHeight();
103 | super.setImageBitmap(bitmap);
104 | scaleToFit();
105 | } else {
106 | super.setImageBitmap(null);
107 | }
108 | }
109 |
110 | public float getScaleRate() {
111 | return scaleRate;
112 | }
113 |
114 | public int getIntrinsicWidth() {
115 | return intrinsicWidth;
116 | }
117 |
118 | public int getIntrinsicHeight() {
119 | return intrinsicHeight;
120 | }
121 |
122 | @Override
123 | public void onDetachedFromWindow() {
124 | super.onDetachedFromWindow();
125 | free();
126 | }
127 |
128 | private void free() {
129 | if (getDrawingCache() != null) {
130 | Bitmap b = getDrawingCache();
131 | setImageBitmap(null);
132 | b.recycle();
133 | b = null;
134 | }
135 |
136 | }
137 |
138 | private void scaleToFit() {
139 | scaleToFit(true);
140 | }
141 |
142 | private void scaleToFit(boolean forceLayout){
143 | Matrix matrix = new Matrix();
144 | float rate = (float) width / (float) intrinsicWidth;
145 | scaleRate = rate;
146 | matrix.postScale(rate, rate);
147 | setImageMatrix(matrix);
148 | if(forceLayout) requestLayout();
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/views/GroupedAvatarImageView.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.views;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.drawable.Drawable;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.widget.ImageView;
9 |
10 | import com.sevenheaven.uilibrary.drawables.GroupedAvatarDrawable;
11 |
12 | import java.security.acl.Group;
13 |
14 | /**
15 | * Created by 7heaven on 16/8/13.
16 | */
17 | public class GroupedAvatarImageView extends ImageView {
18 |
19 | private GroupedAvatarDrawable mDrawable;
20 |
21 | public GroupedAvatarImageView(Context context){
22 | this(context, null);
23 | }
24 |
25 | public GroupedAvatarImageView(Context context, AttributeSet attrs){
26 | this(context, attrs, 0);
27 | }
28 |
29 | public GroupedAvatarImageView(Context context, AttributeSet attrs, int defStyle){
30 | super(context, attrs, defStyle);
31 | }
32 |
33 | public void setAvatars(Bitmap... bitmaps){
34 |
35 | }
36 |
37 | @Override
38 | public void setImageDrawable(Drawable drawable){
39 | if(drawable instanceof GroupedAvatarDrawable){
40 | super.setImageDrawable(drawable);
41 | mDrawable = (GroupedAvatarDrawable) drawable;
42 | }
43 | }
44 |
45 | @Override
46 | public void setImageBitmap(Bitmap bitmap){
47 | super.setImageBitmap(bitmap);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/uilibrary/src/main/java/com/sevenheaven/uilibrary/views/PageIndicator.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary.views;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.util.AttributeSet;
8 | import android.util.TypedValue;
9 | import android.view.View;
10 |
11 | import com.sevenheaven.uilibrary.R;
12 |
13 | /**
14 | * PageIndicator that can easily customize contents, with many pre-defined style such as circle, square, alphabet, etc.
15 | * Created by 7heaven on 16/5/25.
16 | */
17 | public class PageIndicator extends View {
18 |
19 | public enum BlockType{
20 |
21 | SQUARE(0),
22 | CIRCLE(1),
23 | VERTICAL_LINE(2),
24 | HORIZONTAL_LINE(3),
25 | ALPHABET(4),
26 | NUMMERIC(5),
27 | CUSTOM_CHAR(6);
28 |
29 | private int mValue;
30 |
31 | BlockType(int value){
32 | mValue = value;
33 | }
34 | }
35 |
36 | private BlockType mCurrentBlockType;
37 | private int mBlockGap;
38 | private int mBlockSize;
39 | private int mBlockColor;
40 | private int mHighlightColor;
41 |
42 | private int mHalfBlockSize;
43 |
44 | private int mPageCount;
45 | private int mTotalContentWidth;
46 |
47 | /**
48 | * For recording content coordinate after measurement
49 | */
50 | private int mContentX;
51 | private int mContentY;
52 |
53 | /**
54 | * Canvas's drawText do not accept single char,
55 | * so we define a char array here for char content drawing
56 | */
57 | private char[] mAlphabetStore = new char[1];
58 | private char[] mCustomChar;
59 |
60 | private int mCurrentSelection = 0;
61 |
62 | private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
63 | /**
64 | * FontMetric for positioning Text when drawText method is call
65 | */
66 | private Paint.FontMetrics mAlphabetFM = new Paint.FontMetrics();
67 |
68 | public PageIndicator(Context context){
69 | this(context, null);
70 | }
71 |
72 | public PageIndicator(Context context, AttributeSet attrs){
73 | this(context, attrs, 0);
74 | }
75 |
76 | public PageIndicator(Context context, AttributeSet attrs, int defStyle){
77 | super(context, attrs, defStyle);
78 |
79 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PageIndicator);
80 | setBlockType(BlockType.values()[ta.getInt(R.styleable.PageIndicator_blockType, BlockType.CIRCLE.mValue)]);
81 | setBlockSize(ta.getDimensionPixelSize(R.styleable.PageIndicator_blockSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics())));
82 | setBlockGap(ta.getDimensionPixelSize(R.styleable.PageIndicator_gap, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, context.getResources().getDisplayMetrics())));
83 | setBlockColor(ta.getColor(R.styleable.PageIndicator_blockColor, 0xFFF5F5F5));
84 | setHighlightColor(ta.getColor(R.styleable.PageIndicator_highlightColor, 0xFFFFFFFF));
85 | setTotalPageCount(ta.getInt(R.styleable.PageIndicator_totalCount, 3));
86 |
87 | String customChars = ta.getString(R.styleable.PageIndicator_customChar);
88 | if(customChars != null) setCustomChar(customChars.toCharArray());
89 |
90 | ta.recycle();
91 |
92 | }
93 |
94 | public void setCustomChar(char... customChar){
95 | mCustomChar = customChar;
96 | if(mCurrentBlockType == BlockType.CUSTOM_CHAR){
97 | invalidate();
98 | }
99 | }
100 |
101 | public void setTotalPageCount(int pageCount){
102 | if(mPageCount != pageCount){
103 | mPageCount = pageCount;
104 |
105 | requestLayout();
106 | }
107 | }
108 |
109 | public void setCurrentSelection(int current){
110 | if(current != mCurrentSelection){
111 | mCurrentSelection = current;
112 | invalidate();
113 | }
114 | }
115 |
116 | public void setBlockType(BlockType type){
117 | mCurrentBlockType = type;
118 | }
119 |
120 | public void setBlockSize(int size){
121 | if(mBlockSize != size){
122 | mBlockSize = size;
123 |
124 | mHalfBlockSize = size / 2;
125 |
126 | //for convenient when BlockType being set to ALPHABET
127 | mPaint.setTextSize(size);
128 | mPaint.getFontMetrics(mAlphabetFM);
129 |
130 | requestLayout();
131 | }
132 | }
133 |
134 | public void setBlockGap(int gap){
135 | if(mBlockGap != gap){
136 | mBlockGap = gap;
137 |
138 | requestLayout();
139 | }
140 | }
141 |
142 | public void setBlockColor(int blockColor){
143 | if(mBlockColor != blockColor){
144 | mBlockColor = blockColor;
145 |
146 | invalidate();
147 | }
148 | }
149 |
150 | public void setHighlightColor(int highlightColor){
151 | if(mHighlightColor != highlightColor){
152 | mHighlightColor = highlightColor;
153 |
154 | invalidate();
155 | }
156 | }
157 |
158 | @Override
159 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
160 | if(mPageCount > 0){
161 | /**
162 | * calculate minimum content width based on block size and block gap
163 | */
164 | mTotalContentWidth = mPageCount * mBlockSize + (mPageCount - 1) * mBlockGap;
165 |
166 | final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
167 | final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
168 | final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
169 | final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
170 |
171 | int width;
172 | int height;
173 |
174 | if(widthMode == MeasureSpec.EXACTLY){
175 | width = widthSize;
176 | }else{
177 | width = mTotalContentWidth;
178 | }
179 |
180 | if(heightMode == MeasureSpec.EXACTLY){
181 | height = heightSize;
182 | }else{
183 | height = mBlockSize;
184 | }
185 |
186 | setMeasuredDimension(width, height);
187 | }else{
188 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
189 | }
190 | }
191 |
192 | @Override
193 | protected void onSizeChanged(int w, int h, int oldw, int oldh){
194 | int cX = w / 2;
195 | int cY = h / 2;
196 |
197 | mContentX = cX - mTotalContentWidth / 2;
198 | mContentY = cY - mHalfBlockSize;
199 | }
200 |
201 | @Override
202 | public void onDraw(Canvas canvas){
203 | int stepX = mContentX;
204 | for(int i = 0; i < mPageCount; i++){
205 | if(i == mCurrentSelection){
206 | mPaint.setColor(mHighlightColor);
207 | }else{
208 | mPaint.setColor(mBlockColor);
209 | }
210 |
211 | drawContent(canvas, stepX, mContentY, i);
212 | stepX += mBlockSize + mBlockGap;
213 | }
214 | }
215 |
216 | private void drawContent(Canvas canvas, int x, int y, int index){
217 | switch(mCurrentBlockType){
218 | case SQUARE:
219 | canvas.drawRect(x, y, x + mBlockSize, y + mBlockSize, mPaint);
220 | break;
221 | case CIRCLE:
222 | canvas.drawCircle(x + mHalfBlockSize, y + mHalfBlockSize, mHalfBlockSize, mPaint);
223 | break;
224 | case VERTICAL_LINE:
225 | canvas.drawLine(x + mHalfBlockSize, y, x + mHalfBlockSize, y + mBlockSize, mPaint);
226 | break;
227 | case HORIZONTAL_LINE:
228 | canvas.drawLine(x, y + mHalfBlockSize, x + mBlockSize, y + mHalfBlockSize, mPaint);
229 | break;
230 | case ALPHABET:
231 | mAlphabetStore[0] = (char) ((index % 26) + 'A');
232 | canvas.drawText(mAlphabetStore, 0, 1, x, y + mBlockSize - mAlphabetFM.descent, mPaint);
233 | break;
234 | case NUMMERIC:
235 | mAlphabetStore[0] = (char) ((index % 9) + '1');
236 | canvas.drawText(mAlphabetStore, 0, 1, x, y + mBlockSize - mAlphabetFM.descent, mPaint);
237 | break;
238 | case CUSTOM_CHAR:
239 | if(mCustomChar != null){
240 | mAlphabetStore[0] = mCustomChar[(index % mCustomChar.length)];
241 | canvas.drawText(mAlphabetStore, 0, 1, x, y + mBlockSize - mAlphabetFM.descent, mPaint);
242 | }
243 | break;
244 |
245 | }
246 | }
247 | }
248 |
249 |
--------------------------------------------------------------------------------
/uilibrary/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/uilibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | uilibrary
3 |
4 |
--------------------------------------------------------------------------------
/uilibrary/src/test/java/com/sevenheaven/uilibrary/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.sevenheaven.uilibrary;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------