├── base
├── consumer-rules.pro
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── ic_back.png
│ │ │ ├── ic_more.png
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap
│ │ │ │ ├── ic_back.png
│ │ │ │ └── ic_base_more.png
│ │ │ └── layout
│ │ │ │ ├── activity_base_tabbar.xml
│ │ │ │ └── activity_base_titlebar.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── baymax
│ │ │ └── base
│ │ │ ├── activity
│ │ │ ├── BaseActivity.java
│ │ │ ├── BaseTabBarActivity.java
│ │ │ └── BaseTitleBarActivity.java
│ │ │ └── fragment
│ │ │ ├── BaseFragment.java
│ │ │ └── LazyLoadFragment.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── baymax
│ │ │ └── base
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── baymax
│ │ └── base
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── item_menu_selected.xml
│ │ │ │ ├── fragment_layout_round.xml
│ │ │ │ ├── fragment_layout_menu.xml
│ │ │ │ ├── fragment_layout_expand.xml
│ │ │ │ ├── fragment_layout_ratitngstar.xml
│ │ │ │ └── fragment_layout_arrow.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── baymax
│ │ │ │ └── widgetlibrary
│ │ │ │ ├── fragment
│ │ │ │ ├── MenuLayoutFragment.java
│ │ │ │ ├── ArrowLayoutFragment.java
│ │ │ │ ├── RoundLayoutFragment.java
│ │ │ │ ├── RatingStarViewFragment.java
│ │ │ │ └── ExpandLayoutFragment.java
│ │ │ │ └── activity
│ │ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── baymax
│ │ │ └── widgetlibrary
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── baymax
│ │ └── widgetlibrary
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── widget
├── .gitignore
├── consumer-rules.pro
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ └── attrs.xml
│ │ │ ├── mipmap
│ │ │ │ └── ic_more.png
│ │ │ ├── drawable
│ │ │ │ ├── splitter_less.png
│ │ │ │ └── splitter_more.png
│ │ │ └── layout
│ │ │ │ ├── layout_expand.xml
│ │ │ │ └── layout_menu_item.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── baymax
│ │ │ └── widget
│ │ │ ├── ratingstar
│ │ │ ├── VertexF.java
│ │ │ ├── StarModel.java
│ │ │ └── RatingStarView.java
│ │ │ ├── RoundButton.java
│ │ │ ├── ArrowRelativeLayout.java
│ │ │ ├── MenuLayout.java
│ │ │ └── ExpandLayout.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── baymax
│ │ │ └── widget
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── baymax
│ │ └── widget
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── vcs.xml
├── misc.xml
├── runConfigurations.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── codeStyles
│ └── Project.xml
└── dbnavigator.xml
├── .gitignore
├── gradle.properties
├── gradlew.bat
└── gradlew
/base/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/base/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/widget/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/widget/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':widget', ':base'
2 | rootProject.name='WidgetLibrary'
3 |
--------------------------------------------------------------------------------
/base/src/main/res/ic_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/base/src/main/res/ic_back.png
--------------------------------------------------------------------------------
/base/src/main/res/ic_more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/base/src/main/res/ic_more.png
--------------------------------------------------------------------------------
/base/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | base
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WidgetLibrary
3 |
4 |
--------------------------------------------------------------------------------
/widget/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | widget
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/base/src/main/res/mipmap/ic_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/base/src/main/res/mipmap/ic_back.png
--------------------------------------------------------------------------------
/widget/src/main/res/mipmap/ic_more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/widget/src/main/res/mipmap/ic_more.png
--------------------------------------------------------------------------------
/base/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/base/src/main/res/mipmap/ic_base_more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/base/src/main/res/mipmap/ic_base_more.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/widget/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/splitter_less.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/widget/src/main/res/drawable/splitter_less.png
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/splitter_more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/widget/src/main/res/drawable/splitter_more.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oukanggui/WidgetLibrary/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/base/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Sep 26 09:51:00 CST 2019
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-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/base/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/baymax/widget/ratingstar/VertexF.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widget.ratingstar;
2 |
3 | /**
4 | * Created by hxw on 2017-04-23.
5 | * 拐点(顶点)坐标表示实体类
6 | */
7 | public class VertexF {
8 | public VertexF() {
9 | }
10 |
11 | public VertexF(float x, float y) {
12 | this.x = x;
13 | this.y = y;
14 | }
15 |
16 | public float x;
17 | public float y;
18 |
19 | public VertexF next;
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/base/src/test/java/com/baymax/base/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.baymax.base;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/widget/src/test/java/com/baymax/widget/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widget;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/baymax/widgetlibrary/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widgetlibrary;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/baymax/widgetlibrary/fragment/MenuLayoutFragment.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widgetlibrary.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 |
6 | import com.baymax.base.fragment.BaseFragment;
7 | import com.baymax.widgetlibrary.R;
8 |
9 | /**
10 | * @author Baymax
11 | * @date 2019-11-17
12 | * 描述:菜单布局演示Fragment
13 | */
14 | public class MenuLayoutFragment extends BaseFragment {
15 | @Override
16 | public int getLayoutResId() {
17 | return R.layout.fragment_layout_menu;
18 | }
19 |
20 | @Override
21 | public void loadData() {
22 |
23 | }
24 |
25 | @Override
26 | public void initView(View view, Bundle savedInstanceState) {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/baymax/widgetlibrary/fragment/ArrowLayoutFragment.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widgetlibrary.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 |
6 | import com.baymax.base.fragment.BaseFragment;
7 | import com.baymax.widgetlibrary.R;
8 |
9 | /**
10 | * @author Baymax
11 | * @date 2019-11-17
12 | * 描述:三角箭头布局演示Fragment
13 | */
14 | public class ArrowLayoutFragment extends BaseFragment {
15 | @Override
16 | public int getLayoutResId() {
17 | return R.layout.fragment_layout_arrow;
18 | }
19 |
20 | @Override
21 | public void loadData() {
22 |
23 | }
24 |
25 | @Override
26 | public void initView(View view, Bundle savedInstanceState) {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/baymax/widgetlibrary/fragment/RoundLayoutFragment.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widgetlibrary.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 |
6 | import com.baymax.base.fragment.BaseFragment;
7 | import com.baymax.widgetlibrary.R;
8 |
9 | /**
10 | * @author Baymax
11 | * @date 2019-11-17
12 | * 描述:圆角布局演示Fragment
13 | */
14 | public class RoundLayoutFragment extends BaseFragment {
15 | @Override
16 | public int getLayoutResId() {
17 | return R.layout.fragment_layout_round;
18 | }
19 |
20 | @Override
21 | public void loadData() {
22 |
23 | }
24 |
25 | @Override
26 | public void initView(View view, Bundle savedInstanceState) {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/baymax/widgetlibrary/fragment/RatingStarViewFragment.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widgetlibrary.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 |
6 | import com.baymax.base.fragment.BaseFragment;
7 | import com.baymax.widgetlibrary.R;
8 |
9 | /**
10 | * @author oukanggui
11 | * @date 2019/11/18
12 | * 描述:星星评分演示Fragment
13 | */
14 | public class RatingStarViewFragment extends BaseFragment {
15 | @Override
16 | public int getLayoutResId() {
17 | return R.layout.fragment_layout_ratitngstar;
18 | }
19 |
20 | @Override
21 | public void loadData() {
22 |
23 | }
24 |
25 | @Override
26 | public void initView(View view, Bundle savedInstanceState) {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_menu_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
16 |
21 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/base/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/widget/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/base/src/androidTest/java/com/baymax/base/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.baymax.base;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("com.demo.base.test", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/widget/src/androidTest/java/com/baymax/widget/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widget;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("com.baymax.widget.test", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/baymax/widgetlibrary/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widgetlibrary;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("com.baymax.widgetlibrary", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/widget/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 29
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 29
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles 'consumer-rules.pro'
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 |
29 | implementation 'androidx.appcompat:appcompat:1.0.2'
30 | testImplementation 'junit:junit:4.12'
31 | androidTestImplementation 'androidx.test:runner:1.1.1'
32 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
33 | }
34 |
--------------------------------------------------------------------------------
/base/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 29
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 29
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles 'consumer-rules.pro'
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 |
29 | implementation 'androidx.appcompat:appcompat:1.0.2'
30 | implementation "com.google.android.material:material:1.0.0"
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'androidx.test:runner:1.1.1'
33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
34 | }
35 |
--------------------------------------------------------------------------------
/base/src/main/res/layout/activity_base_tabbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
18 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | defaultConfig {
6 | applicationId "com.baymax.widgetlibrary"
7 | minSdkVersion 15
8 | targetSdkVersion 29
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'androidx.appcompat:appcompat:1.0.2'
24 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'androidx.test:runner:1.1.1'
27 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
28 | implementation project(path: ':widget')
29 | implementation project(path: ':base')
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/base/src/main/java/com/baymax/base/activity/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.baymax.base.activity;
2 |
3 | import android.os.Bundle;
4 |
5 | import androidx.annotation.Nullable;
6 | import androidx.appcompat.app.AppCompatActivity;
7 |
8 | /**
9 | * @author oukanggui
10 | * @date 2018/11/11
11 | * 描述 BaseActivity基类,主要功能如下:
12 | * 1、支持显示以及设置加载中、数据为空、加载出错重试等布局
13 | * 2、View以及数据初始化方法回调:
14 | * @see #initView(Bundle)
15 | * @see #initData()
16 | */
17 |
18 | public abstract class BaseActivity extends AppCompatActivity {
19 |
20 | @Override
21 | protected void onCreate(@Nullable Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | setContentView(getLayoutResId());
24 | initView(savedInstanceState);
25 | initData();
26 | }
27 |
28 | @Override
29 | protected void onDestroy() {
30 | super.onDestroy();
31 | }
32 |
33 | /**
34 | * 子Activity重写该方法初始化布局View
35 | */
36 | protected abstract void initView(Bundle savedInstanceState);
37 |
38 | /**
39 | * 子Activity重写该方法初始化数据
40 | */
41 | protected abstract void initData();
42 |
43 | /**
44 | * 子Activity重写该方法提供布局
45 | */
46 | protected abstract int getLayoutResId();
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.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 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_layout_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
22 |
23 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_layout_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 |
20 |
28 |
36 |
37 |
--------------------------------------------------------------------------------
/base/src/main/res/layout/activity_base_titlebar.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
21 |
22 |
29 |
30 |
38 |
39 |
40 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/widget/src/main/res/layout/layout_expand.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
15 |
16 |
23 |
24 |
32 |
33 |
41 |
42 |
43 |
44 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/base/src/main/java/com/baymax/base/fragment/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package com.baymax.base.fragment;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.os.Bundle;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | import androidx.annotation.Nullable;
11 |
12 |
13 | /**
14 | * @author oukanggui
15 | * @date 2018/11/14
16 | * 描述 BaseFragment基类,主要功能如下:
17 | * 1、支持显示以及设置加载中、数据为空、加载出错重试等布局
18 | * 2、View布局初始化以及Fragment第一次可见时数据加载等
19 | */
20 |
21 | public abstract class BaseFragment extends LazyLoadFragment {
22 | private View rootView;
23 | protected Activity mActivity;
24 |
25 | @Override
26 | public void onAttach(Context context) {
27 | mActivity = (Activity) context;
28 | super.onAttach(context);
29 | }
30 |
31 | @Nullable
32 | @Override
33 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
34 | if (rootView == null) {
35 | rootView = inflater.inflate(getLayoutResId(), container, false);
36 | initView(rootView, savedInstanceState);
37 | initData();
38 | } else {
39 | ViewGroup parent = (ViewGroup) rootView.getParent();
40 | if (parent != null) {
41 | parent.removeView(rootView);
42 | }
43 | }
44 | return rootView;
45 | }
46 |
47 | /**
48 | * StateView的根布局,默认是整个界面,如果需要变换可以重写此方法
49 | */
50 | public View getStateViewRoot() {
51 | return rootView;
52 | }
53 |
54 | /**
55 | * 获取Fragment资源布局ID,子类Fragment重写该方法返回Fragment展示布局的资源id
56 | */
57 | public abstract int getLayoutResId();
58 |
59 | /**
60 | * 加载数据,在Fragment第一次可见时回调,子类Fragment实现该方法进行数据加载(网络、数据库等)
61 | */
62 | public abstract void loadData();
63 |
64 | /**
65 | * 初始化UI,子类Fragment实现该方法进行UI初始化相关工作
66 | */
67 | public abstract void initView(View view, Bundle savedInstanceState);
68 |
69 | /**
70 | * 初始化数据,子类Fragment可以选择性实现该方法进行数据初始化相关工作
71 | */
72 | protected void initData() {
73 | }
74 |
75 |
76 | @Override
77 | protected void onFragmentFirstVisible() {
78 | //在Fragment第一次可见时,进行数据加载+
79 | loadData();
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/widget/src/main/res/layout/layout_menu_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
18 |
19 |
29 |
30 |
39 |
49 |
50 |
56 |
--------------------------------------------------------------------------------
/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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/baymax/widgetlibrary/fragment/ExpandLayoutFragment.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widgetlibrary.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 | import android.widget.Toast;
6 |
7 | import androidx.core.content.ContextCompat;
8 |
9 | import com.baymax.base.fragment.BaseFragment;
10 | import com.baymax.widget.ExpandLayout;
11 | import com.baymax.widgetlibrary.R;
12 |
13 | /**
14 | * @author Baymax
15 | * @date 2019-11-17
16 | * 描述:ExpandLayout的演示Fragment
17 | */
18 | public class ExpandLayoutFragment extends BaseFragment {
19 | private ExpandLayout mExpandLayoutDefault, mExpandLayoutIcon, mExpandLayoutText;
20 |
21 | @Override
22 | public int getLayoutResId() {
23 | return R.layout.fragment_layout_expand;
24 | }
25 |
26 | @Override
27 | public void loadData() {
28 |
29 | }
30 |
31 | @Override
32 | public void initView(View view, Bundle savedInstanceState) {
33 | mExpandLayoutDefault = view.findViewById(R.id.my_expand_default);
34 | mExpandLayoutIcon = view.findViewById(R.id.my_expand_icon);
35 | mExpandLayoutText = view.findViewById(R.id.my_expand_text);
36 | //要展示的文字内容
37 | String contentStr = "我是正常的全中文文字,可以点击我展开查看更多或收起,我是图标+文字的默认模式";
38 | mExpandLayoutDefault.setContent(contentStr, new ExpandLayout.OnExpandStateChangeListener() {
39 | @Override
40 | public void onExpand() {
41 | mExpandLayoutDefault.setContentTextColor(ContextCompat.getColor(mActivity, android.R.color.holo_blue_light));
42 | }
43 |
44 | @Override
45 | public void onCollapse() {
46 | mExpandLayoutDefault.setExpandTextColor(ContextCompat.getColor(mActivity, android.R.color.holo_red_dark));
47 | }
48 | });
49 | String contentStr1 = "我是图标样式,Android、Android、Android,中间有换行符\n\n\nAndroid、你好AndroidAndroid、哈哈Android、Android、Android你好";
50 | mExpandLayoutIcon.setContent(contentStr1);
51 | String contentStr2 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
52 | mExpandLayoutText.setContent(contentStr2, new ExpandLayout.OnExpandStateChangeListener() {
53 | @Override
54 | public void onExpand() {
55 | Toast.makeText(mActivity, "onExpand", Toast.LENGTH_SHORT).show();
56 | }
57 |
58 | @Override
59 | public void onCollapse() {
60 | Toast.makeText(mActivity, "onCollapse", Toast.LENGTH_SHORT).show();
61 | }
62 | });
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_layout_expand.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
28 |
44 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/baymax/widgetlibrary/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widgetlibrary.activity;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.widget.PopupWindow;
8 |
9 | import androidx.fragment.app.Fragment;
10 |
11 | import com.baymax.base.activity.BaseTabBarActivity;
12 | import com.baymax.widgetlibrary.R;
13 | import com.baymax.widgetlibrary.fragment.ArrowLayoutFragment;
14 | import com.baymax.widgetlibrary.fragment.ExpandLayoutFragment;
15 | import com.baymax.widgetlibrary.fragment.MenuLayoutFragment;
16 | import com.baymax.widgetlibrary.fragment.RatingStarViewFragment;
17 | import com.baymax.widgetlibrary.fragment.RoundLayoutFragment;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | /**
23 | * 测试主Activity界面
24 | */
25 | public class MainActivity extends BaseTabBarActivity {
26 |
27 | @Override
28 | protected void initView(Bundle savedInstanceState) {
29 | super.initView(savedInstanceState);
30 | setTitle("WidgetLibrary");
31 | setTitleTextColor(Color.WHITE);
32 | showBackView(false);
33 | showRightView(true);
34 | }
35 |
36 | @Override
37 | protected List createFragments() {
38 | List fragments = new ArrayList<>();
39 | fragments.add(new RatingStarViewFragment());
40 | fragments.add(new ExpandLayoutFragment());
41 | fragments.add(new RoundLayoutFragment());
42 | fragments.add(new MenuLayoutFragment());
43 | fragments.add(new ArrowLayoutFragment());
44 | return fragments;
45 | }
46 |
47 | @Override
48 | protected List createPagerTitles() {
49 | List titles = new ArrayList<>();
50 | titles.add("RatingStarView");
51 | titles.add("ExpandLayout");
52 | titles.add("RoundLayout");
53 | titles.add("MenuLayout");
54 | titles.add("ArrowLayout");
55 | return titles;
56 | }
57 |
58 | @Override
59 | protected void onRightViewClicked(View view) {
60 | super.onRightViewClicked(view);
61 | PopupWindow popupWindow = makePopupWindow();
62 | int[] xy = new int[2];
63 | view.getLocationOnScreen(xy);
64 | //popupWindow.showAtLocation(view, Gravity.RIGHT | Gravity.TOP, -xy[0] / 2, xy[1] + button.getWidth());
65 | popupWindow.showAsDropDown(view,20, 20);
66 | }
67 |
68 | /**
69 | * 创建PopupWindow
70 | */
71 | private PopupWindow makePopupWindow()
72 | {
73 | PopupWindow popupWindow = new PopupWindow(this);
74 |
75 | View contentView = LayoutInflater.from(this).inflate(R.layout.item_menu_selected, null);
76 | popupWindow.setContentView(contentView);
77 | popupWindow.setBackgroundDrawable(null);
78 | //设置PopupWindow可获得焦点
79 | popupWindow.setFocusable(true);
80 | //设置PopupWindow可触摸
81 | popupWindow.setTouchable(true);
82 | //设置非PopupWindow区域可触摸
83 | popupWindow.setOutsideTouchable(true);
84 | return popupWindow;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/base/src/main/java/com/baymax/base/activity/BaseTabBarActivity.java:
--------------------------------------------------------------------------------
1 | package com.baymax.base.activity;
2 |
3 | import android.os.Bundle;
4 | import android.view.ViewGroup;
5 |
6 |
7 | import androidx.fragment.app.Fragment;
8 | import androidx.fragment.app.FragmentManager;
9 | import androidx.fragment.app.FragmentPagerAdapter;
10 | import androidx.viewpager.widget.ViewPager;
11 |
12 | import com.baymax.base.R;
13 | import com.google.android.material.tabs.TabLayout;
14 |
15 | import java.util.List;
16 |
17 | /**
18 | * @author oukanggui
19 | * @date 2018/11/19
20 | * 描述 导航栏TabBarActivity
21 | */
22 |
23 | public abstract class BaseTabBarActivity extends BaseTitleBarActivity {
24 | protected ViewPager mViewPager;
25 | protected TabLayout mTabLayout;
26 | protected FragmentPagerAdapter mPagerAdapter;
27 |
28 | @Override
29 | protected void initView(Bundle savedInstanceState) {
30 | mViewPager = findViewById(R.id.base_viewpager);
31 | mTabLayout = findViewById(R.id.base_tablayout);
32 | }
33 |
34 | @Override
35 | protected void initData() {
36 | mPagerAdapter = new CommonFragmentPagerAdapter(getSupportFragmentManager(),createFragments(),createPagerTitles());
37 | mViewPager.setAdapter(mPagerAdapter);
38 | if (createFragments() != null) {
39 | mViewPager.setOffscreenPageLimit(createFragments().size());
40 | }
41 | mTabLayout.setupWithViewPager(mViewPager);
42 | }
43 |
44 | @Override
45 | protected int getLayoutResId() {
46 | return R.layout.activity_base_tabbar;
47 | }
48 |
49 | /**
50 | * 供子类重写创建显示的Fragment
51 | *
52 | * */
53 | protected abstract List createFragments();
54 |
55 | /**
56 | * 供子类重写创建显示的Title
57 | *
58 | * */
59 | protected abstract List createPagerTitles();
60 |
61 | private static class CommonFragmentPagerAdapter extends FragmentPagerAdapter {
62 | private List fragments;
63 | private List titles;
64 |
65 | public CommonFragmentPagerAdapter(FragmentManager fm, List fragments, List titles) {
66 | super(fm);
67 | this.fragments = fragments;
68 | this.titles = titles;
69 | }
70 |
71 | @Override
72 | public int getCount() {
73 | return titles.size();
74 | }
75 |
76 | @Override
77 | public Fragment getItem(int position) {
78 | return fragments == null ? null : fragments.get(position);
79 | }
80 |
81 | @Override
82 | public Object instantiateItem(ViewGroup container, int position) {
83 | return super.instantiateItem(container, position);
84 | }
85 |
86 | @Override
87 | public void destroyItem(ViewGroup container, int position, Object object) {
88 | super.destroyItem(container, position, object);
89 | }
90 |
91 | @Override
92 | public CharSequence getPageTitle(int position) {
93 | return titles == null ? null : titles.get(position);
94 | }
95 |
96 | @Override
97 | public long getItemId(int position) {
98 | return position;
99 | }
100 |
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/base/src/main/java/com/baymax/base/fragment/LazyLoadFragment.java:
--------------------------------------------------------------------------------
1 | package com.baymax.base.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.util.Log;
5 | import android.view.View;
6 |
7 | import androidx.annotation.Nullable;
8 | import androidx.fragment.app.Fragment;
9 |
10 | /**
11 | * @author oukanggui
12 | * @date 2018/11/14
13 | * 描述 懒加载Fragment
14 | * 在使用了ViewPager + Fragment的情况下,Fragment的生命周期因ViewPager的缓存机制而失去了具体的意义
15 | * 该抽象类自定义新的回调方法,当Fragment可见状态改变时或Fragment第一次可见时会回调的方法
16 | * @see #onFragmentVisibleChange(boolean)
17 | * @see #onFragmentFirstVisible()
18 | */
19 |
20 | public abstract class LazyLoadFragment extends Fragment {
21 |
22 | protected String TAG = getClass().getSimpleName();
23 | /**
24 | * 标识是否是第一次进来
25 | */
26 | private boolean mIsFirstEnter = true;
27 | /**
28 | * 页面布局根View
29 | */
30 | private View mRootView;
31 |
32 | /**
33 | * 该方法的回调时机:
34 | * 1)在Fragment创建时会先回调一次(isVisibleToUser=false),此时Fragment还不可见,UI可能还没有初始化
35 | * 2)Fragment创建完可见时,该方法会再次回调一次(isVisibleToUser=true)
36 | * 3)Fragment从可见-->不可见时,该方法也会被回调(isVisibleToUser=false)
37 | * 总结:setUserVisibleHint()除了在Fragment的可见状态发生变化时会被回调外,在new Fragment()时也会被回调
38 | * 如果我们需要在 Fragment 可见与不可见时干点事,用这个的话就会有多余的回调了,就需要重新封装一个
39 | */
40 | @Override
41 | public void setUserVisibleHint(boolean isVisibleToUser) {
42 | Log.i(TAG, "setUserVisibleHint = " + isVisibleToUser);
43 | super.setUserVisibleHint(isVisibleToUser);
44 | //该方法有可能在fragment的生命周期外被调用
45 | //保证在View初始化的时候在进行处理
46 | if (mRootView == null) {
47 | return;
48 | }
49 | if (mIsFirstEnter && isVisibleToUser) {
50 | //第一次进来且可见的时候,回调Fragment首次可见
51 | onFragmentFirstVisible();
52 | mIsFirstEnter = false;
53 | }
54 | //过滤在Fragment创建时回调该方法的情况
55 | if (!mIsFirstEnter) {
56 | //如果不是第一次进来或者已处理完第一次可见(保证onFragmentFirstVisible()先回调),回调Fragment可见状态
57 | onFragmentVisibleChange(isVisibleToUser);
58 | }
59 | }
60 |
61 | @Override
62 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
63 | Log.i(TAG, "onViewCreated");
64 | //如果setUserVisibleHint()在rootView创建前调用时(此时setUserVisibleHint没有回调处理),那么
65 | //就等到rootView创建完后才回调onFragmentFirstVisible()以及onFragmentVisibleChange(true)
66 | //保证这两个方法的回调发生在rootView创建完成之后,以便支持ui操作
67 | if (mRootView == null) {
68 | mRootView = view;
69 | if (getUserVisibleHint()) {
70 | if (mIsFirstEnter) {
71 | onFragmentFirstVisible();
72 | mIsFirstEnter = false;
73 | }
74 | onFragmentVisibleChange(true);
75 | }
76 | }
77 | super.onViewCreated(mRootView, savedInstanceState);
78 | }
79 |
80 |
81 | /**
82 | * 当Fragment可见状态变化时回调该方法:
83 | * 1)去除setUserVisibleHint()多余的回调场景,保证只有当Fragment可见状态发生变化时才回调
84 | * 2)回调时机在view创建完后,支持ui操作,解决在setUserVisibleHint()里进行ui操作有可能报null异常的问题
85 | * 可以在该方法中进行UI操作相关的操作,比如加载框的显示与隐藏、动画操作等
86 | *
87 | * @param isVisible true 不可见 -> 可见
88 | * false 可见 -> 不可见
89 | */
90 | protected void onFragmentVisibleChange(boolean isVisible) {
91 |
92 | }
93 |
94 | /**
95 | * 在Fragment首次可见时回调,可在这里进行加载数据,保证只在第一次打开Fragment时才会加载数据,
96 | * 实现懒加载,防止每次进入都重复加载数据
97 | * 该方法会在onFragmentVisibleChange()之前调用
98 | */
99 | protected void onFragmentFirstVisible() {
100 |
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_layout_ratitngstar.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
14 |
21 |
35 |
42 |
55 |
72 |
85 |
86 |
101 |
102 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.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 | xmlns:android
38 |
39 | ^$
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | xmlns:.*
49 |
50 | ^$
51 |
52 |
53 | BY_NAME
54 |
55 |
56 |
57 |
58 |
59 |
60 | .*:id
61 |
62 | http://schemas.android.com/apk/res/android
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | .*:name
72 |
73 | http://schemas.android.com/apk/res/android
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | name
83 |
84 | ^$
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | style
94 |
95 | ^$
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | ^$
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 | .*
117 |
118 | http://schemas.android.com/apk/res/android
119 |
120 |
121 | ANDROID_ATTRIBUTE_ORDER
122 |
123 |
124 |
125 |
126 |
127 |
128 | .*
129 |
130 | .*
131 |
132 |
133 | BY_NAME
134 |
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/base/src/main/java/com/baymax/base/activity/BaseTitleBarActivity.java:
--------------------------------------------------------------------------------
1 | package com.baymax.base.activity;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.View;
5 | import android.widget.FrameLayout;
6 | import android.widget.ImageView;
7 | import android.widget.TextView;
8 |
9 | import com.baymax.base.R;
10 |
11 |
12 | public abstract class BaseTitleBarActivity extends BaseActivity {
13 |
14 | private View mTitleLayout;
15 | private FrameLayout mContainer;
16 | private ImageView mBackView, mIvRight;
17 | private TextView mTitleView;
18 |
19 | @Override
20 | public void setContentView(View view) {
21 | setContentView(R.layout.activity_base_titlebar);
22 | mContainer = (FrameLayout) findViewById(R.id.base_fl_container);
23 | mContainer.addView(view);
24 | }
25 |
26 | @Override
27 | public void setContentView(int layoutResID) {
28 | super.setContentView(R.layout.activity_base_titlebar);
29 | if (layoutResID != R.layout.activity_base_titlebar) {
30 | View contentView = LayoutInflater.from(this).inflate(layoutResID, null);
31 | mContainer = findViewById(R.id.base_fl_container);
32 | mContainer.addView(contentView);
33 | }
34 | initTitleLayout();
35 | }
36 |
37 | /**
38 | * 初始化头部Title布局
39 | */
40 | private void initTitleLayout() {
41 | mTitleLayout = findViewById(R.id.base_layout_title);
42 | mTitleView = findViewById(R.id.base_tv_title);
43 | mBackView = findViewById(R.id.base_iv_back);
44 | mBackView.setOnClickListener(new View.OnClickListener() {
45 | @Override
46 | public void onClick(View v) {
47 | onBackViewClicked();
48 | }
49 | });
50 | mIvRight = findViewById(R.id.base_iv_right);
51 | mIvRight.setOnClickListener(new View.OnClickListener() {
52 | @Override
53 | public void onClick(View v) {
54 | onRightViewClicked(v);
55 | }
56 | });
57 | }
58 |
59 | /**
60 | * 设置Title
61 | *
62 | * @param color Title布局显示的背景颜色
63 | */
64 | public void setTitleBarBackgroundColor(int color) {
65 | if (mTitleLayout != null) {
66 | mTitleLayout.setBackgroundColor(color);
67 | }
68 | }
69 |
70 | /**
71 | * 设置Title
72 | *
73 | * @param drawable Title布局显示的背景drawable资源
74 | */
75 | public void setTitleBarBackgroundDrawable(int drawable) {
76 | if (mTitleLayout != null) {
77 | mTitleLayout.setBackgroundResource(drawable);
78 | }
79 | }
80 |
81 | /**
82 | * 设置Title
83 | *
84 | * @param title 显示的Title
85 | */
86 | public void setTitle(String title) {
87 | if (mTitleView != null) {
88 | mTitleView.setText(title);
89 | }
90 | }
91 |
92 | /**
93 | * 设置Title字体颜色
94 | *
95 | * @param color
96 | */
97 | public void setTitleTextColor(int color) {
98 | if (mTitleView != null) {
99 | mTitleView.setTextColor(color);
100 | }
101 | }
102 |
103 | /**
104 | * 设置标题栏左边图片显示的图片资源
105 | *
106 | * @param resId 待显示的图片资源ID
107 | */
108 | public void setBackViewRes(int resId) {
109 | setImageViewRes(mBackView, resId);
110 | }
111 |
112 | /**
113 | * 设置标题栏右边显示的图片资源
114 | *
115 | * @param resId 待显示的图片资源ID
116 | */
117 | public void setRightViewRes(int resId) {
118 | setImageViewRes(mIvRight, resId);
119 | }
120 |
121 | /**
122 | * 设置Title是否显示
123 | *
124 | * @param isShow 是否显示
125 | */
126 | public void showTitle(boolean isShow) {
127 | setViewVisibility(mTitleView, isShow);
128 | }
129 |
130 | /**
131 | * 设置返回BackView是否显示
132 | *
133 | * @param isShow 是否显示
134 | */
135 | public void showBackView(boolean isShow) {
136 | setViewVisibility(mBackView, isShow);
137 | }
138 |
139 | /**
140 | * 设置标题栏右边图标是否显示
141 | *
142 | * @param isShow 是否显示
143 | */
144 | public void showRightView(boolean isShow) {
145 | setViewVisibility(mIvRight, isShow);
146 | }
147 |
148 | /**
149 | * 设置View是否显示
150 | *
151 | * @param view 待处理的View
152 | * @param isShow 是否显示
153 | */
154 | private void setViewVisibility(View view, boolean isShow) {
155 | if (view != null) {
156 | view.setVisibility(isShow ? View.VISIBLE : View.GONE);
157 | }
158 | }
159 |
160 | /**
161 | * 设置ImageView显示的资源
162 | *
163 | * @param imageView 待处理的ImageView
164 | * @param resId 待显示的图片资源ID
165 | */
166 | private void setImageViewRes(ImageView imageView, int resId) {
167 | if (imageView != null) {
168 | imageView.setImageResource(resId);
169 | }
170 | }
171 |
172 | /**
173 | * 子页面重写该方法处理BackView点击时的处理逻辑,默认为finish当前页面
174 | */
175 | protected void onBackViewClicked() {
176 | finish();
177 | }
178 |
179 | /**
180 | * 子页面重写该方法处理RightView点击时的处理逻辑
181 | *
182 | * @param view 被点击的View
183 | */
184 | protected void onRightViewClicked(View view) {
185 |
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/widget/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 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/baymax/widget/RoundButton.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widget;
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.graphics.RectF;
8 | import android.util.AttributeSet;
9 |
10 | import androidx.appcompat.widget.AppCompatButton;
11 |
12 | /**
13 | * @author oukanggui
14 | * @date 2019/9/26
15 | * 描述:支持圆角、描边以及背景色设置的Button
16 | */
17 | public class RoundButton extends AppCompatButton {
18 |
19 | /**
20 | * 背景填充矩形
21 | */
22 | RectF mFillRect;
23 | /**
24 | * 画笔
25 | */
26 | Paint mPaint;
27 | /**
28 | * 背景填充颜色
29 | */
30 | int mBackgroundColor;
31 | /**
32 | * 圆角x,y轴半径
33 | */
34 | float mRadiusX = 0, mRadiusY = 0;
35 | /**
36 | * 边框颜色
37 | */
38 | int mStrokeColor = -1;
39 | /**
40 | * 边框颜色集合
41 | */
42 | int mStrokeColorSet = -1;
43 | /**
44 | * 边框宽度
45 | */
46 | float mStrokeWidth = 5;
47 | /**
48 | * 字体初始化颜色
49 | */
50 | int mTextColorInit = -1;
51 | /**
52 | * 默认圆角x,y半径
53 | */
54 | private static final int DEFAULT_RADIUS_XY = 5;
55 |
56 | /**
57 | * @param context
58 | */
59 |
60 | public RoundButton(Context context) {
61 | super(context);
62 | init(context, null);
63 | }
64 |
65 | public RoundButton(Context context, AttributeSet attrs) {
66 | super(context, attrs);
67 | init(context, attrs);
68 | }
69 |
70 | public RoundButton(Context context, AttributeSet attrs, int defStyleAttr) {
71 | super(context, attrs, defStyleAttr);
72 | init(context, attrs);
73 | }
74 |
75 | /**
76 | * 初始化
77 | *
78 | * @param context
79 | * @param attrs
80 | */
81 | void init(Context context, AttributeSet attrs) {
82 | if (attrs != null) {
83 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundButton);
84 | if (a != null) {
85 | mBackgroundColor = a.getColor(R.styleable.RoundButton_backgroundColorFill, 0);
86 | mStrokeWidth = a.getDimension(R.styleable.RoundButton_strokeWidth, 1);
87 | mStrokeColor = a.getColor(R.styleable.RoundButton_strokeColor, 0);
88 | mStrokeColorSet = mStrokeColor;
89 | mRadiusX = a.getDimension(R.styleable.RoundButton_rx, DEFAULT_RADIUS_XY);
90 | mRadiusY = a.getDimension(R.styleable.RoundButton_ry, DEFAULT_RADIUS_XY);
91 | a.recycle();
92 | }
93 | }
94 | mTextColorInit = getCurrentTextColor();
95 | mPaint = new Paint();
96 | mPaint.setColor(mBackgroundColor);
97 | mPaint.setStyle(Paint.Style.FILL);
98 | mPaint.setAntiAlias(true);
99 | }
100 |
101 | @Override
102 | public void setEnabled(boolean enabled) {
103 | super.setEnabled(enabled);
104 | if (enabled) {
105 | if (mStrokeColorSet != -1) {
106 | mStrokeColor = mStrokeColorSet;
107 | }
108 | if (mTextColorInit != -1) {
109 | setTextColor(mTextColorInit);
110 | }
111 | } else {
112 | int color = getContext().getResources().getColor(android.R.color.darker_gray);
113 | if (mStrokeColorSet != -1) {
114 | mStrokeColor = color;
115 | }
116 | if (mTextColorInit != -1) {
117 | setTextColor(color);
118 | }
119 | }
120 | }
121 |
122 | @Override
123 | protected void onDraw(Canvas canvas) {
124 | drawInnerFillBackground(canvas);
125 | drawOuterStroke(canvas);
126 | super.onDraw(canvas);
127 | }
128 |
129 | /**
130 | * 画内部填充背景
131 | *
132 | * @param canvas
133 | */
134 | private void drawInnerFillBackground(Canvas canvas) {
135 | if (mBackgroundColor != 0) {
136 | if (mFillRect == null || mFillRect.width() != getMeasuredWidth()) {
137 | mFillRect = new RectF(0, 0, getMeasuredWidth() - 0, getMeasuredHeight() - 0);
138 | }
139 | mPaint.setColor(mBackgroundColor);
140 | mPaint.setStyle(Paint.Style.FILL);
141 | canvas.drawRoundRect(mFillRect, mRadiusX, mRadiusY, mPaint);
142 | }
143 | }
144 |
145 | /**
146 | * 画外部边框
147 | *
148 | * @param canvas
149 | */
150 | private void drawOuterStroke(Canvas canvas) {
151 | if (mStrokeColor != 0) {
152 | mPaint.setColor(mStrokeColor);
153 | mPaint.setStyle(Paint.Style.STROKE);
154 | mPaint.setStrokeWidth(mStrokeWidth);
155 | RectF strokeRect = new RectF(mStrokeWidth / 2.0f, mStrokeWidth / 2.0f, getMeasuredWidth() - mStrokeWidth / 2.0f, getMeasuredHeight() - mStrokeWidth / 2.0f);
156 | canvas.drawRoundRect(strokeRect, mRadiusX, mRadiusY, mPaint);
157 | }
158 | }
159 |
160 | public void setRadiusX(float radiusX) {
161 | mRadiusX = radiusX;
162 | }
163 |
164 | public void setRadiusY(float radiusY) {
165 | mRadiusY = radiusY;
166 | }
167 |
168 | public void setmStrokeWidth(float width) {
169 | mStrokeWidth = width;
170 | }
171 |
172 | public void setFillBackgroundColor(int fillBackgroundColor) {
173 | this.mBackgroundColor = fillBackgroundColor;
174 | }
175 |
176 |
177 | public void setStrokeColor(int color) {
178 | mStrokeColor = color;
179 | mStrokeColorSet = mStrokeColor;
180 | mBackgroundColor = 0;
181 | }
182 |
183 | }
184 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_layout_arrow.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
22 |
26 |
27 |
41 |
45 |
46 |
63 |
67 |
68 |
80 |
84 |
85 |
100 |
104 |
105 |
120 |
124 |
125 |
126 |
129 |
130 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/baymax/widget/ArrowRelativeLayout.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widget;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.graphics.RectF;
11 | import android.util.AttributeSet;
12 | import android.widget.RelativeLayout;
13 |
14 | /**
15 | * @author Baymax
16 | * @date 2019-11-10
17 | * 描述:带三角的指示器相对布局
18 | */
19 | public class ArrowRelativeLayout extends RelativeLayout {
20 | public static final int POSTION_TOP = 0;
21 | public static final int POSTION_BOTTOM = 1;
22 |
23 | /**
24 | * 三角形的宽度
25 | */
26 | private float mArrowWidth = 20;
27 | /**
28 | * 三角形的高度
29 | */
30 | private float mArrowHeight = 20;
31 | /**
32 | * 三角形左边底点距离布局左边的偏移量,默认居中显示
33 | */
34 | private float mArrowStartOffset = -1;
35 | /**
36 | * 三角形顶点与三角形底边中心的水平偏移量,负值代表箭头往左偏,正值代表箭头往右偏,默认居中
37 | */
38 | private float mArrowVertexOffset = 0;
39 | /**
40 | * 三角形的位置,目前支持在布局上方、在布局下方,默认在上方
41 | */
42 | private int mArrowPosition = POSTION_TOP;
43 | /**
44 | * 圆角的X半径
45 | */
46 | private float mRadiuX;
47 | /**
48 | * 圆角的Y半径
49 | */
50 | private float mRadiuY;
51 | /**
52 | * 填充的颜色值
53 | */
54 | private int mBackgroundColor = Color.RED;
55 |
56 | /**
57 | * 画笔
58 | */
59 | private Paint mPaint;
60 |
61 | public ArrowRelativeLayout(Context context) {
62 | super(context);
63 | init(null);
64 | }
65 |
66 | public ArrowRelativeLayout(Context context, AttributeSet attrs) {
67 | super(context, attrs);
68 | init(attrs);
69 | }
70 |
71 | public ArrowRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
72 | super(context, attrs, defStyleAttr);
73 | init(attrs);
74 | }
75 |
76 | @TargetApi(21)
77 | public ArrowRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
78 | super(context, attrs, defStyleAttr, defStyleRes);
79 | init(attrs);
80 | }
81 |
82 | @Override
83 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
84 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
85 | }
86 |
87 | private void init(AttributeSet attrs) {
88 | initPaint();
89 | initAttrs(attrs);
90 | }
91 |
92 | /**
93 | * 初始化画笔
94 | */
95 | private void initPaint() {
96 | mPaint = new Paint();
97 | mPaint.setAntiAlias(true);
98 | mPaint.setStyle(Paint.Style.FILL);
99 | }
100 |
101 | /**
102 | * 初始化属性
103 | *
104 | * @param attrs
105 | */
106 | private void initAttrs(AttributeSet attrs) {
107 | if (attrs == null) {
108 | return;
109 | }
110 | TypedArray ta = getResources().obtainAttributes(attrs, R.styleable.ArrowRelativeLayout);
111 | if (ta != null) {
112 | mArrowWidth = ta.getDimension(R.styleable.ArrowRelativeLayout_arrow_width, 20);
113 | mArrowHeight = ta.getDimension(R.styleable.ArrowRelativeLayout_arrow_height, 20);
114 | mArrowStartOffset = ta.getDimension(R.styleable.ArrowRelativeLayout_arrow_start_offset, -1);
115 | mArrowVertexOffset = ta.getDimension(R.styleable.ArrowRelativeLayout_arrow_vertex_offset, 0);
116 | mArrowPosition = ta.getInt(R.styleable.ArrowRelativeLayout_arrow_position, POSTION_TOP);
117 | mRadiuX = ta.getDimension(R.styleable.ArrowRelativeLayout_radiu_x, 0);
118 | mRadiuY = ta.getDimension(R.styleable.ArrowRelativeLayout_radiu_y, 0);
119 | mBackgroundColor = ta.getColor(R.styleable.ArrowRelativeLayout_background_color, Color.GRAY);
120 | ta.recycle();
121 | }
122 | // 根据方向重设TOP、BOTTOM的Padding值,追加三角形箭头高度mArrowHeight,以便控件内容显示在矩形区域内
123 | if (mArrowPosition == POSTION_TOP) {
124 | setPadding(getPaddingLeft(), getPaddingTop() + (int) Math.ceil(mArrowHeight), getPaddingRight(), getPaddingBottom());
125 | } else {
126 | setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom() + (int) Math.ceil(mArrowHeight));
127 | }
128 | }
129 |
130 | @Override
131 | protected void dispatchDraw(Canvas canvas) {
132 | mPaint.setColor(mBackgroundColor);
133 | // 画背景
134 | drawRoundRectArea(canvas, getWidth(), getHeight());
135 | // 画箭头
136 | drawArrowArea(canvas, getWidth(), getHeight());
137 | super.dispatchDraw(canvas);
138 | }
139 |
140 | /**
141 | * 画矩形背景
142 | *
143 | * @param canvas
144 | * @param width
145 | * @param height
146 | */
147 | private void drawRoundRectArea(Canvas canvas, int width, int height) {
148 | //框定文本显示的区域,根据不同的方向,选择不同的矩形面积,给三角形显示的控件
149 | RectF rectF;
150 | if (mArrowPosition == POSTION_TOP) {
151 | rectF = new RectF(0, mArrowHeight, width, height);
152 | } else {
153 | rectF = new RectF(0, 0, width, height - mArrowHeight);
154 | }
155 | canvas.drawRoundRect(rectF, mRadiuX, mRadiuY, mPaint);
156 | }
157 |
158 | /**
159 | * 画三角箭头
160 | *
161 | * @param canvas
162 | * @param width
163 | * @param height
164 | */
165 | private void drawArrowArea(Canvas canvas, int width, int height) {
166 | Path path = new Path();
167 | // 根据三角箭头箭头方向和位置决定三角形三点的坐标
168 | if (mArrowStartOffset < 0) {
169 | mArrowStartOffset = width / 2.0f - mArrowWidth / 2.0f;
170 | }
171 | if ((mArrowStartOffset + mArrowWidth) > width) {
172 | mArrowStartOffset = width - mArrowWidth;
173 | }
174 | // 三角形左边点坐标
175 | float leftPointX = mArrowStartOffset;
176 | float leftPointY = mArrowHeight;
177 | // 顶点
178 | float topPointX = leftPointX + mArrowWidth / 2 + mArrowVertexOffset;
179 | float topPointY = 0;
180 | // 三角形右边点坐标
181 | float rightPointX = leftPointX + mArrowWidth;
182 | float rightPointY = mArrowHeight;
183 | // 根据三角形的位置,确定三角形三点的y坐标,默认在上方
184 | if (mArrowPosition == POSTION_BOTTOM) {
185 | topPointY = height;
186 | leftPointY = rightPointY = height - mArrowHeight;
187 | }
188 | // 三角形顶点
189 | path.moveTo(topPointX, topPointY);
190 | // 三角形左边的点
191 | path.lineTo(leftPointX, leftPointY);
192 | // 三角形右边的点
193 | path.lineTo(rightPointX, rightPointY);
194 | path.close();
195 | canvas.drawPath(path, mPaint);
196 | }
197 |
198 | /**
199 | * 设置背景填充颜色
200 | *
201 | * @param backgroundColor
202 | */
203 | public void setFillBackgroundColor(int backgroundColor) {
204 | this.mBackgroundColor = backgroundColor;
205 | invalidate();
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/baymax/widget/ratingstar/StarModel.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widget.ratingstar;
2 |
3 | import android.graphics.RectF;
4 |
5 | /**
6 | * Holds all vertexes (x,y) and bounds (outerRect) info about a drawing Star.
7 | * used by {@link RatingStarView}, Most calculations (about x,y、size etc.) is done here.
8 | *
9 | * [Based Idea or Concept]
10 | *
11 | * ## Standard Coordinate:
12 | * The coordinate is —— toward right for x+ ,toward up for y+ .
13 | *
14 | * ## 5 outer vertexes
15 | * The outer circle's (means "circumcircle") radius is 1f, original point O is the star's center,
16 | * so, the 5 vertexes at 5 outer corner is (from top A, at clockwise order):
17 | *
18 | *
A(0,1)
19 | * B(cos18°,sin18°)
20 | * C(cos54°,-sin54°)
21 | * D(-cos54°,-sin54°)
22 | * E(-cos18°,sin18°)
23 | *
24 | * @author
25 | * Created by hxw on 2017-04-23.
26 | * 每一个要显示的star由一个StarModel类来表示,它持有一个星星的坐标信息并完成相应的计算
27 | */
28 | public class StarModel {
29 |
30 | public static final float DEFAULT_THICKNESS = 0.5f;
31 | public static final float MIN_THICKNESS = 0.3f;
32 | public static final float MAX_THICKNESS = 0.9f;
33 | public static final float DEFAULT_SCALE_FACTOR = 0.9511f;
34 | private float currentScaleFactor = DEFAULT_SCALE_FACTOR;
35 | private RectF outerRect = new RectF();
36 | private float currentThicknessFactor = DEFAULT_THICKNESS;
37 |
38 | public StarModel() {
39 | this(DEFAULT_THICKNESS);
40 | }
41 |
42 | /**
43 | * @param thicknessFactor see {@link #setThickness(float)}
44 | */
45 | public StarModel(float thicknessFactor) {
46 | reset(thicknessFactor);
47 | }
48 |
49 | /**
50 | * Reset all vertexes values to based on radius-1f, will call adjustCoordinate() automatically,
51 | * So after reset() the Coordinate is match with Android.
52 | *
53 | * @param thickness {@link #setThicknessOnStandardCoordinate }
54 | */
55 | private void reset(float thickness) {
56 | currentScaleFactor = DEFAULT_SCALE_FACTOR;
57 | initAllVertexesToStandard();
58 | updateOuterRect();
59 | setThicknessOnStandardCoordinate(thickness);
60 | adjustCoordinate();
61 | }
62 |
63 | public void setDrawingOuterRect(int left, int top, int height) {
64 | // ScaleFactor=1f means width is 1f
65 | float resizeFactor = height / aspectRatio;
66 | offsetStar(-outerRect.left, -outerRect.top);
67 | changeScaleFactor(resizeFactor);
68 | offsetStar(left, top);
69 | updateOuterRect();
70 | }
71 |
72 | public void moveStarTo(float left, float top) {
73 | float offsetX = left - outerRect.left;
74 | // TODO 错误计算??float offsetY = left - outerRect.top;
75 | float offsetY = top - outerRect.top;
76 | offsetStar(offsetX, offsetY);
77 | updateOuterRect();
78 | }
79 |
80 | // region vertexes fields
81 |
82 | /**
83 | * 10 float values for star's 5 vertex's (x,y) —— outer circle's radius is 1f (
84 | * NOTE: In the "Standard Coordinate".) , first vertex is for top corner, in clockwise order.
85 | */
86 | private static final float[] starVertexes = new float[]{
87 | -0.9511f, 0.3090f, // E (left)
88 | 0.0000f, 1.0000f, // A (top vertex)
89 | 0.9511f, 0.3090f, // B (right)
90 | 0.5878f, -0.8090f, // C (bottom right)
91 | -0.5878f, -0.8090f, // D (bottom left)
92 | };
93 |
94 | /**
95 | * ratio = height / width.
96 | * width is think as 1f, because the star's width is lager.
97 | * NOTE: In the "Standard Coordinate"
98 | */
99 | private static final float aspectRatio
100 | = (starVertexes[3] - starVertexes[7]) / (starVertexes[4] - starVertexes[0]);
101 |
102 | /**
103 | * firstVertex is vertex: E (very left one)
104 | *
105 | * @see StarModel
106 | */
107 | private VertexF firstVertex;
108 |
109 | /**
110 | * All star vertexes, from the most left one. then clockwise.
111 | *
112 | * NOTE: init or update by {@link #initAllVertexesToStandard() }
113 | *
114 | * @see #firstVertex
115 | * @see #starVertexes
116 | */
117 | private VertexF[] vertexes;
118 |
119 | // endregion
120 |
121 | private void initAllVertexesToStandard() {
122 | if (firstVertex == null) {
123 | firstVertex = new VertexF(starVertexes[0], starVertexes[1]);
124 | } else {
125 | firstVertex.x = starVertexes[0];
126 | firstVertex.y = starVertexes[1];
127 | }
128 |
129 | // create all 10 vertexes into #vertexes
130 | if (vertexes == null) {
131 | vertexes = new VertexF[10];
132 | vertexes[0] = firstVertex;
133 |
134 | for (int i = 1; i < 10; i++) {
135 | vertexes[i] = new VertexF();
136 | vertexes[i - 1].next = vertexes[i];
137 | }
138 |
139 | // link tail and head
140 | vertexes[9].next = vertexes[0];
141 | }
142 |
143 | // update all 5 outer vertexes.
144 | VertexF current = firstVertex;
145 | for (int i = 0; i < 5; i++) {
146 | current.x = starVertexes[i * 2];
147 | current.y = starVertexes[i * 2 + 1];
148 |
149 | current = current.next.next;
150 | }
151 |
152 | // update all 5 inner vertexes.
153 | VertexF prevOuter = firstVertex;
154 | for (int i = 0; i < 5; i++) {
155 | VertexF innerV = prevOuter.next;
156 |
157 | innerV.x = (prevOuter.x + innerV.next.x) / 2f;
158 | innerV.y = (prevOuter.y + innerV.next.y) / 2f;
159 |
160 | prevOuter = innerV.next;
161 | }
162 | }
163 |
164 | /**
165 | * Get vertex at index in {@link #vertexes}
166 | *
167 | * @param index see {@link #vertexes}
168 | */
169 | public VertexF getVertex(int index) {
170 | return vertexes[index];
171 | }
172 |
173 | public RectF getOuterRect() {
174 | return new RectF(outerRect);
175 | }
176 |
177 | /**
178 | * Keep the star's outer bounds exactly.
179 | * NOTE: call this after any vertex value changed.
180 | */
181 | private void updateOuterRect() {
182 | outerRect.top = vertexes[2].y;
183 | outerRect.right = vertexes[4].x;
184 | outerRect.bottom = vertexes[8].y;
185 | outerRect.left = vertexes[0].x;
186 | }
187 |
188 | private void offsetStar(float left, float top) {
189 | for (int i = 0; i < vertexes.length; i++) {
190 | vertexes[i].x += left;
191 | vertexes[i].y += top;
192 | }
193 | }
194 |
195 | private void changeScaleFactor(float newFactor) {
196 | float scale = newFactor / currentScaleFactor;
197 | if (scale == 1f) {
198 | return;
199 | }
200 | for (int i = 0; i < vertexes.length; i++) {
201 | vertexes[i].x *= scale;
202 | vertexes[i].y *= scale;
203 | }
204 | currentScaleFactor = newFactor;
205 | }
206 |
207 | /**
208 | * change the thickness of star.
209 | * value {@link #DEFAULT_THICKNESS}is about to make a standard star.
210 | *
211 | * @param factor between {@link #MIN_THICKNESS} and {@link #MAX_THICKNESS}.
212 | */
213 | public void setThickness(float factor) {
214 | if (currentThicknessFactor == factor) {
215 | return;
216 | }
217 | float oldScale = currentScaleFactor;
218 | float left = outerRect.left;
219 | float top = outerRect.top;
220 |
221 | reset(factor);
222 |
223 | changeScaleFactor(oldScale);
224 | moveStarTo(left, top);
225 | }
226 |
227 | private void setThicknessOnStandardCoordinate(float thicknessFactor) {
228 | if (thicknessFactor < MIN_THICKNESS) {
229 | thicknessFactor = MIN_THICKNESS;
230 | } else if (thicknessFactor > MAX_THICKNESS) {
231 | thicknessFactor = MAX_THICKNESS;
232 | }
233 |
234 | for (int i = 1; i < vertexes.length; i += 2) {
235 | vertexes[i].x *= thicknessFactor;
236 | vertexes[i].y *= thicknessFactor;
237 | }
238 |
239 | currentThicknessFactor = thicknessFactor;
240 | }
241 |
242 | /**
243 | * reverse Y, and move to y=0
244 | */
245 | private void adjustCoordinate() {
246 | float offsetX = -outerRect.left;
247 | float offsetY = outerRect.top;
248 |
249 | for (int i = 0; i < vertexes.length; i++) {
250 | vertexes[i].y = -vertexes[i].y + offsetY;
251 | vertexes[i].x += offsetX;
252 |
253 | // standard value is in radius = 1f, so..
254 | vertexes[i].x /= 2f;
255 | vertexes[i].y /= 2f;
256 | }
257 |
258 | updateOuterRect();
259 | }
260 |
261 | /**
262 | * ratio = height / width. width is think as 1f, because the star's width is lager.
263 | * NOTE: In the "Standard Coordinate"
264 | *
265 | * @return ratio = height / width.
266 | */
267 | public static float getOuterRectAspectRatio() {
268 | return aspectRatio;
269 | }
270 |
271 | public static float getStarWidth(float starHeight) {
272 | return starHeight / getOuterRectAspectRatio();
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/baymax/widget/MenuLayout.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widget;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.drawable.Drawable;
7 | import android.os.Build;
8 | import android.util.AttributeSet;
9 | import android.util.Log;
10 | import android.view.View;
11 | import android.widget.ImageView;
12 | import android.widget.RelativeLayout;
13 | import android.widget.TextView;
14 |
15 | import androidx.annotation.Dimension;
16 |
17 | /**
18 | * @author oukanggui
19 | * @date 2019/9/26
20 | * 描述:设置菜单条Layout
21 | */
22 | public class MenuLayout extends RelativeLayout {
23 |
24 | /**
25 | * Item根布局
26 | */
27 | private View mItemLayout;
28 | /**
29 | * 左侧图标
30 | */
31 | private ImageView mIconLeft;
32 | /**
33 | * 右侧图标
34 | */
35 | private ImageView mIconRight;
36 | /**
37 | * 左侧文字
38 | */
39 | private TextView mTextLeft;
40 | /**
41 | * 右侧文字
42 | */
43 | private TextView mTextRight;
44 | /**
45 | * 底部分隔线
46 | */
47 | private View mLineView;
48 |
49 | public MenuLayout(Context context) {
50 | super(context);
51 | init(context, null);
52 | }
53 |
54 | public MenuLayout(Context context, AttributeSet attrs) {
55 | super(context, attrs);
56 | init(context, attrs);
57 | }
58 |
59 | public MenuLayout(Context context, AttributeSet attrs, int defStyleAttr) {
60 | super(context, attrs, defStyleAttr);
61 | init(context, attrs);
62 | }
63 |
64 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
65 | public MenuLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
66 | super(context, attrs, defStyleAttr, defStyleRes);
67 | init(context, attrs);
68 | }
69 |
70 | /**
71 | * 初始化,进行控件和属性初始化工作
72 | *
73 | * @param context
74 | * @param attrs
75 | */
76 | private void init(Context context, AttributeSet attrs) {
77 | // MenuLayout作为R.layout.layout_menu_item的父View,最后内部通过addView的形式添加到MenuLayout中
78 | inflate(context, R.layout.layout_menu_item, this);
79 | mIconLeft = findViewById(R.id.menu_iv_left);
80 | mIconRight = findViewById(R.id.menu_iv_right);
81 | mTextLeft = findViewById(R.id.menu_tv_left);
82 | mTextRight = findViewById(R.id.menu_tv_right);
83 | mLineView = findViewById(R.id.menu_line);
84 | mItemLayout = findViewById(R.id.menu_item_layout);
85 | if (attrs != null) {
86 | TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.MenuLayout);
87 | if (typedArray != null) {
88 | int backgroundColorId = typedArray.getColor(R.styleable.MenuLayout_itemBackgroundColor, 0);
89 | setMenuBackgroundColor(backgroundColorId);
90 |
91 | int leftIconResId = typedArray.getResourceId(R.styleable.MenuLayout_leftIcon, 0);
92 | setLeftIcon(leftIconResId);
93 | int rightIconResId = typedArray.getResourceId(R.styleable.MenuLayout_rightIcon, 0);
94 | setRightIcon(rightIconResId);
95 |
96 | String textLeft = typedArray.getString(R.styleable.MenuLayout_leftText);
97 | setLeftText(textLeft);
98 | String textRight = typedArray.getString(R.styleable.MenuLayout_rightText);
99 | setRightText(textRight);
100 |
101 | int leftTextColorId = typedArray.getColor(R.styleable.MenuLayout_leftTextColor, 0);
102 | setLeftTextColor(leftTextColorId);
103 | int rightTextColorId = typedArray.getColor(R.styleable.MenuLayout_rightTextColor, 0);
104 | setRightTextColor(rightTextColorId);
105 |
106 | int spLeftTextSize = typedArray.getInteger(R.styleable.MenuLayout_leftTextSize, 0);
107 | setLeftTextSize(spLeftTextSize);
108 | int spRightTextSize = typedArray.getInteger(R.styleable.MenuLayout_rightTextSize, 0);
109 | setRightTextSize(spRightTextSize);
110 |
111 | boolean isBottomLineVisible = typedArray.getBoolean(R.styleable.MenuLayout_lineVisible, true);
112 | if (mLineView != null) {
113 | mLineView.setVisibility(isBottomLineVisible ? View.VISIBLE : View.GONE);
114 | }
115 |
116 | typedArray.recycle();
117 | }
118 | }
119 | }
120 |
121 | /**
122 | * 获取Menu Item根布局
123 | */
124 | public View getMenuView() {
125 | return mItemLayout;
126 | }
127 |
128 | /**
129 | * 获取左边图标对象
130 | *
131 | * @return
132 | */
133 | public ImageView getIconLeft() {
134 | return mIconLeft;
135 | }
136 |
137 | /**
138 | * 获取右边图标对象
139 | *
140 | * @return
141 | */
142 | public ImageView getIconRight() {
143 | return mIconRight;
144 | }
145 |
146 | /**
147 | * 通过资源id设置左边图片资源
148 | *
149 | * @param resId
150 | */
151 | public void setLeftIcon(int resId) {
152 | if (mIconLeft != null) {
153 | if (resId != 0) {
154 | setViewVisible(mIconLeft);
155 | mIconLeft.setImageResource(resId);
156 | } else {
157 | // 资源id == 0,默认为不配置
158 | setViewInvisible(mIconLeft);
159 | }
160 | }
161 | }
162 |
163 | /**
164 | * 通过Drawable设置左边图片资源
165 | *
166 | * @param drawable
167 | */
168 | public void setLeftIcon(Drawable drawable) {
169 | if (mIconLeft != null) {
170 | setViewVisible(mIconLeft);
171 | mIconLeft.setImageDrawable(drawable);
172 | }
173 | }
174 |
175 | /**
176 | * 通过资源id设置右边图片资源
177 | *
178 | * @param resId
179 | */
180 | public void setRightIcon(int resId) {
181 | if (mIconRight != null) {
182 | if (resId != 0) {
183 | setViewVisible(mIconRight);
184 | mIconRight.setImageResource(resId);
185 | } else {
186 | // 资源id == 0,默认为不配置
187 | setViewInvisible(mIconRight);
188 | }
189 | }
190 | }
191 |
192 | /**
193 | * 通过Drawable设置左边图片资源
194 | *
195 | * @param drawable
196 | */
197 | public void setRightIcon(Drawable drawable) {
198 | if (mIconRight != null) {
199 | setViewVisible(mIconRight);
200 | mIconRight.setImageDrawable(drawable);
201 | }
202 | }
203 |
204 | /**
205 | * 设置左边显示文字
206 | *
207 | * @param resId
208 | */
209 | public void setLeftText(int resId) {
210 | if (mTextLeft != null) {
211 | setViewVisible(mTextLeft);
212 | mTextLeft.setText(resId);
213 | }
214 | }
215 |
216 | /**
217 | * 设置左边显示文字
218 | *
219 | * @param text
220 | */
221 | public void setLeftText(String text) {
222 | if (mTextLeft != null) {
223 | setViewVisible(mTextLeft);
224 | mTextLeft.setText(text);
225 | }
226 | }
227 |
228 | /**
229 | * 设置右边显示文字
230 | *
231 | * @param resId
232 | */
233 | public void setRightText(int resId) {
234 | if (mTextRight != null) {
235 | setViewVisible(mTextRight);
236 | mTextRight.setText(resId);
237 | }
238 | }
239 |
240 | /**
241 | * 设置右边显示文字
242 | *
243 | * @param text
244 | */
245 | public void setRightText(String text) {
246 | if (mTextRight != null) {
247 | setViewVisible(mTextRight);
248 | mTextRight.setText(text);
249 | }
250 | }
251 |
252 | /**
253 | * 设置左边文字颜色
254 | *
255 | * @param colorId
256 | */
257 | public void setLeftTextColor(int colorId) {
258 | // colorId = 0,使用默认颜色
259 | if (mTextLeft != null && colorId != 0) {
260 | mTextLeft.setTextColor(colorId);
261 | }
262 | }
263 |
264 | /**
265 | * 设置右边文字颜色
266 | *
267 | * @param colorId
268 | */
269 | public void setRightTextColor(int colorId) {
270 | // colorId = 0,使用默认颜色
271 | if (mTextRight != null && colorId != 0) {
272 | mTextRight.setTextColor(colorId);
273 | }
274 | }
275 |
276 | /**
277 | * 设置左边字体大小
278 | *
279 | * @param spSize sp
280 | */
281 | public void setLeftTextSize(int spSize) {
282 | // pxSize == 0时,使用默认布局的字体大小
283 | if (mTextLeft != null && spSize > 0) {
284 | mTextLeft.setTextSize(Dimension.SP, spSize);
285 | }
286 | }
287 |
288 | /**
289 | * 设置右边字体大小
290 | *
291 | * @param spSize sp
292 | */
293 | public void setRightTextSize(int spSize) {
294 | // pxSize == 0时,使用默认布局的字体大小
295 | if (mTextRight != null && spSize > 0) {
296 | mTextRight.setTextSize(Dimension.SP, spSize);
297 | }
298 | }
299 |
300 | /**
301 | * 设置背景颜色
302 | *
303 | * @param colorId
304 | */
305 | public void setMenuBackgroundColor(int colorId) {
306 | if (mItemLayout != null && colorId != 0) {
307 | mItemLayout.setBackgroundColor(colorId);
308 | }
309 | }
310 |
311 | /**
312 | * 设置背景
313 | *
314 | * @param resId
315 | */
316 | public void setMenuBackground(int resId) {
317 | if (mItemLayout != null) {
318 | mItemLayout.setBackgroundResource(resId);
319 | }
320 | }
321 |
322 | /**
323 | * 设置View可见
324 | *
325 | * @param view
326 | */
327 | private void setViewVisible(View view) {
328 | if (view != null) {
329 | view.setVisibility(View.VISIBLE);
330 | }
331 | }
332 |
333 | /**
334 | * 设置View不可见(保留原位置信息)
335 | *
336 | * @param view
337 | */
338 | private void setViewInvisible(View view) {
339 | if (view != null) {
340 | view.setVisibility(View.INVISIBLE);
341 | }
342 | }
343 |
344 |
345 | }
346 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/baymax/widget/ratingstar/RatingStarView.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widget.ratingstar;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.CornerPathEffect;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.graphics.RectF;
11 | import android.os.Build;
12 | import android.util.AttributeSet;
13 | import android.util.Log;
14 | import android.view.MotionEvent;
15 | import android.view.View;
16 |
17 | import com.baymax.widget.R;
18 |
19 | import java.util.ArrayList;
20 |
21 | /**
22 | * @author oukanggui
23 | * @date 2019/11/18
24 | * 描述:强大的星星评分控件View,参考优化改造于GitHub https://github.com/everhad/AndroidRatingStar
25 | * RatingStar is specific RatingBar use star drawable as the progress mark.
26 | *
27 | * NOTE:
28 | * Padding will be larger if is {@link #cornerRadius} is set (No exact calc to handle this issue).
29 | */
30 | public class RatingStarView extends View implements View.OnClickListener {
31 | private static final String TAG = "RatingStarView";
32 | private static final int DEFAULT_STAR_HEIGHT = 32;
33 | private float cornerRadius = 4f;
34 | private int starForegroundColor = 0xffED4A4B;
35 | private int strokeColor = 0xffED4A4B;
36 | private int starBackgroundColor = Color.WHITE;
37 | /**
38 | * used to make round smooth star horn
39 | */
40 | private CornerPathEffect pathEffect;
41 | private ArrayList starList;
42 | private float rating;
43 | /**
44 | * expected star number.
45 | */
46 | private int starNum = 5;
47 | /**
48 | * real drawn star number.
49 | */
50 | private int starCount;
51 | /**
52 | * calculated value
53 | */
54 | private float starWidth;
55 | /**
56 | * calculated value
57 | */
58 | private float starHeight;
59 | private float starMargin = 8;
60 | private float strokeWidth = 0;
61 | private boolean drawStrokeForFullStar = false;
62 | private boolean drawStrokeForHalfStar = false;
63 | private boolean drawStrokeForEmptyStar = false;
64 | private boolean enableSelectRating = false;
65 | private boolean onlyHalfStar = false;
66 | private float starThicknessFactor = StarModel.DEFAULT_THICKNESS;
67 | private float dividerX;
68 | private float clickedX, clickedY;
69 | private Paint paint;
70 | private OnClickListener mOuterOnClickListener;
71 |
72 | public RatingStarView(Context context) {
73 | super(context);
74 | init(null, 0);
75 | }
76 |
77 | public RatingStarView(Context context, AttributeSet attrs) {
78 | super(context, attrs);
79 | init(attrs, 0);
80 | }
81 |
82 | public RatingStarView(Context context, AttributeSet attrs, int defStyle) {
83 | super(context, attrs, defStyle);
84 | init(attrs, defStyle);
85 | }
86 | // endregion
87 |
88 | private void init(AttributeSet attrs, int defStyle) {
89 | loadAttributes(attrs, defStyle);
90 |
91 | // init paint
92 | paint = new Paint();
93 | paint.setFlags(Paint.ANTI_ALIAS_FLAG);
94 | paint.setStrokeWidth(strokeWidth);
95 |
96 | // properties
97 | pathEffect = new CornerPathEffect(cornerRadius);
98 |
99 | // click to rate
100 | super.setOnClickListener(this);
101 | }
102 |
103 | private void loadAttributes(AttributeSet attrs, int defStyle) {
104 | final TypedArray a = getContext().obtainStyledAttributes(
105 | attrs, R.styleable.RatingStarView, defStyle, 0);
106 | strokeColor = a.getColor(R.styleable.RatingStarView_rsv_strokeColor, strokeColor);
107 | starForegroundColor = a.getColor(R.styleable.RatingStarView_rsv_starForegroundColor, starForegroundColor);
108 | starBackgroundColor = a.getColor(R.styleable.RatingStarView_rsv_starBackgroundColor, starBackgroundColor);
109 | cornerRadius = a.getDimension(R.styleable.RatingStarView_rsv_cornerRadius, cornerRadius);
110 | starMargin = a.getDimension(R.styleable.RatingStarView_rsv_starMargin, starMargin);
111 | strokeWidth = a.getDimension(R.styleable.RatingStarView_rsv_strokeWidth, strokeWidth);
112 | starThicknessFactor = a.getFloat(R.styleable.RatingStarView_rsv_starThickness, starThicknessFactor);
113 | rating = a.getFloat(R.styleable.RatingStarView_rsv_rating, rating);
114 | starNum = a.getInteger(R.styleable.RatingStarView_rsv_starNum, starNum);
115 | drawStrokeForEmptyStar = a.getBoolean(R.styleable.RatingStarView_rsv_drawStrokeForEmptyStar, drawStrokeForEmptyStar);
116 | drawStrokeForFullStar = a.getBoolean(R.styleable.RatingStarView_rsv_drawStrokeForFullStar, drawStrokeForFullStar);
117 | drawStrokeForFullStar = a.getBoolean(R.styleable.RatingStarView_rsv_drawStrokeForHalfStar, drawStrokeForFullStar);
118 | enableSelectRating = a.getBoolean(R.styleable.RatingStarView_rsv_enableSelectRating, enableSelectRating);
119 | onlyHalfStar = a.getBoolean(R.styleable.RatingStarView_rsv_onlyHalfStar, onlyHalfStar);
120 | a.recycle();
121 | }
122 |
123 | private void setStarBackgroundColor(int color) {
124 | starBackgroundColor = color;
125 | invalidate();
126 | }
127 |
128 | /**
129 | * @see StarModel#setThickness(float)
130 | */
131 | public void setStarThickness(float thicknessFactor) {
132 | for (StarModel star : starList) {
133 | star.setThickness(thicknessFactor);
134 | }
135 | invalidate();
136 | }
137 |
138 | public void setStrokeWidth(float width) {
139 | this.strokeWidth = width;
140 | invalidate();
141 | }
142 |
143 | /**
144 | * Finally progress is: progress = rating / starNum
145 | *
146 | * @param rating should be [0, starNum]
147 | */
148 | public void setRating(float rating) {
149 | if (rating != this.rating) {
150 | this.rating = rating;
151 | invalidate();
152 | }
153 | }
154 |
155 | /**
156 | * Set the smooth of the star's horn.
157 | *
158 | * @param cornerRadius corner circle radius
159 | */
160 | public void setCornerRadius(float cornerRadius) {
161 | this.cornerRadius = cornerRadius;
162 | invalidate();
163 | }
164 |
165 | /**
166 | * The horizontal margin between two stars. The {@link #setCornerRadius} would make extra space
167 | * as it make the star smaller.
168 | *
169 | * @param margin horizontal space
170 | */
171 | public void setStarMargin(int margin) {
172 | this.starMargin = margin;
173 | calcStars();
174 | invalidate();
175 | }
176 |
177 | /**
178 | * How many stars to show, one star means one score = 1f. See {@link #setRating(float)}
179 | * NOTE: The star's height is made by contentHeight by default.So, be sure to has defined the
180 | * correct StarView's height.
181 | *
182 | * @param count star count.
183 | */
184 | public void setStarNum(int count) {
185 | if (starNum != count) {
186 | starNum = count;
187 | calcStars();
188 | invalidate();
189 | }
190 | }
191 |
192 | private void onPaddingChanged() {
193 | int left = getPaddingLeft();
194 | int top = getPaddingTop();
195 | for (StarModel star : starList) {
196 | star.moveStarTo(left, top);
197 | }
198 | }
199 |
200 | public void setDrawStrokeForFullStar(boolean draw) {
201 | drawStrokeForFullStar = draw;
202 | }
203 |
204 | public void setDrawStrokeForEmptyStar(boolean draw) {
205 | drawStrokeForEmptyStar = draw;
206 | }
207 |
208 | /**
209 | * Create all stars data, according to the contentWidth/contentHeight.
210 | */
211 | private void calcStars() {
212 | int paddingLeft = getPaddingLeft();
213 | int paddingTop = getPaddingTop();
214 | int paddingRight = getPaddingRight();
215 | int paddingBottom = getPaddingBottom();
216 | int contentWidth = getWidth() - paddingLeft - paddingRight;
217 | int contentHeight = getHeight() - paddingTop - paddingBottom;
218 |
219 | int left = paddingLeft;
220 | int top = paddingTop;
221 |
222 | // according to the View's height , make star height.
223 | int starHeight = contentHeight;
224 | if (contentHeight > contentWidth) {
225 | starHeight = contentWidth;
226 | }
227 |
228 | if (starHeight <= 0) {
229 | return;
230 | }
231 | float startWidth = StarModel.getStarWidth(starHeight);
232 |
233 | // starCount * startWidth + (starCount - 1) * starMargin = contentWidth
234 | int starCount = (int) ((contentWidth + starMargin) / (startWidth + starMargin));
235 | if (starCount > starNum) {
236 | starCount = starNum;
237 | }
238 |
239 | this.starHeight = starHeight;
240 | this.starWidth = startWidth;
241 | Log.d(TAG, "drawing starCount = " + starCount + ", contentWidth = " + contentWidth
242 | + ", startWidth = " + startWidth + ", starHeight = " + starHeight);
243 |
244 | starList = new ArrayList<>(starCount);
245 |
246 | for (int i = 0; i < starCount; i++) {
247 | StarModel star = new StarModel(starThicknessFactor);
248 | starList.add(star);
249 | star.setDrawingOuterRect(left, top, starHeight);
250 | left += startWidth + 0.5f + starMargin;
251 | }
252 |
253 | this.starCount = starCount;
254 | this.starWidth = startWidth;
255 | this.starHeight = starHeight;
256 | }
257 |
258 | @Override
259 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
260 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
261 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
262 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
263 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
264 |
265 | float width;
266 | int height; // must have height
267 |
268 | if (heightMode == MeasureSpec.EXACTLY) {
269 | // Parent has told us how big to be. So be it.
270 | height = heightSize;
271 | } else {
272 | height = DEFAULT_STAR_HEIGHT;
273 | if (heightMode == MeasureSpec.AT_MOST) {
274 | height = Math.min(height, heightSize);
275 | }
276 | }
277 |
278 | float starHeight = height - getPaddingBottom() - getPaddingTop();
279 |
280 | if (widthMode == MeasureSpec.EXACTLY) {
281 | // Parent has told us how big to be. So be it.
282 | width = widthSize;
283 | } else {
284 | // get the perfect width
285 | width = getPaddingLeft() + getPaddingRight();
286 | if (starNum > 0) {
287 | if (starHeight > 0) {
288 | width += starMargin * (starNum - 1);
289 | width += StarModel.getStarWidth(starHeight) * starNum;
290 | }
291 | }
292 |
293 | if (widthMode == MeasureSpec.AT_MOST) {
294 | width = Math.min(widthSize, width);
295 | }
296 | }
297 |
298 | Log.d(TAG, "[onMeasure] width = " + width + ", pLeft = " + getPaddingLeft()
299 | + ", pRight = " + getPaddingRight() + ", starMargin = " + starMargin
300 | + ", starHeight = " + starHeight + ", starWidth = " + StarModel.getStarWidth(starHeight));
301 |
302 | int widthInt = (int) (width);
303 | if (widthInt < width) {
304 | widthInt++;
305 | }
306 |
307 | setMeasuredDimension(widthInt, height);
308 | }
309 |
310 | @Override
311 | protected void onDraw(Canvas canvas) {
312 | super.onDraw(canvas);
313 |
314 | if (starList == null) {
315 | calcStars();
316 | }
317 |
318 | if (starList == null || starList.size() == 0) {
319 | return;
320 | }
321 |
322 | for (int i = 0; i < starList.size(); i++) {
323 | if (rating >= i + 1) {
324 | // 满星星
325 | drawFullStar(starList.get(i), canvas);
326 | } else {
327 | float decimal = rating - i;
328 | if (decimal > 0) {
329 | if (onlyHalfStar) {
330 | decimal = 0.5f;
331 | }
332 | //小数星星
333 | drawPartialStar(starList.get(i), canvas, decimal);
334 | } else {
335 | //空星星
336 | drawEmptyStar(starList.get(i), canvas);
337 | }
338 | }
339 | }
340 | }
341 |
342 | private void drawFullStar(StarModel star, Canvas canvas) {
343 | drawSolidStar(star, canvas, starForegroundColor);
344 | if (drawStrokeForFullStar) {
345 | drawStarStroke(star, canvas);
346 | }
347 | }
348 |
349 | private void drawEmptyStar(StarModel star, Canvas canvas) {
350 | drawSolidStar(star, canvas, starBackgroundColor);
351 | if (drawStrokeForEmptyStar) {
352 | drawStarStroke(star, canvas);
353 | }
354 | }
355 |
356 | @Override
357 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
358 | super.onSizeChanged(w, h, oldw, oldh);
359 | if (h != oldh) {
360 | calcStars();
361 | }
362 | }
363 |
364 | private void drawPartialStar(StarModel star, Canvas canvas, float percent) {
365 | Log.d(TAG, "drawPartialStar percent = " + percent);
366 | if (percent <= 0) {
367 | drawEmptyStar(star, canvas);
368 | return;
369 | } else if (percent >= 1) {
370 | drawFullStar(star, canvas);
371 | return;
372 | }
373 |
374 | // layer 1--空星星
375 | drawSolidStar(star, canvas, starBackgroundColor);
376 |
377 | float dividerX = star.getOuterRect().left + star.getOuterRect().width() * percent;
378 | this.dividerX = dividerX;
379 |
380 | // layer 2 单独定义一个与星星等大的图层画布图层,后面的所用操作均在改图层进行绘制,直到调用canvas.restore()
381 | RectF r = star.getOuterRect();
382 | //canvas.saveLayerAlpha(r.left, r.top, r.right, r.bottom, 0xff, CLIP_SAVE_FLAG);
383 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
384 | canvas.saveLayerAlpha(r.left, r.top, r.right, r.bottom, 0xff);
385 | } else {
386 | canvas.saveLayerAlpha(r.left, r.top, r.right, r.bottom, 0xff, Canvas.ALL_SAVE_FLAG);
387 | }
388 | RectF clip = new RectF(star.getOuterRect());
389 | clip.right = dividerX;
390 | canvas.clipRect(clip);
391 | drawSolidStar(star, canvas, starForegroundColor);
392 | canvas.restore();
393 |
394 | // layer 1
395 | if (drawStrokeForHalfStar) {
396 | drawStarStroke(star, canvas);
397 | }
398 | }
399 |
400 | private void drawSolidStar(StarModel star, Canvas canvas, int fillColor) {
401 | paint.setStyle(Paint.Style.FILL_AND_STROKE);
402 | paint.setColor(fillColor);
403 | paint.setPathEffect(pathEffect);
404 |
405 | VertexF prev = star.getVertex(1);
406 | Path path = new Path();
407 | path.moveTo(prev.x, prev.y);
408 |
409 | for (int i = 0; i < 5; i++) {
410 | VertexF next = prev.next;
411 | path.lineTo(next.x, next.y);
412 | path.lineTo(next.next.x, next.next.y);
413 | path.lineTo(next.next.x, next.next.y);
414 | prev = next.next;
415 | }
416 | canvas.drawPath(path, paint);
417 | }
418 |
419 | private void drawStarStroke(StarModel star, Canvas canvas) {
420 | paint.setStyle(Paint.Style.STROKE);
421 | paint.setColor(strokeColor);
422 | paint.setPathEffect(pathEffect);
423 | VertexF prev = star.getVertex(1);
424 | Path path = new Path();
425 |
426 | for (int i = 0; i < 5; i++) {
427 | path.rewind();
428 | path.moveTo(prev.x, prev.y);
429 |
430 | VertexF next = prev.next;
431 |
432 | path.lineTo(next.x, next.y);
433 | path.lineTo(next.next.x, next.next.y);
434 | path.lineTo(next.next.x, next.next.y);
435 |
436 | canvas.drawPath(path, paint);
437 | prev = next.next;
438 | }
439 | }
440 |
441 | @Override
442 | public boolean onTouchEvent(MotionEvent event) {
443 | if (event.getAction() == MotionEvent.ACTION_DOWN) {
444 | clickedX = event.getX();
445 | clickedY = event.getY();
446 | }
447 | return super.onTouchEvent(event);
448 | }
449 |
450 | @Override
451 | public void setOnClickListener(OnClickListener l) {
452 | mOuterOnClickListener = l;
453 | }
454 |
455 | @Override
456 | public void onClick(View v) {
457 | if (mOuterOnClickListener != null) {
458 | mOuterOnClickListener.onClick(v);
459 | }
460 |
461 | if (enableSelectRating) {
462 | changeRatingByClick();
463 | }
464 | }
465 |
466 | private void changeRatingByClick() {
467 | int paddingTop = getPaddingTop();
468 | if (clickedY < paddingTop || clickedY > paddingTop + starHeight) {
469 | return;
470 | }
471 |
472 | int paddingLeft = getPaddingLeft();
473 | float starWidth = this.starWidth;
474 | float starMargin = this.starMargin;
475 |
476 | float left = paddingLeft;
477 | for (int i = 1; i <= starCount; i++) {
478 | float right = left + starWidth;
479 | if (clickedX >= left && clickedX <= right) {
480 | if (this.rating == i) {
481 | setRating(0);
482 | } else {
483 | setRating(i);
484 | }
485 | break;
486 | }
487 |
488 | left += (starWidth + starMargin);
489 | }
490 | }
491 |
492 | public float getRating() {
493 | return rating;
494 | }
495 | }
496 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/baymax/widget/ExpandLayout.java:
--------------------------------------------------------------------------------
1 | package com.baymax.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.os.Build;
6 | import android.text.Layout;
7 | import android.text.StaticLayout;
8 | import android.text.TextPaint;
9 | import android.text.TextUtils;
10 | import android.util.AttributeSet;
11 | import android.util.Log;
12 | import android.util.TypedValue;
13 | import android.view.View;
14 | import android.view.ViewTreeObserver;
15 | import android.widget.ImageView;
16 | import android.widget.LinearLayout;
17 | import android.widget.RelativeLayout;
18 | import android.widget.TextView;
19 |
20 | /**
21 | * @author oukanggui
22 | * @date 2019/10/18
23 | * 描述:展开和收起布局,在TextView最后一行添加“图片+文字样式”展开和收起布局,而且是右对齐
24 | * 优化改造于:https://blog.csdn.net/u014620028/article/details/100145527
25 | */
26 | public class ExpandLayout extends RelativeLayout implements View.OnClickListener {
27 | private static final String TAG = ExpandLayout.class.getSimpleName();
28 | private static final int STYLE_DEFAULT = 0;
29 | private static final int STYLE_ICON = 1;
30 | private static final int STYLE_TEXT = 2;
31 | private Context mContext;
32 | private View mRootView;
33 | private TextView mTvContent;
34 | private LinearLayout mLayoutExpandMore;
35 | private ImageView mIconExpand;
36 | private TextView mTvExpand;
37 | private int mMeasuredWidth;
38 | /**
39 | * 辅助TextView,保证末尾图标和文字与内容文字居中显示
40 | */
41 | private TextView mTvExpandHelper;
42 |
43 | private int mExpandIconResId;
44 | private int mCollapseIconResId;
45 |
46 | private String mExpandMoreStr;
47 | private String mCollapseLessStr;
48 |
49 | private int mMaxLines = 2;
50 |
51 | private int mContentTextSize;
52 | private int mExpandTextSize;
53 |
54 | /**
55 | * 是否展开
56 | */
57 | private boolean mIsExpand = false;
58 |
59 | /**
60 | * 原内容文本
61 | */
62 | private String mOriginContentStr;
63 | /**
64 | * 缩略后展示的文本
65 | */
66 | private CharSequence mEllipsizeStr;
67 |
68 | /**
69 | * 主文字颜色
70 | */
71 | private int mContentTextColor;
72 | /**
73 | * 展开/收起文字颜色
74 | */
75 | private int mExpandTextColor;
76 |
77 | /**
78 | * 样式,默认为图标+文字样式
79 | */
80 | private int mExpandStyle = STYLE_DEFAULT;
81 |
82 | /**
83 | * 展开/收缩布局对应图标的宽度,默认布局是15dp
84 | */
85 | private int mExpandIconWidth = 15;
86 |
87 | /**
88 | * 缩略文本展示时与展开/搜索布局的间距,默认是20dp
89 | */
90 | private int mSpaceMargin = 20;
91 |
92 | /**
93 | * 文本显示的lineSpacingExtra,对应于TextView的lineSpacingExtra属性
94 | */
95 | private float mLineSpacingExtra = 0.0f;
96 |
97 | /**
98 | * 文本显示的lineSpacingMultiplier,对应于TextView的lineSpacingMultiplier属性
99 | */
100 | private float mLineSpacingMultiplier = 1.0f;
101 |
102 |
103 | private OnExpandStateChangeListener mOnExpandStateChangeListener;
104 |
105 | /**
106 | * 监听器
107 | */
108 | public interface OnExpandStateChangeListener {
109 | /**
110 | * 展开时回调
111 | */
112 | void onExpand();
113 |
114 | /**
115 | * 收起时回调
116 | */
117 | void onCollapse();
118 |
119 | }
120 |
121 | public ExpandLayout(Context context) {
122 | this(context, null);
123 | }
124 |
125 | public ExpandLayout(Context context, AttributeSet attrs) {
126 | this(context, attrs, 0);
127 | }
128 |
129 | public ExpandLayout(Context context, AttributeSet attrs, int defStyleAttr) {
130 | super(context, attrs, defStyleAttr);
131 | mContext = context;
132 | initAttributeSet(context, attrs);
133 | initView();
134 | }
135 |
136 | /**
137 | * 初始化自定义属性
138 | * @param context
139 | * @param attrs
140 | */
141 | private void initAttributeSet(Context context, AttributeSet attrs){
142 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandLayout);
143 | if (ta != null) {
144 | mMaxLines = ta.getInt(R.styleable.ExpandLayout_maxLines, 2);
145 | mExpandIconResId = ta.getResourceId(R.styleable.ExpandLayout_expandIconResId, 0);
146 | mCollapseIconResId = ta.getResourceId(R.styleable.ExpandLayout_collapseIconResId, 0);
147 | mExpandMoreStr = ta.getString(R.styleable.ExpandLayout_expandMoreText);
148 | mCollapseLessStr = ta.getString(R.styleable.ExpandLayout_collapseLessText);
149 | mContentTextSize = ta.getDimensionPixelSize(R.styleable.ExpandLayout_contentTextSize, sp2px(context, 14));
150 | mContentTextColor = ta.getColor(R.styleable.ExpandLayout_contentTextColor, 0);
151 | mExpandTextSize = ta.getDimensionPixelSize(R.styleable.ExpandLayout_expandTextSize, sp2px(context, 14));
152 | mExpandTextColor = ta.getColor(R.styleable.ExpandLayout_expandTextColor, 0);
153 | mExpandStyle = ta.getInt(R.styleable.ExpandLayout_expandStyle, STYLE_DEFAULT);
154 | mExpandIconWidth = ta.getDimensionPixelSize(R.styleable.ExpandLayout_expandIconWidth, dp2px(context, 15));
155 | mSpaceMargin = ta.getDimensionPixelSize(R.styleable.ExpandLayout_spaceMargin, dp2px(context, 20));
156 | mLineSpacingExtra = ta.getDimensionPixelSize(R.styleable.ExpandLayout_lineSpacingExtra, 0);
157 | mLineSpacingMultiplier = ta.getFloat(R.styleable.ExpandLayout_lineSpacingMultiplier, 1.0f);
158 | ta.recycle();
159 | }
160 | // mMaxLines应该保证大于等于1
161 | if (mMaxLines < 1) {
162 | mMaxLines = 1;
163 | }
164 | }
165 |
166 | /**
167 | * 初始化View
168 | */
169 | private void initView() {
170 | mRootView = inflate(mContext, R.layout.layout_expand, this);
171 | mTvContent = findViewById(R.id.expand_content_tv);
172 | mLayoutExpandMore = findViewById(R.id.expand_ll);
173 | mIconExpand = findViewById(R.id.expand_iv);
174 | mTvExpand = findViewById(R.id.expand_tv);
175 | mTvExpandHelper = findViewById(R.id.expand_helper_tv);
176 |
177 | mTvExpand.setText(mExpandMoreStr);
178 | mTvContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, mContentTextSize);
179 | // 辅助TextView,与内容TextView大小相等,保证末尾图标和文字与内容文字居中显示
180 | mTvExpandHelper.setTextSize(TypedValue.COMPLEX_UNIT_PX, mContentTextSize);
181 | mTvExpand.setTextSize(TypedValue.COMPLEX_UNIT_PX, mExpandTextSize);
182 | mTvContent.setLineSpacing(mLineSpacingExtra, mLineSpacingMultiplier);
183 | mTvExpandHelper.setLineSpacing(mLineSpacingExtra, mLineSpacingMultiplier);
184 | mTvExpand.setLineSpacing(mLineSpacingExtra, mLineSpacingMultiplier);
185 | //默认设置展开图标
186 | setExpandMoreIcon(mExpandIconResId);
187 | setContentTextColor(mContentTextColor);
188 | setExpandTextColor(mExpandTextColor);
189 | switch (mExpandStyle) {
190 | case STYLE_ICON:
191 | mIconExpand.setVisibility(VISIBLE);
192 | mTvExpand.setVisibility(GONE);
193 | break;
194 | case STYLE_TEXT:
195 | mIconExpand.setVisibility(GONE);
196 | mTvExpand.setVisibility(VISIBLE);
197 | break;
198 | default:
199 | mIconExpand.setVisibility(VISIBLE);
200 | mTvExpand.setVisibility(VISIBLE);
201 | break;
202 | }
203 | }
204 |
205 | @Override
206 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
207 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
208 | Log.d(TAG, "onMeasure,measureWidth = " + getMeasuredWidth());
209 | if (mMeasuredWidth <= 0 && getMeasuredWidth() > 0) {
210 | mMeasuredWidth = getMeasuredWidth();
211 | measureEllipsizeText(mMeasuredWidth);
212 | }
213 | }
214 |
215 | /**
216 | * 设置文本内容
217 | *
218 | * @param contentStr
219 | */
220 | public void setContent(String contentStr) {
221 | setContent(contentStr, null);
222 | }
223 |
224 | /**
225 | * 设置文本内容
226 | *
227 | * @param contentStr
228 | * @param onExpandStateChangeListener 状态回调监听器
229 | */
230 | public void setContent(String contentStr, final OnExpandStateChangeListener onExpandStateChangeListener) {
231 | if (TextUtils.isEmpty(contentStr) || mRootView == null) {
232 | return;
233 | }
234 | mOriginContentStr = contentStr;
235 | mOnExpandStateChangeListener = onExpandStateChangeListener;
236 | // 此处需要先设置mTvContent的text属性,防止在列表中,由于没有获取到控件宽度mMeasuredWidth,先执行onMeasure方法测量时,导致文本只能显示一行的问题
237 | // 提前设置好text,再执行onMeasure,则没有该问题
238 | mTvContent.setMaxLines(mMaxLines);
239 | mTvContent.setText(mOriginContentStr);
240 | // 获取文字的宽度
241 | if (mMeasuredWidth <= 0) {
242 | Log.d(TAG, "宽度尚未获取到,第一次加载");
243 | getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
244 | @Override
245 | public void onGlobalLayout() {
246 | // 用完后立即移除监听,防止多次回调的问题
247 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
248 | getViewTreeObserver().removeOnGlobalLayoutListener(this);
249 | } else {
250 | getViewTreeObserver().removeGlobalOnLayoutListener(this);
251 | }
252 | mMeasuredWidth = getMeasuredWidth();
253 | Log.d(TAG, "onGlobalLayout,控件宽度 = " + mMeasuredWidth);
254 | measureEllipsizeText(mMeasuredWidth);
255 | }
256 | });
257 | } else {
258 | Log.d(TAG, "宽度已获取到,非第一次加载");
259 | measureEllipsizeText(mMeasuredWidth);
260 | }
261 | }
262 |
263 | /**
264 | * 处理文本分行
265 | *
266 | * @param lineWidth
267 | */
268 | private void measureEllipsizeText(int lineWidth) {
269 | if (TextUtils.isEmpty(mOriginContentStr)) {
270 | return;
271 | }
272 | handleMeasureEllipsizeText(lineWidth);
273 | }
274 |
275 | /**
276 | * 使用StaticLayout处理文本分行和布局
277 | *
278 | * @param lineWidth 文本(布局)宽度
279 | */
280 | private void handleMeasureEllipsizeText(int lineWidth) {
281 | TextPaint textPaint = mTvContent.getPaint();
282 | StaticLayout staticLayout = new StaticLayout(mOriginContentStr, textPaint, lineWidth, Layout.Alignment.ALIGN_NORMAL, mLineSpacingMultiplier, mLineSpacingExtra, false);
283 | int lineCount = staticLayout.getLineCount();
284 | if (lineCount <= mMaxLines) {
285 | // 不足最大行数,直接设置文本
286 | //少于最小展示行数,不再展示更多相关布局
287 | mEllipsizeStr = mOriginContentStr;
288 | mLayoutExpandMore.setVisibility(View.GONE);
289 | mTvContent.setMaxLines(Integer.MAX_VALUE);
290 | mTvContent.setText(mOriginContentStr);
291 | } else {
292 | // 超出最大行数
293 | mRootView.setOnClickListener(this);
294 | mLayoutExpandMore.setVisibility(View.VISIBLE);
295 | // Step1:第mMinLineNum行的处理
296 | handleEllipsizeString(staticLayout, lineWidth);
297 | // Step2:最后一行的处理
298 | handleLastLine(staticLayout, lineWidth);
299 | if (mIsExpand) {
300 | expand();
301 | } else {
302 | // 默认收缩
303 | collapse();
304 | }
305 | }
306 | }
307 |
308 | /**
309 | * 处理缩略的字符串
310 | *
311 | * @param staticLayout
312 | * @param lineWidth
313 | */
314 | private void handleEllipsizeString(StaticLayout staticLayout, int lineWidth) {
315 | if (staticLayout == null) {
316 | return;
317 | }
318 | TextPaint textPaint = mTvContent.getPaint();
319 | // 获取到第mMinLineNum行的起始和结束位置
320 | int startPos = staticLayout.getLineStart(mMaxLines - 1);
321 | int endPos = staticLayout.getLineEnd(mMaxLines - 1);
322 | Log.d(TAG, "startPos = " + startPos);
323 | Log.d(TAG, "endPos = " + endPos);
324 | // 修正,防止取子串越界
325 | if (startPos < 0) {
326 | startPos = 0;
327 | }
328 | if (endPos > mOriginContentStr.length()) {
329 | endPos = mOriginContentStr.length();
330 | }
331 | if (startPos > endPos) {
332 | startPos = endPos;
333 | }
334 | String lineContent = mOriginContentStr.substring(startPos, endPos);
335 | float textLength = 0f;
336 | if (lineContent != null) {
337 | textLength = textPaint.measureText(lineContent);
338 | }
339 | Log.d(TAG, "第" + mMaxLines + "行 = " + lineContent);
340 | Log.d(TAG, "第" + mMaxLines + "行 文本长度 = " + textLength);
341 |
342 | String strEllipsizeMark = "...";
343 | // 展开控件需要预留的长度,预留宽度:"..." + 展开布局与文本间距 +图标长度 + 展开文本长度
344 | float reservedWidth = mSpaceMargin + textPaint.measureText(strEllipsizeMark) + getExpandLayoutReservedWidth();
345 | Log.d(TAG, "需要预留的长度 = " + reservedWidth);
346 | int correctEndPos = endPos;
347 | if (reservedWidth + textLength > lineWidth) {
348 | // 空间不够,需要按比例截取最后一行的字符串,以确保展示的最后一行文本不会与可展开布局重叠
349 | float exceedSpace = reservedWidth + textLength - lineWidth;
350 | if (textLength != 0) {
351 | correctEndPos = endPos - (int) ((exceedSpace / textLength) * 1.0f * (endPos - startPos));
352 | }
353 | }
354 | Log.d(TAG, "correctEndPos = " + correctEndPos);
355 | String ellipsizeStr = mOriginContentStr.substring(0, correctEndPos);
356 | mEllipsizeStr = removeEndLineBreak(ellipsizeStr) + strEllipsizeMark;
357 | }
358 |
359 | /**
360 | * 处理最后一行文本
361 | *
362 | * @param staticLayout
363 | * @param lineWidth
364 | */
365 | private void handleLastLine(StaticLayout staticLayout, int lineWidth) {
366 | if (staticLayout == null) {
367 | return;
368 | }
369 | int lineCount = staticLayout.getLineCount();
370 | if (lineCount < 1) {
371 | return;
372 | }
373 | int startPos = staticLayout.getLineStart(lineCount - 1);
374 | int endPos = staticLayout.getLineEnd(lineCount - 1);
375 | Log.d(TAG, "最后一行 startPos = " + startPos);
376 | Log.d(TAG, "最后一行 endPos = " + endPos);
377 | // 修正,防止取子串越界
378 | if (startPos < 0) {
379 | startPos = 0;
380 | }
381 | if (endPos > mOriginContentStr.length()) {
382 | endPos = mOriginContentStr.length();
383 | }
384 | if (startPos > endPos) {
385 | startPos = endPos;
386 | }
387 | String lastLineContent = mOriginContentStr.substring(startPos, endPos);
388 | Log.d(TAG, "最后一行 内容 = " + lastLineContent);
389 | float textLength = 0f;
390 | TextPaint textPaint = mTvContent.getPaint();
391 | if (lastLineContent != null) {
392 | textLength = textPaint.measureText(lastLineContent);
393 | }
394 | Log.d(TAG, "最后一行 文本长度 = " + textLength);
395 | float reservedWidth = getExpandLayoutReservedWidth();
396 | if (textLength + reservedWidth > lineWidth) {
397 | //文字宽度+展开布局的宽度 > 一行最大展示宽度
398 | //换行展示“收起”按钮及文字
399 | mOriginContentStr += "\n";
400 | }
401 | }
402 |
403 | /**
404 | * 获取展开布局的展开收缩控件的预留宽度
405 | *
406 | * @return value = 图标长度 + 展开提示文本长度
407 | */
408 | private float getExpandLayoutReservedWidth() {
409 | int iconWidth = 0;
410 | if (mExpandStyle == STYLE_DEFAULT || mExpandStyle == STYLE_ICON) {
411 | // ll布局中的内容,图标iv的宽是15dp,参考布局
412 | iconWidth = mExpandIconWidth;
413 | }
414 | float textWidth = 0f;
415 | if (mExpandStyle == STYLE_DEFAULT || mExpandStyle == STYLE_TEXT) {
416 | textWidth = mTvExpand.getPaint().measureText(mExpandMoreStr);
417 | }
418 | // 展开控件需要预留的长度
419 | return iconWidth + textWidth;
420 | }
421 |
422 | /**
423 | * 清除行末换行符
424 | *
425 | * @param text
426 | * @return
427 | */
428 | private String removeEndLineBreak(CharSequence text) {
429 | if (text == null) {
430 | return null;
431 | }
432 | String str = text.toString();
433 | if (str.endsWith("\n")) {
434 | str = str.substring(0, str.length() - 1);
435 | }
436 | return str;
437 | }
438 |
439 | /**
440 | * 设置内容文字颜色
441 | */
442 | public void setContentTextColor(int colorId) {
443 | if (colorId != 0) {
444 | mContentTextColor = colorId;
445 | mTvContent.setTextColor(colorId);
446 | }
447 | }
448 |
449 | /**
450 | * 设置更多/收起文字颜色
451 | */
452 | public void setExpandTextColor(int colorId) {
453 | if (colorId != 0) {
454 | mExpandTextColor = colorId;
455 | mTvExpand.setTextColor(colorId);
456 | }
457 | }
458 |
459 | /**
460 | * 设置展开更多图标
461 | *
462 | * @param resId
463 | */
464 | public void setExpandMoreIcon(int resId) {
465 | if (resId != 0) {
466 | mExpandIconResId = resId;
467 | // 当前处于收缩状态时,立即更新图标
468 | if (!mIsExpand) {
469 | mIconExpand.setImageResource(resId);
470 | }
471 | }
472 | }
473 |
474 | /**
475 | * 设置收缩图标
476 | *
477 | * @param resId
478 | */
479 | public void setCollapseLessIcon(int resId) {
480 | if (resId != 0) {
481 | mCollapseIconResId = resId;
482 | // 当前处于展开状态时,立即更新图标
483 | if (mIsExpand) {
484 | mIconExpand.setImageResource(resId);
485 | }
486 | }
487 | }
488 |
489 | /**
490 | * 展开
491 | */
492 | public void expand() {
493 | setIsExpand(true);
494 | mTvContent.setMaxLines(Integer.MAX_VALUE);
495 | mTvContent.setText(mOriginContentStr);
496 | mTvExpand.setText(mCollapseLessStr);
497 | if (mCollapseIconResId != 0) {
498 | mIconExpand.setImageResource(mCollapseIconResId);
499 | }
500 | }
501 |
502 | /**
503 | * 收起
504 | */
505 | public void collapse() {
506 | setIsExpand(false);
507 | mTvContent.setMaxLines(Integer.MAX_VALUE);
508 | mTvContent.setText(mEllipsizeStr);
509 | mTvExpand.setText(mExpandMoreStr);
510 | if (mExpandIconResId != 0) {
511 | mIconExpand.setImageResource(mExpandIconResId);
512 | }
513 | }
514 |
515 | public int getLineCount() {
516 | return mTvContent == null ? 0 : mTvContent.getLineCount();
517 | }
518 |
519 | public void setShrinkLines(int shrinkLines) {
520 | mMaxLines = shrinkLines;
521 | }
522 |
523 | public void setIsExpand(boolean isExpand) {
524 | mIsExpand = isExpand;
525 | }
526 |
527 | public boolean isExpand() {
528 | return mIsExpand;
529 | }
530 |
531 | /**
532 | * 转换dp为px
533 | *
534 | * @param context
535 | * @param dipValue
536 | * @return
537 | */
538 | private int dp2px(Context context, float dipValue) {
539 | final float scale = context.getResources().getDisplayMetrics().density;
540 | return (int) (dipValue * scale + 0.5f);
541 | }
542 |
543 | /**
544 | * 转换sp为px
545 | *
546 | * @param context
547 | * @param spValue
548 | * @return
549 | */
550 | public int sp2px(Context context, float spValue) {
551 | float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
552 | return (int) (spValue * fontScale + 0.5f);
553 | }
554 |
555 | @Override
556 | public void onClick(View v) {
557 | if (!mIsExpand) {
558 | //之前是收缩状态,点击后展开
559 | expand();
560 | if (mOnExpandStateChangeListener != null) {
561 | mOnExpandStateChangeListener.onExpand();
562 | }
563 | } else {
564 | //之前是展开状态,点击后收缩
565 | collapse();
566 | if (mOnExpandStateChangeListener != null) {
567 | mOnExpandStateChangeListener.onCollapse();
568 | }
569 | }
570 | }
571 | }
--------------------------------------------------------------------------------
/.idea/dbnavigator.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 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
--------------------------------------------------------------------------------