├── .gitignore
├── README.md
├── Wechat.jpeg
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── huichongzi
│ │ └── fastwidget4android
│ │ ├── MainActivity.java
│ │ ├── activity
│ │ ├── BannerViewActivity.java
│ │ ├── BlindsListViewActivity.java
│ │ ├── BookActivity.java
│ │ ├── CurveFolioListViewActivity.java
│ │ ├── DragGridViewActivity.java
│ │ ├── FloatInputActivity.kt
│ │ ├── FloodAndSpreadActivity.kt
│ │ ├── FolioListViewActivity.java
│ │ ├── ScrollFoldActivity.java
│ │ ├── SpinnerActivity.kt
│ │ ├── SwipeMenuListActivity.java
│ │ ├── WaveBallProgressActivity.java
│ │ └── WheelActivity.java
│ │ ├── adapter
│ │ ├── AnimationListAdapter.java
│ │ └── ScrollFoldAdapter.java
│ │ ├── utils
│ │ ├── BezierCurve.kt
│ │ └── DisplayUtils.java
│ │ └── widget
│ │ ├── AnimationListView.java
│ │ ├── AnimationViewInterface.java
│ │ ├── BannerView.java
│ │ ├── BlindsView.java
│ │ ├── BookPageView.java
│ │ ├── CurveFolioView.java
│ │ ├── DragGridView.java
│ │ ├── FloatInputView.kt
│ │ ├── FloatSideBall.java
│ │ ├── FolioView.java
│ │ ├── OnAnimationViewListener.java
│ │ ├── RotateView.java
│ │ ├── SwipeMenuTouchListener.java
│ │ ├── SwipeMenuViewHolder.java
│ │ └── WaveBallProgress.java
│ └── res
│ ├── drawable-xhdpi
│ ├── a.png
│ ├── arrow.webp
│ ├── b.png
│ ├── banner_a.jpg
│ ├── banner_b.jpg
│ ├── banner_c.jpeg
│ ├── banner_d.jpeg
│ ├── banner_e.jpeg
│ ├── bg1.jpg
│ ├── c.png
│ ├── camera_bottom.png
│ ├── camera_top.png
│ ├── d.png
│ ├── e.png
│ ├── f.png
│ ├── floating_add_left.png
│ ├── floating_add_move.png
│ ├── floating_add_right.png
│ ├── floating_bg_big.png
│ ├── floating_closed_normal.png
│ ├── g.png
│ ├── girl1.jpg
│ ├── h.png
│ ├── i.png
│ ├── j.png
│ ├── market_a.jpeg
│ ├── market_b.jpg
│ ├── market_c.jpg
│ ├── market_d.jpg
│ ├── page_a.jpg
│ ├── page_b.jpg
│ └── spinner_bg.9.png
│ ├── layout
│ ├── activity_main.xml
│ ├── animation_listview_item.xml
│ ├── banner_view_act.xml
│ ├── blinds_listview_act.xml
│ ├── book_act.xml
│ ├── drag_gridview_act.xml
│ ├── float_input_act.xml
│ ├── float_input_default_layout.xml
│ ├── flood_and_spread_activity.xml
│ ├── folio_listview_act.xml
│ ├── recyclerview_activity.xml
│ ├── scroll_fold_list_footer.xml
│ ├── scroll_fold_list_item.xml
│ ├── spinner_activity.xml
│ ├── spinner_item.xml
│ ├── spinner_layout.xml
│ ├── swipe_menu_list_item.xml
│ ├── test_act.xml
│ └── wave_ball_progress_act.xml
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ids.xml
│ ├── strings.xml
│ └── styles.xml
├── blind.gif
├── build.gradle
├── float_side_ball.gif
├── flood-and-spread.gif
├── folio.gif
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── qrinfo-BennuCTech-scan.jpg
├── scroll-fold-list-iloveimg-compressed.gif
├── settings.gradle
├── swipe_menu_recyclerview.gif
├── wave-ball-progress.gif
└── wheel.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/
5 | .DS_Store
6 | /build
7 | /captures
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This library include many amazing widgets or views.
2 |
3 | # WaveBallProgress
4 | It is a ProgressBar witch\`s shape is glass ball with water in it. The line of water represents the value of progress. And when the value of progress changed, the water will fluctuate. Show in the gif below.
5 |
6 | 
7 |
8 | # FloodAndSpreadActivity
9 | It is a complex animation which include two step: flood and spead. This can be used as the transition. Show in the gif below.
10 |
11 | 
12 |
13 | # BlindsView
14 | It is a view which can show an animation as blind. You can use it in AnimationListView, such as the used in BlindsListViewActivity. Show in the gif below.
15 |
16 | 
17 |
18 | # FolioView
19 | It is a view which can show an animation as folio. You can use it in AnimationListView, such as the used in FolioListViewActivity. Show in the gif below.
20 |
21 | 
22 |
23 | # ScrollFoldList
24 | It is a list view which can highlight the first item. If scroll the list, the first item will fold and the second item will spread. Show in the gif below.
25 |
26 | 
27 |
28 | # SwipeMenuTouchListener&SwipeMenuViewHolder
29 | It is a simple solution about swipe menu in RecyclerView. You can refer to the class "SwipeMenuListActivity" for how to use. Show in the gif below.
30 |
31 | 
32 |
33 | # Contact Me
34 | 
35 |
--------------------------------------------------------------------------------
/Wechat.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/Wechat.jpeg
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 26
7 | buildToolsVersion "26.0.0"
8 |
9 | defaultConfig {
10 | applicationId "com.huichongzi.fastwidget4android"
11 | minSdkVersion 16
12 | targetSdkVersion 26
13 | versionCode 1
14 | versionName "1.0"
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | buildscript {
25 | repositories {
26 | jcenter()
27 | }
28 | dependencies {
29 | classpath 'org.jetbrains.kotlin:kotlin-android-extensions:1.1.4-3'
30 | }
31 | }
32 |
33 | dependencies {
34 | implementation fileTree(include: ['*.jar'], dir: 'libs')
35 | implementation 'com.android.support:support-v4:25.3.0'
36 | implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.1.4-3'
37 | implementation 'org.jetbrains.anko:anko-common:0.10.0'
38 | implementation 'com.android.support:recyclerview-v7:25.3.0'
39 | }
40 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\android_sdk\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
12 |
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/java/com/huichongzi/fastwidget4android/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.provider.Settings;
8 | import android.view.Gravity;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.AbsListView;
12 | import android.widget.AdapterView;
13 | import android.widget.BaseAdapter;
14 | import android.widget.ListView;
15 | import android.widget.TextView;
16 |
17 | import com.huichongzi.fastwidget4android.activity.BannerViewActivity;
18 | import com.huichongzi.fastwidget4android.activity.BlindsListViewActivity;
19 | import com.huichongzi.fastwidget4android.activity.BookActivity;
20 | import com.huichongzi.fastwidget4android.activity.CurveFolioListViewActivity;
21 | import com.huichongzi.fastwidget4android.activity.DragGridViewActivity;
22 | import com.huichongzi.fastwidget4android.activity.FloatInputActivity;
23 | import com.huichongzi.fastwidget4android.activity.FloodAndSpreadActivity;
24 | import com.huichongzi.fastwidget4android.activity.FolioListViewActivity;
25 | import com.huichongzi.fastwidget4android.activity.ScrollFoldActivity;
26 | import com.huichongzi.fastwidget4android.activity.SpinnerActivity;
27 | import com.huichongzi.fastwidget4android.activity.SwipeMenuListActivity;
28 | import com.huichongzi.fastwidget4android.activity.WaveBallProgressActivity;
29 | import com.huichongzi.fastwidget4android.activity.WheelActivity;
30 | import com.huichongzi.fastwidget4android.utils.DisplayUtils;
31 | import com.huichongzi.fastwidget4android.widget.FloatSideBall;
32 |
33 |
34 | public class MainActivity extends Activity {
35 | private static final String[] names = {
36 | "页面对折翻页效果", "百叶窗翻页效果", "水晶球波浪进度条",
37 | "滑动折叠(喵街)列表", "淹没展开动画", "滑动删除的RecyclerView",
38 | "轮盘效果",
39 | "循环轮播BannerView", "可拖动排序的GridView", "仿书页", "日历翻页效果", "下拉组件", "悬浮输入框"};
40 | private static final Class[] clazzs = {
41 | FolioListViewActivity.class, BlindsListViewActivity.class, WaveBallProgressActivity.class,
42 | ScrollFoldActivity.class, FloodAndSpreadActivity.class, SwipeMenuListActivity.class,
43 | WheelActivity.class,
44 | BannerViewActivity.class, DragGridViewActivity.class, BookActivity.class, CurveFolioListViewActivity.class,
45 | SpinnerActivity.class, FloatInputActivity.class};
46 |
47 | private ListView list;
48 | @Override
49 | protected void onCreate(Bundle savedInstanceState) {
50 | super.onCreate(savedInstanceState);
51 | setContentView(R.layout.activity_main);
52 | list = (ListView)findViewById(R.id.list);
53 | list.setAdapter(new BaseAdapter() {
54 | @Override
55 | public int getCount() {
56 | return names.length;
57 | }
58 | @Override
59 | public Object getItem(int position) {
60 | return position;
61 | }
62 | @Override
63 | public long getItemId(int position) {
64 | return position;
65 | }
66 | @Override
67 | public View getView(int position, View convertView, ViewGroup parent) {
68 | TextView view = new TextView(MainActivity.this);
69 | view.setText(names[position]);
70 | AbsListView.LayoutParams params = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(MainActivity.this, 40));
71 | view.setLayoutParams(params);
72 | view.setGravity(Gravity.CENTER);
73 | return view;
74 | }
75 | });
76 | list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
77 | @Override
78 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
79 | if(position < clazzs.length){
80 | Intent intent = new Intent(MainActivity.this, clazzs[position]);
81 | MainActivity.this.startActivity(intent);
82 | }
83 | }
84 | });
85 |
86 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
87 | if(Settings.canDrawOverlays(this)) {
88 | showFloatBall();
89 | }
90 | }
91 | else{
92 | showFloatBall();
93 | }
94 | }
95 |
96 | private void showFloatBall(){
97 | FloatSideBall floatSideBall = new FloatSideBall(this, R.drawable.floating_add_move, R.drawable.floating_add_left, R.drawable.floating_add_right, new View.OnClickListener() {
98 | @Override
99 | public void onClick(View view) {
100 |
101 | }
102 | });
103 | floatSideBall.showSideball();
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/BannerViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 |
4 | import android.app.Activity;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.ImageView;
8 |
9 | import com.huichongzi.fastwidget4android.R;
10 | import com.huichongzi.fastwidget4android.widget.BannerView;
11 |
12 |
13 | public class BannerViewActivity extends Activity {
14 |
15 | int[] imgs = {R.drawable.banner_a, R.drawable.banner_b, R.drawable.banner_c, R.drawable.banner_d, R.drawable.banner_e};
16 | String[] titles = {"有你有我", "一路同行", "福利来了", "全场5折", "疯狂抢购"};
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.banner_view_act);
22 | initView();
23 | }
24 |
25 | private void initView(){
26 | BannerView viewPager = (BannerView)findViewById(R.id.banner_act_viewpager);
27 | MyAdapter adapter = new MyAdapter();
28 | viewPager.setBannerAdapter(adapter);
29 | }
30 |
31 | class MyAdapter extends BannerView.BannerAdapter{
32 |
33 | @Override
34 | public View getView(int position, View item) {
35 | if(item == null) {
36 | item = new ImageView(BannerViewActivity.this);
37 | }
38 | ((ImageView)item).setImageResource(imgs[position]);
39 | ((ImageView)item).setScaleType(ImageView.ScaleType.CENTER_CROP);
40 | return item;
41 | }
42 |
43 | @Override
44 | public int getSize() {
45 | return imgs.length;
46 | }
47 |
48 | @Override
49 | public String getTitle(int position) {
50 | return titles[position];
51 | }
52 |
53 | @Override
54 | public boolean isTitleShow() {
55 | return true;
56 | }
57 |
58 | @Override
59 | public boolean isIndicatorShow() {
60 | return true;
61 | }
62 | }
63 |
64 | @Override
65 | protected void onDestroy() {
66 | super.onDestroy();
67 | }
68 |
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/BlindsListViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | import com.huichongzi.fastwidget4android.R;
7 | import com.huichongzi.fastwidget4android.adapter.AnimationListAdapter;
8 | import com.huichongzi.fastwidget4android.widget.AnimationListView;
9 | import com.huichongzi.fastwidget4android.widget.BlindsView;
10 |
11 | /**
12 | * @author chz
13 | * @description
14 | * @date 2016/1/27 9:42
15 | */
16 | public class BlindsListViewActivity extends Activity {
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.blinds_listview_act);
22 | initView();
23 | }
24 |
25 | private void initView(){
26 | AnimationListView blindsListView = (AnimationListView)findViewById(R.id.blinds_listview_act_list);
27 | blindsListView.setAdapter(new AnimationListAdapter(this));
28 | blindsListView.setAnimationClass(BlindsView.class);
29 | blindsListView.setIsVertical(false);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/BookActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 |
4 | import android.app.Activity;
5 | import android.graphics.BitmapFactory;
6 | import android.os.Bundle;
7 |
8 | import com.huichongzi.fastwidget4android.R;
9 | import com.huichongzi.fastwidget4android.widget.BookPageView;
10 |
11 |
12 | public class BookActivity extends Activity {
13 |
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.book_act);
19 | BookPageView bookPageView = (BookPageView)findViewById(R.id.book);
20 | bookPageView.setBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.page_a),
21 | BitmapFactory.decodeResource(getResources(), R.drawable.page_b));
22 | initView();
23 | }
24 |
25 | private void initView(){
26 | }
27 |
28 |
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/CurveFolioListViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 |
4 | import android.app.Activity;
5 | import android.os.Bundle;
6 |
7 | import com.huichongzi.fastwidget4android.R;
8 | import com.huichongzi.fastwidget4android.adapter.AnimationListAdapter;
9 | import com.huichongzi.fastwidget4android.widget.AnimationListView;
10 | import com.huichongzi.fastwidget4android.widget.CurveFolioView;
11 | import com.huichongzi.fastwidget4android.widget.FolioView;
12 |
13 |
14 | public class CurveFolioListViewActivity extends Activity {
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.folio_listview_act);
20 | initView();
21 | }
22 |
23 | private void initView(){
24 | final AnimationListView folioListView = (AnimationListView)findViewById(R.id.folio_listview_act_list);
25 | folioListView.setAdapter(new AnimationListAdapter(this));
26 | folioListView.setAnimationClass(CurveFolioView.class);
27 | folioListView.setIsVertical(true);
28 |
29 | // folioListView.createAnimationView();
30 | // folioListView.setAnimationViewVisible(true);
31 | // folioListView.postDelayed(new Runnable() {
32 | // @Override
33 | // public void run() {
34 | // folioListView.switchAniamtionBitmap(0);
35 | // folioListView.mAnimationView.setAnimationPercent(-0.1f, null, true);
36 | // folioListView.mAnimationView.startAnimation(true, null, -1);
37 | // }
38 | // }, 3000);
39 | }
40 |
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/DragGridViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 |
4 | import android.app.Activity;
5 | import android.os.Bundle;
6 | import android.view.Gravity;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.AbsListView;
10 | import android.widget.TextView;
11 |
12 | import com.huichongzi.fastwidget4android.R;
13 | import com.huichongzi.fastwidget4android.utils.DisplayUtils;
14 | import com.huichongzi.fastwidget4android.widget.DragGridView;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 |
20 | public class DragGridViewActivity extends Activity {
21 |
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.drag_gridview_act);
27 | initView();
28 | }
29 |
30 | private void initView(){
31 | DragGridView gridView = (DragGridView)findViewById(R.id.drag_grid_act_gridview);
32 | List list = new ArrayList();
33 | for(int i = 0; i < 41; i++){
34 | GridModel model = new GridModel();
35 | model.id = i;
36 | list.add(model);
37 | }
38 | MyAdapter adapter = new MyAdapter();
39 | adapter.setData(list);
40 | gridView.setAdapter(adapter);
41 | }
42 |
43 | class MyAdapter extends DragGridView.DragGridAdapter{
44 |
45 | @Override
46 | public View getItemView(int position, View convertView, ViewGroup parent) {
47 | TextView view = new TextView(DragGridViewActivity.this);
48 | view.setText(mData.get(position).id + "号项目项目项目");
49 | view.setGravity(Gravity.CENTER);
50 | AbsListView.LayoutParams params = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(DragGridViewActivity.this, 60));
51 | view.setLayoutParams(params);
52 | return view;
53 | }
54 |
55 | @Override
56 | public int getNumColumns() {
57 | return 3;
58 | }
59 | }
60 |
61 | @Override
62 | protected void onDestroy() {
63 | super.onDestroy();
64 | }
65 |
66 | class GridModel{
67 | public int id;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/FloatInputActivity.kt:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity
2 |
3 | import android.app.Activity
4 | import android.os.Bundle
5 | import android.widget.TextView
6 | import com.huichongzi.fastwidget4android.R
7 | import com.huichongzi.fastwidget4android.widget.FloatInputView
8 |
9 | class FloatInputActivity : Activity() {
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 | setContentView(R.layout.float_input_act)
13 | findViewById(R.id.input_tv).setOnClickListener {
14 | findViewById(R.id.float_input_view).showInput()
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/FloodAndSpreadActivity.kt:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity
2 |
3 | import android.animation.Animator
4 | import android.animation.ObjectAnimator
5 | import android.app.Activity
6 | import android.os.Bundle
7 | import android.view.View
8 | import com.huichongzi.fastwidget4android.R
9 | import kotlinx.android.synthetic.main.flood_and_spread_activity.*
10 |
11 | /**
12 | * Created by hcui on 9/6/17.
13 | */
14 | class FloodAndSpreadActivity : Activity() {
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 | setContentView(R.layout.flood_and_spread_activity)
18 | init()
19 | }
20 |
21 | fun init(){
22 | val height: Int = window.windowManager.defaultDisplay.height
23 | var floodWrapper = ViewWrapper(animation_content)
24 | var spreadWrapper = ViewWrapper(spread_view)
25 | var floodAnimation = ObjectAnimator.ofInt(floodWrapper, "height", height)
26 | floodAnimation.duration = 1000
27 | floodAnimation.start()
28 | floodAnimation.addListener(object: Animator.AnimatorListener{
29 | override fun onAnimationRepeat(p0: Animator?) {
30 | }
31 |
32 | override fun onAnimationCancel(p0: Animator?) {
33 | }
34 |
35 | override fun onAnimationStart(p0: Animator?) {
36 | }
37 |
38 | override fun onAnimationEnd(p0: Animator?) {
39 | ObjectAnimator.ofInt(spreadWrapper, "height", height).setDuration(1000).start()
40 | }
41 | })
42 | }
43 |
44 | class ViewWrapper (var mTarget: View){
45 | fun getHeight():Int{
46 | return mTarget.layoutParams.height
47 | }
48 |
49 | fun setHeight(height: Int){
50 | mTarget.layoutParams.height = height
51 | mTarget.requestLayout()
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/FolioListViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 |
4 | import android.app.Activity;
5 | import android.os.Bundle;
6 | import android.view.MotionEvent;
7 | import android.view.View;
8 |
9 | import com.huichongzi.fastwidget4android.R;
10 | import com.huichongzi.fastwidget4android.adapter.AnimationListAdapter;
11 | import com.huichongzi.fastwidget4android.widget.AnimationListView;
12 | import com.huichongzi.fastwidget4android.widget.FolioView;
13 |
14 |
15 | public class FolioListViewActivity extends Activity {
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.folio_listview_act);
21 | initView();
22 | }
23 |
24 | private void initView(){
25 | final AnimationListView folioListView = (AnimationListView)findViewById(R.id.folio_listview_act_list);
26 | folioListView.setAdapter(new AnimationListAdapter(this));
27 | folioListView.setAnimationClass(FolioView.class);
28 | folioListView.setIsVertical(true);
29 |
30 | // folioListView.createAnimationView();
31 | // folioListView.setAnimationViewVisible(true);
32 | // folioListView.postDelayed(new Runnable() {
33 | // @Override
34 | // public void run() {
35 | // folioListView.switchAniamtionBitmap(0);
36 | // folioListView.mAnimationView.setAnimationPercent(-0.1f, null, true);
37 | // folioListView.mAnimationView.startAnimation(true, null, -1);
38 | // }
39 | // }, 3000);
40 | }
41 |
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/ScrollFoldActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 |
4 | import android.app.Activity;
5 | import android.os.Bundle;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 |
11 | import com.huichongzi.fastwidget4android.R;
12 | import com.huichongzi.fastwidget4android.adapter.ScrollFoldAdapter;
13 |
14 | public class ScrollFoldActivity extends Activity {
15 |
16 | private int itemHeight;
17 | private int itemSmallHeight;
18 | private ScrollFoldAdapter adapter;
19 | private RecyclerView list;
20 | private LinearLayoutManager linearLayoutManager;
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.recyclerview_activity);
26 | itemHeight = getResources().getDimensionPixelSize(
27 | R.dimen.scroll_fold_item_height);
28 | itemSmallHeight = (int) (itemHeight / ScrollFoldAdapter.ITEM_CONTENT_TEXT_SCALE);
29 | list = (RecyclerView) findViewById(R.id.recycle_scrollview_activity_list);
30 | linearLayoutManager = new LinearLayoutManager(this);
31 | list.setLayoutManager(linearLayoutManager);
32 | adapter = new ScrollFoldAdapter(this, list);
33 | list.setAdapter(adapter);
34 | list.addOnScrollListener(new RecyclerView.OnScrollListener() {
35 | @Override
36 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
37 | changeItemState();
38 | }
39 |
40 | @Override
41 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
42 | if (newState == RecyclerView.SCROLL_STATE_IDLE) {
43 | int firstVisibleIndex = linearLayoutManager.findFirstVisibleItemPosition();
44 | View first = linearLayoutManager.findViewByPosition(firstVisibleIndex);
45 | int firstVisibleOffset = -first.getTop();
46 | if (firstVisibleOffset == 0) {
47 | return;
48 | }
49 | if (firstVisibleOffset < itemSmallHeight / 2) {
50 | list.scrollBy(0, -firstVisibleOffset);
51 | } else {
52 | list.scrollBy(0, itemSmallHeight - firstVisibleOffset);
53 | }
54 | changeItemState();
55 | }
56 | }
57 | });
58 | }
59 |
60 | private void changeItemState(){
61 | int firstVisibleIndex = linearLayoutManager.findFirstVisibleItemPosition();
62 | ViewGroup first = (ViewGroup) linearLayoutManager.findViewByPosition(firstVisibleIndex);
63 | int firstVisibleOffset = -first.getTop();
64 | int changeheight = (int) (firstVisibleOffset * (ScrollFoldAdapter.ITEM_CONTENT_TEXT_SCALE - 1));
65 |
66 | // 减少当前展示的第一个item的高度。
67 | if (first == null) {
68 | return;
69 | }
70 | changeItemHeight(first, itemHeight - changeheight);
71 | changeItemState(first, ScrollFoldAdapter.ITEM_CONTENT_TEXT_SCALE, ScrollFoldAdapter.ITEM_SHADE_LIGHT_ALPHA);
72 |
73 | // 增大当前展示的第二个item的高度,改变内容大小,改变透明度
74 | if (firstVisibleIndex + 1 < adapter.getItemCount() - 1) {
75 | ViewGroup second = (ViewGroup) linearLayoutManager.findViewByPosition(firstVisibleIndex + 1);
76 | changeItemHeight(second, itemSmallHeight + changeheight);
77 |
78 | float scale = (float) firstVisibleOffset / itemSmallHeight
79 | * (ScrollFoldAdapter.ITEM_CONTENT_TEXT_SCALE - 1) + 1.0f;
80 | float alpha = (ScrollFoldAdapter.ITEM_SHADE_DARK_ALPHA - ScrollFoldAdapter.ITEM_SHADE_LIGHT_ALPHA)
81 | * (1 - (float) firstVisibleOffset / itemSmallHeight)
82 | + ScrollFoldAdapter.ITEM_SHADE_LIGHT_ALPHA;
83 | changeItemState(second, scale, alpha);
84 | }
85 |
86 | /**
87 | * 由于快速滑动,导致计算及状态有误 所以下面就是消除这种误差,校准状态。具体如下
88 | * 将第一个item上面(存在的)的和第二个Item下面的都变为收缩的高度,内容缩放到最小,透明度为0。65
89 | */
90 | for (int i = 0; i <= linearLayoutManager.findLastVisibleItemPosition(); i++) {
91 | if (i < adapter.getItemCount() - 1 && i != firstVisibleIndex && i != firstVisibleIndex + 1) {
92 | ViewGroup item = (ViewGroup) linearLayoutManager.findViewByPosition(i);
93 | if(item == null){
94 | continue;
95 | }
96 | changeItemHeight(item, itemSmallHeight);
97 | float scale = 1;
98 | float alpha = ScrollFoldAdapter.ITEM_SHADE_DARK_ALPHA;
99 | changeItemState(item, scale, alpha);
100 | }
101 | }
102 | }
103 |
104 | /**
105 | * 改变一个item的高度。
106 | *
107 | * @param item
108 | * @param height
109 | */
110 | private void changeItemHeight(View item, int height) {
111 | ViewGroup.LayoutParams itemParams = item.getLayoutParams();
112 | itemParams.height = height;
113 | item.setLayoutParams(itemParams);
114 | }
115 |
116 | /**
117 | * 改变一个item的状态,包括透明度,大小等
118 | * @param item
119 | * @param scale
120 | * @param alpha
121 | */
122 | private void changeItemState(ViewGroup item, float scale, float alpha) {
123 | if (item.getChildCount() > 0) {
124 | View changeView = item.findViewById(R.id.scale_item_content);
125 | changeView.setScaleX(scale);
126 | changeView.setScaleY(scale);
127 |
128 | View shade = item.findViewById(R.id.item_img_shade);
129 | shade.setAlpha(alpha);
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/SpinnerActivity.kt:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity
2 |
3 |
4 | import android.app.Activity
5 | import android.content.Context
6 | import android.os.Build
7 | import android.os.Bundle
8 | import android.support.annotation.ArrayRes
9 | import android.support.annotation.LayoutRes
10 | import android.support.annotation.Nullable
11 | import android.view.View
12 | import android.view.ViewGroup
13 | import android.widget.AdapterView
14 | import android.widget.AdapterView.OnItemSelectedListener
15 | import android.widget.ArrayAdapter
16 | import android.widget.Spinner
17 | import android.widget.TextView
18 | import com.huichongzi.fastwidget4android.R
19 | import com.huichongzi.fastwidget4android.utils.DisplayUtils
20 |
21 |
22 | class SpinnerActivity : Activity() {
23 | var adapter : SpinnerAdapter? = null
24 |
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | super.onCreate(savedInstanceState)
27 | setContentView(R.layout.spinner_activity)
28 | val spinner = findViewById(R.id.spinner)
29 | adapter = SpinnerAdapter.createFromResource(this, R.array.grade, R.layout.spinner_layout)
30 | adapter?.setDropDownViewResource(R.layout.spinner_item)
31 | spinner.setBackgroundColor(0x0)
32 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
33 | spinner.setPopupBackgroundResource(R.drawable.spinner_bg)
34 | spinner.dropDownVerticalOffset = DisplayUtils.dip2px(this, 0f)
35 | }
36 | spinner.setAdapter(adapter)
37 | spinner.setOnItemSelectedListener(object : OnItemSelectedListener {
38 | override fun onItemSelected(
39 | parent: AdapterView<*>?,
40 | view: View,
41 | position: Int,
42 | id: Long
43 | ) {
44 | adapter?.setSelectedPostion(position)
45 | }
46 |
47 | override fun onNothingSelected(parent: AdapterView<*>?) {}
48 | })
49 | }
50 |
51 | class SpinnerAdapter(context: Context, resource: Int, objects: Array) :
52 | ArrayAdapter(context, resource, objects) {
53 | private var selectedPostion = 0
54 | fun setSelectedPostion(selectedPostion: Int) {
55 | this.selectedPostion = selectedPostion
56 | }
57 |
58 | override fun getDropDownView(
59 | position: Int,
60 | @Nullable convertView: View?,
61 | parent: ViewGroup
62 | ): View {
63 | val view: View = super.getDropDownView(position, convertView, parent)
64 | val textView = view as TextView
65 | if (selectedPostion == position) {
66 | textView.setTextColor(-0xc8c8bf)
67 | textView.paint.isFakeBoldText = true
68 | } else {
69 | textView.setTextColor(-0x929293)
70 | textView.paint.isFakeBoldText = false
71 | }
72 | return view
73 | }
74 |
75 | companion object {
76 | fun createFromResource(
77 | context: Context,
78 | @ArrayRes textArrayResId: Int, @LayoutRes textViewResId: Int
79 | ): SpinnerAdapter {
80 | val strings: Array =
81 | context.getResources().getTextArray(textArrayResId)
82 | return SpinnerAdapter(context, textViewResId, strings)
83 | }
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/SwipeMenuListActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.os.Bundle;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import com.huichongzi.fastwidget4android.R;
13 | import com.huichongzi.fastwidget4android.widget.SwipeMenuTouchListener;
14 | import com.huichongzi.fastwidget4android.widget.SwipeMenuViewHolder;
15 |
16 | /**
17 | * Created by hcui on 9/15/17.
18 | */
19 |
20 | public class SwipeMenuListActivity extends Activity {
21 | RecyclerView list;
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.recyclerview_activity);
27 | list = (RecyclerView) findViewById(R.id.recycle_scrollview_activity_list);
28 | list.setLayoutManager(new LinearLayoutManager(this));
29 | list.setAdapter(new ListAdapter());
30 | list.addOnItemTouchListener(new SwipeMenuTouchListener());
31 | }
32 |
33 | class ListAdapter extends RecyclerView.Adapter{
34 |
35 | @Override
36 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
37 | View item = LayoutInflater.from(SwipeMenuListActivity.this).inflate(R.layout.swipe_menu_list_item, null);
38 | return new ViewHolder(item);
39 | }
40 |
41 | @Override
42 | public void onBindViewHolder(ViewHolder holder, final int position) {
43 | holder.contentLayout.setTranslationX(0);
44 | holder.menuLayout.setOnClickListener(new View.OnClickListener() {
45 | @Override
46 | public void onClick(View view) {
47 | list.getAdapter().notifyItemRemoved(position);
48 | }
49 | });
50 | }
51 |
52 | @Override
53 | public int getItemCount() {
54 | return 20;
55 | }
56 |
57 | class ViewHolder extends SwipeMenuViewHolder{
58 |
59 | public ViewHolder(View itemView) {
60 | super(itemView);
61 | menuLayout = itemView.findViewById(R.id.menu_layout);
62 | contentLayout = itemView.findViewById(R.id.content_layout);
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/WaveBallProgressActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 |
4 | import android.app.Activity;
5 | import android.os.Bundle;
6 | import android.widget.SeekBar;
7 |
8 | import com.huichongzi.fastwidget4android.R;
9 | import com.huichongzi.fastwidget4android.widget.WaveBallProgress;
10 |
11 |
12 | public class WaveBallProgressActivity extends Activity {
13 |
14 | private WaveBallProgress progress;
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.wave_ball_progress_act);
19 | initView();
20 | }
21 |
22 | private void initView(){
23 | progress = (WaveBallProgress)findViewById(R.id.wave_ball_progress_act_view);
24 |
25 |
26 | SeekBar seekBar = (SeekBar)findViewById(R.id.seekbar);
27 | seekBar.setMax(100);
28 | seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
29 | @Override
30 | public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
31 | progress.startProgress(i);
32 | }
33 |
34 | @Override
35 | public void onStartTrackingTouch(SeekBar seekBar) {
36 |
37 | }
38 |
39 | @Override
40 | public void onStopTrackingTouch(SeekBar seekBar) {
41 |
42 | }
43 | });
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/activity/WheelActivity.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.activity;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import android.app.Activity;
7 | import android.graphics.Point;
8 | import android.graphics.drawable.ColorDrawable;
9 | import android.os.Bundle;
10 | import android.os.Handler;
11 | import android.view.MotionEvent;
12 | import android.view.View;
13 | import android.view.View.OnClickListener;
14 | import android.view.View.OnTouchListener;
15 | import android.view.Window;
16 | import android.view.WindowManager;
17 | import android.widget.ImageView;
18 | import android.widget.RelativeLayout;
19 | import android.widget.RelativeLayout.LayoutParams;
20 |
21 | import com.huichongzi.fastwidget4android.R;
22 |
23 |
24 | public class WheelActivity extends Activity{
25 |
26 | private static final float circleAngle = 360;
27 | private List items = new ArrayList();
28 | private int[] images = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e,
29 | R.drawable.f, R.drawable.g, R.drawable.h, R.drawable.i, R.drawable.j};
30 | private int centerY;
31 | private int bg_radius;
32 | private int radius;
33 | private int width;
34 | private float spaceAngle;
35 |
36 | public void onCreate(Bundle savedInstanceState){
37 | super.onCreate(savedInstanceState);
38 | WindowManager manager = getWindow().getWindowManager();
39 | int screen_width = manager.getDefaultDisplay().getWidth();
40 | int screen_height = manager.getDefaultDisplay().getHeight();
41 | centerY = getIntent().getIntExtra("y", -1);
42 | if(centerY == -1){
43 | centerY = screen_height / 2;
44 | }
45 | getWindow().requestFeature(Window.FEATURE_NO_TITLE);
46 | getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
47 | getWindow().setBackgroundDrawable(new ColorDrawable(0x50000000));
48 | if(screen_width > screen_height){
49 | screen_width = screen_height;
50 | screen_height = manager.getDefaultDisplay().getWidth();
51 | }
52 | bg_radius = screen_width / 2;
53 | spaceAngle = circleAngle / images.length;
54 |
55 | RelativeLayout layout = new RelativeLayout(this);
56 | layout.setBackgroundResource(R.drawable.bg1);
57 | layout.setOnClickListener(new OnClickListener(){
58 | public void onClick(View view) {
59 | WheelActivity.this.finish();
60 | }
61 | });
62 | setContentView(layout);
63 |
64 | RelativeLayout bg_layout = new RelativeLayout(this);
65 | bg_layout.setBackgroundResource(R.drawable.floating_bg_big);
66 | bg_layout.setOnTouchListener(new OnTouchListener(){
67 | private double startAngle;
68 | public boolean onTouch(View view, MotionEvent e) {
69 | switch(e.getAction()){
70 | case MotionEvent.ACTION_DOWN:
71 | startAngle = getAngle(new Point((int)e.getRawX(), (int)e.getRawY()), new Point(0, centerY));
72 | break;
73 | case MotionEvent.ACTION_MOVE:
74 | double endAngle = getAngle(new Point((int)e.getRawX(), (int)e.getRawY()), new Point(0, centerY));
75 | double angle = endAngle - startAngle;
76 | if(Math.abs(angle) > 5){
77 | updateWheel(angle);
78 | startAngle = endAngle;
79 | }
80 | break;
81 | }
82 | return true;
83 | }
84 | });
85 | LayoutParams params = new LayoutParams(bg_radius , bg_radius * 2);
86 | int topMargin = centerY - bg_radius;
87 | if(topMargin < 0){
88 | topMargin = 0;
89 | }
90 | if(topMargin + screen_width > screen_height){
91 | topMargin = screen_height - screen_width;
92 | }
93 | params.topMargin = topMargin;
94 | centerY = topMargin + bg_radius;
95 | layout.addView(bg_layout, params);
96 | width = screen_width / 6;
97 |
98 | ImageView image_close = new ImageView(this);
99 | image_close.setImageResource(R.drawable.floating_closed_normal);
100 | LayoutParams params_close = new LayoutParams(width , screen_width / 3);
101 | params_close.addRule(RelativeLayout.CENTER_VERTICAL);
102 | bg_layout.addView(image_close, params_close);
103 | image_close.setOnClickListener(new OnClickListener(){
104 | public void onClick(View view) {
105 | WheelActivity.this.finish();
106 | }
107 | });
108 |
109 | radius = (bg_radius + width) / 2;
110 | for(int i = 0; i < images.length; i++){
111 | ImageView image = new ImageView(this);
112 | image.setImageResource(images[i]);
113 | image.setTag(i + "");
114 | double angle = i * spaceAngle - spaceAngle * 2 / 3;
115 | if(angle > circleAngle){
116 | angle = angle - circleAngle;
117 | }
118 | if(angle < 0){
119 | angle = angle + circleAngle;
120 | }
121 | bg_layout.addView(image, getItemParams(angle));
122 | items.add(image);
123 | }
124 | }
125 |
126 | private synchronized void updateWheel(double angle){
127 | ImageView image = items.get(0);
128 | int top = image.getTop();
129 | int left = image.getLeft();
130 | double startAngle = getAngle(new Point(left + width / 2, top + width / 2), new Point(0, bg_radius));
131 | double endAngle = startAngle + angle;
132 | if(endAngle > circleAngle){
133 | endAngle = endAngle - circleAngle;
134 | }
135 | if(endAngle < 0){
136 | endAngle = endAngle + circleAngle;
137 | }
138 | updateItem(image, endAngle);
139 | for(int i = 1; i < items.size(); i++){
140 | double nAngle = endAngle + i * spaceAngle;
141 | if(nAngle > circleAngle){
142 | nAngle = nAngle - circleAngle;
143 | }
144 | if(nAngle < 0){
145 | nAngle = nAngle + circleAngle;
146 | }
147 | updateItem(items.get(i), nAngle);
148 | }
149 | }
150 |
151 | private void updateItem(View view, double angle){
152 | int endX = (int)(radius * Math.cos(angle * Math.PI / 180));
153 | int endY = (int)(radius * Math.sin(angle * Math.PI / 180));
154 | int margintop = bg_radius - endY - width / 2;
155 | int marginleft = endX - width / 2;
156 | LayoutParams params = (LayoutParams)(view.getLayoutParams());
157 | params.setMargins(marginleft, margintop, 0, 0);
158 | view.setLayoutParams(params);
159 |
160 | view.requestLayout();
161 | }
162 |
163 | private LayoutParams getItemParams(double angle){
164 | int endX = (int)(radius * Math.cos(angle * Math.PI / 180));
165 | int endY = (int)(radius * Math.sin(angle * Math.PI / 180));
166 | int margintop = bg_radius - endY - width / 2;
167 | int marginleft = endX - width / 2;
168 | LayoutParams params = new LayoutParams(width, width);
169 | params.setMargins(marginleft, margintop, 0, 0);
170 | return params;
171 | }
172 |
173 | private double getAngle(Point p, Point center){
174 | int x = p.x - center.x;
175 | int y = center.y - p.y;
176 | return Math.atan2(y, x) * 180 / Math.PI;
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/adapter/AnimationListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.adapter;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import com.huichongzi.fastwidget4android.R;
12 |
13 | /**
14 | * @author chz
15 | * @description
16 | * @date 2016/1/27 9:37
17 | */
18 | public class AnimationListAdapter extends BaseAdapter {
19 | private int[] imgs = {R.drawable.banner_a, R.drawable.banner_b, R.drawable.banner_c, R.drawable.banner_d, R.drawable.banner_e};
20 |
21 | private Context mContext;
22 |
23 | public AnimationListAdapter(Context context){
24 | mContext = context;
25 | }
26 |
27 | @Override
28 | public int getCount() {
29 | return imgs.length;
30 | }
31 |
32 | @Override
33 | public Object getItem(int position) {
34 | return position;
35 | }
36 |
37 | @Override
38 | public long getItemId(int position) {
39 | return position;
40 | }
41 |
42 | @Override
43 | public View getView(int position, View convertView, ViewGroup parent) {
44 | ViewHolder holder = null;
45 | if(convertView == null) {
46 | holder = new ViewHolder();
47 | convertView = LayoutInflater.from(mContext).inflate(R.layout.animation_listview_item, null);
48 | holder.img = (ImageView)convertView.findViewById(R.id.animation_listview_item_img);
49 | holder.title = (TextView)convertView.findViewById(R.id.animation_listview_item_title);
50 | convertView.setTag(holder);
51 | }
52 | else{
53 | holder = (ViewHolder)convertView.getTag();
54 | }
55 | holder.img.setImageResource(imgs[position]);
56 | holder.title.setText("page " + position);
57 | return convertView;
58 | }
59 |
60 | class ViewHolder{
61 | public ImageView img;
62 | public TextView title;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/adapter/ScrollFoldAdapter.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import com.huichongzi.fastwidget4android.R;
12 |
13 | public class ScrollFoldAdapter extends RecyclerView.Adapter {
14 | private final int[] IMGS = {R.drawable.market_a, R.drawable.market_b, R.drawable.market_c, R.drawable.market_d,
15 | R.drawable.market_a, R.drawable.market_b, R.drawable.market_c, R.drawable.market_d,
16 | R.drawable.market_a, R.drawable.market_b, R.drawable.market_c, R.drawable.market_d,
17 | R.drawable.market_a, R.drawable.market_b, R.drawable.market_c, R.drawable.market_d};
18 | private final String[] NAMES = {"万达百货——北京海淀店", "恒隆广场——大连中山店", "万达广场——大连高新园区", "翠微百货——北京店",
19 | "新世纪百货——北京店", "万达广场——上海宝山", "财神到商场——天津店", "新玛特——河南郑州",
20 | "兴隆广场——营口店", "锦辉商城——大连", "老佛爷百货——北京西单", "新世界百货——上海",
21 | "易初莲花——北京", "华联商城——上海", "家乐福——北京", "物美大卖场——北京"};
22 | //item的透明度,当未置顶显示时
23 | public static final float ITEM_SHADE_DARK_ALPHA = 0.65f;
24 | //item的透明度,当置顶显示时
25 | public static final float ITEM_SHADE_LIGHT_ALPHA = 0.15f;
26 | //item置顶时,内容扩展的倍数
27 | public static final float ITEM_CONTENT_TEXT_SCALE = 1.5f;
28 |
29 | private Context mContext;
30 | private RecyclerView recyclerView;
31 |
32 | //item置顶时的高度
33 | private int itemHeight;
34 | //item未置顶时的高度
35 | private int itemSmallHeight;
36 |
37 | public ScrollFoldAdapter(Context context, RecyclerView recyclerView){
38 | mContext = context;
39 | this.recyclerView = recyclerView;
40 | itemHeight = context.getResources().getDimensionPixelSize(
41 | R.dimen.scroll_fold_item_height);
42 | itemSmallHeight = (int)(itemHeight /ITEM_CONTENT_TEXT_SCALE);
43 | }
44 |
45 | @Override
46 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
47 | if(viewType == 0) {
48 | View item = LayoutInflater.from(mContext).inflate(R.layout.scroll_fold_list_item, null);
49 | return new ItemViewHolder(item);
50 | }
51 | else{
52 | View bottom = LayoutInflater.from(mContext).inflate(R.layout.scroll_fold_list_footer, null);
53 | return new BottomViewHolder(bottom);
54 | }
55 | }
56 |
57 | @Override
58 | public void onBindViewHolder(ViewHolder holder, int position) {
59 | holder.initData(position);
60 | }
61 |
62 | @Override
63 | public long getItemId(int position) {
64 | return position;
65 | }
66 |
67 | @Override
68 | public int getItemCount() {
69 | return IMGS.length + 1;
70 | }
71 |
72 | @Override
73 | public int getItemViewType(int position) {
74 | if(position < IMGS.length){
75 | return 0;
76 | }
77 | return 1;
78 | }
79 |
80 | abstract class ViewHolder extends RecyclerView.ViewHolder{
81 | View item;
82 |
83 | public ViewHolder(View itemView) {
84 | super(itemView);
85 | item = itemView;
86 | }
87 |
88 | abstract void initData(int position);
89 | }
90 |
91 | class BottomViewHolder extends ViewHolder{
92 |
93 | public BottomViewHolder(View itemView) {
94 | super(itemView);
95 | }
96 |
97 | @Override
98 | void initData(int position) {
99 | ViewGroup.LayoutParams bottomParams = itemView.getLayoutParams();
100 | if(bottomParams == null){
101 | bottomParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
102 | }
103 | bottomParams.height = recyclerView.getHeight() - itemHeight + 10;
104 | itemView.setLayoutParams(bottomParams);
105 | }
106 | }
107 |
108 | class ItemViewHolder extends ViewHolder{
109 | View content;
110 | ImageView image;
111 | TextView name;
112 |
113 | public ItemViewHolder(View itemView) {
114 | super(itemView);
115 | item = itemView;
116 | content = itemView.findViewById(R.id.item_content);
117 | image = (ImageView)itemView.findViewById(R.id.item_img);
118 | name = (TextView) itemView.findViewById(R.id.item_name);
119 | }
120 |
121 | void initData(int position){
122 | image.setImageResource(IMGS[position]);
123 | name.setText(NAMES[position]);
124 | ViewGroup.LayoutParams params = item.getLayoutParams();
125 | if(params == null){
126 | params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
127 | }
128 | params.height = itemSmallHeight;
129 | content.findViewById(R.id.item_img_shade).setAlpha(ITEM_SHADE_DARK_ALPHA);
130 | item.setLayoutParams(params);
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/utils/BezierCurve.kt:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.utils
2 |
3 | open class ThirdOrderBezierCurve(private val p0: FloatArray, private val p1: FloatArray, val p2: FloatArray) {
4 |
5 | open fun getPointBy01(p : FloatArray) : FloatArray{
6 | if(p[0] == p0[0] && p[1] == p0[1]){
7 | return p0
8 | }
9 | if(p[0] == p2[0] && p[1] == p2[1]){
10 | return p2
11 | }
12 | var res = FloatArray(2)
13 | var t = 0f
14 | if(p0[0] == p1[0]){
15 | t = (p0[1] - p[1]) / (p0[1] - p1[1])
16 | }
17 | else{
18 | t = (p0[0] - p[0]) / (p0[0] - p1[0])
19 | }
20 | var qx = p1[0] - t * (p1[0] - p2[0])
21 | var qy = p1[1] - t * (p1[1] - p2[1])
22 | res[0] = p[0] - t * (p[0] - qx)
23 | res[1] = p[1] - t * (p[1] - qy)
24 | return res
25 | }
26 |
27 | open fun getPointBy12(p : FloatArray) : FloatArray{
28 | if(p[0] == p0[0] && p[1] == p0[1]){
29 | return p0
30 | }
31 | if(p[0] == p2[0] && p[1] == p2[1]){
32 | return p2
33 | }
34 | var t = 0f
35 | if(p0[0] == p1[0]){
36 | t = (p0[1] - p[1]) / (p0[1] - p1[1])
37 | }
38 | else{
39 | t = (p0[0] - p[0]) / (p0[0] - p1[0])
40 | }
41 | var qx = p0[0] - t * (p0[0] - p1[0])
42 | var qy = p0[1] - t * (p0[1] - p1[1])
43 | var res = FloatArray(2)
44 | res[0] = qx - t * (qx - p[0])
45 | res[1] = qy - t * (qy - p[1])
46 | return res
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/utils/DisplayUtils.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.utils;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 | import android.view.WindowManager;
6 |
7 | public class DisplayUtils {
8 |
9 | private static float mDensity = 0;
10 | private static int mDensityDpi = 0;
11 | private static int mDensityW = 0;
12 | private static int mDensityH = 0;
13 |
14 | /**
15 | * 获取屏幕密度
16 | *
17 | * @param context
18 | * @return
19 | */
20 | public static float getDensity(Context context) {
21 | if (mDensity == 0) {
22 | initDisplay(context);
23 | }
24 | return mDensity;
25 | }
26 |
27 | /**
28 | * 获取屏幕密度dpi
29 | *
30 | * @param context
31 | * @return
32 | */
33 | public static int getDensityDpi(Context context) {
34 | if(mDensityDpi == 0) {
35 | initDisplay(context);
36 | }
37 | return mDensityDpi;
38 | }
39 |
40 |
41 | /**
42 | * 获取屏幕宽度
43 | *
44 | * @param context
45 | * @return
46 | */
47 | public static int getDisplayW(Context context) {
48 | if(mDensityW == 0) {
49 | initDisplay(context);
50 | }
51 | return mDensityW;
52 | }
53 |
54 | /**
55 | * 获取屏幕高度
56 | *
57 | * @param context
58 | * @return
59 | */
60 | public static int getDisplayH(Context context) {
61 | if(mDensityH == 0) {
62 | initDisplay(context);
63 | }
64 | return mDensityH;
65 | }
66 |
67 | private static void initDisplay(Context context){
68 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
69 | DisplayMetrics displayMetrics = new DisplayMetrics();
70 | windowManager.getDefaultDisplay().getMetrics(displayMetrics);
71 | mDensity = displayMetrics.density;
72 | mDensityDpi = displayMetrics.densityDpi;
73 | if(displayMetrics.widthPixels > displayMetrics.heightPixels){
74 | mDensityW = displayMetrics.heightPixels;
75 | mDensityH = displayMetrics.widthPixels;
76 | }
77 | else{
78 | mDensityH = displayMetrics.heightPixels;
79 | mDensityW = displayMetrics.widthPixels;
80 | }
81 | }
82 |
83 | /**
84 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
85 | */
86 | public static int dip2px(Context context, float dpValue) {
87 | return (int) (dpValue * getDensity(context) + 0.5f);
88 | }
89 |
90 | /**
91 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
92 | */
93 | public static int px2dip(Context context, float pxValue) {
94 | return (int) (pxValue / getDensity(context) + 0.5f);
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/AnimationListView.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.database.DataSetObserver;
6 | import android.graphics.Bitmap;
7 | import android.os.Build;
8 | import android.util.AttributeSet;
9 | import android.view.MotionEvent;
10 | import android.view.View;
11 | import android.widget.Adapter;
12 | import android.widget.FrameLayout;
13 |
14 | import java.lang.reflect.Constructor;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * 以过场动画来实现切换的ListView
20 | * 每次只展示一个item,在切换时会有过场动画
21 | * @author chz
22 | * @description
23 | * @date 2016/1/26 15:49
24 | */
25 | public class AnimationListView extends FrameLayout{
26 |
27 | private Class extends AnimationViewInterface> animationClass;
28 | protected int mCurrentPosition;
29 |
30 | /**
31 | * 执行过场动画的view
32 | */
33 | public AnimationViewInterface mAnimationView;
34 |
35 | /**
36 | * 缓存的item
37 | * 一共有三个,分别是当前、前一个和后一个。
38 | * 可以达到预加载的目的,同时重复利用减少内存占用
39 | */
40 | protected List mCacheItems;
41 |
42 | protected LayoutParams mLayoutParams;
43 | protected Adapter mAdapter;
44 |
45 | /**
46 | * 水平/垂直方法翻转
47 | */
48 | private boolean isVertical;
49 | private float mTmpX;
50 | private float mTmpY;
51 | private float mMoveX = 0;
52 | private float mMoveY = 0;
53 |
54 | public AnimationListView(Context context) {
55 | super(context);
56 | init();
57 | }
58 |
59 | public AnimationListView(Context context, AttributeSet attrs) {
60 | super(context, attrs);
61 | init();
62 | }
63 |
64 | public AnimationListView(Context context, AttributeSet attrs, int defStyleAttr) {
65 | super(context, attrs, defStyleAttr);
66 | init();
67 | }
68 |
69 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
70 | public AnimationListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
71 | super(context, attrs, defStyleAttr, defStyleRes);
72 | init();
73 | }
74 |
75 | protected void init() {
76 | mCacheItems = new ArrayList();
77 | mLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
78 | }
79 |
80 | /**
81 | * 设置adapter,设置监听并重新布局页面
82 | * @param adapter
83 | */
84 | public void setAdapter(Adapter adapter) {
85 | mAdapter = adapter;
86 | mAdapter.registerDataSetObserver(new DataSetObserver() {
87 | @Override
88 | public void onChanged() {
89 | super.onChanged();
90 | refreshByAdapter();
91 | }
92 |
93 | @Override
94 | public void onInvalidated() {
95 | super.onInvalidated();
96 | refreshByAdapter();
97 | }
98 | });
99 | mCurrentPosition = 0;
100 | refreshByAdapter();
101 | }
102 |
103 | /**
104 | * 重新布局页面
105 | * 先添加mCacheItems,再添加mFolioView。这样mFolioView一直处于顶端,不会被遮挡。
106 | */
107 | private void refreshByAdapter() {
108 | removeAllViews();
109 | if (mCurrentPosition < 0) {
110 | mCurrentPosition = 0;
111 | }
112 | if (mCurrentPosition >= mAdapter.getCount()) {
113 | mCurrentPosition = mAdapter.getCount() - 1;
114 | }
115 | //如果缓存item不够3个,用第一个item添补
116 | while(mCacheItems.size() < 3){
117 | View item = mAdapter.getView(0, null, null);
118 | addView(item, mLayoutParams);
119 | mCacheItems.add(item);
120 | }
121 | //刷新缓存item的数据。
122 | for (int i = 0; i < mCacheItems.size(); i++) {
123 | int index = mCurrentPosition + i - 1;
124 | View item = mCacheItems.get(i);
125 | //当在列表顶部或底部,会有一个缓存Item不刷新,因为当前位置没有上一个或下一个位置
126 | if (index >= 0 && index < mAdapter.getCount()) {
127 | item = mAdapter.getView(index, item, null);
128 | }
129 | }
130 | //刷新界面
131 | initItemVisible();
132 | //添加翻转处理的view
133 | setAnimationViewVisible(false);
134 | }
135 |
136 | /**
137 | * 下一页
138 | */
139 | protected void pageNext() {
140 | setAnimationViewVisible(false);
141 | //当前位置加1
142 | mCurrentPosition++;
143 | if (mCurrentPosition >= mAdapter.getCount()) {
144 | mCurrentPosition = mAdapter.getCount() - 1;
145 | }
146 | //移出缓存的第一个item,并且刷新成当前位置的下一位,并添加到缓存列表最后
147 | View first = mCacheItems.remove(0);
148 | if (mCurrentPosition + 1 < mAdapter.getCount()) {
149 | first = mAdapter.getView(mCurrentPosition + 1, first, null);
150 | }
151 | mCacheItems.add(first);
152 | //刷新界面
153 | initItemVisible();
154 | }
155 |
156 | /**
157 | * 上一页
158 | */
159 | protected void pagePrevious() {
160 | //当前位置减1
161 | mCurrentPosition--;
162 | if (mCurrentPosition < 0) {
163 | mCurrentPosition = 0;
164 | }
165 | //移出缓存的最后一个item,并且刷新成当前位置的上一位,并添加到缓存列表开始
166 | View last = mCacheItems.remove(mCacheItems.size() - 1);
167 | if (mCurrentPosition - 1 >= 0) {
168 | last = mAdapter.getView(mCurrentPosition - 1, last, null);
169 | }
170 | mCacheItems.add(0, last);
171 | //刷新界面
172 | initItemVisible();
173 | setAnimationViewVisible(false);
174 | }
175 |
176 |
177 | /**
178 | * 刷新所有的item,并且只显示当前位置即中间的item
179 | */
180 | private void initItemVisible() {
181 | for (int i = 0; i < mCacheItems.size(); i++) {
182 | View item = mCacheItems.get(i);
183 | item.invalidate();
184 | if (item == null) {
185 | continue;
186 | }
187 | if (i == 1) {
188 | item.setVisibility(VISIBLE);
189 | } else {
190 | item.setVisibility(INVISIBLE);
191 | }
192 | }
193 | }
194 |
195 | /**
196 | * 获取view的截图
197 | * @param view
198 | * @return
199 | */
200 | protected Bitmap getViewBitmap(View view) {
201 | view.setDrawingCacheEnabled(true);
202 | view.buildDrawingCache();
203 | return view.getDrawingCache();
204 | }
205 |
206 | /**
207 | * 设置动画组件的显示隐藏
208 | * 实际上是添加移除的动作
209 | * @param visible
210 | */
211 | public void setAnimationViewVisible(boolean visible) {
212 | if(mAnimationView == null){
213 | return;
214 | }
215 | if (visible) {
216 | addView((View) mAnimationView, mLayoutParams);
217 | } else {
218 | removeView((View) mAnimationView);
219 | }
220 | }
221 | protected boolean isAnimationViewVisible(){
222 | return mAnimationView != null && ((View) mAnimationView).getParent() != null;
223 | }
224 |
225 | public boolean isVertical() {
226 | return isVertical;
227 | }
228 |
229 | public void setIsVertical(boolean isVertical) {
230 | this.isVertical = isVertical;
231 | }
232 |
233 | public AnimationViewInterface getAnimationView() {
234 | return mAnimationView;
235 | }
236 |
237 | @Override
238 | public boolean onTouchEvent(MotionEvent event) {
239 | if (getWidth() <= 0 || getHeight() <= 0) {
240 | return false;
241 | }
242 | //当动画组件动画执行中,则忽略touch事件
243 | if(mAnimationView != null && mAnimationView.isAnimationRunning()){
244 | return true;
245 | }
246 | switch (event.getAction()) {
247 | case MotionEvent.ACTION_DOWN:
248 | mTmpX = event.getX();
249 | mTmpY = event.getY();
250 | break;
251 | case MotionEvent.ACTION_MOVE:
252 | /**
253 | * 计算移动的距离
254 | * 这里加了判断,是为了防止mMoveX或mMoveY为0,因为后面会根据这俩个判断移动方向。
255 | */
256 | if (event.getX() != mTmpX) {
257 | mMoveX = event.getX() - mTmpX;
258 | }
259 | if (event.getY() != mTmpY) {
260 | mMoveY = event.getY() - mTmpY;
261 | }
262 | //创建动画组件
263 | createAnimationView();
264 | /**
265 | * 计算当前的位置百分比
266 | * 0则代表初始位置
267 | * 0.x则代表下一页翻转的百分比
268 | * 1则代表翻到了下一页。
269 | * -0.x则代表上一页翻转的百分比
270 | * -1则代表翻到上一页。
271 | */
272 | float percent = mAnimationView.getAnimationPercent();
273 | if (isVertical) {
274 | percent += mMoveY / getHeight();
275 | } else {
276 | percent += mMoveX / getWidth();
277 | }
278 | //保证位置在1到-1之间
279 | if(percent < -1){
280 | percent = -1;
281 | }
282 | else if(percent > 1){
283 | percent = 1;
284 | }
285 | if(canPage(mMoveX, mMoveY, percent)) {
286 | //如果动画组件未展示将其展示
287 | if (!isAnimationViewVisible()) {
288 | setAnimationViewVisible(true);
289 | }
290 | //装载或切换动画的图片
291 | switchAniamtionBitmap(percent);
292 | mAnimationView.setAnimationPercent(percent, event, isVertical);
293 | }
294 | mTmpX = event.getX();
295 | mTmpY = event.getY();
296 | break;
297 | case MotionEvent.ACTION_UP:
298 | case MotionEvent.ACTION_CANCEL:
299 | case MotionEvent.ACTION_OUTSIDE:
300 | /**
301 | * 计算移动的距离
302 | * 这里加了判断,是为了防止mMoveX或mMoveY为0,因为后面会根据这俩个判断移动方向。
303 | */
304 | if (event.getX() != mTmpX) {
305 | mMoveX = event.getX() - mTmpX;
306 | }
307 | if (event.getY() != mTmpY) {
308 | mMoveY = event.getY() - mTmpY;
309 | }
310 | /**
311 | * 计算结束位置百分比
312 | * 0则代表初始位置
313 | * 1则代表翻到了下一页。
314 | * -1则代表翻到上一页。
315 | */
316 | float toPercent = 0;
317 | if (isVertical) {
318 | toPercent = mMoveY > 0 ? 1 : 0;
319 | } else {
320 | toPercent = mMoveX > 0 ? 1 : 0;
321 | }
322 | if(mAnimationView.getAnimationPercent() < 0){
323 | //如果是翻上一页的状态,则起点终点应该是0和-1
324 | toPercent -= 1;
325 | }
326 | //如果可以翻页,则播放翻页动画
327 | if(canPage(mMoveX, mMoveY, toPercent)) {
328 | mAnimationView.startAnimation(isVertical, event, toPercent);
329 | }
330 | mMoveX = 0;
331 | mMoveY = 0;
332 | break;
333 | }
334 | return true;
335 | }
336 |
337 |
338 | /**
339 | * 是否可以翻页
340 | * @param moveX
341 | * @param moveY
342 | * @param toPercent
343 | * @return
344 | */
345 | private boolean canPage(float moveX, float moveY, float toPercent) {
346 | if (isVertical) {
347 | if(moveY == 0){
348 | return false;
349 | }
350 | else if(moveY > 0){
351 | /**
352 | * 是否可以下翻。
353 | * 当toPercent小于0则意味着此时动画组件不在初始位置,处于上翻中的状态,这样是可以下翻的。
354 | * 如果toPercent大于0,而此时处于第一个item,则无法下翻,因为上面没有item了。
355 | */
356 | return toPercent <= 0 || mCurrentPosition > 0;
357 | }
358 | else{
359 | /**
360 | * 是否可以上翻。
361 | * 当toPercent大于0则意味着此时动画组件不在初始位置,处于下翻中的状态,这样是可以上翻的。
362 | * 如果toPercent小于0,而此时处于最后一个item,则无法上翻,因为下面没有item了。
363 | */
364 | return toPercent >= 0 || mCurrentPosition < mAdapter.getCount() - 1;
365 | }
366 | } else {
367 | if(moveX == 0){
368 | return false;
369 | }
370 | else if(moveX > 0){
371 | /**
372 | * 是否可以右翻。
373 | * 当toPercent小于0则意味着此时动画组件不在初始位置,处于左翻中的状态,这样是可以右翻的。
374 | * 如果toPercent大于0,而此时处于第一个item,则无法右翻,因为右面没有item了。
375 | */
376 | return toPercent <= 0 || mCurrentPosition > 0;
377 | }
378 | else{
379 | /**
380 | * 是否可以左翻。
381 | * 当toPercent大于0则意味着此时动画组件不在初始位置,处于右翻中的状态,这样是可以左翻的。
382 | * 如果toPercent小于0,而此时处于最后一个item,则无法左翻,因为左面没有item了。
383 | */
384 | return toPercent >= 0 || mCurrentPosition < mAdapter.getCount() - 1;
385 | }
386 | }
387 | }
388 |
389 | /**
390 | * 装载或切换前景背景图
391 | * @param percent
392 | */
393 | public void switchAniamtionBitmap(float percent){
394 | //如果当前为初始状态即未翻转,或转变了翻转方向则需切换背景图
395 | if(mAnimationView.getAnimationPercent() == 0
396 | || mAnimationView.getAnimationPercent() * percent < 0) {
397 | //前景图是当前页面,即缓存页面中的第二个
398 | Bitmap frontBitmap = getViewBitmap(mCacheItems.get(1));
399 | Bitmap backBitmap = null;
400 | /**
401 | * 背景图根据翻转方向不同改变。
402 | * 如果要翻到上一页,则背景图为缓存页面中的第一个
403 | * 如果要翻到下一页,则背景图为缓存页面中的第二个
404 | */
405 | if (isVertical) {
406 | backBitmap = getViewBitmap(mCacheItems.get(mMoveY > 0 ? 0 : 2));
407 | } else {
408 | backBitmap = getViewBitmap(mCacheItems.get(mMoveX > 0 ? 0 : 2));
409 | }
410 | //初始化动画组件
411 | initAniamtionView(frontBitmap, backBitmap);
412 | }
413 | }
414 |
415 | public void setAnimationClass(Class extends AnimationViewInterface> clazz){
416 | animationClass = clazz;
417 | }
418 |
419 | /**
420 | * 创建动画组件
421 | * 如果组件以及存在且type一样,则不再创建新的
422 | */
423 | public void createAnimationView(){
424 | if(mAnimationView == null){
425 | try {
426 | Constructor extends AnimationViewInterface> constructor = animationClass.getConstructor(Context.class);
427 | mAnimationView = constructor.newInstance(getContext());
428 | } catch (Exception e) {
429 | e.printStackTrace();
430 | }
431 | }
432 | mAnimationView.setOnAnimationViewListener(new OnAnimationViewListener() {
433 | @Override
434 | public void pageNext() {
435 | AnimationListView.this.pageNext();
436 | }
437 |
438 | @Override
439 | public void pagePrevious() {
440 | AnimationListView.this.pagePrevious();
441 | }
442 | });
443 | }
444 |
445 | /**
446 | * 初始化动画组件
447 | * 主要是为动画组件添加前景背景图
448 | * @param frontBitmap
449 | * @param backBitmap
450 | */
451 | private void initAniamtionView(Bitmap frontBitmap, Bitmap backBitmap){
452 | mAnimationView.setBitmap(frontBitmap, backBitmap);
453 | }
454 | }
455 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/AnimationViewInterface.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.graphics.Bitmap;
4 | import android.view.MotionEvent;
5 |
6 | /**
7 | * @author chz
8 | * @description 动画组件接口
9 | * @date 2016/2/4 16:52
10 | */
11 | public interface AnimationViewInterface {
12 | /**
13 | * 初始化图片
14 | * @param frontBitmap 前景图片
15 | * @param backBitmap 背景图片
16 | */
17 | void setBitmap(Bitmap frontBitmap, Bitmap backBitmap);
18 | boolean isAnimationRunning();
19 |
20 | /**
21 | * 开启动画
22 | * 从当前状态到toPercent的状态
23 | * @param isVertical
24 | * @param event
25 | * @param toPercent 动画的最终位置百分比
26 | */
27 | void startAnimation(boolean isVertical, MotionEvent event, float toPercent);
28 | float getAnimationPercent();
29 |
30 | /**
31 | * 设置动画到某一帧的状态
32 | * 用于滑动过程中实时改变animationview的状态
33 | * @param percent 当前处于动画的位置百分比
34 | * @param event
35 | * @param isVertical
36 | */
37 | void setAnimationPercent(float percent, MotionEvent event, boolean isVertical);
38 | void setDuration(long duration);
39 | void setOnAnimationViewListener(OnAnimationViewListener onAnimationViewListener);
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/BlindsView.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ValueAnimator;
5 | import android.annotation.TargetApi;
6 | import android.content.Context;
7 | import android.graphics.Bitmap;
8 | import android.graphics.BitmapFactory;
9 | import android.graphics.Color;
10 | import android.os.Build;
11 | import android.os.Handler;
12 | import android.os.Message;
13 | import android.util.AttributeSet;
14 | import android.view.MotionEvent;
15 | import android.widget.LinearLayout;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | /**
21 | * 实现百叶窗动画的view
22 | * @author chz
23 | * @description
24 | * @date 2016/1/20 21:04
25 | */
26 | public class BlindsView extends LinearLayout implements AnimationViewInterface{
27 |
28 | /**
29 | * 每个叶面的动画时间
30 | */
31 | private long mDuration = 2000;
32 | /**
33 | * 每列/行叶面转动相差的角度
34 | */
35 | private float mSpace = 15;
36 |
37 | private float mAnimationPercent;
38 | private int mRowCount = 10;
39 | private int mColumnCount = 6;
40 |
41 | private ValueAnimator mAnimator;
42 | private OnAnimationViewListener mOnAnimationViewListener;
43 |
44 | public BlindsView(Context context) {
45 | super(context);
46 | init();
47 | }
48 |
49 | public BlindsView(Context context, AttributeSet attrs) {
50 | super(context, attrs);
51 | init();
52 | }
53 |
54 | public BlindsView(Context context, AttributeSet attrs, int defStyleAttr) {
55 | super(context, attrs, defStyleAttr);
56 | init();
57 | }
58 |
59 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
60 | public BlindsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
61 | super(context, attrs, defStyleAttr, defStyleRes);
62 | init();
63 | }
64 |
65 | private void init(){
66 | setOrientation(VERTICAL);
67 | setBackgroundColor(Color.BLACK);
68 | }
69 |
70 | /**
71 | * 初始化百叶窗图片
72 | * @param frontBitmapResource 前景图片
73 | * @param backBitmapResource 背景图片
74 | */
75 | public void setBitmap(int frontBitmapResource, int backBitmapResource){
76 | this.setBitmap(
77 | BitmapFactory.decodeResource(getContext().getResources(), frontBitmapResource),
78 | BitmapFactory.decodeResource(getContext().getResources(), backBitmapResource));
79 | }
80 |
81 |
82 | @Override
83 | public void setBitmap(Bitmap frontBitmap, Bitmap backBitmap){
84 | //处理图片
85 | List subFrontBitmaps = getSubBitmaps(mRowCount, mColumnCount, frontBitmap);
86 | List subBackBitmaps = getSubBitmaps(mRowCount, mColumnCount, backBitmap);
87 | setBitmaps(mRowCount, mColumnCount, subFrontBitmaps, subBackBitmaps);
88 | }
89 |
90 | /**
91 | * 获取图片阵列
92 | * 将大图片分割为rowCount*columnCount阵列的小图片
93 | * @param rowCount
94 | * @param columnCount
95 | * @param bitmap
96 | * @return
97 | */
98 | private List getSubBitmaps(int rowCount, int columnCount, Bitmap bitmap){
99 | List subBitmaps = new ArrayList();
100 | int subWidth = bitmap.getWidth() / columnCount;
101 | int subHeight = bitmap.getHeight() / rowCount;
102 | for(int i = 0; i < rowCount; i++){
103 | for(int j = 0; j < columnCount; j++){
104 | /**
105 | * 这里计算每个叶面图片的大小
106 | * 由于有余数,所以最后一张图片大小单独计算
107 | */
108 | int height = i == rowCount - 1 ? bitmap.getHeight() - subHeight * i : subHeight;
109 | int width = j == columnCount - 1 ? bitmap.getWidth() - subWidth * j : subWidth;
110 | Bitmap subBitmap = Bitmap.createBitmap(bitmap, subWidth * j, subHeight * i, width, height);
111 | subBitmaps.add(subBitmap);
112 | }
113 | }
114 | return subBitmaps;
115 | }
116 |
117 | /**
118 | * 设置图片阵列
119 | * 将前景和背景图片的阵列放入每个rotateview中
120 | * @param rowCount
121 | * @param columnCount
122 | * @param mFrontBitmaps
123 | * @param mBackBitmaps
124 | */
125 | private void setBitmaps(int rowCount, int columnCount, List mFrontBitmaps, List mBackBitmaps){
126 | /**
127 | * 为了复用,需要做些处理
128 | * 首先判断现有行/列是否多余,多余直接remove,不足补充
129 | */
130 | //最大行数,是取现有行数和目标行数的最大值。
131 | int maxRow = Math.max(getChildCount() , rowCount);
132 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
133 | LinearLayout.LayoutParams.MATCH_PARENT, 1);
134 | params.weight = 1;
135 | for(int i = 0; i < maxRow; i++){
136 | LinearLayout subView = null;
137 | if(i >= getChildCount() && i < rowCount){
138 | //如果现有行数不足,则补充。每一行都是水平的linearlayout
139 | subView = new LinearLayout(getContext());
140 | subView.setOrientation(HORIZONTAL);
141 | addView(subView, params);
142 | }
143 | else if(i < getChildCount() && i >= rowCount){
144 | //如果现有行数过多,则移除
145 | removeViewAt(i);
146 | i--;
147 | maxRow--;
148 | }
149 | else{
150 | subView = (LinearLayout)getChildAt(i);
151 | }
152 | //开始处理每一行中的每项
153 | if(subView != null){
154 | //最大列数,是取现有列数和目标列数的最大值。
155 | int maxColumn = Math.max(subView.getChildCount() , columnCount);
156 | LinearLayout.LayoutParams subParams = new LinearLayout.LayoutParams(
157 | 1, LinearLayout.LayoutParams.MATCH_PARENT);
158 | subParams.weight = 1;
159 | for(int j = 0; j < maxColumn; j++){
160 | RotateView rotateView = null;
161 | if(j >= columnCount && j < subView.getChildCount()){
162 | //如果现有列过多,则移除
163 | subView.removeViewAt(j);
164 | j--;
165 | maxColumn--;
166 | }
167 | else if(j < columnCount && j >= subView.getChildCount()){
168 | //如果现有列不足,则补充。每个叶面是RotateView
169 | rotateView = new RotateView(getContext());
170 | subView.addView(rotateView, subParams);
171 | }
172 | else{
173 | rotateView = (RotateView)subView.getChildAt(j);
174 | }
175 | //为重新整理好的矩阵填充图片
176 | if(rotateView != null){
177 | int index = i * columnCount + j;
178 | rotateView.setBitmap(mFrontBitmaps.get(index), mBackBitmaps.get(index));
179 | rotateView.setScaleMin(0.5f);
180 | }
181 | }
182 | }
183 | }
184 | }
185 |
186 | /**
187 | * 设置百叶窗的行列数
188 | * 注意这个方法一定要在setBitmap()之前执行才有效果
189 | * @param rowCount
190 | * @param columnCount
191 | */
192 | public void setRowsAndColumns(int rowCount, int columnCount){
193 | mRowCount = rowCount;
194 | mColumnCount = columnCount;
195 | }
196 |
197 | @Override
198 | public boolean isAnimationRunning(){
199 | if(mAnimator == null){
200 | return false;
201 | }
202 | return mAnimator.isRunning();
203 | }
204 |
205 | @Override
206 | public float getAnimationPercent(){
207 | return mAnimationPercent;
208 | }
209 |
210 | @Override
211 | public void setAnimationPercent(float percent, MotionEvent event, boolean isVertical){
212 | mAnimationPercent = percent;
213 | //获取总的转动的角度
214 | float value = mAnimationPercent * getTotalVaule(isVertical);
215 | /**
216 | * 遍历每一个小叶面设置当前的角度
217 | * 根据转动的方向不同,从不同的位置开始翻转
218 | */
219 | for(int i = 0; i < mRowCount; i++){
220 | LinearLayout parent = (LinearLayout)getChildAt(i);
221 | for(int j = 0; j < mColumnCount; j++){
222 | RotateView view = (RotateView)parent.getChildAt(j);
223 | float subValue;
224 | if(value > 0){
225 | if(isVertical){
226 | //向下滑动。从第一行开始转动,每行转动角度依次递减
227 | subValue = value - mSpace * i;
228 | }
229 | else{
230 | //向右滑动。从第一列开始转动,每列转动角度依次递减
231 | subValue = value - mSpace * j;
232 | }
233 | //保证转动角度在0到180度内
234 | if(subValue < 0){
235 | subValue = 0;
236 | }
237 | else if(subValue > 180){
238 | subValue = 180;
239 | }
240 | }
241 | else{
242 | if(isVertical){
243 | //向下滑动。从最后一行开始转动,每行转动角度依次递减(注意由于value是负数,所以数值上是递增)
244 | subValue = value + mSpace * (mRowCount - i - 1);
245 | }
246 | else{
247 | //向左滑动。从最后一列开始转动,每列转动角度依次递减(注意由于value是负数,所以数值上是递增)
248 | subValue = value + mSpace * (mColumnCount - j - 1);
249 | }
250 | //保证转动角度在0到-180度内
251 | if(subValue < -180){
252 | subValue = -180;
253 | }
254 | else if(subValue > 0){
255 | subValue = 0;
256 | }
257 | }
258 | //注意,如果是上下翻动,角度需要转为负值,否则转动的方向有误
259 | view.setRotation(isVertical ? -subValue : subValue, isVertical);
260 | }
261 | }
262 | }
263 |
264 |
265 | /**
266 | * 获取一次翻面需要的总的转动角度
267 | * @param isVertical
268 | * @return
269 | */
270 | private float getTotalVaule(boolean isVertical){
271 | if(isVertical) {
272 | return mSpace * (mRowCount - 1) + 180;
273 | }
274 | else{
275 | return mSpace * (mColumnCount - 1) + 180;
276 | }
277 | }
278 |
279 | @Override
280 | public void setDuration(long duration) {
281 | mDuration = duration;
282 | }
283 |
284 | /**
285 | * 设置每队叶面翻转相差的角度,即控制叶面翻转速度
286 | * @param space
287 | */
288 | public void setSpace(float space) {
289 | mSpace = space;
290 | }
291 |
292 |
293 | @Override
294 | public void setOnAnimationViewListener(OnAnimationViewListener onAnimationViewListener) {
295 | mOnAnimationViewListener = onAnimationViewListener;
296 | }
297 |
298 | @Override
299 | public void startAnimation(boolean isVertical, MotionEvent event, float toPercent){
300 | if(mAnimator != null && mAnimator.isRunning()){
301 | return;
302 | }
303 | mAnimator = ValueAnimator.ofFloat(mAnimationPercent, toPercent);
304 | //动画持续时间根据起始位置不同
305 | mAnimator.setDuration((long) (Math.abs(toPercent - mAnimationPercent) * mDuration));
306 | mAnimator.start();
307 | OnAnimationListener onAnimationListener = new OnAnimationListener(isVertical, toPercent);
308 | mAnimator.addUpdateListener(onAnimationListener);
309 | mAnimator.addListener(onAnimationListener);
310 | }
311 |
312 | class OnAnimationListener implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener{
313 | private boolean isVertical;
314 | private float toPercent;
315 | public OnAnimationListener(boolean isVertical, float toPercent){
316 | this.isVertical = isVertical;
317 | this.toPercent = toPercent;
318 | }
319 | @Override
320 | public void onAnimationUpdate(ValueAnimator animation) {
321 | setAnimationPercent((float)animation.getAnimatedValue(), null, isVertical);
322 | }
323 |
324 | @Override
325 | public void onAnimationStart(Animator animation) {
326 | }
327 |
328 | @Override
329 | public void onAnimationEnd(Animator animation) {
330 | mAnimationPercent = 0;
331 | if(mOnAnimationViewListener == null){
332 | return;
333 | }
334 | if(toPercent == 1){
335 | mOnAnimationViewListener.pagePrevious();
336 | }
337 | else if(toPercent == -1){
338 | mOnAnimationViewListener.pageNext();
339 | }
340 | }
341 |
342 | @Override
343 | public void onAnimationCancel(Animator animation) {
344 | mAnimationPercent = 0;
345 | }
346 |
347 | @Override
348 | public void onAnimationRepeat(Animator animation) {
349 | }
350 | }
351 |
352 | }
353 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/BookPageView.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ValueAnimator;
5 | import android.annotation.TargetApi;
6 | import android.content.Context;
7 | import android.graphics.Bitmap;
8 | import android.graphics.BlurMaskFilter;
9 | import android.graphics.Canvas;
10 | import android.graphics.Color;
11 | import android.graphics.Paint;
12 | import android.graphics.Path;
13 | import android.graphics.Point;
14 | import android.graphics.Rect;
15 | import android.graphics.drawable.BitmapDrawable;
16 | import android.graphics.drawable.GradientDrawable;
17 | import android.os.Build;
18 | import android.util.AttributeSet;
19 | import android.util.Log;
20 | import android.view.MotionEvent;
21 | import android.view.View;
22 |
23 | /**
24 | * @author chz
25 | * @description
26 | * @date 2016/2/17 16:11
27 | */
28 | public class BookPageView extends View implements AnimationViewInterface {
29 | private static final int PAGE_DIRECTION_NO = 0;
30 | private static final int PAGE_DIRECTION_UP = 1;
31 | private static final int PAGE_DIRECTION_DOWN = 2;
32 | private static final int PAGE_DIRECTION_CENTER = 3;
33 |
34 | private float mPercent;
35 | private int mPageDirection = PAGE_DIRECTION_NO;
36 | private int mTouchX;
37 | private int mTouchY;
38 | private int mTouchAction = -1;
39 |
40 | private Bitmap mFrontBitmap;
41 | private Bitmap mBackBitmap;
42 |
43 | private ValueAnimator mBookAnimator;
44 |
45 | public BookPageView(Context context) {
46 | super(context);
47 | }
48 |
49 | public BookPageView(Context context, AttributeSet attrs) {
50 | super(context, attrs);
51 | }
52 |
53 | public BookPageView(Context context, AttributeSet attrs, int defStyleAttr) {
54 | super(context, attrs, defStyleAttr);
55 | }
56 |
57 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
58 | public BookPageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
59 | super(context, attrs, defStyleAttr, defStyleRes);
60 | }
61 |
62 | @Override
63 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
64 | if(w > 0 && h > 0){
65 | mTouchX = w;
66 | mTouchY = h;
67 | }
68 | super.onSizeChanged(w, h, oldw, oldh);
69 | }
70 |
71 | @Override
72 | protected void onDraw(Canvas canvas) {
73 | if(mFrontBitmap == null || mBackBitmap == null){
74 | return;
75 | }
76 | if(mTouchAction == MotionEvent.ACTION_DOWN || getHeight() == 0 || getWidth() == 0){
77 | return;
78 | }
79 |
80 | /**
81 | * 移动时判断是向上,向下或向左卷页
82 | */
83 | if(mTouchAction == MotionEvent.ACTION_MOVE && mPageDirection == PAGE_DIRECTION_NO){
84 | if(mTouchY < getHeight() / 3){
85 | mPageDirection = PAGE_DIRECTION_DOWN;
86 | }
87 | else if(mTouchY < getHeight() * 2 / 3){
88 | mPageDirection = PAGE_DIRECTION_CENTER;
89 | }
90 | else{
91 | mPageDirection = PAGE_DIRECTION_UP;
92 | }
93 | }
94 |
95 |
96 | /**
97 | * 计算卷页用到的几个顶点
98 | */
99 | Point curveXStart = new Point();
100 | Point curveXEnd = new Point();
101 | Point curveXCenter = new Point();
102 | Point curveXControl = new Point();
103 | Point curveYEnd = new Point();
104 | Point curveYCenter = new Point();
105 | Point curveYControl = new Point();
106 | Point zeroPoint = new Point(getWidth(), getHeight());
107 |
108 | //上边或下边卷曲的起始点,注意这个是计算点,未必是实际卷曲点
109 | int tmpX;
110 | //右边卷曲的起始点,注意这个是计算点,未必是实际卷曲点
111 | int tmpY;
112 | //实际的卷曲点与touch点的百分比。
113 | float tmpPercent = 1;
114 | switch (mPageDirection){
115 | case PAGE_DIRECTION_CENTER:
116 | zeroPoint = new Point(getWidth(), 0);
117 | break;
118 | case PAGE_DIRECTION_UP:
119 | zeroPoint = new Point(getWidth(), getHeight());
120 | break;
121 | case PAGE_DIRECTION_DOWN:
122 | zeroPoint = new Point(getWidth(), 0);
123 | break;
124 | }
125 |
126 | curveXStart.y = zeroPoint.y;
127 | curveYEnd.x = zeroPoint.x;
128 | int relativeX = zeroPoint.x - mTouchX;
129 | int relativeY = zeroPoint.y - mTouchY;
130 | if(relativeX < 0){
131 | relativeX = 0;
132 | }
133 | if((relativeY > 0) == (zeroPoint.y == 0)){
134 | relativeY = 0;
135 | }
136 |
137 | //未卷页的部分
138 | Path unFoldPath = new Path();
139 | //卷页的部分
140 | Path foldPath = new Path();
141 |
142 | /**
143 | * 计算当卷页垂直时的各部分
144 | */
145 | if(mPageDirection == PAGE_DIRECTION_CENTER || Math.abs(relativeY) == 0){
146 | unFoldPath.moveTo(0, 0);
147 | unFoldPath.lineTo(mTouchX, 0);
148 | unFoldPath.lineTo(mTouchX, getHeight());
149 | unFoldPath.lineTo(0, getHeight());
150 | unFoldPath.close();
151 |
152 | foldPath.moveTo(mTouchX, 0);
153 | foldPath.lineTo(getWidth() - relativeX * 3 / 4, 0);
154 | foldPath.lineTo(getWidth() - relativeX * 3 / 4, getHeight());
155 | foldPath.lineTo(mTouchX, getHeight());
156 | foldPath.close();
157 |
158 | curveXEnd.x = mTouchX;
159 | curveXEnd.y = zeroPoint.y;
160 | curveXStart.x = mTouchX;
161 | curveXStart.y = zeroPoint.y;
162 | }
163 | /**
164 | * 计算当卷页不垂直时的各部分
165 | */
166 | else {
167 | /**
168 | * 计算卷页的各个点
169 | */
170 | tmpX = relativeX == 0 ? zeroPoint.x : zeroPoint.x - (int)((Math.pow(relativeY, 2) + Math.pow(relativeX, 2)) / relativeX);
171 | tmpY = zeroPoint.y - (int)((Math.pow(relativeY, 2) + Math.pow(relativeX, 2)) / relativeY);
172 | if (tmpX < 0) {
173 | tmpPercent = (float)zeroPoint.x / (zeroPoint.x - tmpX);
174 | curveXStart.x = 0;
175 | } else {
176 | curveXStart.x = tmpX;
177 | }
178 | curveYEnd.y = zeroPoint.y - (int)((zeroPoint.y - tmpY) * tmpPercent);
179 |
180 | curveXControl = getCenterPoint(curveXStart, zeroPoint);
181 | curveYControl = getCenterPoint(curveYEnd, zeroPoint);
182 | curveXEnd.x = zeroPoint.x - (int)((zeroPoint.x - mTouchX) * tmpPercent);
183 | curveXEnd.y = zeroPoint.y - (int)((zeroPoint.y - mTouchY) * tmpPercent);
184 |
185 | curveXCenter = getCenterPoint(getCenterPoint(curveXStart, curveXEnd), curveXControl);
186 | curveYCenter = getCenterPoint(getCenterPoint(curveYEnd, curveXEnd), curveYControl);
187 |
188 | /**
189 | * 计算卷页部分的路径
190 | */
191 | foldPath.moveTo(curveXCenter.x, curveXCenter.y);
192 | foldPath.moveTo(curveXCenter.x, curveXCenter.y);
193 | foldPath.quadTo(getCenterPoint(curveXControl, curveXEnd).x, getCenterPoint(curveXControl, curveXEnd).y, curveXEnd.x, curveXEnd.y);
194 | foldPath.quadTo(getCenterPoint(curveYControl, curveXEnd).x, getCenterPoint(curveYControl, curveXEnd).y, curveYCenter.x, curveYCenter.y);
195 | foldPath.close();
196 |
197 | /**
198 | * 计算非卷页部分的路径
199 | */
200 | unFoldPath.moveTo(0, 0);
201 | unFoldPath.lineTo(0, zeroPoint.y);
202 | unFoldPath.lineTo(curveXStart.x, curveXStart.y);
203 | unFoldPath.quadTo(curveXControl.x, curveXControl.y, curveXEnd.x, curveXEnd.y);
204 | unFoldPath.quadTo(curveYControl.x, curveYControl.y, curveYEnd.x, curveYEnd.y);
205 | unFoldPath.lineTo(getWidth(), getHeight() - zeroPoint.y);
206 | unFoldPath.lineTo(0, getHeight() - zeroPoint.y);
207 | unFoldPath.close();
208 | }
209 |
210 |
211 | float degree = -90 - (float) (180 * Math.atan2(curveXEnd.x - zeroPoint.x, curveXEnd.y - zeroPoint.y) / Math.PI);
212 |
213 | /**
214 | * 阴影1
215 | * 是卷页后面的阴影
216 | */
217 | canvas.save();
218 | canvas.rotate(degree, curveXStart.x, curveXStart.y);
219 | GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
220 | new int[]{Color.BLACK, Color.TRANSPARENT});
221 | gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
222 | int y = (int)Math.sqrt(Math.pow(zeroPoint.x - curveXStart.x, 2) + Math.pow(getHeight(), 2));
223 | int foldShadeWidth = (int)Math.sqrt(Math.pow(zeroPoint.x - curveXEnd.x, 2) + Math.pow(zeroPoint.y - curveXEnd.y, 2)) / 2;
224 | gradientDrawable.setBounds(curveXStart.x, zeroPoint.y == 0 ? 0 : zeroPoint.y - y, curveXStart.x + foldShadeWidth, zeroPoint.y == 0 ? y : curveXStart.y);
225 | gradientDrawable.draw(canvas);
226 | canvas.restore();
227 |
228 | /**
229 | * 画未卷页部分
230 | */
231 | canvas.save();
232 | canvas.clipPath(unFoldPath);
233 | Rect src = new Rect(0, 0, mFrontBitmap.getWidth(), mFrontBitmap.getHeight());
234 | Rect dst = new Rect(0, 0, getWidth(), getHeight());
235 | canvas.drawBitmap(mFrontBitmap, src, dst, null);
236 | canvas.restore();
237 |
238 | /**
239 | * 阴影2
240 | * 卷页前面的阴影。注意想阴影显示正常,需要将targetSdkVersion设置为14以下
241 | */
242 | Paint p = new Paint();
243 | p.setColor(Color.BLACK);
244 | p.setShadowLayer(20, -5, 0, Color.BLACK);
245 | canvas.drawPath(foldPath, p);
246 |
247 | /**
248 | * 画卷页部分
249 | */
250 | canvas.save();
251 | canvas.clipPath(foldPath);
252 | canvas.drawColor(Color.RED);
253 | canvas.restore();
254 |
255 |
256 | super.onDraw(canvas);
257 | }
258 |
259 | @Override
260 | public void setBitmap(Bitmap frontBitmap, Bitmap backBitmap) {
261 | mFrontBitmap = frontBitmap;
262 | mBackBitmap = backBitmap;
263 | }
264 |
265 | @Override
266 | public boolean isAnimationRunning() {
267 | return false;
268 | }
269 |
270 | @Override
271 | public void startAnimation(boolean isVertical, MotionEvent event, float toPercent) {
272 | if(mBookAnimator != null && mBookAnimator.isRunning()){
273 | return;
274 | }
275 | if(getHeight() == 0 || getWidth() == 0){
276 | return;
277 | }
278 |
279 | mTouchAction = event.getAction();
280 | mTouchX = (int)event.getX();
281 | mTouchY = (int)event.getY();
282 |
283 | mBookAnimator = ValueAnimator.ofInt(mTouchX, -getWidth() / 3);
284 | mBookAnimator.setDuration(3000);
285 | mBookAnimator.start();
286 | OnAnimationListener onAnimationListener = new OnAnimationListener(mTouchX, mTouchY, mPageDirection);
287 | mBookAnimator.addUpdateListener(onAnimationListener);
288 | mBookAnimator.addListener(onAnimationListener);
289 | }
290 |
291 | @Override
292 | public float getAnimationPercent() {
293 | return mPercent;
294 | }
295 |
296 | @Override
297 | public void setAnimationPercent(float percent, MotionEvent event, boolean isVertical) {
298 | setBackgroundDrawable(new BitmapDrawable(mBackBitmap));
299 | mTouchAction = event.getAction();
300 | mTouchX = (int)event.getX();
301 | mTouchY = (int)event.getY();
302 | invalidate();
303 | }
304 |
305 | @Override
306 | public void setDuration(long duration) {
307 |
308 | }
309 |
310 | @Override
311 | public void setOnAnimationViewListener(OnAnimationViewListener onAnimationViewListener) {
312 |
313 | }
314 |
315 | private Point getCenterPoint(Point a, Point b){
316 | Point reault = new Point();
317 | reault.x = (a.x + b.x) / 2;
318 | reault.y = (a.y + b.y) / 2;
319 | return reault;
320 | }
321 |
322 | @Override
323 | public boolean onTouchEvent(MotionEvent event) {
324 | switch (event.getAction()){
325 | case MotionEvent.ACTION_DOWN:
326 | break;
327 | case MotionEvent.ACTION_MOVE:
328 | setAnimationPercent(0, event, true);
329 | break;
330 | case MotionEvent.ACTION_UP:
331 | startAnimation(true, event, 0);
332 | break;
333 | }
334 | return true;
335 | }
336 |
337 |
338 | class OnAnimationListener implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener{
339 | private int mStartX;
340 | private int mStartY;
341 | private int mPageDirection;
342 | public OnAnimationListener(int startX, int startY, int pageDirection){
343 | mStartX = startX;
344 | mStartY = startY;
345 | mPageDirection = pageDirection;
346 | }
347 |
348 | @Override
349 | public void onAnimationUpdate(ValueAnimator animation) {
350 | mTouchX = (int)animation.getAnimatedValue();
351 | switch (mPageDirection){
352 | case PAGE_DIRECTION_DOWN:
353 | if(mTouchX > 0 && mStartX > 0) {
354 | mTouchY = mTouchX * mStartY / mStartX;
355 | }
356 | else{
357 | mTouchY = 0;
358 | }
359 | break;
360 | case PAGE_DIRECTION_UP:
361 | if(mTouchX > 0 && mStartX > 0) {
362 | mTouchY = getHeight() - mTouchX * (getHeight() - mStartY) / mStartX;
363 | }
364 | else{
365 | mTouchY = getHeight();
366 | }
367 | break;
368 | case PAGE_DIRECTION_CENTER:
369 | break;
370 | }
371 | invalidate();
372 | }
373 |
374 | @Override
375 | public void onAnimationStart(Animator animation) {
376 | }
377 |
378 | @Override
379 | public void onAnimationEnd(Animator animation) {
380 | }
381 |
382 | @Override
383 | public void onAnimationCancel(Animator animation) {
384 | }
385 |
386 | @Override
387 | public void onAnimationRepeat(Animator animation) {
388 | }
389 | }
390 |
391 | }
392 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/CurveFolioView.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.annotation.TargetApi;
6 | import android.content.Context;
7 | import android.graphics.Bitmap;
8 | import android.graphics.Canvas;
9 | import android.graphics.Color;
10 | import android.graphics.LinearGradient;
11 | import android.graphics.Paint;
12 | import android.graphics.Rect;
13 | import android.graphics.Shader;
14 | import android.os.Build;
15 | import android.util.AttributeSet;
16 | import android.util.Log;
17 | import android.view.MotionEvent;
18 | import android.view.View;
19 |
20 | import com.huichongzi.fastwidget4android.utils.ThirdOrderBezierCurve;
21 |
22 | /**
23 | * 对折翻转view
24 | * 可以实现俩张图片对折翻转效果
25 | * Created by chz on 2015/12/8.
26 | */
27 | public class CurveFolioView extends View implements AnimationViewInterface{
28 | /**
29 | * 翻转时拉伸变形的最大值
30 | * 即翻转90度时,最外边长度的增加倍数
31 | */
32 | private static final float FOLIO_SCALE = 0.5f;
33 | /**
34 | * 翻转时阴影效果最大值
35 | */
36 | private static final int FOLIO_SHADOW_ALPHA = 100;
37 |
38 | /**
39 | * 当前翻转的位置
40 | * 即翻转的最外边在屏幕上的y坐标
41 | */
42 | private float mFolioY;
43 | private float mCurrentPercent;
44 | private long mduration = 2000;
45 |
46 | /**
47 | * 前景图上半部分
48 | */
49 | private Bitmap mFrontBitmapTop;
50 | /**
51 | * 背景图上半部分
52 | */
53 | private Bitmap mBackBitmapTop;
54 |
55 | /**
56 | * 前景图下半部分
57 | */
58 | private Bitmap mFrontBitmapBottom;
59 | /**
60 | * 背景图下半部分
61 | */
62 | private Bitmap mBackBitmapBottom;
63 | /**
64 | * 翻转中的图片
65 | * 根据情况,会是mTopBitmap的下半部分或上半部分
66 | */
67 | private Bitmap mFolioBitmap;
68 | private ObjectAnimator mFolioAnimation;
69 | private OnAnimationViewListener mOnAnimationViewListener;
70 |
71 | private float[] verts = new float[201 * 201 * 2];
72 |
73 | public CurveFolioView(Context context) {
74 | super(context);
75 | init();
76 | }
77 |
78 | public CurveFolioView(Context context, AttributeSet attrs) {
79 | super(context, attrs);
80 | init();
81 | }
82 |
83 | public CurveFolioView(Context context, AttributeSet attrs, int defStyleAttr) {
84 | super(context, attrs, defStyleAttr);
85 | init();
86 | }
87 |
88 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
89 | public CurveFolioView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
90 | super(context, attrs, defStyleAttr, defStyleRes);
91 | init();
92 | }
93 |
94 | private void init() {
95 | }
96 |
97 | @Override
98 | public void setBitmap(Bitmap frontBitmap, Bitmap backBitmap) {
99 | mFrontBitmapTop = Bitmap.createBitmap(frontBitmap, 0, 0, frontBitmap.getWidth(), frontBitmap.getHeight() / 2);
100 | mBackBitmapTop = Bitmap.createBitmap(backBitmap, 0, 0, backBitmap.getWidth(), backBitmap.getHeight() / 2);
101 |
102 | mFrontBitmapBottom = Bitmap.createBitmap(frontBitmap, 0, frontBitmap.getHeight() / 2, frontBitmap.getWidth(), frontBitmap.getHeight() / 2);
103 | mBackBitmapBottom = Bitmap.createBitmap(backBitmap, 0, backBitmap.getHeight() / 2, backBitmap.getWidth(), backBitmap.getHeight() / 2);
104 | }
105 |
106 |
107 | /**
108 | * @hide
109 | */
110 | public float getFolioY() {
111 | return mFolioY;
112 | }
113 |
114 | /**
115 | * @hide
116 | */
117 | public void setFolioY(float folioY) {
118 | mFolioY = folioY;
119 | invalidate();
120 | }
121 |
122 | @Override
123 | protected void onDraw(Canvas canvas) {
124 | super.onDraw(canvas);
125 | if (mFrontBitmapTop == null || mBackBitmapTop == null) {
126 | return;
127 | }
128 | if(getHeight() <= 0){
129 | return;
130 | }
131 | /**
132 | * 计算翻转的比率
133 | * 用于计算图片的拉伸和阴影效果
134 | */
135 | float rate;
136 | if (mFolioY >= getHeight() / 2) {
137 | rate = (float) (getHeight() - mFolioY) * 2 / getHeight();
138 | } else {
139 | rate = (float) mFolioY * 2 / getHeight();
140 | }
141 |
142 | /**
143 | * 根据上翻下翻判断上下的图片
144 | */
145 | Bitmap topBitmap = null;
146 | Bitmap bottomBitmap = null;
147 |
148 | Bitmap topBitmapFolie = null;
149 | Bitmap bottomBitmapFolie = null;
150 | if(mCurrentPercent < 0){
151 | topBitmap = mFrontBitmapTop;
152 | bottomBitmap = mBackBitmapBottom;
153 | topBitmapFolie = mFrontBitmapBottom;
154 | bottomBitmapFolie = mBackBitmapTop;
155 | }
156 | else if(mCurrentPercent > 0){
157 | topBitmap = mBackBitmapTop;
158 | bottomBitmap = mFrontBitmapBottom;
159 | topBitmapFolie = mBackBitmapBottom;
160 | bottomBitmapFolie = mFrontBitmapTop ;
161 | }
162 | if (topBitmap == null || bottomBitmap == null) {
163 | return;
164 | }
165 | /**
166 | * 在上半部分绘制topBitmap
167 | */
168 | Rect topHoldSrc = new Rect(0, 0, topBitmap.getWidth(), topBitmap.getHeight());
169 | Rect topHoldDst = new Rect(0, 0, getWidth(), getHeight() / 2);
170 | canvas.drawBitmap(topBitmap, topHoldSrc, topHoldDst, null);
171 |
172 | /**
173 | * 在下半部分绘制bottomBitmap
174 | */
175 | Rect bottomHoldSrc = new Rect(0, 0, bottomBitmap.getWidth(), bottomBitmap.getHeight());
176 | Rect bottomHoldDst = new Rect(0, getHeight() / 2, getWidth(), getHeight());
177 | canvas.drawBitmap(bottomBitmap, bottomHoldSrc, bottomHoldDst, null);
178 |
179 | /**
180 | * 绘制阴影(光线在正前方)
181 | * 阴影与翻转是在同一区域,并且根据翻转程度改变
182 | */
183 | // Paint shadowP = new Paint();
184 | // shadowP.setColor(0xff000000);
185 | // shadowP.setAlpha((int) ((1 - rate) * FOLIO_SHADOW_ALPHA));
186 | // if (mFolioY >= getHeight() / 2) {
187 | // canvas.drawRect(bottomHoldDst, shadowP);
188 | // } else {
189 | // canvas.drawRect(topHoldDst, shadowP);
190 | // }
191 |
192 | /**
193 | * 绘制阴影(光线在上方,更真实)
194 | * 阴影一直在下面的区域,并且根据翻转程度改变范围,并渐变
195 | */
196 | LinearGradient gradient = new LinearGradient(0, getHeight() / 2, 0, getHeight() / 2 + mFolioY / 2, 0x80000000, Color.TRANSPARENT, Shader.TileMode.MIRROR);
197 | Paint shadowP = new Paint();
198 | shadowP.setShader(gradient);
199 | Rect shadowRect = new Rect(0, getHeight() / 2, getWidth(), getHeight() / 2 + (int)mFolioY / 2);
200 | canvas.drawRect(shadowRect, shadowP);
201 |
202 | /**
203 | * 绘制翻转效果的图片
204 | * 翻转图片是一个梯形,根据情况梯形大小位置等不相同
205 | */
206 | mFolioBitmap = null;
207 | float w = topBitmap.getWidth();
208 | float h = topBitmap.getHeight();
209 | ThirdOrderBezierCurve thirdOrderBezierCurve;
210 | if (mFolioY >= getHeight() / 2) {
211 | //当翻转位置在中部偏下时,取topBitmapFolie,同时绘制区域为一个正梯形
212 | mFolioBitmap = topBitmapFolie;
213 | thirdOrderBezierCurve = new ThirdOrderBezierCurve(new float[]{0, h}, new float[]{0, h * 2 - h * rate}, new float[]{-w * rate / 2, h * 2 - h * rate});
214 | int index = 0;
215 | for(int i = 0; i < 201; i++){
216 | float fy = h / 200 * i;
217 | fy = h + fy * (1- rate);
218 | float[] p = thirdOrderBezierCurve.getPointBy01(new float[]{0, fy});
219 | float rateW = (w - p[0] * 2) / w;
220 | for(int j = 0; j < 201; j++){
221 | float fx = w / 200 * j;
222 | verts[index * 2] = p[0] + fx * rateW;
223 | verts[index * 2 + 1] = p[1];
224 | index++;
225 | }
226 | }
227 |
228 | } else {
229 | //当翻转位置在中部偏上时,取bottomBitmapFolie,同时绘制区域为一个倒梯形
230 | mFolioBitmap = bottomBitmapFolie;
231 | thirdOrderBezierCurve = new ThirdOrderBezierCurve(new float[]{-w * rate / 2, h * rate}, new float[]{-w * rate / 2, h}, new float[]{0, h});
232 | int index = 0;
233 | for(int i = 0; i < 201; i++){
234 | float fy = h / 200 * i;
235 | fy = h * rate + h * (1 - rate) * fy / h;
236 | float[] p = thirdOrderBezierCurve.getPointBy01(new float[]{-w * rate / 2, fy});
237 | float rateW = (w - p[0] * 2) / w;
238 | for(int j = 0; j < 201; j++){
239 | float fx = w / 200 * j;
240 | verts[index * 2] = p[0] + fx * rateW;
241 | verts[index * 2 + 1] = p[1];
242 | index++;
243 | }
244 | }
245 | }
246 |
247 |
248 | canvas.drawBitmapMesh(mFolioBitmap, 200, 200, verts, 0, null, 0, null);
249 |
250 | invalidate();
251 | }
252 |
253 |
254 | @Override
255 | public boolean isAnimationRunning() {
256 | return mFolioAnimation != null && mFolioAnimation.isRunning();
257 | }
258 |
259 | @Override
260 | public void startAnimation(boolean isVertical, MotionEvent event, final float toPercent) {
261 | if(!isVertical){
262 | return;
263 | }
264 | if(getHeight() <= 0){
265 | return;
266 | }
267 | /**
268 | * 播放翻转动画
269 | * 先计算动画结束的位置,然后设定动画从当前位置翻到结束点
270 | * 动画的实质上是不停改变翻转位置并重绘
271 | */
272 | float endPosition = 0;
273 | if (mCurrentPercent < 0) {
274 | endPosition = toPercent == 0 ? getHeight() : 0;
275 | } else{
276 | endPosition = toPercent == 0 ? 0 : getHeight();
277 | }
278 | mFolioAnimation = ObjectAnimator.ofFloat(this, "folioY", endPosition);
279 | mFolioAnimation.setDuration((long)(mduration * Math.abs(toPercent - mCurrentPercent)));
280 | mFolioAnimation.addListener(new Animator.AnimatorListener() {
281 | @Override
282 | public void onAnimationStart(Animator animation) {
283 | }
284 |
285 | @Override
286 | public void onAnimationEnd(Animator animation) {
287 | mCurrentPercent = 0;
288 | if(mOnAnimationViewListener != null){
289 | if(toPercent == 1){
290 | mOnAnimationViewListener.pagePrevious();
291 | }
292 | else if(toPercent == -1){
293 | mOnAnimationViewListener.pageNext();
294 | }
295 | }
296 | }
297 |
298 | @Override
299 | public void onAnimationCancel(Animator animation) {
300 | }
301 |
302 | @Override
303 | public void onAnimationRepeat(Animator animation) {
304 | }
305 | });
306 | mFolioAnimation.start();
307 | }
308 |
309 | @Override
310 | public float getAnimationPercent() {
311 | return mCurrentPercent;
312 | }
313 |
314 | @Override
315 | public void setAnimationPercent(float percent, MotionEvent event, boolean isVertical) {
316 | if(!isVertical){
317 | return;
318 | }
319 | if(getHeight() <= 0){
320 | return;
321 | }
322 | /**
323 | * 计算翻转的位置
324 | * 如果位置超出了区域,则完成翻转
325 | */
326 | mFolioY = percent > 0 ? percent * getHeight() : (1 + percent) * getHeight();
327 | invalidate();
328 | mCurrentPercent = percent;
329 | }
330 |
331 | @Override
332 | public void setDuration(long duration) {
333 | mduration = duration;
334 | }
335 |
336 | @Override
337 | public void setOnAnimationViewListener(OnAnimationViewListener onAnimationViewListener) {
338 | mOnAnimationViewListener = onAnimationViewListener;
339 | }
340 |
341 |
342 | }
343 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/FloatInputView.kt:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.graphics.Rect
6 | import android.os.Build
7 | import android.support.annotation.IdRes
8 | import android.support.annotation.LayoutRes
9 | import android.support.annotation.RequiresApi
10 | import android.util.AttributeSet
11 | import android.view.LayoutInflater
12 | import android.view.View
13 | import android.view.Gravity
14 | import android.view.ViewTreeObserver
15 | import android.view.inputmethod.InputMethodManager
16 | import android.widget.EditText
17 | import android.widget.FrameLayout
18 | import com.huichongzi.fastwidget4android.R
19 |
20 |
21 | class FloatInputView : FrameLayout {
22 |
23 | var activity : Activity? = null
24 | lateinit var content : View
25 | lateinit var edit : EditText
26 |
27 | var rootListener = ViewTreeObserver.OnGlobalLayoutListener {
28 | var rect = Rect()
29 | //这里用R.id.content,而不用rootView(DecorView)的原因是rootView还包含虚拟导航栏区域,这就需要判断导航栏是否显示,否则计算键盘高度会出现偏差
30 | //但是android系统目前还没有官方的api去判断导航栏是否显示,网上的方法都不完全保险。
31 | //而R.id.content区域则是不包含虚拟导航栏区域的(如果隐藏了虚拟导航栏,这个就与rootView区域一致),所以可以忽略这个问题
32 | var contentView = rootView.findViewById(android.R.id.content)
33 | contentView.getWindowVisibleDisplayFrame(rect) //获取窗口的显示区域
34 |
35 | if(lastBottom == rect.bottom)
36 | return@OnGlobalLayoutListener
37 |
38 | //这里使用bottom而不是height来比较,是因为height还需要考虑全屏与否的通知栏问题。
39 | //如果显示区域的bottom小于窗口的实际bottom,说明键盘弹出
40 | if(rect.bottom < contentView.bottom){
41 | visibility = View.VISIBLE
42 | //因为content初始位置在最底部,虽然showInput中设置了setY(),但是这个函数实际上也是设置translationY,没有改变初始位置,只改变了显示位置
43 | //这时候键盘弹出,想要显示在键盘上面就必须将content向上移动键盘的高度。而contentView.bottom - rect.bottom就是键盘高度。
44 | translationY = -(contentView.bottom - rect.bottom).toFloat()
45 | }
46 | else{
47 | visibility = View.INVISIBLE
48 | }
49 | lastBottom = rect.bottom
50 | }
51 | var lastBottom = 0
52 |
53 | constructor(context: Context) : super(context){
54 | init()
55 | }
56 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs){
57 | init()
58 | }
59 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr){
60 | init()
61 | }
62 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
63 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes){
64 | init()
65 | }
66 |
67 | fun init(){
68 | setOnClickListener {
69 | hideInput()
70 | }
71 | setContent(R.layout.float_input_default_layout, R.id.edit)
72 | initDefaultLayout()
73 | visibility = View.INVISIBLE
74 | }
75 |
76 | private fun initDefaultLayout(){
77 | findViewById(R.id.send).setOnClickListener {
78 | hideInput()
79 | }
80 | }
81 |
82 |
83 | override fun onAttachedToWindow() {
84 | super.onAttachedToWindow()
85 | //延时一下,防止还没计算完布局导致键盘显示出来
86 | postDelayed({
87 | rootView.findViewById(android.R.id.content).viewTreeObserver.addOnGlobalLayoutListener(rootListener)
88 | }, 500)
89 | }
90 |
91 | @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
92 | override fun onDetachedFromWindow() {
93 | super.onDetachedFromWindow()
94 | rootView.findViewById(android.R.id.content).viewTreeObserver.removeOnGlobalLayoutListener(rootListener)
95 | }
96 |
97 | fun setContent(@LayoutRes layoutId : Int, @IdRes editId : Int){
98 | removeAllViews()
99 | content = LayoutInflater.from(context).inflate(layoutId, null)
100 | var params = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
101 | params.gravity = Gravity.BOTTOM or Gravity.CENTER
102 | addView(content, params)
103 | content.isClickable = true
104 |
105 | edit = content.findViewById(editId)
106 | }
107 |
108 | fun showInput(){
109 | edit.requestFocus()
110 | y = -resources.displayMetrics.heightPixels.toFloat() //先移到屏幕顶部,这样键盘弹起时不会遮挡住输入框,就不会重新调整布局。否则会出现布局上推,内部布局缩小等情况
111 | val inputManager: InputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
112 | inputManager.showSoftInput(edit, 0)
113 | }
114 |
115 | fun hideInput(){
116 | val inputManager: InputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
117 | inputManager.hideSoftInputFromWindow(edit.windowToken, 0)
118 | }
119 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/FloatSideBall.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.IntentFilter;
9 | import android.graphics.Bitmap;
10 | import android.graphics.BitmapFactory;
11 | import android.graphics.PixelFormat;
12 | import android.os.Build;
13 | import android.util.DisplayMetrics;
14 | import android.util.Log;
15 | import android.view.Display;
16 | import android.view.Gravity;
17 | import android.view.MotionEvent;
18 | import android.view.View;
19 | import android.view.View.OnClickListener;
20 | import android.view.View.OnTouchListener;
21 | import android.view.WindowManager;
22 | import android.widget.ImageView;
23 | import android.widget.ImageView.ScaleType;
24 |
25 | /**
26 | * Created by lcx on 2017/10/15.
27 | */
28 |
29 | public class FloatSideBall {
30 | public static final String ACTION_SIDEBAR_UPDATE = "com.huichongzi.sideball.UpdateAction";
31 | private WindowManager manager;
32 | private WindowManager.LayoutParams win_params;
33 | private ImageView ball;
34 | private boolean isShow;
35 | private Context context;
36 | private boolean isPortrait = true;
37 | private Bitmap float_move, float_left, float_right;
38 | private SidebarReceiver update_receiver;
39 | private AlarmManager alarm_manager;
40 | private PendingIntent update_pi;
41 |
42 |
43 | public boolean isShow(){
44 | return isShow;
45 | }
46 |
47 |
48 | private void changeOrientation(){
49 | win_params.x = (win_params.x + win_params.width) * manager.getDefaultDisplay().getWidth()
50 | / manager.getDefaultDisplay().getHeight() - win_params.width;
51 | win_params.y = (win_params.y + win_params.height) * manager.getDefaultDisplay().getHeight()
52 | / manager.getDefaultDisplay().getWidth() - win_params.height;
53 | }
54 |
55 |
56 |
57 | private void updateView(){
58 | boolean isSame = isPortrait == (manager.getDefaultDisplay().getWidth() < manager.getDefaultDisplay().getHeight());
59 | if(!isSame){
60 | changeOrientation();
61 | isPortrait = !isPortrait;
62 | }
63 | if(win_params.x < 0)
64 | win_params.x = 0;
65 | if(win_params.y < 0)
66 | win_params.y = 0;
67 | if(win_params.x > manager.getDefaultDisplay().getWidth() - win_params.width)
68 | win_params.x = manager.getDefaultDisplay().getWidth() - win_params.width;
69 | if(win_params.y > manager.getDefaultDisplay().getHeight() - win_params.height)
70 | win_params.y = manager.getDefaultDisplay().getHeight() - win_params.height;
71 | if(win_params.x >= (manager.getDefaultDisplay().getWidth() - win_params.width) / 2){
72 | win_params.x = manager.getDefaultDisplay().getWidth() - win_params.width;
73 | ball.setImageBitmap(float_right);
74 | }
75 | else{
76 | win_params.x = 0;
77 | ball.setImageBitmap(float_left);
78 | }
79 | manager.updateViewLayout(ball, win_params);
80 | }
81 |
82 |
83 | public FloatSideBall(Context context, int float_move, int float_left, int float_right, OnClickListener clickListener){
84 | this(context, BitmapFactory.decodeResource(context.getResources(), float_move), BitmapFactory.decodeResource(context.getResources(), float_left),
85 | BitmapFactory.decodeResource(context.getResources(), float_right), clickListener);
86 | }
87 |
88 |
89 | public FloatSideBall(Context context, Bitmap float_move, Bitmap float_left, Bitmap float_right, OnClickListener clickListener){
90 | if(context == null){
91 | Log.e("sideball", "context为空!");
92 | return;
93 | }
94 | this.context = context;
95 | this.float_move = float_move;
96 | this.float_left = float_left;
97 | this.float_right = float_right;
98 | initSidebar(clickListener);
99 | }
100 |
101 | private void initSidebar(OnClickListener clickListener){
102 | if(float_move == null || float_left == null || float_right == null){
103 | Log.e("sideball", "bitmap初始化失败!");
104 | return;
105 | }
106 | if(clickListener == null){
107 | Log.e("sideball", "点击事件未设定!");
108 | return;
109 | }
110 | int width = getDpFromDx(context, 50);
111 | manager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
112 | ball = new ImageView(context);
113 | ball.setBackgroundColor(0);
114 | ball.setScaleType(ScaleType.FIT_XY);
115 | ball.setOnTouchListener(new OnTouchListener(){
116 | public boolean onTouch(View v, MotionEvent event) {
117 | return touch(v, event);
118 | }
119 | });
120 | ball.setOnClickListener(clickListener);
121 | win_params = new WindowManager.LayoutParams();
122 | win_params.height = width;
123 | win_params.width = width;
124 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
125 | win_params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
126 | }
127 | else {
128 | win_params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
129 | }
130 | win_params.format = PixelFormat.TRANSPARENT;
131 | win_params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
132 | win_params.gravity = Gravity.LEFT | Gravity.TOP;
133 | }
134 |
135 | public void showSideball(){
136 | if(!isShow){
137 | if(float_move == null || float_left == null || float_right == null){
138 | Log.e("sideball", "bitmap未设定!");
139 | return;
140 | }
141 | ball.setImageBitmap(float_move);
142 | manager.addView(ball, win_params);
143 | //起始位置
144 | win_params.x = manager.getDefaultDisplay().getWidth();
145 | win_params.y = manager.getDefaultDisplay().getHeight() / 2;
146 | updateView();
147 | isShow = true;
148 | //开启定时更新
149 | if(update_receiver == null){
150 | update_receiver = new SidebarReceiver(this);
151 | }
152 | IntentFilter filter = new IntentFilter(ACTION_SIDEBAR_UPDATE);
153 | context.registerReceiver(update_receiver, filter);
154 | if(alarm_manager == null){
155 | alarm_manager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
156 | }
157 | Intent intent = new Intent(ACTION_SIDEBAR_UPDATE);
158 | update_pi = PendingIntent.getBroadcast(context, 0, intent, 0);
159 | alarm_manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, 1000, update_pi);
160 | }
161 | }
162 |
163 | public void hideSideball(){
164 | if(isShow && manager != null && ball != null){
165 | manager.removeView(ball);
166 | isShow = false;
167 | //关闭定时更新
168 | if(alarm_manager != null && update_pi != null){
169 | alarm_manager.cancel(update_pi);
170 | }
171 | if(update_receiver != null){
172 | context.unregisterReceiver(update_receiver);
173 | }
174 | }
175 | }
176 |
177 |
178 |
179 | private float eventX, eventY;
180 | private int viewX, viewY;
181 | private boolean isMove;
182 | public boolean touch_down;
183 | private boolean touch(View view, MotionEvent event) {
184 | if(view == ball){
185 | if(event.getAction() == MotionEvent.ACTION_DOWN){
186 | isMove = false;
187 | touch_down = true;
188 | eventX = event.getRawX();
189 | eventY = event.getRawY();
190 | viewX = win_params.x;
191 | viewY = win_params.y;
192 | ball.setImageBitmap(float_move);
193 | if(isMove)
194 | return true;
195 | return false;
196 | }
197 | else if(event.getAction() == MotionEvent.ACTION_MOVE){
198 | if(Math.abs(event.getRawX() - eventX) > 10
199 | && Math.abs(event.getRawY() - eventY) > 10)
200 | isMove = true;
201 | int x = (int)(event.getRawX() - eventX) + viewX;
202 | int y = (int)(event.getRawY() - eventY) + viewY;
203 | win_params.x = x;
204 | win_params.y = y;
205 | if(win_params.x < 0)
206 | win_params.x = 0;
207 | if(win_params.y < 0)
208 | win_params.y = 0;
209 | if(win_params.x > manager.getDefaultDisplay().getWidth() - win_params.width)
210 | win_params.x = manager.getDefaultDisplay().getWidth() - win_params.width;
211 | if(win_params.y > manager.getDefaultDisplay().getHeight() - win_params.height)
212 | win_params.y = manager.getDefaultDisplay().getHeight() - win_params.height;
213 | manager.updateViewLayout(ball, win_params);
214 | return true;
215 | }
216 | else if(event.getAction() == MotionEvent.ACTION_UP){
217 | touch_down = false;
218 | if(isMove){
219 | updateView();
220 | return true;
221 | }
222 | if((event.getEventTime() - event.getDownTime()) > 1000){
223 | hideSideball();
224 | return true;
225 | }
226 | else{
227 | updateView();
228 | return false;
229 | }
230 |
231 | }
232 | else{
233 | return true;
234 | }
235 | }
236 | return false;
237 | }
238 |
239 |
240 | private int getDpFromDx(Context context, int dx){
241 | return (int)(dx * getDpRatio(context));
242 | }
243 |
244 | private float getDpRatio(Context context){
245 | int densityDpi = getDensityDpi(context);
246 | float dpRatio = 0;
247 | if (densityDpi <= 120) {
248 | dpRatio = 0.75f;
249 | } else if (densityDpi <= 160) {
250 | dpRatio = 1.0f;
251 | } else if (densityDpi <= 240) {
252 | dpRatio = 1.5f;
253 | } else if (densityDpi <= 320) {
254 | dpRatio = 2.0f;
255 | } else {
256 | dpRatio = 3.0f;
257 | }
258 | return dpRatio;
259 | }
260 |
261 | private int getDensityDpi(Context context){
262 | WindowManager manager = (WindowManager)context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
263 | Display display = manager.getDefaultDisplay();
264 | DisplayMetrics dm = new DisplayMetrics();
265 | display.getMetrics(dm);
266 | int densityDpi = dm.densityDpi;
267 | return densityDpi;
268 | }
269 |
270 |
271 | class SidebarReceiver extends BroadcastReceiver {
272 | private FloatSideBall sideball;
273 | public SidebarReceiver(FloatSideBall sidebar){
274 | this.sideball = sidebar;
275 | }
276 | public void onReceive(Context context, Intent intent){
277 | if(intent.getAction().equals(FloatSideBall.ACTION_SIDEBAR_UPDATE)){
278 | if(!sideball.isShow){
279 | context.unregisterReceiver(this);
280 | return;
281 | }
282 | if(sideball.touch_down){
283 | return;
284 | }
285 | sideball.updateView();
286 | }
287 | }
288 | }
289 | }
290 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/FolioView.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.annotation.TargetApi;
6 | import android.content.Context;
7 | import android.graphics.Bitmap;
8 | import android.graphics.Canvas;
9 | import android.graphics.Color;
10 | import android.graphics.LinearGradient;
11 | import android.graphics.Matrix;
12 | import android.graphics.Paint;
13 | import android.graphics.Rect;
14 | import android.graphics.Shader;
15 | import android.os.Build;
16 | import android.util.AttributeSet;
17 | import android.util.Log;
18 | import android.view.MotionEvent;
19 | import android.view.View;
20 |
21 | /**
22 | * 对折翻转view
23 | * 可以实现俩张图片对折翻转效果
24 | * Created by chz on 2015/12/8.
25 | */
26 | public class FolioView extends View implements AnimationViewInterface{
27 | /**
28 | * 翻转时拉伸变形的最大值
29 | * 即翻转90度时,最外边长度的增加倍数
30 | */
31 | private static final float FOLIO_SCALE = 0.5f;
32 | /**
33 | * 翻转时阴影效果最大值
34 | */
35 | private static final int FOLIO_SHADOW_ALPHA = 100;
36 |
37 | /**
38 | * 当前翻转的位置
39 | * 即翻转的最外边在屏幕上的y坐标
40 | */
41 | private float mFolioY;
42 | private float mCurrentPercent;
43 | private long mduration = 2000;
44 |
45 | /**
46 | * 前景图上半部分
47 | */
48 | private Bitmap mFrontBitmapTop;
49 | /**
50 | * 背景图上半部分
51 | */
52 | private Bitmap mBackBitmapTop;
53 |
54 | /**
55 | * 前景图下半部分
56 | */
57 | private Bitmap mFrontBitmapBottom;
58 | /**
59 | * 背景图下半部分
60 | */
61 | private Bitmap mBackBitmapBottom;
62 | /**
63 | * 翻转中的图片
64 | * 根据情况,会是mTopBitmap的下半部分或上半部分
65 | */
66 | private Bitmap mFolioBitmap;
67 | private ObjectAnimator mFolioAnimation;
68 | private OnAnimationViewListener mOnAnimationViewListener;
69 |
70 | public FolioView(Context context) {
71 | super(context);
72 | init();
73 | }
74 |
75 | public FolioView(Context context, AttributeSet attrs) {
76 | super(context, attrs);
77 | init();
78 | }
79 |
80 | public FolioView(Context context, AttributeSet attrs, int defStyleAttr) {
81 | super(context, attrs, defStyleAttr);
82 | init();
83 | }
84 |
85 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
86 | public FolioView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
87 | super(context, attrs, defStyleAttr, defStyleRes);
88 | init();
89 | }
90 |
91 | private void init() {
92 | }
93 |
94 | @Override
95 | public void setBitmap(Bitmap frontBitmap, Bitmap backBitmap) {
96 | mFrontBitmapTop = Bitmap.createBitmap(frontBitmap, 0, 0, frontBitmap.getWidth(), frontBitmap.getHeight() / 2);
97 | mBackBitmapTop = Bitmap.createBitmap(backBitmap, 0, 0, backBitmap.getWidth(), backBitmap.getHeight() / 2);
98 |
99 | mFrontBitmapBottom = Bitmap.createBitmap(frontBitmap, 0, frontBitmap.getHeight() / 2, frontBitmap.getWidth(), frontBitmap.getHeight() / 2);
100 | mBackBitmapBottom = Bitmap.createBitmap(backBitmap, 0, backBitmap.getHeight() / 2, backBitmap.getWidth(), backBitmap.getHeight() / 2);
101 | }
102 |
103 |
104 |
105 | /**
106 | * @hide
107 | */
108 | public float getFolioY() {
109 | return mFolioY;
110 | }
111 |
112 | /**
113 | * @hide
114 | */
115 | public void setFolioY(float folioY) {
116 | mFolioY = folioY;
117 | invalidate();
118 | }
119 |
120 | @Override
121 | protected void onDraw(Canvas canvas) {
122 | if (mFrontBitmapTop == null || mBackBitmapTop == null) {
123 | return;
124 | }
125 | if(getHeight() <= 0){
126 | return;
127 | }
128 | /**
129 | * 计算翻转的比率
130 | * 用于计算图片的拉伸和阴影效果
131 | */
132 | float rate;
133 | if (mFolioY >= getHeight() / 2) {
134 | rate = (float) (getHeight() - mFolioY) * 2 / getHeight();
135 | } else {
136 | rate = (float) mFolioY * 2 / getHeight();
137 | }
138 |
139 | /**
140 | * 根据上翻下翻判断上下的图片
141 | */
142 | Bitmap topBitmap = null;
143 | Bitmap bottomBitmap = null;
144 |
145 | Bitmap topBitmapFolie = null;
146 | Bitmap bottomBitmapFolie = null;
147 | if(mCurrentPercent < 0){
148 | topBitmap = mFrontBitmapTop;
149 | bottomBitmap = mBackBitmapBottom;
150 | topBitmapFolie = mFrontBitmapBottom;
151 | bottomBitmapFolie = mBackBitmapTop;
152 | }
153 | else if(mCurrentPercent > 0){
154 | topBitmap = mBackBitmapTop;
155 | bottomBitmap = mFrontBitmapBottom;
156 | topBitmapFolie = mBackBitmapBottom;
157 | bottomBitmapFolie = mFrontBitmapTop ;
158 | }
159 | if (topBitmap == null || bottomBitmap == null) {
160 | return;
161 | }
162 | /**
163 | * 在上半部分绘制topBitmap
164 | */
165 | Rect topHoldSrc = new Rect(0, 0, topBitmap.getWidth(), topBitmap.getHeight());
166 | Rect topHoldDst = new Rect(0, 0, getWidth(), getHeight() / 2);
167 | canvas.drawBitmap(topBitmap, topHoldSrc, topHoldDst, null);
168 |
169 | /**
170 | * 在下半部分绘制bottomBitmap
171 | */
172 | Rect bottomHoldSrc = new Rect(0, 0, bottomBitmap.getWidth(), bottomBitmap.getHeight());
173 | Rect bottomHoldDst = new Rect(0, getHeight() / 2, getWidth(), getHeight());
174 | canvas.drawBitmap(bottomBitmap, bottomHoldSrc, bottomHoldDst, null);
175 |
176 | /**
177 | * 绘制阴影(光线在正前方)
178 | * 阴影与翻转是在同一区域,并且根据翻转程度改变
179 | */
180 | // Paint shadowP = new Paint();
181 | // shadowP.setColor(0xff000000);
182 | // shadowP.setAlpha((int) ((1 - rate) * FOLIO_SHADOW_ALPHA));
183 | // if (mFolioY >= getHeight() / 2) {
184 | // canvas.drawRect(bottomHoldDst, shadowP);
185 | // } else {
186 | // canvas.drawRect(topHoldDst, shadowP);
187 | // }
188 |
189 | /**
190 | * 绘制阴影(光线在上方,更真实)
191 | * 阴影一直在下面的区域,并且根据翻转程度改变范围,并渐变
192 | */
193 | LinearGradient gradient = new LinearGradient(0, getHeight() / 2, 0, getHeight() / 2 + mFolioY / 2, 0x80000000, Color.TRANSPARENT, Shader.TileMode.MIRROR);
194 | Paint shadowP = new Paint();
195 | shadowP.setShader(gradient);
196 | Rect shadowRect = new Rect(0, getHeight() / 2, getWidth(), getHeight() / 2 + (int)mFolioY / 2);
197 | canvas.drawRect(shadowRect, shadowP);
198 |
199 | /**
200 | * 绘制翻转效果的图片
201 | * 翻转图片是一个梯形,根据情况梯形大小位置等不相同
202 | */
203 | mFolioBitmap = null;
204 | float[] folioSrc = null;
205 | float[] folioDst = null;
206 | if (mFolioY >= getHeight() / 2) {
207 | //当翻转位置在中部偏下时,取topBitmapFolie,同时绘制区域为一个正梯形
208 | mFolioBitmap = topBitmapFolie;
209 | folioDst = new float[]{0, getHeight() / 2,
210 | getWidth(), getHeight() / 2,
211 | rate * FOLIO_SCALE * getWidth() + getWidth(), mFolioY,
212 | -rate * FOLIO_SCALE * getWidth(), mFolioY};
213 | } else {
214 | //当翻转位置在中部偏上时,取bottomBitmapFolie,同时绘制区域为一个倒梯形
215 | mFolioBitmap = bottomBitmapFolie;
216 | folioDst = new float[]{
217 | -rate * FOLIO_SCALE * getWidth(), mFolioY,
218 | rate * FOLIO_SCALE * getWidth() + getWidth(), mFolioY,
219 | getWidth(), getHeight() / 2,
220 | 0, getHeight() / 2
221 | };
222 | }
223 | folioSrc = new float[]{0, 0,
224 | mFolioBitmap.getWidth(), 0,
225 | mFolioBitmap.getWidth(), mFolioBitmap.getHeight(),
226 | 0, mFolioBitmap.getHeight()};
227 | Matrix matrix = new Matrix();
228 | matrix.setPolyToPoly(folioSrc, 0, folioDst, 0, folioSrc.length >> 1);
229 | canvas.drawBitmap(mFolioBitmap, matrix, null);
230 |
231 | super.onDraw(canvas);
232 | }
233 |
234 |
235 | @Override
236 | public boolean isAnimationRunning() {
237 | return mFolioAnimation != null && mFolioAnimation.isRunning();
238 | }
239 |
240 | @Override
241 | public void startAnimation(boolean isVertical, MotionEvent event, final float toPercent) {
242 | if(!isVertical){
243 | return;
244 | }
245 | if(getHeight() <= 0){
246 | return;
247 | }
248 | /**
249 | * 播放翻转动画
250 | * 先计算动画结束的位置,然后设定动画从当前位置翻到结束点
251 | * 动画的实质上是不停改变翻转位置并重绘
252 | */
253 | float endPosition = 0;
254 | if (mCurrentPercent < 0) {
255 | endPosition = toPercent == 0 ? getHeight() : 0;
256 | } else{
257 | endPosition = toPercent == 0 ? 0 : getHeight();
258 | }
259 | mFolioAnimation = ObjectAnimator.ofFloat(this, "folioY", endPosition);
260 | mFolioAnimation.setDuration((long)(mduration * Math.abs(toPercent - mCurrentPercent)));
261 | mFolioAnimation.addListener(new Animator.AnimatorListener() {
262 | @Override
263 | public void onAnimationStart(Animator animation) {
264 | }
265 |
266 | @Override
267 | public void onAnimationEnd(Animator animation) {
268 | mCurrentPercent = 0;
269 | if(mOnAnimationViewListener != null){
270 | if(toPercent == 1){
271 | mOnAnimationViewListener.pagePrevious();
272 | }
273 | else if(toPercent == -1){
274 | mOnAnimationViewListener.pageNext();
275 | }
276 | }
277 | }
278 |
279 | @Override
280 | public void onAnimationCancel(Animator animation) {
281 | }
282 |
283 | @Override
284 | public void onAnimationRepeat(Animator animation) {
285 | }
286 | });
287 | mFolioAnimation.start();
288 | }
289 |
290 | @Override
291 | public float getAnimationPercent() {
292 | return mCurrentPercent;
293 | }
294 |
295 | @Override
296 | public void setAnimationPercent(float percent, MotionEvent event, boolean isVertical) {
297 | if(!isVertical){
298 | return;
299 | }
300 | if(getHeight() <= 0){
301 | return;
302 | }
303 | /**
304 | * 计算翻转的位置
305 | * 如果位置超出了区域,则完成翻转
306 | */
307 | mFolioY = percent > 0 ? percent * getHeight() : (1 + percent) * getHeight();
308 | invalidate();
309 | mCurrentPercent = percent;
310 | }
311 |
312 | @Override
313 | public void setDuration(long duration) {
314 | mduration = duration;
315 | }
316 |
317 | @Override
318 | public void setOnAnimationViewListener(OnAnimationViewListener onAnimationViewListener) {
319 | mOnAnimationViewListener = onAnimationViewListener;
320 | }
321 |
322 |
323 | }
324 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/OnAnimationViewListener.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | /**
4 | * @author chz
5 | * @description
6 | * @date 2016/2/4 16:55
7 | */
8 | public interface OnAnimationViewListener {
9 | public void pageNext();
10 | public void pagePrevious();
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/RotateView.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.annotation.TargetApi;
5 | import android.content.Context;
6 | import android.graphics.Bitmap;
7 | import android.graphics.BitmapFactory;
8 | import android.graphics.Matrix;
9 | import android.os.Build;
10 | import android.util.AttributeSet;
11 | import android.widget.ImageView;
12 |
13 | /**
14 | * 动画翻转的imageview
15 | * @author chz
16 | * @description
17 | * @date 2016/1/20 16:42
18 | */
19 | public class RotateView extends ImageView {
20 | private static final long DEFAULT_DURATION = 1000;
21 | /**
22 | * 翻转时缩放的最小值
23 | */
24 | private float mScaleMin = 1.0f;
25 | private boolean isRotateX;
26 |
27 | /**
28 | * 前景图片
29 | */
30 | private Bitmap mFrontBitmap;
31 | /**
32 | * 背景图片
33 | */
34 | private Bitmap mBackBitmap;
35 | //翻转后的背景图片
36 | private Bitmap mRotatedBackBitmap;
37 | private ValueAnimator mAnimator;
38 |
39 | public RotateView(Context context) {
40 | super(context);
41 | }
42 |
43 | public RotateView(Context context, AttributeSet attrs) {
44 | super(context, attrs);
45 | }
46 |
47 | public RotateView(Context context, AttributeSet attrs, int defStyleAttr) {
48 | super(context, attrs, defStyleAttr);
49 | }
50 |
51 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
52 | public RotateView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
53 | super(context, attrs, defStyleAttr, defStyleRes);
54 | }
55 |
56 | /**
57 | * 设置前景背景图片
58 | * @param frontId
59 | * @param backId
60 | */
61 | public void setBitmapResource(int frontId, int backId){
62 | setBitmap(BitmapFactory.decodeResource(getContext().getResources(), frontId),
63 | BitmapFactory.decodeResource(getContext().getResources(), backId));
64 | }
65 |
66 | /**
67 | * 设置前景背景图片
68 | * @param frontBitmap
69 | * @param backBitmap
70 | */
71 | public void setBitmap(Bitmap frontBitmap, Bitmap backBitmap){
72 | if(frontBitmap == null){
73 | return;
74 | }
75 | mFrontBitmap = frontBitmap;
76 | mBackBitmap = backBitmap;
77 | mRotatedBackBitmap = null;
78 | setImageBitmap(frontBitmap);
79 | setScaleType(ScaleType.FIT_XY);
80 | //初始化翻转角度
81 | setRotationX(0);
82 | setRotationY(0);
83 | }
84 |
85 | /**
86 | * 设置缩放最小值
87 | * 图片翻转时会先缩小再恢复
88 | * @param scaleMin
89 | */
90 | public void setScaleMin(float scaleMin) {
91 | mScaleMin = scaleMin;
92 | }
93 |
94 | /**
95 | * 设置某值的翻转效果,包括图片处理等
96 | * @param value
97 | * @param isRotateX
98 | */
99 | public void setRotation(float value, boolean isRotateX){
100 | //设置翻转角度
101 | if(isRotateX){
102 | setRotationX(value);
103 | }
104 | else {
105 | setRotationY(value);
106 | }
107 | //将角度转换为0-360之间,以便后面判断
108 | float rotate = value % 360;
109 | if (rotate < 0) {
110 | rotate += 360;
111 | }
112 | /**
113 | * 设置缩放:当向垂直翻转时缩小,反之恢复
114 | * 缩放的主要原因是在翻转时,图像会变形为梯形,这时图片中心轴保持原来的宽度,
115 | * 则向上翻转那边会变大,部分图像会超出无法显示。所以这里用缩放处理一下,
116 | * 至于缩放大小,根据实际需求改变。
117 | */
118 | float scale = rotate > 180 ? Math.abs(rotate - 270) : Math.abs(rotate - 90);
119 | scale = scale / 90 * (1 - mScaleMin) + mScaleMin;
120 | if(isRotateX){
121 | setScaleX(scale);
122 | }
123 | else{
124 | setScaleY(scale);
125 | }
126 |
127 | //根据翻转的位置,设置前景/背景图片
128 | if(mBackBitmap != null) {
129 | if(mRotatedBackBitmap == null || this.isRotateX != isRotateX) {
130 | /**
131 | * 首先会根据翻转的方向,对背景图片进行一次翻转
132 | * 这样当翻转时背景图片不会左右上下颠倒
133 | */
134 | Matrix matrix = new Matrix();
135 | if (isRotateX) {
136 | matrix.postScale(1, -1);
137 | } else {
138 | matrix.postScale(-1, 1);
139 | }
140 | mRotatedBackBitmap = Bitmap.createBitmap(mBackBitmap, 0, 0,
141 | mBackBitmap.getWidth(), mBackBitmap.getHeight(), matrix, true);
142 | }
143 | /**
144 | * 当翻转在2、3象限显示背景图,在1、4象限显示前景图
145 | */
146 | if (rotate > 90 && rotate < 270) {
147 | setImageBitmap(mRotatedBackBitmap);
148 | } else {
149 | setImageBitmap(mFrontBitmap);
150 | }
151 | }
152 | this.isRotateX = isRotateX;
153 | }
154 |
155 | /**
156 | * 以Y轴翻转
157 | * @param fromRotate 开始角度
158 | * @param toRotate 结束角度
159 | */
160 | public void rotateYAnimation(float fromRotate, float toRotate){
161 | rotateYAnimation(fromRotate, toRotate, DEFAULT_DURATION);
162 | }
163 |
164 | /**
165 | * 以Y轴翻转
166 | * @param fromRotate 开始角度
167 | * @param toRotate 结束角度
168 | * @param duration
169 | */
170 | public void rotateYAnimation(float fromRotate, float toRotate, long duration){
171 | rotateAnimation(fromRotate, toRotate, duration, 0, false);
172 | }
173 |
174 | /**
175 | * 以Y轴翻转
176 | * @param fromRotate 开始角度
177 | * @param toRotate 结束角度
178 | * @param duration
179 | * @param delay 动画延时
180 | */
181 | public void rotateYAnimation(float fromRotate, float toRotate, long duration, long delay){
182 | rotateAnimation(fromRotate, toRotate, duration, delay, false);
183 | }
184 |
185 | /**
186 | * 以X轴翻转
187 | * @param fromRotate 开始角度
188 | * @param toRotate 结束角度
189 | */
190 | public void rotateXAnimation(float fromRotate, float toRotate){
191 | rotateXAnimation(fromRotate, toRotate, DEFAULT_DURATION);
192 | }
193 |
194 | /**
195 | * 以X轴翻转
196 | * @param fromRotate 开始角度
197 | * @param toRotate 结束角度
198 | * @param duration
199 | * @param delay 动画延时
200 | */
201 | public void rotateXAnimation(float fromRotate, float toRotate, long duration, long delay){
202 | rotateAnimation(fromRotate, toRotate, duration, delay, true);
203 | }
204 |
205 | /**
206 | * 以X轴翻转
207 | * @param fromRotate 开始角度
208 | * @param toRotate 结束角度
209 | * @param duration
210 | */
211 | public void rotateXAnimation(float fromRotate, float toRotate, long duration){
212 | rotateAnimation(fromRotate, toRotate, duration, 0, true);
213 | }
214 |
215 | /**
216 | * 翻转动画
217 | * @param fromRotate 开始角度
218 | * @param toRotate 结束角度
219 | * @param duration
220 | * @param delay 动画延时
221 | * @param isRotateX 是否以X为轴
222 | */
223 | private void rotateAnimation(float fromRotate, float toRotate, long duration, long delay, boolean isRotateX){
224 | if(mAnimator != null){
225 | mAnimator.cancel();
226 | }
227 | mAnimator = ValueAnimator.ofFloat(fromRotate, toRotate);
228 | mAnimator.setStartDelay(delay);
229 | mAnimator.setDuration(duration);
230 | mAnimator.start();
231 | mAnimator.addUpdateListener(new RotateListener(isRotateX));
232 | }
233 |
234 | class RotateListener implements ValueAnimator.AnimatorUpdateListener{
235 | private boolean isRotateX;
236 |
237 | public RotateListener(boolean isRotateX){
238 | this.isRotateX = isRotateX;
239 | }
240 | @Override
241 | public void onAnimationUpdate(ValueAnimator animation) {
242 | float value = (Float)(animation.getAnimatedValue());
243 | setRotation(value, isRotateX);
244 | }
245 | }
246 |
247 | }
248 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/SwipeMenuTouchListener.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.MotionEvent;
5 | import android.view.View;
6 |
7 | /**
8 | * Created by hcui on 9/15/17.
9 | */
10 |
11 | public class SwipeMenuTouchListener implements RecyclerView.OnItemTouchListener{
12 |
13 | private float downX;
14 | private float downY;
15 | private float tmpX;
16 | private float orientation = 0;
17 | private boolean moving = false;
18 | private View currentChild;
19 | private SwipeMenuViewHolder currentHolder;
20 |
21 | @Override
22 | public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
23 |
24 | View child = rv.findChildViewUnder(e.getX(), e.getY());
25 | if(child == null){
26 | return false;
27 | }
28 | switch (e.getAction()){
29 | case MotionEvent.ACTION_DOWN:
30 | currentChild = rv.findChildViewUnder(e.getX(), e.getY());
31 | currentHolder = (SwipeMenuViewHolder) rv.getChildViewHolder(currentChild);
32 | downX = e.getX();
33 | downY = e.getY();
34 | moving = false;
35 | break;
36 | case MotionEvent.ACTION_MOVE:
37 | float moveX = e.getX() - downX;
38 | float moveY = e.getY() - downY;
39 | if(moveX == 0 || Math.abs(moveX) < Math.abs(moveY)){
40 | orientation = 0;
41 | break;
42 | }
43 | if(Math.abs(e.getX() - tmpX) > 10){
44 | orientation = e.getX() - tmpX;
45 | }
46 | if(child != currentChild){
47 | break;
48 | }
49 | if(moveX > 20) {
50 | moving = true;
51 | }
52 | if(moveX < 0) {
53 | if(-moveX <= currentHolder.menuLayout.getWidth() && -currentHolder.contentLayout.getTranslationX() < currentHolder.menuLayout.getWidth()) {
54 | currentHolder.contentLayout.setTranslationX(moveX);
55 | }
56 | else{
57 | currentHolder.contentLayout.setTranslationX(-currentHolder.menuLayout.getWidth());
58 | }
59 | }
60 | else if(moveX > 0 && currentHolder.contentLayout.getTranslationX() < 0){
61 | float m = -currentHolder.menuLayout.getWidth() + moveX;
62 | if(m > 0){
63 | currentHolder.contentLayout.setTranslationX(0);
64 | }
65 | else{
66 | currentHolder.contentLayout.setTranslationX(m);
67 | }
68 | }
69 | tmpX = e.getX();
70 | break;
71 | case MotionEvent.ACTION_UP:
72 | if(orientation >= 0){
73 | currentHolder.contentLayout.setTranslationX(0);
74 | }
75 | else{
76 | currentHolder.contentLayout.setTranslationX(-currentHolder.menuLayout.getWidth());
77 | }
78 | return moving;
79 | //break;
80 | }
81 | return false;
82 | }
83 |
84 | @Override
85 | public void onTouchEvent(RecyclerView rv, MotionEvent e) {
86 | }
87 |
88 | @Override
89 | public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/SwipeMenuViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by hcui on 9/15/17.
8 | */
9 |
10 | public class SwipeMenuViewHolder extends RecyclerView.ViewHolder{
11 | public View contentLayout;
12 | public View menuLayout;
13 |
14 | public SwipeMenuViewHolder(View itemView) {
15 | super(itemView);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/huichongzi/fastwidget4android/widget/WaveBallProgress.java:
--------------------------------------------------------------------------------
1 | package com.huichongzi.fastwidget4android.widget;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.animation.PropertyValuesHolder;
6 | import android.animation.ValueAnimator;
7 | import android.annotation.TargetApi;
8 | import android.content.Context;
9 | import android.graphics.Bitmap;
10 | import android.graphics.Canvas;
11 | import android.graphics.Color;
12 | import android.graphics.Paint;
13 | import android.graphics.PorterDuff;
14 | import android.graphics.PorterDuffXfermode;
15 | import android.graphics.RectF;
16 | import android.os.Build;
17 | import android.util.AttributeSet;
18 | import android.view.View;
19 |
20 | import com.huichongzi.fastwidget4android.utils.DisplayUtils;
21 |
22 | /**
23 | * 水晶球波浪进度条
24 | * Created by chz on 2015/12/14.
25 | */
26 | public class WaveBallProgress extends View {
27 | private final static int HANDLER_WHAT_UPDATE = 0x100;
28 |
29 | /**
30 | * 波浪A的速度
31 | */
32 | private int mWaveSpeedA;
33 | /**
34 | * 波浪B的速度
35 | */
36 | private int mWaveSpeedB;
37 | /**
38 | * 波浪A的振幅
39 | */
40 | private int mWaveHeightA;
41 | /**
42 | * 波浪B的振幅
43 | */
44 | private int mWaveHeightB;
45 | /**
46 | * 波浪A的周期
47 | */
48 | private float mWaveACycle;
49 | /**
50 | * 波浪B的周期
51 | */
52 | private float mWaveBCycle;
53 | /**
54 | * 波浪颜色
55 | */
56 | private int mWaveColor = 0x880000aa;
57 | /**
58 | * 波浪A的偏移
59 | */
60 | private int mOffsetA;
61 | /**
62 | * 波浪B的偏移
63 | */
64 | private int mOffsetB;
65 | /**
66 | * 当前的进度
67 | */
68 | private int mProgress;
69 | /**
70 | * 是否处于波浪状态
71 | */
72 | private boolean isWaveMoving = true;
73 |
74 | private Paint mWavePaint;
75 | /**
76 | * 球形遮罩
77 | */
78 | private Bitmap mBallBitmap;
79 | /**
80 | * 进度增长的动画
81 | */
82 | private ObjectAnimator mProgressAnimator;
83 | /**
84 | * 波浪停止的动画
85 | */
86 | private ObjectAnimator mWaveStopAnimator;
87 |
88 | public WaveBallProgress(Context context) {
89 | super(context);
90 | init();
91 | }
92 |
93 | public WaveBallProgress(Context context, AttributeSet attrs) {
94 | super(context, attrs);
95 | init();
96 | }
97 |
98 | public WaveBallProgress(Context context, AttributeSet attrs, int defStyleAttr) {
99 | super(context, attrs, defStyleAttr);
100 | init();
101 | }
102 |
103 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
104 | public WaveBallProgress(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
105 | super(context, attrs, defStyleAttr, defStyleRes);
106 | init();
107 | }
108 |
109 | private void init() {
110 | //初始化画笔
111 | mWavePaint = new Paint();
112 | mWavePaint.setColor(mWaveColor);
113 | mWavePaint.setFilterBitmap(true);
114 | }
115 |
116 | /**
117 | * 获取进度
118 | * @return
119 | */
120 | public int getProgress() {
121 | return mProgress;
122 | }
123 |
124 | /**
125 | * 设置进度
126 | * @param progress
127 | */
128 | private void setProgress(int progress) {
129 | mProgress = progress;
130 | }
131 |
132 | public int getWaveHeightA() {
133 | return mWaveHeightA;
134 | }
135 |
136 | public void setWaveHeightA(int waveHeightA) {
137 | mWaveHeightA = waveHeightA;
138 | }
139 |
140 | public int getWaveHeightB() {
141 | return mWaveHeightB;
142 | }
143 |
144 | public void setWaveHeightB(int waveHeightB) {
145 | mWaveHeightB = waveHeightB;
146 | }
147 |
148 | public void startProgress(int progress){
149 | startProgress(progress, 1000, 0);
150 | }
151 |
152 | /**
153 | * 设置进度,并且以动画的形式上涨到该进度
154 | * @param progress 进度
155 | * @param duration 持续时间
156 | * @param delay 延时
157 | */
158 | public void startProgress(int progress, long duration, long delay){
159 | if(mProgressAnimator != null && mProgressAnimator.isRunning()){
160 | mProgressAnimator.cancel();
161 | }
162 | if(mWaveStopAnimator != null && mWaveStopAnimator.isRunning()){
163 | mWaveStopAnimator.cancel();
164 | }
165 | isWaveMoving = true;
166 | mProgressAnimator = ObjectAnimator.ofInt(this, "Progress", progress);
167 | mProgressAnimator.setDuration(duration);
168 | mProgressAnimator.setStartDelay(delay);
169 | mProgressAnimator.addListener(new Animator.AnimatorListener() {
170 | @Override
171 | public void onAnimationStart(Animator animation) {
172 | }
173 |
174 | @Override
175 | public void onAnimationEnd(Animator animation) {
176 | mWaveStopAnimator.start();
177 | }
178 |
179 | @Override
180 | public void onAnimationCancel(Animator animation) {
181 | }
182 |
183 | @Override
184 | public void onAnimationRepeat(Animator animation) {
185 | }
186 | });
187 | mProgressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
188 | @Override
189 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
190 | //改变曲线的偏移,达到波浪运动的效果
191 | mOffsetA += mWaveSpeedA;
192 | mOffsetB += mWaveSpeedB;
193 | invalidate();
194 | }
195 | });
196 | mProgressAnimator.start();
197 | }
198 |
199 | @Override
200 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
201 | super.onSizeChanged(w, h, oldw, oldh);
202 | if (w > 0 && h > 0) {
203 | /**
204 | * 根据宽高初始化波浪的一些参数
205 | * 波浪的速度根据宽度的一定比例,这样不同宽度波浪移动的效果保持差不多
206 | * 波浪的振幅根据高度和默认值,当高度太小就设为高度的一定比例,这样保证不同高度下波浪效果明显
207 | * 波浪的周期固定即可
208 | */
209 | mWaveSpeedA = w / 30;
210 | mWaveSpeedB = w / 51;
211 | mWaveHeightA = DisplayUtils.dip2px(getContext(), 10);
212 | mWaveHeightB = DisplayUtils.dip2px(getContext(), 5);
213 | if (h / 10 < mWaveHeightA) {
214 | mWaveHeightA = h / 10;
215 | mWaveHeightB = h / 20;
216 | }
217 | initStopAnimator(mWaveHeightA, mWaveHeightB);
218 | mWaveACycle = (float) (3 * Math.PI / w);
219 | mWaveBCycle = (float) (4 * Math.PI / w);
220 |
221 | /**
222 | * 初始化圆形遮罩
223 | * 圆形遮罩是一个与组件同大小的椭圆,并且四周为透明
224 | * 注意:
225 | * 不在onDraw中直接绘制这个遮罩,因为那样绘制后遮罩只是一个椭圆,使用DST_IN的话在椭圆外的部分
226 | * 就不会做任何处理,达不到效果;而先做成bitmap的话遮罩是一个方形,椭圆外部分就会去掉,达到效果
227 | *
228 | */
229 | mBallBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
230 | Canvas canvas = new Canvas(mBallBitmap);
231 | RectF ball = new RectF(0, 0, w, h);
232 | canvas.drawOval(ball, mWavePaint);
233 | }
234 | }
235 |
236 | private void initStopAnimator(final int waveHeightA, final int waveHeightB){
237 | /**
238 | * 创建波浪停止动画
239 | * 两条波浪振幅逐渐减小
240 | */
241 | PropertyValuesHolder holderA = PropertyValuesHolder.ofInt("WaveHeightA", 0);
242 | PropertyValuesHolder holderB = PropertyValuesHolder.ofInt("WaveHeightB", 0);
243 | mWaveStopAnimator = ObjectAnimator.ofPropertyValuesHolder(this, holderA, holderB);
244 | mWaveStopAnimator.setDuration(1000);
245 | mWaveStopAnimator.addListener(new Animator.AnimatorListener() {
246 | @Override
247 | public void onAnimationStart(Animator animation) {
248 | }
249 | @Override
250 | public void onAnimationEnd(Animator animation) {
251 | isWaveMoving = false;
252 | mWaveHeightA = waveHeightA;
253 | mWaveHeightB = waveHeightB;
254 | }
255 | @Override
256 | public void onAnimationCancel(Animator animation) {
257 | mWaveHeightA = waveHeightA;
258 | mWaveHeightB = waveHeightB;
259 | }
260 | @Override
261 | public void onAnimationRepeat(Animator animation) {
262 | }
263 | });
264 | mWaveStopAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
265 | @Override
266 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
267 | //改变曲线的偏移,达到波浪运动的效果
268 | mOffsetA += mWaveSpeedA;
269 | mOffsetB += mWaveSpeedB;
270 | invalidate();
271 | }
272 | });
273 | }
274 |
275 | @Override
276 | protected void onDraw(Canvas canvas) {
277 | super.onDraw(canvas);
278 | if (getHeight() > 0 && getWidth() > 0) {
279 | //绘制边缘
280 | Paint paint = new Paint();
281 | paint.setColor(mWaveColor);
282 | paint.setStyle(Paint.Style.STROKE);
283 | RectF edge = new RectF(0, 0, getWidth(), getHeight());
284 | canvas.drawArc(edge, 0, 360, false, paint);
285 |
286 | canvas.drawColor(Color.TRANSPARENT);
287 | int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
288 | if (isWaveMoving) {
289 | /**
290 | * 如果有波浪,则绘制两条波浪
291 | * 波浪实际上是一条条一像素的直线组成的,线的顶端是根据正弦函数得到的
292 | */
293 | for (int i = 0; i < getWidth(); i++) {
294 | canvas.drawLine(i, (int) getWaveY(i, mOffsetA, mWaveHeightA, mWaveACycle), i, getHeight(), mWavePaint);
295 | canvas.drawLine(i, (int) getWaveY(i, mOffsetB, mWaveHeightB, mWaveBCycle), i, getHeight(), mWavePaint);
296 | }
297 | } else {
298 | /**
299 | * 如果没有波浪,则绘制两次矩形
300 | * 之所以绘制两次,是因为波浪有两条,所以除了浪尖的部分,其他部分都是重合的,颜色较重
301 | */
302 | float height = (1 - mProgress / 100.0f) * getHeight();
303 | canvas.drawRect(0, height, getWidth(), getHeight(), mWavePaint);
304 | canvas.drawRect(0, height, getWidth(), getHeight(), mWavePaint);
305 | }
306 | //设置遮罩效果,绘制遮罩
307 | mWavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
308 | canvas.drawBitmap(mBallBitmap, 0, 0, mWavePaint);
309 | mWavePaint.setXfermode(null);
310 | canvas.restoreToCount(sc);
311 | }
312 |
313 | }
314 |
315 | /**
316 | * 波浪的函数,用于求y值
317 | * 函数为a*sin(b*(x + c))+d
318 | * @param x x轴
319 | * @param offset 偏移
320 | * @param waveHeight 振幅
321 | * @param waveCycle 周期
322 | * @return
323 | */
324 | private double getWaveY(int x, int offset, int waveHeight, float waveCycle) {
325 | return waveHeight * Math.sin(waveCycle * (x + offset)) + (1 - mProgress / 100.0) * getHeight();
326 | }
327 |
328 | }
329 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/a.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/arrow.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/arrow.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/banner_a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/banner_a.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/banner_b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/banner_b.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/banner_c.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/banner_c.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/banner_d.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/banner_d.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/banner_e.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/banner_e.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/bg1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/bg1.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/c.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/camera_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/camera_bottom.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/camera_top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/camera_top.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/d.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/e.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/f.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/floating_add_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/floating_add_left.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/floating_add_move.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/floating_add_move.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/floating_add_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/floating_add_right.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/floating_bg_big.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/floating_bg_big.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/floating_closed_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/floating_closed_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/g.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/g.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/girl1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/girl1.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/h.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/i.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/i.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/j.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/j.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/market_a.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/market_a.jpeg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/market_b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/market_b.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/market_c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/market_c.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/market_d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/market_d.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/page_a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/page_a.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/page_b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/page_b.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/spinner_bg.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/app/src/main/res/drawable-xhdpi/spinner_bg.9.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/animation_listview_item.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
15 |
16 |
21 |
22 |
28 |
29 |
37 |
38 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/banner_view_act.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/blinds_listview_act.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/book_act.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/drag_gridview_act.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/float_input_act.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
13 |
14 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/float_input_default_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/flood_and_spread_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
11 |
15 |
20 |
26 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/folio_listview_act.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recyclerview_activity.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scroll_fold_list_footer.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scroll_fold_list_item.xml:
--------------------------------------------------------------------------------
1 |
4 |
8 |
9 |
14 |
15 |
20 |
21 |
27 |
28 |
31 |
32 |
41 |
42 |
48 |
49 |
52 |
53 |
63 |
64 |
65 |
66 |
72 |
73 |
82 |
83 |
89 |
90 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/spinner_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/spinner_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/spinner_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/swipe_menu_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
23 |
24 |
32 |
33 |
34 |
41 |
42 |
48 |
49 |
50 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/test_act.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/wave_ball_progress_act.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
12 |
13 |
21 |
--------------------------------------------------------------------------------
/app/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 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 200dp
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | FastWidget4Android
3 |
4 | - 一年级
5 | - 二年级
6 | - 三年级
7 | - 四年级
8 | - 五年级
9 | - 六年级
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/blind.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/blind.gif
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | compose_ui_version = '1.2.0'
4 | }
5 | }// Top-level build file where you can add configuration options common to all sub-projects/modules.
6 | plugins {
7 | id 'com.android.application' version '7.4.1' apply false
8 | id 'com.android.library' version '7.4.1' apply false
9 | id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
10 | }
--------------------------------------------------------------------------------
/float_side_ball.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/float_side_ball.gif
--------------------------------------------------------------------------------
/flood-and-spread.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/flood-and-spread.gif
--------------------------------------------------------------------------------
/folio.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/folio.gif
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Sep 15 14:39:27 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/qrinfo-BennuCTech-scan.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/qrinfo-BennuCTech-scan.jpg
--------------------------------------------------------------------------------
/scroll-fold-list-iloveimg-compressed.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/scroll-fold-list-iloveimg-compressed.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | maven{ url 'https://repo1.maven.org/maven2/' }
5 | maven{ url "https://maven.aliyun.com/repository/google"}
6 | maven{ url "https://maven.aliyun.com/repository/jcenter"}
7 | maven{ url "https://maven.aliyun.com/repository/central"}
8 | google()
9 | mavenCentral()
10 | }
11 | }
12 | dependencyResolutionManagement {
13 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
14 | repositories {
15 | maven{ url 'https://repo1.maven.org/maven2/' }
16 | maven{ url "https://maven.aliyun.com/repository/google"}
17 | maven{ url "https://maven.aliyun.com/repository/jcenter"}
18 | maven{ url "https://maven.aliyun.com/repository/central"}
19 | google()
20 | mavenCentral()
21 | }
22 | }
23 | include ':app'
24 |
--------------------------------------------------------------------------------
/swipe_menu_recyclerview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/swipe_menu_recyclerview.gif
--------------------------------------------------------------------------------
/wave-ball-progress.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/wave-ball-progress.gif
--------------------------------------------------------------------------------
/wheel.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chzphoenix/FastWidget4Android_HCZ/376061473ea65ad3910ef91be92ec17c97fc4855/wheel.gif
--------------------------------------------------------------------------------