├── .DS_Store
├── .gitignore
├── README.md
├── YanXuanRefresh
├── .gitignore
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── sheng
│ │ │ └── yanxuanrefresh
│ │ │ └── ExampleInstrumentedTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── sheng
│ │ │ │ └── yanxuanrefresh
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── YXRefreshHeader.java
│ │ │ │ └── refresh
│ │ │ │ ├── Point.java
│ │ │ │ ├── ViewStatus.java
│ │ │ │ └── YanXuanRefreshView.java
│ │ └── res
│ │ │ ├── layout
│ │ │ ├── activity_main.xml
│ │ │ └── yx_refresh_header.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── all_refresh_de_ic.png
│ │ │ ├── all_refresh_gui_ic.png
│ │ │ ├── all_refresh_hao_ic.png
│ │ │ ├── all_refresh_huo_ic.png
│ │ │ ├── all_refresh_me_ic.png
│ │ │ ├── all_refresh_mei_ic.png
│ │ │ ├── all_refresh_na_ic.png
│ │ │ ├── all_refresh_sheng_ic.png
│ │ │ ├── all_refresh_yanxuan_ic.png
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── sheng
│ │ └── yanxuanrefresh
│ │ └── ExampleUnitTest.java
└── build.gradle
└── refresh.gif
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | YanXuanRefresh/.idea/compiler.xml
3 |
4 | .DS_Store
5 |
6 | .DS_Store
7 |
8 | YanXuanRefresh/.idea/copyright/profiles_settings.xml
9 |
10 | YanXuanRefresh/.idea/gradle.xml
11 |
12 | .DS_Store
13 |
14 | YanXuanRefresh/.idea/misc.xml
15 |
16 | YanXuanRefresh/.idea/modules.xml
17 |
18 | YanXuanRefresh/.idea/runConfigurations.xml
19 |
20 | YanXuanRefresh/gradle.properties
21 |
22 | YanXuanRefresh/gradle/wrapper/gradle-wrapper.jar
23 |
24 | YanXuanRefresh/gradle/wrapper/gradle-wrapper.properties
25 |
26 | YanXuanRefresh/gradlew
27 |
28 | YanXuanRefresh/gradlew.bat
29 |
30 | YanXuanRefresh/settings.gradle
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # YanXuanRefresh
2 | 仿网易严选下拉刷新动画效果
3 |
4 | [下拉刷新框架的地址](https://github.com/scwang90/SmartRefreshLayout)
5 |
6 | 代码中有注释,实现起来还是比较简单的,还有一小点问题待完善,后续会写一篇文章简单讲解一下我的思路。喜欢的话给个star吧,谢谢😊
7 |
8 | # 效果展示
9 |
10 | 
11 |
--------------------------------------------------------------------------------
/YanXuanRefresh/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.3"
6 | defaultConfig {
7 | applicationId "com.sheng.yanxuanrefresh"
8 | minSdkVersion 12
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:25.3.1'
28 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
29 | testCompile 'junit:junit:4.12'
30 | compile 'com.scwang.smartrefresh:SmartRefreshLayout:1.0.3'
31 | compile 'com.scwang.smartrefresh:SmartRefreshHeader:1.0.3'
32 | }
33 |
--------------------------------------------------------------------------------
/YanXuanRefresh/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 /Users/wangsheng/Documents/developSoftware/android-sdk-macosx/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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/androidTest/java/com/sheng/yanxuanrefresh/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.sheng.yanxuanrefresh;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.sheng.yanxuanrefresh", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/java/com/sheng/yanxuanrefresh/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.sheng.yanxuanrefresh;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | import com.scwang.smartrefresh.layout.api.RefreshLayout;
7 |
8 | public class MainActivity extends AppCompatActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_main);
14 | RefreshLayout refreshLayout = (RefreshLayout) findViewById(R.id.refreshLayout);
15 | refreshLayout.autoRefresh(400);
16 | refreshLayout.setReboundDuration(1000);
17 | refreshLayout.setDisableContentWhenRefresh(true);
18 | refreshLayout.finishRefresh(8000);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/java/com/sheng/yanxuanrefresh/YXRefreshHeader.java:
--------------------------------------------------------------------------------
1 | package com.sheng.yanxuanrefresh;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import android.util.AttributeSet;
7 | import android.util.Log;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.widget.LinearLayout;
11 |
12 | import com.scwang.smartrefresh.layout.api.RefreshHeader;
13 | import com.scwang.smartrefresh.layout.api.RefreshKernel;
14 | import com.scwang.smartrefresh.layout.api.RefreshLayout;
15 | import com.scwang.smartrefresh.layout.constant.RefreshState;
16 | import com.scwang.smartrefresh.layout.constant.SpinnerStyle;
17 | import com.sheng.yanxuanrefresh.refresh.ViewStatus;
18 | import com.sheng.yanxuanrefresh.refresh.YanXuanRefreshView;
19 |
20 | /**
21 | * Created by wangsheng on 17/8/17.
22 | */
23 |
24 | public class YXRefreshHeader extends LinearLayout implements RefreshHeader{
25 |
26 | private YanXuanRefreshView yanXuanRefreshView;
27 |
28 | public YXRefreshHeader(Context context) {
29 | super(context);
30 | }
31 |
32 | public YXRefreshHeader(Context context, @Nullable AttributeSet attrs) {
33 | super(context,attrs);
34 | LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
35 | View view = layoutInflater.inflate(R.layout.yx_refresh_header,this);
36 | yanXuanRefreshView = (YanXuanRefreshView) view.findViewById(R.id.refreshView);
37 | }
38 |
39 | public YXRefreshHeader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
40 | super(context, attrs, defStyleAttr);
41 | }
42 |
43 | @Override
44 | public void onPullingDown(float percent, int offset, int headerHeight, int extendHeight) {
45 | yanXuanRefreshView.setDistance(offset);
46 | Log.d("下拉距离",offset+"");
47 | }
48 |
49 | @Override
50 | public void onReleasing(float percent, int offset, int headerHeight, int extendHeight) {
51 |
52 | }
53 |
54 | @NonNull
55 | @Override
56 | public View getView() {
57 | return this;
58 | }
59 |
60 | @Override
61 | public SpinnerStyle getSpinnerStyle() {
62 | return SpinnerStyle.Translate;
63 | }
64 |
65 | @Override
66 | public void setPrimaryColors(int... colors) {
67 |
68 | }
69 |
70 | @Override
71 | public void onInitialized(RefreshKernel kernel, int height, int extendHeight) {
72 |
73 | }
74 |
75 | @Override
76 | public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) {
77 |
78 | }
79 |
80 | @Override
81 | public void onStartAnimator(RefreshLayout layout, int height, int extendHeight) {
82 |
83 | }
84 |
85 | @Override
86 | public int onFinish(RefreshLayout layout, boolean success) {
87 | return 0;
88 | }
89 |
90 | @Override
91 | public boolean isSupportHorizontalDrag() {
92 | return false;
93 | }
94 |
95 | @Override
96 | public void onStateChanged(RefreshLayout refreshLayout, RefreshState oldState, RefreshState newState) {
97 | switch (newState){
98 | case Refreshing:
99 | yanXuanRefreshView.setViewStatus(ViewStatus.REFRESHING);
100 | break;
101 | case None:
102 | if (oldState== RefreshState.RefreshFinish) {
103 | yanXuanRefreshView.restoreView();
104 | }
105 | break;
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/java/com/sheng/yanxuanrefresh/refresh/Point.java:
--------------------------------------------------------------------------------
1 | package com.sheng.yanxuanrefresh.refresh;
2 |
3 | //点坐标的模型 包括x,y两个属性
4 | public class Point {
5 | public float pointX;
6 | public float pointY;
7 |
8 | public Point() {
9 | }
10 |
11 | public Point(float pointX, float pointY) {
12 | this.pointX = pointX;
13 | this.pointY = pointY;
14 | }
15 |
16 | public void setPointX(float pointX) {
17 | this.pointX = pointX;
18 | }
19 |
20 | public void setPointY(float pointY) {
21 | this.pointY = pointY;
22 | }
23 | }
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/java/com/sheng/yanxuanrefresh/refresh/ViewStatus.java:
--------------------------------------------------------------------------------
1 | package com.sheng.yanxuanrefresh.refresh;
2 |
3 | public enum ViewStatus {
4 | START("初始状态",0),
5 | REFRESHING("正在刷新",1), //这个状态由刷新框架设置
6 | START_SLOGAN("绘制标语",2),
7 | START_SHAKE("抖动",3);
8 |
9 | private String name;
10 | private int id;
11 |
12 | ViewStatus(String name, int id) {
13 | this.name = name;
14 | this.id = id;
15 | }
16 |
17 | public String getName() {
18 | return name;
19 | }
20 |
21 | public void setName(String name) {
22 | this.name = name;
23 | }
24 |
25 | public int getId() {
26 | return id;
27 | }
28 |
29 | public void setId(int id) {
30 | this.id = id;
31 | }
32 | }
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/java/com/sheng/yanxuanrefresh/refresh/YanXuanRefreshView.java:
--------------------------------------------------------------------------------
1 | package com.sheng.yanxuanrefresh.refresh;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ValueAnimator;
5 | import android.content.Context;
6 | import android.graphics.Bitmap;
7 | import android.graphics.BitmapFactory;
8 | import android.graphics.Canvas;
9 | import android.graphics.Color;
10 | import android.graphics.Paint;
11 | import android.graphics.Path;
12 | import android.graphics.RectF;
13 | import android.support.annotation.Nullable;
14 | import android.util.AttributeSet;
15 | import android.view.View;
16 | import android.view.animation.DecelerateInterpolator;
17 |
18 | import com.sheng.yanxuanrefresh.R;
19 |
20 | /**
21 | * @author wangsheng 1.0.0 第一版效果基本有了 但是需要调整
22 | *
23 | * todo 一些位置计算待完善
24 | *
25 | * 根据角度计算圆上某点的坐标
26 | * 圆点坐标:(x0,y0)
27 | * 半径:r
28 | * 角度:angle
29 | * 则圆上任一点为:(x1,y1)
30 | * x1 = x0 + r * cos(angle * 3.14 /180 )
31 | * y1 = y0 + r * sin(angle * 3.14 /180 )
32 | */
33 |
34 | public class YanXuanRefreshView extends View {
35 | Paint paint1;
36 | //盒子上方的角的路径
37 | Path leftPath = new Path();
38 | Path rightPath = new Path();
39 |
40 | //盒子的两个盖子的上方的角坐标
41 | Point pointLeftOut;
42 | Point pointLeftInner;
43 | Point pointRightOut;
44 | Point pointRightInner;
45 |
46 | //盒子上方的四个角相当于四个圆心 盖子相当于围绕圆心做运动
47 | Point lCCPointOut;
48 | Point lCCPointInner;
49 | Point rCCPointOut;
50 | Point rCCPointInner;
51 |
52 | Path path1 = new Path();
53 | Path path2 = new Path();
54 | Path path3 = new Path();
55 | private int viewSizeHeight;
56 | private int viewSizeWidth;
57 |
58 | //标语字的集合
59 | private Bitmap[] slogan;
60 | //字坐标的集合
61 | private Point[] points;
62 | //严选logo
63 | private Bitmap yxLogo;
64 | //阴影的矩形
65 | private RectF rectShadow;
66 |
67 | private int jumpInt;
68 | private int shakeInt;
69 | private ViewStatus viewStatus;
70 |
71 | //当前的下拉距离
72 | int distance = 0;
73 | int leftAngle = -0;
74 | int rightAngle = Math.abs(leftAngle) - 180;
75 |
76 | private int outCircleR = 100;
77 | private int innerCircleR = 70;
78 |
79 | public YanXuanRefreshView(Context context) {
80 | this(context, null);
81 | }
82 |
83 | public YanXuanRefreshView(Context context, @Nullable AttributeSet attrs) {
84 | this(context, attrs, 0);
85 | }
86 |
87 | public YanXuanRefreshView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
88 | super(context, attrs, defStyleAttr);
89 | init();
90 | }
91 |
92 | private void init() {
93 | paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
94 | //初始所有的点
95 | pointLeftOut = new Point(0, 0);
96 | pointLeftInner = new Point(0, 0);
97 | pointRightOut = new Point(0, 0);
98 | pointRightInner = new Point(0, 0);
99 | lCCPointOut = new Point(0, 0);
100 | lCCPointInner = new Point(0, 0);
101 | rCCPointOut = new Point(0, 0);
102 | rCCPointInner = new Point(0, 0);
103 |
104 | //获取所有的字
105 | int[] arrayOfInt = {R.mipmap.all_refresh_hao_ic, R.mipmap.all_refresh_de_ic, R.mipmap.all_refresh_sheng_ic, R.mipmap.all_refresh_huo_ic
106 | , R.mipmap.all_refresh_mei_ic, R.mipmap.all_refresh_na_ic, R.mipmap.all_refresh_me_ic, R.mipmap.all_refresh_gui_ic};
107 | slogan = new Bitmap[arrayOfInt.length];
108 | points = new Point[arrayOfInt.length];
109 | for (int i = 0; i < arrayOfInt.length; i++) {
110 | slogan[i] = BitmapFactory.decodeResource(getResources(), arrayOfInt[i]);
111 | points[i] = new Point();
112 | }
113 |
114 | yxLogo = BitmapFactory.decodeResource(getResources(), R.mipmap.all_refresh_yanxuan_ic);
115 |
116 | rectShadow = new RectF();
117 | viewStatus = ViewStatus.START;
118 |
119 | }
120 |
121 | @Override
122 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
123 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
124 | viewSizeHeight = getMeasuredHeight();
125 | viewSizeWidth = getMeasuredWidth();
126 | }
127 |
128 | @Override
129 | protected void onDraw(Canvas canvas) {
130 | lCCPointOut.pointX = viewSizeWidth / 2 - outCircleR;
131 | lCCPointOut.pointY = viewSizeHeight / 2;
132 | lCCPointInner.pointX = viewSizeWidth / 2 - innerCircleR;
133 | lCCPointInner.pointY = viewSizeHeight / 2 - 30;
134 | rCCPointOut.pointX = viewSizeWidth / 2 + outCircleR;
135 | rCCPointOut.pointY = viewSizeHeight / 2;
136 | rCCPointInner.pointX = viewSizeWidth / 2 + innerCircleR;
137 | rCCPointInner.pointY = viewSizeHeight / 2 - 30;
138 |
139 | pointLeftOut.setPointX((float) (lCCPointOut.pointX + outCircleR * Math.cos(leftAngle * 3.14 / 180)));
140 | pointLeftOut.setPointY((float) (lCCPointOut.pointY + outCircleR * Math.sin(leftAngle * 3.14 / 180)));
141 |
142 | pointLeftInner.setPointX((float) (lCCPointInner.pointX + innerCircleR * Math.cos(leftAngle * 3.14 / 180)));
143 | pointLeftInner.setPointY((float) (lCCPointInner.pointY + innerCircleR * Math.sin(leftAngle * 3.14 / 180)));
144 |
145 | pointRightOut.setPointX((float) (rCCPointOut.pointX + outCircleR * Math.cos(rightAngle * 3.14 / 180)));
146 | pointRightOut.setPointY((float) (rCCPointOut.pointY + outCircleR * Math.sin(rightAngle * 3.14 / 180)));
147 |
148 | pointRightInner.setPointX((float) (rCCPointInner.pointX + innerCircleR * Math.cos(rightAngle * 3.14 / 180)));
149 | pointRightInner.setPointY((float) (rCCPointInner.pointY + innerCircleR * Math.sin(rightAngle * 3.14 / 180)));
150 |
151 | paint1.setAntiAlias(true);
152 | paint1.setColor(Color.parseColor("#FF7C7C7C"));
153 | //绘制第一个梯形
154 | path1.moveTo(lCCPointInner.pointX, lCCPointInner.pointY);
155 | path1.lineTo(rCCPointInner.pointX, rCCPointInner.pointY);
156 | path1.lineTo(rCCPointOut.pointX, rCCPointOut.pointY);
157 | path1.lineTo(lCCPointOut.pointX, lCCPointOut.pointY);
158 | path1.close();
159 | canvas.drawPath(path1, paint1);
160 |
161 | //绘制内侧矩形
162 | paint1.setColor(Color.parseColor("#FF979797"));
163 | path2.moveTo(lCCPointInner.pointX, lCCPointInner.pointY);
164 | path2.lineTo(rCCPointInner.pointX, rCCPointInner.pointY);
165 | path2.lineTo(rCCPointInner.pointX, rCCPointOut.pointY);
166 | path2.lineTo(lCCPointInner.pointX, lCCPointOut.pointY);
167 | path2.close();
168 | canvas.drawPath(path2, paint1);
169 |
170 | drawCover(canvas);
171 | drawSlogan(canvas);
172 |
173 | //绘制阴影
174 | paint1.setColor(Color.parseColor("#FFDCDCDE"));
175 | rectShadow.left = lCCPointOut.pointX - 50;
176 | rectShadow.right = rCCPointOut.pointX + 50;
177 | rectShadow.top = lCCPointOut.pointY + 80;
178 | rectShadow.bottom = lCCPointOut.pointY + 120;
179 | canvas.drawOval(rectShadow, paint1);
180 |
181 | //绘制下方的矩形
182 | paint1.setColor(Color.parseColor("#FFC6C6C6"));
183 | path3.moveTo(lCCPointOut.pointX, lCCPointOut.pointY);
184 | path3.lineTo(rCCPointOut.pointX, rCCPointOut.pointY);
185 | path3.lineTo(rCCPointOut.pointX, rCCPointOut.pointY + 100);
186 | path3.lineTo(lCCPointOut.pointX, lCCPointOut.pointY + 100);
187 | path3.close();
188 | canvas.drawPath(path3, paint1);
189 | paint1.reset();
190 |
191 | //绘制严选文字logo
192 | canvas.drawBitmap(yxLogo, lCCPointOut.pointX + 100 - yxLogo.getWidth() / 2, lCCPointOut.pointY + 50 - yxLogo.getHeight() / 2, paint1);
193 | super.onDraw(canvas);
194 | }
195 |
196 | //绘制盒子的盖子
197 | private void drawCover(Canvas canvas) {
198 | //绘制左边的盖子
199 | leftPath.moveTo(lCCPointInner.pointX, lCCPointInner.pointY);
200 | leftPath.lineTo(lCCPointOut.pointX, lCCPointOut.pointY);
201 | leftPath.lineTo(pointLeftOut.pointX, pointLeftOut.pointY);
202 | leftPath.lineTo(pointLeftInner.pointX, pointLeftInner.pointY);
203 | leftPath.close();
204 | //绘制右边的盖子
205 | rightPath.moveTo(rCCPointInner.pointX, rCCPointInner.pointY);
206 | rightPath.lineTo(rCCPointOut.pointX, rCCPointOut.pointY);
207 | rightPath.lineTo(pointRightOut.pointX, pointRightOut.pointY);
208 | rightPath.lineTo(pointRightInner.pointX, pointRightInner.pointY);
209 | rightPath.close();
210 | paint1.setColor(Color.parseColor("#FFCDCDCE"));
211 | canvas.drawPath(leftPath, paint1);
212 | canvas.drawPath(rightPath, paint1);
213 | //path需要重置 不然会有之前的绘制图像
214 | leftPath.reset();
215 | rightPath.reset();
216 | }
217 |
218 | //绘制标语
219 | //// TODO: 17/8/17 关于每个字的位置 这里简单写了一下 需要做调整
220 | private void drawSlogan(Canvas canvas) {
221 | if (viewStatus == ViewStatus.START_SLOGAN || viewStatus == ViewStatus.START_SHAKE) {
222 | float x;
223 | float y;
224 | for (int i = 0; i < slogan.length; i++) {
225 | if (viewStatus == ViewStatus.START_SLOGAN) {
226 | //好的生活
227 | if (i <= 3) {
228 | x = lCCPointOut.pointX + 100 - slogan[i].getWidth() - jumpInt * (2 - i) / 4;
229 | y = lCCPointOut.pointY - jumpInt * 5 / 7;
230 | } else {//没那么贵
231 | x = lCCPointOut.pointX + 100 + jumpInt * (i - 5) / 4;
232 | y = lCCPointOut.pointY - jumpInt * 2 / 5;
233 | }
234 | points[i].setPointX(x);
235 | points[i].setPointY(y);
236 | canvas.drawBitmap(slogan[i], points[i].pointX, points[i].pointY, paint1);
237 | } else {
238 | canvas.drawBitmap(slogan[i], points[i].pointX, points[i].pointY + (i % 2 == 0 ? shakeInt : -shakeInt), paint1);
239 | }
240 | }
241 | }
242 | }
243 |
244 |
245 | //文字重盒子中弹起的动画
246 | public void startJumpAnim() {
247 | viewStatus = ViewStatus.START_SLOGAN;
248 | ValueAnimator animator = ValueAnimator.ofInt(0, 200);
249 | animator.setDuration(700);
250 | animator.setInterpolator(new DecelerateInterpolator(2f));
251 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
252 | @Override
253 | public void onAnimationUpdate(ValueAnimator animation) {
254 | jumpInt = (int) animation.getAnimatedValue();
255 | invalidate();
256 | }
257 | });
258 | animator.addListener(new Animator.AnimatorListener() {
259 | @Override
260 | public void onAnimationEnd(Animator animation) {
261 | viewStatus = ViewStatus.START_SHAKE;
262 | //开始抖动文字
263 | shakeSlogan();
264 | }
265 |
266 | @Override
267 | public void onAnimationStart(Animator animation) {
268 |
269 | }
270 |
271 | @Override
272 | public void onAnimationCancel(Animator animation) {
273 |
274 | }
275 |
276 | @Override
277 | public void onAnimationRepeat(Animator animation) {
278 |
279 | }
280 | });
281 | animator.start();
282 | }
283 |
284 | //标语抖动动画
285 | public void shakeSlogan() {
286 | //简单实现的抖动动画 想要实现更好的效果可以查看https://github.com/hujiaweibujidao/wava
287 | ValueAnimator animator = ValueAnimator.ofInt(-2, 2);
288 | animator.setRepeatCount(-1);
289 | animator.setDuration(200);
290 | animator.setRepeatMode(ValueAnimator.REVERSE);
291 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
292 | @Override
293 | public void onAnimationUpdate(ValueAnimator animation) {
294 | shakeInt = (int) animation.getAnimatedValue();
295 | invalidate();
296 | }
297 | });
298 | animator.start();
299 | }
300 |
301 | //设置偏移 这里为了方便计算 设置210偏移对应盖子打开的最大角度 +-210
302 | public void setDistance(int distance) {
303 | this.distance = distance;
304 | int animOffset = distance - viewSizeHeight/2-30;
305 | if (viewStatus == ViewStatus.START && animOffset > 0) {
306 | if (leftAngle >= -210) {
307 | //规避原则 防止下拉速度过快 distance并不是以+1的形式递增 而是一下子加很多 造成画图不准
308 | if (animOffset >= 210) {
309 | animOffset = 210;
310 | }
311 | leftAngle = -animOffset;
312 | rightAngle = Math.abs(leftAngle) - 180;
313 | invalidate();
314 | }
315 | }else if(viewStatus == ViewStatus.START&&animOffset<=0){
316 | restoreView();
317 | }
318 | }
319 |
320 | //设置是否正在刷新
321 | public void setViewStatus(ViewStatus viewStatus) {
322 | this.viewStatus = viewStatus;
323 | if (viewStatus == ViewStatus.REFRESHING) {
324 | //可能下拉没有到210 就开始刷新了 看具体xml中的height // TODO: 17/8/18 需要调整完善的地方
325 | leftAngle = -210;
326 | rightAngle = 30;
327 | invalidate();
328 | startJumpAnim();
329 | }
330 | }
331 |
332 | //刷新完毕 重置view的状态
333 | public void restoreView() {
334 | viewStatus = ViewStatus.START;
335 | leftAngle = 0;
336 | rightAngle = -180;
337 | distance = 0;
338 | invalidate();
339 | }
340 |
341 | }
342 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
14 |
15 |
22 |
23 |
26 |
27 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/layout/yx_refresh_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_de_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_de_ic.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_gui_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_gui_ic.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_hao_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_hao_ic.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_huo_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_huo_ic.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_me_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_me_ic.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_mei_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_mei_ic.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_na_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_na_ic.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_sheng_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_sheng_ic.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_yanxuan_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/all_refresh_yanxuan_ic.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/YanXuanRefresh/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | YanXuanRefresh
3 |
4 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/YanXuanRefresh/app/src/test/java/com/sheng/yanxuanrefresh/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.sheng.yanxuanrefresh;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/YanXuanRefresh/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/refresh.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cccdada/YanXuanRefresh/521cc5d3ca50d658c7cf7d6c8b16d0a303c37d00/refresh.gif
--------------------------------------------------------------------------------