15 | * Author: peixing.yang 16 | * Date: 2019/3/11 17 | */ 18 | public class PullToRefreshRecyclerView extends LinearLayout { 19 | 20 | public PullToRefreshRecyclerView(Context context) { 21 | super(context); 22 | } 23 | 24 | public PullToRefreshRecyclerView(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | } 27 | 28 | // private void init(){ 29 | //// View view=LayoutInflater.from(getContext()).inflate(R.layout.) 30 | //// } 31 | //// 32 | //// public View setFootView(){ 33 | //// 34 | //// } 35 | } 36 | -------------------------------------------------------------------------------- /YPX_RefreshLayout_demo/app/src/main/java/com/ypx/jiehunle/ypx_bezierqqrefreshdemo/YPXTabIndicator/YPXTabIndicator.java: -------------------------------------------------------------------------------- 1 | package com.ypx.jiehunle.ypx_bezierqqrefreshdemo.YPXTabIndicator; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.RectF; 8 | import android.graphics.drawable.GradientDrawable; 9 | import android.support.v4.view.ViewPager; 10 | import android.support.v4.view.ViewPager.OnPageChangeListener; 11 | import android.text.TextPaint; 12 | import android.util.AttributeSet; 13 | import android.util.DisplayMetrics; 14 | import android.util.TypedValue; 15 | import android.view.Gravity; 16 | import android.view.View; 17 | import android.view.ViewGroup; 18 | import android.view.WindowManager; 19 | import android.widget.FrameLayout; 20 | import android.widget.HorizontalScrollView; 21 | import android.widget.LinearLayout; 22 | import android.widget.TextView; 23 | 24 | 25 | /** 26 | * 仿网易圆形移动viewpager指示器 27 | *
28 | * 博客地址:http://blog.csdn.net/qq_16674697/article/details/51954228
29 | *
30 | * @author yangpeixing
31 | */
32 | @SuppressWarnings("unused")
33 | public class YPXTabIndicator extends LinearLayout implements
34 | OnPageChangeListener, YPXViewPager.OnPageChangeListener {
35 | private Paint mLinePaint;
36 | /**
37 | * 自定义指示器构造器
38 | */
39 | private DrawIndicatorCreator creator;
40 | private int screenWidth = 0;
41 | /**
42 | * 内部椭圆背景paint
43 | */
44 | private Paint indicatorPaint;
45 | /**
46 | * 外围背景线条宽度,默认为0px
47 | */
48 | private int strokeWidth = 0;
49 | /**
50 | * 背景色,,默认为红色#ff0000
51 | */
52 | private int backgroundColor = Color.WHITE;
53 | /**
54 | * 背景线条色
55 | */
56 | private int backgroundLineColor = Color.WHITE;
57 | /**
58 | * 指示器颜色
59 | */
60 | private int indicatorColor = Color.RED;
61 | /**
62 | * 背景和指示器半径,默认无背景
63 | */
64 | private int backgroundRadius = 0;
65 | /**
66 | * 记录tab宽度数组
67 | */
68 | private int[] tabLengthArray;
69 | /**
70 | * x轴偏移量
71 | */
72 | private int mTransitX = 0;
73 | /**
74 | * tab默认大小为14
75 | */
76 | private int tabTextSize = 14;
77 | /**
78 | * 设置tab默认颜色,既未选中时颜色
79 | */
80 | private int tabTextColor = Color.parseColor("#666666");
81 | /**
82 | * tab选中后颜色,默认和背景色一致
83 | */
84 | private int tabPressColor = indicatorColor;
85 | private int mTabWidth = 0;
86 | private String[] titles;
87 | private ViewPager viewPager;
88 | private YPXViewPager ypxViewPager;
89 | /**
90 | * 默认高度,用户可自己设置高度,默认50px
91 | */
92 | private int defaultHeight = 50;
93 | /**
94 | * viewpager坐标
95 | */
96 | private int mCurrentIndex = 0;
97 | /**
98 | * 默认选中第几个tab
99 | */
100 | private int mInitIndex = 0;
101 | /**
102 | * 判断是否点击
103 | */
104 | private boolean isClick = false;
105 | /**
106 | * 是否显示tab中间分割线
107 | */
108 | private boolean isShowTabDivider = false;
109 | /**
110 | * 是否设置背景
111 | */
112 | private boolean isShowBackground = true;
113 | /**
114 | * 是否设置指示器
115 | */
116 | private boolean isShowIndicator = true;
117 | /**
118 | * 是否设置tab字体变换
119 | */
120 | private boolean isShowTabSizeChange = true;
121 | /**
122 | * 是否平分tab的宽度,若false,则返回每个tab自适应的宽度,适合在多种数目下使用
123 | */
124 | private boolean isDeuceTabWidth = false;
125 | /**
126 | * 最大字体大小,默认为16
127 | */
128 | private int maxTabTextSize = 16;
129 | /**
130 | * tab总数
131 | */
132 | private int totalCount = 0;
133 | /**
134 | * 是否手动设置了tab的宽度
135 | */
136 | private boolean isSetTabWidth = false;
137 | /**
138 | * 是否是HorizonScrollView的子View
139 | */
140 | private boolean isChildOfHorizontalScrollView = false;
141 |
142 | private int tabPaddingLeft = dp(10), tabPaddingRight = dp(10), tabPaddingTop = 0,
143 | tabPaddingBottom = 0;
144 |
145 | /**
146 | * Tab排版模式
147 | */
148 | private int tabGravity = Gravity.CENTER;
149 | /**
150 | * 对外的ViewPager的回调接口
151 | */
152 | private PageChangeListener onPageChangeListener;
153 | private TabClickListener tabClickListener;
154 |
155 | private int tabLeftMargin = 0;
156 | private int tabRightMargin = 0;
157 |
158 | private int dividerTopAndBottomMargin = 0;
159 | private int dividerColor = Color.parseColor("#EBEBEB");
160 |
161 | public YPXTabIndicator(Context context) {
162 | this(context, null);
163 | }
164 |
165 | public YPXTabIndicator(Context context, AttributeSet attrs) {
166 | this(context, attrs, 0);
167 | }
168 |
169 | public YPXTabIndicator(Context context, AttributeSet attrs,
170 | int defStyleAttr) {
171 | super(context, attrs, defStyleAttr);
172 | setOrientation(HORIZONTAL);
173 | setGravity(Gravity.CENTER);
174 | initView();
175 | }
176 |
177 | private void initView() {
178 | mCurrentIndex = 0;
179 | mInitIndex = 0;
180 | mTabWidth = 0;
181 | mTransitX = 0;
182 | defaultHeight = dp(44);
183 | titles = new String[]{"tab1", "tab2", "tab3"};
184 | tabLengthArray = new int[titles.length];
185 | screenWidth = getScreenWidth();
186 | initPaints();
187 | setBackgroundShape();
188 | refreshTabLayouts();
189 | }
190 |
191 | /**
192 | * 初始化指示器画笔
193 | */
194 | private void initPaints() {
195 | indicatorPaint = new Paint();
196 | indicatorPaint.setAntiAlias(true);
197 | indicatorPaint.setColor(indicatorColor);
198 | indicatorPaint.setStyle(Paint.Style.FILL);
199 | }
200 |
201 | /**
202 | * 设置圆角背景,可定制圆角和边框(大小和颜色)
203 | */
204 | private void setBackgroundShape() {
205 | GradientDrawable gd = new GradientDrawable();
206 | gd.setColor(backgroundColor);
207 | gd.setCornerRadius(backgroundRadius);
208 | gd.setStroke(strokeWidth, backgroundLineColor);
209 | if (isShowBackground) {
210 | setBackground(gd);
211 | } else {
212 | setBackgroundColor(backgroundColor);
213 | }
214 | }
215 |
216 | /**
217 | * 重置刷新所有Tab
218 | */
219 | private void refreshTabLayouts() {
220 | removeAllViews();
221 | for (int i = 0; i < titles.length; i++) {
222 | addView(createDefaultTab(titles[i], i));
223 | }
224 | refreshCurrentTab();
225 | }
226 |
227 | /**
228 | * 刷新选中Tab
229 | */
230 | public void refreshCurrentTab() {
231 | resetTabColor();
232 | resetTabSize();
233 | getTab(mCurrentIndex).setTextColor(tabPressColor);
234 | if (isShowTabSizeChange) {
235 | getTab(mCurrentIndex).setTextSize(maxTabTextSize);
236 | } else {
237 | getTab(mCurrentIndex).setTextSize(tabTextSize);
238 | }
239 | mTransitX = getTransitXByPosition(mCurrentIndex);
240 | mTabWidth = tabLengthArray[mCurrentIndex];
241 | int centerX = mTransitX - (screenWidth - mTabWidth) / 2 + tabLeftMargin;
242 | parentScrollTo(centerX, 0);
243 | invalidate();
244 | }
245 |
246 | /**
247 | * 创建默认tab(TextView)
248 | *
249 | * @param string 要显示的文本
250 | * @param i 坐标
251 | */
252 | private TextView createDefaultTab(String string, final int i) {
253 | TextView textView = new TextView(getContext());
254 | textView.setGravity(Gravity.CENTER);
255 | textView.setTextColor(tabTextColor);
256 | textView.setTextSize(tabTextSize);
257 | textView.setText(string);
258 | textView.setPadding(tabPaddingLeft, tabPaddingTop, tabPaddingRight,
259 | tabPaddingBottom);
260 | TextPaint mTextPaint;
261 | if (isShowTabSizeChange) {//设置是否字体变换
262 | TextView dTextView = new TextView(getContext());
263 | dTextView.setTextSize(maxTabTextSize);
264 | mTextPaint = dTextView.getPaint();//得到最大尺寸textView的Paint,用于测量宽度
265 | } else {
266 | mTextPaint = textView.getPaint();
267 | }
268 | if (!isSetTabWidth) {
269 | mTabWidth = (int) mTextPaint.measureText(isDeuceTabWidth ? getMaxLengthString(titles) : string)
270 | + tabPaddingLeft + tabPaddingRight;
271 | }
272 | tabLengthArray[i] = mTabWidth;
273 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mTabWidth, defaultHeight + tabPaddingBottom + tabPaddingTop);
274 | if (i == 0) {
275 | params.leftMargin = tabLeftMargin;
276 | }
277 |
278 | if (i == totalCount - 1) {
279 | params.rightMargin = tabRightMargin;
280 | }
281 | textView.setOnClickListener(new OnClickListener() {
282 | @Override
283 | public void onClick(View v) {
284 | isClick = true;
285 | setCurrentIndex(i);
286 | if (tabClickListener != null) {
287 | tabClickListener.onClick((TextView) v, i);
288 | }
289 | }
290 | });
291 | textView.setLayoutParams(params);
292 | return textView;
293 | }
294 |
295 | /**
296 | * 设置整个指示器左边距和右边距,即第一个tab的左边距和最后一个tab的右边距
297 | * 如果当前tab布局排版模式为居中,则设置左右间距无效
298 | *
299 | * @param tabLeftMargin 左边距
300 | * @param tabRightMargin 右边距
301 | */
302 | public void setTabLeftAndRightMargin(int tabLeftMargin, int tabRightMargin) {
303 | if (tabGravity == Gravity.CENTER) {
304 | return;
305 | }
306 | this.tabLeftMargin = tabLeftMargin;
307 | this.tabRightMargin = tabRightMargin;
308 |
309 | LinearLayout.LayoutParams pa = (LinearLayout.LayoutParams) getTab(0).getLayoutParams();
310 | pa.leftMargin = tabLeftMargin;
311 | getTab(0).setLayoutParams(pa);
312 |
313 | LinearLayout.LayoutParams pa2 = (LinearLayout.LayoutParams) getTab(getChildCount() - 1).getLayoutParams();
314 | pa2.rightMargin = tabRightMargin;
315 | getTab(getChildCount() - 1).setLayoutParams(pa2);
316 | }
317 |
318 |
319 | /**
320 | * 获取position前几项tab宽度之和
321 | *
322 | * @param position 索引
323 | */
324 | private int getTransitXByPosition(int position) {
325 | int defaultNum = 0;
326 | for (int i = 0; i < position; i++) {
327 | defaultNum += tabLengthArray[i];
328 | }
329 | return defaultNum;
330 | }
331 |
332 | /**
333 | * 显示tab分割线
334 | *
335 | * @param isShowTabDivider 显示tab分割线
336 | * @param dividerTopAndBottomMargin tab分割线上下间距
337 | */
338 | public void setShowTabDivider(boolean isShowTabDivider, int dividerTopAndBottomMargin, int dividerColor) {
339 | this.isShowTabDivider = isShowTabDivider;
340 | this.dividerTopAndBottomMargin = dividerTopAndBottomMargin;
341 | this.dividerColor = dividerColor;
342 | invalidate();
343 | }
344 |
345 | public void setShowTabDivider(boolean isShowTabDivider) {
346 | this.isShowTabDivider = isShowTabDivider;
347 | invalidate();
348 | }
349 |
350 | @Override
351 | protected void dispatchDraw(Canvas canvas) {
352 | defaultHeight = getMeasuredHeight();
353 | if (mCurrentIndex == 0) {
354 | mTabWidth = tabLengthArray[0];
355 | }
356 | int left = mTransitX + mInitIndex * mTabWidth + getTab(0).getLeft();// tab左边距离原点的位置
357 | int right = mTabWidth + left;// 整个tab的位置
358 | int top = 0;// tab距离顶端的位置
359 | int bottom = defaultHeight;// 整个tab的高度
360 |
361 | if (isShowTabDivider) {
362 | drawDivider(canvas);
363 | }
364 |
365 | if (isShowIndicator) {
366 | if (creator != null) {
367 | creator.drawIndicator(canvas, left, top, right, bottom,
368 | indicatorPaint, backgroundRadius);
369 | } else {
370 | drawUnderLineIndicatorWithTransitX(canvas, left, top, right, bottom, indicatorPaint);
371 | }
372 | }
373 | if (mInitIndex != 0) {
374 | (getTab(mInitIndex)).setTextColor(backgroundColor);
375 | int centerX = getTransitXByPosition(mInitIndex)
376 | - (screenWidth - tabLengthArray[mInitIndex]) / 2;
377 | parentScrollTo(centerX, 0);
378 | }
379 | mInitIndex = 0;// 清除第一次默认index
380 | super.dispatchDraw(canvas);
381 | }
382 |
383 |
384 | /**
385 | * 画分割线
386 | *
387 | * @param canvas 画布
388 | */
389 | private void drawDivider(Canvas canvas) {
390 | if (mLinePaint == null) {
391 | mLinePaint = new Paint();
392 | mLinePaint.setColor(dividerColor);
393 | mLinePaint.setStrokeWidth(1);
394 | mLinePaint.setAntiAlias(true);
395 | }
396 | for (int i = 1; i < totalCount; i++) {
397 | float x = (getTransitXByPosition(i) + tabLeftMargin) * 1.0f;
398 | float y = dividerTopAndBottomMargin;
399 | float endY = defaultHeight * 1.0f - dividerTopAndBottomMargin;
400 | canvas.drawLines(new float[]{x, y, x, endY}, mLinePaint);
401 | }
402 | }
403 |
404 | /**
405 | * 默认为圆角矩形指示器,用户可继承重写自定义指示器样式
406 | *
407 | * @param canvas 画布
408 | * @param left tab左边距离原点的位置
409 | * @param top 整个tab的位置
410 | * @param right tab距离顶端的位置
411 | * @param bottom 整个tab的高度,既控件高度
412 | * @param paint 指示器画笔
413 | */
414 | public void drawIndicatorWithTransitX(Canvas canvas, int left, int top,
415 | int right, int bottom, Paint paint) {
416 | if (backgroundRadius < defaultHeight / 2) {
417 | // 真机运行用这种方式,模拟器圆角会失真
418 | RectF oval = new RectF(left, top, right, bottom);// 设置个新的长方形,扫描测量
419 | canvas.drawRoundRect(oval, backgroundRadius, backgroundRadius,
420 | paint);
421 | } else {// 画三段代替圆角矩形,既圆、矩形、圆
422 | RectF oval2 = new RectF(bottom / 2 + left, top, right - bottom / 2,
423 | bottom);
424 | canvas.drawCircle(oval2.left, bottom / 2, bottom / 2,
425 | indicatorPaint);
426 | canvas.drawRect(oval2, indicatorPaint);
427 | canvas.drawCircle(oval2.right, bottom / 2, bottom / 2, paint);
428 | }
429 | }
430 |
431 | public void setViewPager(ViewPager viewPager, int index) {
432 | this.viewPager = viewPager;
433 | this.mCurrentIndex = index;
434 | mInitIndex = index;
435 | isChildOfHorizontalScrollView = (getParent() != null
436 | && (getParent() instanceof HorizontalScrollView));
437 |
438 | viewPager.addOnPageChangeListener(this);
439 | setCurrentIndex(index);
440 | }
441 |
442 | public void setViewPager(YPXViewPager viewPager, int index) {
443 | this.ypxViewPager = viewPager;
444 | this.mCurrentIndex = index;
445 | mInitIndex = index;
446 | isChildOfHorizontalScrollView = (getParent() != null
447 | && (getParent() instanceof HorizontalScrollView));
448 | ypxViewPager.setOnPageChangeListener(this);
449 | setCurrentIndex(index);
450 |
451 | }
452 |
453 | /**
454 | * 如果父控件是HorizonScrollView,则控制父控件移动
455 | *
456 | * @param x x
457 | * @param y y
458 | */
459 | public void parentScrollTo(int x, int y) {
460 | if (isChildOfHorizontalScrollView) {
461 | ((HorizontalScrollView) getParent()).smoothScrollTo(x, y);
462 | }
463 | }
464 |
465 | /**
466 | * 设置颜色变换
467 | *
468 | * @param position 索引
469 | * @param positionOffset 变化因子
470 | */
471 | protected void setTabColorChange(int position, float positionOffset) {
472 | getTab(position).setTextColor(
473 | blendColors(tabPressColor, tabTextColor, positionOffset));
474 |
475 | }
476 |
477 | /**
478 | * 设置字体大小变换
479 | *
480 | * @param position 索引
481 | * @param positionOffset 变化因子
482 | */
483 | protected void setTabSizeChange(int position, float positionOffset) {
484 | getTab(position).setTextSize(
485 | blendSize(tabTextSize, maxTabTextSize, positionOffset));
486 | }
487 |
488 | /**
489 | * 两个大小渐变
490 | *
491 | * @param minSize 最小尺寸
492 | * @param maxSize 最大尺寸
493 | * @param ratio 渐变率
494 | * @return 计算后的尺寸
495 | */
496 | private float blendSize(int minSize, int maxSize, float ratio) {
497 | return (minSize + (maxSize - minSize) * ratio * 1.0f);
498 | }
499 |
500 | /**
501 | * 两个颜色渐变转化
502 | *
503 | * @param color1 默认色
504 | * @param color2 目标色
505 | * @param ratio 渐变率(0~1)
506 | * @return 计算后的颜色
507 | */
508 | private int blendColors(int color1, int color2, float ratio) {
509 | final float inverseRation = 1f - ratio;
510 | float r = (Color.red(color1) * ratio)
511 | + (Color.red(color2) * inverseRation);
512 | float g = (Color.green(color1) * ratio)
513 | + (Color.green(color2) * inverseRation);
514 | float b = (Color.blue(color1) * ratio)
515 | + (Color.blue(color2) * inverseRation);
516 | return Color.rgb((int) r, (int) g, (int) b);
517 | }
518 |
519 | /**
520 | * 设置Tab的排版模式,比如居中和居左(相对父布局)
521 | * 如果可以横向滑动,则限定居左加载(已限定死,外部无法更改)
522 | * 如果不能横向滑动,则根据用户传入参数决定排版,默认居中
523 | *
524 | * @param tabGravity 排版模式,只针对tab数量不足一屏时生效
525 | */
526 | public void setTabLayoutGravity(int tabGravity) {
527 | this.tabGravity = tabGravity;
528 | if (isChildOfHorizontalScrollView) {
529 | FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) this.getLayoutParams();
530 | if (params == null) {
531 | return;
532 | }
533 | if (parentCanScroll()) {
534 | params.gravity = Gravity.START | Gravity.CENTER;
535 | } else {
536 | params.gravity = tabGravity;
537 | }
538 | setLayoutParams(params);
539 | } else {
540 | if (this.getLayoutParams() instanceof LinearLayout.LayoutParams) {
541 | LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) this.getLayoutParams();
542 | params.gravity = tabGravity;
543 | setLayoutParams(params);
544 | } else if (this.getLayoutParams() instanceof FrameLayout.LayoutParams) {
545 | FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) this.getLayoutParams();
546 | params.gravity = tabGravity;
547 | setLayoutParams(params);
548 | }
549 |
550 | }
551 |
552 | }
553 |
554 | /**
555 | * 检测父布局是否可以滑动,换句话说,用来检测当前tab数目是否大于屏幕宽度或父布局宽度
556 | *
557 | * @return 是否大于
558 | */
559 | private boolean parentCanScroll() {
560 | int parentWidth = ((ViewGroup) getParent()).getMeasuredWidth();
561 | if (isDeuceTabWidth) {
562 | return parentWidth == 0 ? mTabWidth * totalCount > screenWidth
563 | : mTabWidth * totalCount > parentWidth;
564 | }
565 | int totalWidth = tabLeftMargin + tabRightMargin;
566 | for (int i = 0; i < totalCount; i++) {
567 | totalWidth += tabLengthArray[i];
568 | if ((parentWidth != 0 && totalWidth > parentWidth) || totalWidth >= screenWidth) {
569 | return true;
570 | }
571 | }
572 | return false;
573 | }
574 |
575 | /**
576 | * 设置外框宽度,默认为0px
577 | *
578 | * @param strokeWidth 背景边框宽度
579 | */
580 | public void setBackgroundStrokeWidth(int strokeWidth) {
581 | this.strokeWidth = strokeWidth;
582 | setBackgroundShape();
583 | }
584 |
585 | public ViewPager getViewPager() {
586 | return viewPager;
587 | }
588 |
589 | public YPXViewPager getYPXViewPager() {
590 | return ypxViewPager;
591 | }
592 |
593 | public int getDefaultHeight() {
594 | return defaultHeight;
595 | }
596 |
597 | /**
598 | * 设置指示器高度,默认50px
599 | *
600 | * @param defaultHeight 指示器高度
601 | */
602 | public void setDefaultHeight(int defaultHeight) {
603 | this.defaultHeight = defaultHeight;
604 | refreshTabLayouts();
605 | }
606 |
607 | public int getStrokeWidth() {
608 | return strokeWidth;
609 | }
610 |
611 | public String[] getTitles() {
612 | return titles;
613 | }
614 |
615 | /**
616 | * 设置指示器内容,默认tab1,tab2,tab3
617 | *
618 | * @param titles 设置标题数组
619 | */
620 | public void setTitles(String[] titles) {
621 | if (titles != null && titles.length > 0) {
622 | this.titles = titles;
623 | totalCount = titles.length;
624 | tabLengthArray = new int[totalCount];
625 | refreshTabLayouts();
626 | setTabLayoutGravity(tabGravity);
627 | }
628 | }
629 |
630 | /**
631 | * 获取tab
632 | *
633 | * @param i 索引
634 | * @return 该索引对应的tab(textView)
635 | */
636 | public TextView getTab(int i) {
637 | if (i < titles.length) {
638 | return (TextView) getChildAt(i);
639 | } else {
640 | return (TextView) getChildAt(titles.length - 1);
641 | }
642 | }
643 |
644 | /**
645 | * 重置tab颜色
646 | */
647 | public void resetTabColor() {
648 | for (int i = 0; i < totalCount; i++) {
649 | getTab(i).setTextColor(tabTextColor);
650 | }
651 | }
652 |
653 | /**
654 | * 重置tab大小
655 | */
656 | public void resetTabSize() {
657 | for (int i = 0; i < totalCount; i++) {
658 | getTab(i).setTextSize(tabTextSize);
659 | }
660 | }
661 |
662 | public int getTabTextColor() {
663 | return tabTextColor;
664 | }
665 |
666 | /**
667 | * 设置tab默认颜色,既未选中时颜色,默认为红色
668 | *
669 | * @param tabTextColor 文字颜色
670 | */
671 | public void setTabTextColor(int tabTextColor) {
672 | this.tabTextColor = tabTextColor;
673 | for (int i = 0; i < totalCount; i++) {
674 | getTab(i).setTextColor(tabTextColor);
675 | }
676 | refreshCurrentTab();
677 | }
678 |
679 | public int getmBackgroundColor() {
680 | return backgroundColor;
681 | }
682 |
683 | /**
684 | * 设置背景色,同时也是选中tab的文本颜色,默认为白色
685 | *
686 | * @param backgroundColor 背景色
687 | */
688 | public void setmBackgroundColor(int backgroundColor) {
689 | this.backgroundColor = backgroundColor;
690 | setBackgroundShape();
691 | }
692 |
693 | public int getBackgroundRadius() {
694 | return backgroundRadius;
695 | }
696 |
697 | /**
698 | * 设置背景圆角大小,默认为40px
699 | *
700 | * @param backgroundRadius 背景圆角大小
701 | */
702 | public void setBackgroundRadius(int backgroundRadius) {
703 | this.backgroundRadius = backgroundRadius;
704 | setBackgroundShape();
705 | }
706 |
707 | /**
708 | * 设置外框颜色,默认为白色
709 | *
710 | * @param backgroundLineColor 外框颜色
711 | */
712 | public void setBackgroundLineColor(int backgroundLineColor) {
713 | this.backgroundLineColor = backgroundLineColor;
714 | setBackgroundShape();
715 | }
716 |
717 | /**
718 | * 获取当前viewpager选中项
719 | *
720 | * @return 获取当前选中项
721 | */
722 | public int getCurrentIndex() {
723 | return mCurrentIndex;
724 | }
725 |
726 | /**
727 | * 设置默认项
728 | *
729 | * @param mCurrentIndex 默认
730 | */
731 | public void setCurrentIndex(final int mCurrentIndex) {
732 | this.mCurrentIndex = mCurrentIndex;
733 | if (ypxViewPager != null && ypxViewPager.getAdapter() != null) {
734 | ypxViewPager.setCurrentItem(mCurrentIndex, true);
735 | }
736 |
737 | if (viewPager != null && viewPager.getAdapter() != null) {
738 | viewPager.setCurrentItem(mCurrentIndex, true);
739 | }
740 |
741 | postDelayed(new Runnable() {
742 | @Override
743 | public void run() {
744 | refreshCurrentTab();
745 | }
746 | }, 50);
747 | }
748 |
749 | public boolean isShowBackground() {
750 | return isShowBackground;
751 | }
752 |
753 | /**
754 | * 是否显示背景
755 | *
756 | * @param isShowBackground 是否显示背景
757 | */
758 | public void setShowBackground(boolean isShowBackground) {
759 | this.isShowBackground = isShowBackground;
760 | setBackgroundShape();
761 | }
762 |
763 | public boolean isShowIndicator() {
764 | return isShowIndicator;
765 | }
766 |
767 | /**
768 | * 是否显示指示器
769 | *
770 | * @param isShowIndicator 是否显示指示器
771 | */
772 | public void setShowIndicator(boolean isShowIndicator) {
773 | this.isShowIndicator = isShowIndicator;
774 | invalidate();
775 | }
776 |
777 | public int getIndicatorColor() {
778 | return indicatorColor;
779 | }
780 |
781 | /**
782 | * 设置指示器颜色,默认为红色
783 | *
784 | * @param indicatorColor 指示器颜色
785 | */
786 | public void setIndicatorColor(int indicatorColor) {
787 | this.indicatorColor = indicatorColor;
788 | indicatorPaint.setColor(indicatorColor);
789 | invalidate();
790 | }
791 |
792 | /**
793 | * 设置最大字体大小,默认为16
794 | *
795 | * @param maxTabTextSize 最大字体尺寸
796 | */
797 | public void setTabMaxTextSize(int maxTabTextSize) {
798 | this.maxTabTextSize = maxTabTextSize;
799 | refreshTabLayouts();
800 | refreshCurrentTab();
801 | }
802 |
803 |
804 | public int getTabWidth() {
805 | return mTabWidth;
806 | }
807 |
808 | /**
809 | * 设置tab的宽度
810 | *
811 | * @param mTabWidth 强制设置每个tab的宽度
812 | */
813 | public void setTabWidth(int mTabWidth) {
814 | if (mTabWidth < 0) {
815 | this.isSetTabWidth = false;
816 | refreshTabLayouts();
817 | return;
818 | }
819 | this.mTabWidth = mTabWidth;
820 | this.isSetTabWidth = true;
821 | refreshTabLayouts();
822 | }
823 |
824 | public int getMaxTabTextSize() {
825 | return maxTabTextSize;
826 | }
827 |
828 | /**
829 | * 是否设置tab字体变换效果
830 | *
831 | * @param isShowTabSizeChange 是否显示tab字体大小变换效果
832 | */
833 | public void setShowTabSizeChange(boolean isShowTabSizeChange) {
834 | this.isShowTabSizeChange = isShowTabSizeChange;
835 | }
836 |
837 | public int getTabPressColor() {
838 | return tabPressColor;
839 | }
840 |
841 | /**
842 | * 设置tab选中颜色,默认为红色#ff0000
843 | *
844 | * @param tabPressColor tab选中的颜色
845 | */
846 | public void setTabPressColor(int tabPressColor) {
847 | this.tabPressColor = tabPressColor;
848 | refreshCurrentTab();
849 | }
850 |
851 | public int getTabTextSize() {
852 | return tabTextSize;
853 | }
854 |
855 | /**
856 | * 设置tab大小,默认大小为14
857 | *
858 | * @param tabTextSize 正常尺寸
859 | */
860 | public void setTabTextSize(int tabTextSize) {
861 | this.tabTextSize = tabTextSize;
862 | resetTabSize();
863 | }
864 |
865 | /**
866 | * 设置tab的边距,默认左右为10dp,上下无间距
867 | *
868 | * @param l 左
869 | * @param t 上
870 | * @param r 右
871 | * @param b 下
872 | */
873 | public void setTabPadding(int l, int t, int r, int b) {
874 | this.tabPaddingLeft = l;
875 | this.tabPaddingRight = r;
876 | this.tabPaddingTop = t;
877 | this.tabPaddingBottom = b;
878 | refreshTabLayouts();
879 | }
880 |
881 | /**
882 | * 设置指定tab的边距
883 | *
884 | * @param position 要设置的tab索引
885 | * @param l 左
886 | * @param t 上
887 | * @param r 右
888 | * @param b 下
889 | */
890 | public void setTabPaddingWithPosition(int position, int l, int t, int r, int b) {
891 | getTab(position).setPadding(l, t, r, b);
892 | refreshTabLayouts();
893 | }
894 |
895 | public boolean isDeuceTabWidth() {
896 | return isDeuceTabWidth;
897 | }
898 |
899 | /**
900 | * 是否平分tab的宽度,若false,则返回每个tab自适应的宽度,适合在多种数目下使用
901 | *
902 | * @param isDeuceTabWidth 是否平分
903 | */
904 | public void setDeuceTabWidth(boolean isDeuceTabWidth) {
905 | this.isDeuceTabWidth = isDeuceTabWidth;
906 | refreshTabLayouts();
907 | refreshCurrentTab();
908 | }
909 |
910 | /**
911 | * 获取tab中最长的tab文本作为最小tab宽度
912 | *
913 | * @param arrStr tab文本数组
914 | * @return 获取最大的tab文本
915 | */
916 | private String getMaxLengthString(String[] arrStr) {
917 | String max = arrStr[0];
918 | for (int x = 1; x < arrStr.length; x++) {
919 | if (arrStr[x].length() > max.length())
920 | max = arrStr[x];
921 | }
922 | return max;
923 | }
924 |
925 |
926 | /**
927 | * 设置指示器样式,默认为下划线样式,可定制成网易样式
928 | *
929 | * @param creator 构造器
930 | */
931 | public void setDrawIndicatorCreator(DrawIndicatorCreator creator) {
932 | this.creator = creator;
933 | }
934 |
935 | /**
936 | * 对外的ViewPager的回调接口的设置
937 | *
938 | * @param pageChangeListener 滑动接口
939 | */
940 | public void setOnPageChangeListener(PageChangeListener pageChangeListener) {
941 | this.onPageChangeListener = pageChangeListener;
942 | }
943 |
944 | /**
945 | * 对外的设置tab点击回调
946 | *
947 | * @param tabClickListener tab点击回调
948 | */
949 | public void setOnTabClickListener(TabClickListener tabClickListener) {
950 | this.tabClickListener = tabClickListener;
951 | }
952 |
953 | @Override
954 | public void onPageScrollStateChanged(int state) {
955 | if (state == ViewPager.SCROLL_STATE_IDLE) {//滑动停止,关闭点击
956 | isClick = false;
957 | }
958 | if (onPageChangeListener != null) { // 回调
959 | onPageChangeListener.onPageScrollStateChanged(state);
960 | }
961 | }
962 |
963 | @Override
964 | public void onPageScrolled(int position, float positionOffset,
965 | int positionOffsetPixels) {
966 | if (isClick || position + 1 >= totalCount) {
967 | return;
968 | }
969 | int transitX = getTransitXByPosition(position);
970 | int tabWidth = tabLengthArray[position];
971 |
972 | if (isShowTabSizeChange) {// 判断是否变换
973 | setTabSizeChange(position, 1 - positionOffset);
974 | setTabSizeChange(position + 1, positionOffset);
975 | }
976 | setTabColorChange(position, 1 - positionOffset);
977 | setTabColorChange(position + 1, positionOffset);
978 |
979 | mTransitX = (int) (tabWidth * positionOffset + transitX);
980 | mTabWidth = (int) (tabWidth + (tabLengthArray[position + 1] - tabWidth) * positionOffset);
981 | invalidate();
982 |
983 | if (positionOffset != 0.0f) {
984 | int centerX = (mTransitX - (screenWidth - mTabWidth) / 2) + tabLeftMargin;
985 | parentScrollTo(centerX, 0);
986 | }
987 |
988 | if (onPageChangeListener != null) { // 回调
989 | onPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
990 | }
991 |
992 | }
993 |
994 | @Override
995 | public void onPageSelected(int arg0) {
996 | mCurrentIndex = arg0;
997 | if (onPageChangeListener != null) {
998 | onPageChangeListener.onPageSelected(arg0);
999 | }
1000 | }
1001 |
1002 | /**
1003 | * 获得屏幕宽度
1004 | *
1005 | * @return 屏幕宽度
1006 | */
1007 | public int getScreenWidth() {
1008 | WindowManager wm = (WindowManager) getContext()
1009 | .getSystemService(Context.WINDOW_SERVICE);
1010 | DisplayMetrics outMetrics = new DisplayMetrics();
1011 | wm.getDefaultDisplay().getMetrics(outMetrics);
1012 | return outMetrics.widthPixels;
1013 | }
1014 |
1015 |
1016 | /**
1017 | * 下划线指示器
1018 | *
1019 | * @param canvas 画布
1020 | * @param left 左边距
1021 | * @param top 上边距
1022 | * @param right 右边距
1023 | * @param bottom 下边距
1024 | * @param paint 画笔
1025 | */
1026 | public void drawUnderLineIndicatorWithTransitX(Canvas canvas, int left,
1027 | int top, int right, int bottom, Paint paint) {
1028 | RectF oval = new RectF(left, bottom - dp(3), right, bottom);
1029 | canvas.drawRect(oval, paint);
1030 | }
1031 |
1032 |
1033 | public int dp(int dp) {
1034 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
1035 | dp, this.getResources().getDisplayMetrics());
1036 | }
1037 |
1038 | /**
1039 | * 指示器实现者
1040 | *
1041 | * @author yangpeixing
1042 | */
1043 | public interface DrawIndicatorCreator {
1044 | /**
1045 | * 默认为圆角矩形指示器,用户可继承重写自定义指示器样式
1046 | *
1047 | * @param canvas 画布
1048 | * @param left tab左边距离原点的位置
1049 | * @param top 整个tab的位置
1050 | * @param right tab距离顶端的位置
1051 | * @param bottom 整个tab的高度,既控件高度
1052 | * @param paint 指示器画笔
1053 | * @param raduis 外围圆角半径
1054 | */
1055 | void drawIndicator(Canvas canvas, int left, int top, int right,
1056 | int bottom, Paint paint, int raduis);
1057 | }
1058 |
1059 | /**
1060 | * 对外的ViewPager的回调接口
1061 | *
1062 | * @author yangpeixing
1063 | */
1064 | public interface PageChangeListener {
1065 | void onPageScrolled(int position, float positionOffset,
1066 | int positionOffsetPixels);
1067 |
1068 | void onPageSelected(int position);
1069 |
1070 | void onPageScrollStateChanged(int state);
1071 | }
1072 |
1073 |
1074 | /**
1075 | * 对外的ViewPager的回调接口
1076 | *
1077 | * @author yangpeixing
1078 | */
1079 | public interface TabClickListener {
1080 | void onClick(TextView tab, int position);
1081 | }
1082 |
1083 | }
1084 |
--------------------------------------------------------------------------------
/YPX_RefreshLayout_demo/app/src/main/java/com/ypx/jiehunle/ypx_bezierqqrefreshdemo/adapters/ListViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.ypx.jiehunle.ypx_bezierqqrefreshdemo.adapters;
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.TextView;
9 | import android.widget.Toast;
10 |
11 | import com.ypx.jiehunle.ypx_bezierqqrefreshdemo.R;
12 |
13 | /**
14 | * Created by yangpeixing on 17/1/17.
15 | */
16 | public class ListViewAdapter extends BaseAdapter {
17 | Context context;
18 | public ListViewAdapter(Context context){
19 | this.context=context;
20 | }
21 |
22 | @Override
23 | public int getCount() {
24 | return 5;
25 | }
26 |
27 | @Override
28 | public Object getItem(int i) {
29 | return null;
30 | }
31 |
32 | @Override
33 | public long getItemId(int i) {
34 | return i;
35 | }
36 |
37 | @Override
38 | public View getView(int i, View view, ViewGroup viewGroup) {
39 | final ViewHolder holder;
40 | if(view==null) {
41 | holder = new ViewHolder();
42 | view=LayoutInflater.from(context).inflate(R.layout.item,null);
43 | holder.tv= (TextView) view.findViewById(R.id.tv);
44 | view.setTag(holder);
45 | }else {
46 | holder= (ViewHolder) view.getTag();
47 | }
48 | holder.tv.setText("第" + i + "个文本");
49 | holder.tv.setOnClickListener(new View.OnClickListener() {
50 | @Override
51 | public void onClick(View view) {
52 | Toast.makeText(context,holder.tv.getText(),0).show();
53 | }
54 | });
55 | return view;
56 | }
57 | }
58 |
59 | class ViewHolder{
60 | TextView tv;
61 | }
62 |
--------------------------------------------------------------------------------
/YPX_RefreshLayout_demo/app/src/main/java/com/ypx/jiehunle/ypx_bezierqqrefreshdemo/adapters/RecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.ypx.jiehunle.ypx_bezierqqrefreshdemo.adapters;
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.TextView;
9 | import android.widget.Toast;
10 |
11 | import com.ypx.jiehunle.ypx_bezierqqrefreshdemo.R;
12 |
13 | import java.util.List;
14 |
15 | /**
16 | * Created by jiehunle on 17/1/17.
17 | */
18 | public class RecyclerViewAdapter extends RecyclerView.Adapter
20 | * 作者:yangpeixing on 17/1/18 14:52
21 | * 博客主页:http://blog.csdn.net/qq_16674697?viewmode=list
22 | */
23 | public abstract class YPXRefreshBaseView extends LinearLayout {
24 | /**
25 | * 下拉刷新状态
26 | */
27 | public static final int REFRESH_BY_PULLDOWN = 0;
28 | /**
29 | * 松开刷新状态
30 | */
31 | public static final int REFRESH_BY_RELEASE = 1;
32 |
33 | /**
34 | * 正在刷新状态
35 | */
36 | public static final int REFRESHING = 2;
37 | /**
38 | * 刷新成功状态
39 | */
40 | public static final int REFRESHING_SUCCESS = 3;
41 | /**
42 | * 刷新失败状态
43 | */
44 | public static final int REFRESHING_FAILED = 4;
45 |
46 | /**
47 | * 收回到刷新位置状态
48 | */
49 | public static final int TAKEBACK_REFRESH = -1;
50 | /**
51 | * 收回到初始位置状态
52 | */
53 | public static final int TAKEBACK_RESET = -2;
54 | /**
55 | * 从头收到尾,不考虑中间状态
56 | */
57 | public static final int TAKEBACK_ALL = -3;
58 |
59 |
60 | protected RefreshListener refreshListener;
61 | protected int lastTop;
62 | /**
63 | * 刷新状态
64 | */
65 | protected int refreshState = REFRESH_BY_PULLDOWN;
66 | /**
67 | * 收回状态
68 | */
69 | protected int takeBackState = TAKEBACK_RESET;
70 | protected int refreshTargetTop = -dp(60);//刷新头部高度
71 | protected View refreshView;
72 | protected ObjectAnimator anim;
73 | protected Context mContext;
74 | private int lastY;
75 | /**
76 | * 是否可刷新标记
77 | */
78 | private boolean isRefreshEnabled = true;
79 |
80 | private onRefreshStateChanged refreshStateChanged;
81 | private onRefreshMoveEvent onRefreshMoveEvent;
82 |
83 |
84 | public YPXRefreshBaseView(Context context) {
85 | this(context, null);
86 | }
87 |
88 | public YPXRefreshBaseView(Context context, AttributeSet attrs) {
89 | super(context, attrs);
90 | mContext = context;
91 | initRefreshView();
92 | initAnimator();
93 | }
94 |
95 | /**
96 | * 抽象方法,由用户实现,返回刷新头部布局
97 | *
98 | * @return 刷新头布局
99 | */
100 | protected abstract View getRefreshHeaderView();
101 |
102 | /**
103 | * 抽象方法2,由用户实现,处理下滑事件
104 | * lastTop < 0 代表下拉刷新状态
105 | * lastTop > 0 代表松开刷新状态
106 | *
107 | * @param lp 刷新头布局的LayoutParams
108 | * @param lastTop 手指滑动的距离 ,lastTop∈[refreshTargetTop,+无穷]
109 | */
110 | protected abstract void doMovement(LayoutParams lp, int lastTop);
111 |
112 | /**
113 | * 抽象方法3,由用户实现,处理手指抬起后操作,过滤掉在刷新过程中以及触发刷新后的手指操作
114 | *
115 | * @param lp LayoutParams
116 | */
117 | protected abstract void doMoveUp(LayoutParams lp);
118 |
119 | private void initRefreshView() {
120 | //刷新视图顶端的的view
121 | refreshView = getRefreshHeaderView();
122 | LayoutParams lp = new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, -refreshTargetTop);
123 | lp.topMargin = refreshTargetTop;
124 | addView(refreshView, lp);
125 | lastTop = refreshTargetTop;
126 | }
127 |
128 | private void initAnimator() {
129 | anim = ObjectAnimator.ofFloat(refreshView, "ypx", 0.0f, 1.0f);
130 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
131 | @Override
132 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
133 | float cVal = (Float) valueAnimator.getAnimatedValue();
134 | LayoutParams lp = (LayoutParams) refreshView.getLayoutParams();
135 | switch (takeBackState) {
136 | case TAKEBACK_REFRESH:
137 | lp.height = lp.height + (int) (cVal * (-refreshTargetTop - lp.height));
138 | lp.topMargin = lp.topMargin + (int) (cVal * (0 - lp.topMargin));
139 | break;
140 | case TAKEBACK_RESET:
141 | lp.topMargin = lp.topMargin + (int) (cVal * (refreshTargetTop - lp.topMargin));
142 | lp.height = -refreshTargetTop;
143 | break;
144 | case TAKEBACK_ALL:
145 | lp.topMargin = lp.topMargin + (int) (cVal * (refreshTargetTop - lp.topMargin));
146 | lp.height = lp.height + (int) (cVal * (-refreshTargetTop - lp.height));
147 | break;
148 | }
149 | if (onRefreshMoveEvent != null) {
150 | onRefreshMoveEvent.onMove(lp.topMargin);
151 | }
152 | refreshView.setLayoutParams(lp);
153 | refreshView.invalidate();
154 | invalidate();
155 | if (lp.height == -refreshTargetTop
156 | && lp.topMargin == refreshTargetTop) {//动画完成
157 | resetRefreshView();
158 | }
159 | }
160 | });
161 | }
162 |
163 |
164 | @Override
165 | public boolean onTouchEvent(MotionEvent event) {
166 | int y = (int) event.getRawY();
167 | switch (event.getAction()) {
168 | case MotionEvent.ACTION_DOWN:
169 | //记录下y坐标
170 | lastY = y;
171 | break;
172 |
173 | case MotionEvent.ACTION_MOVE:
174 | //y移动坐标
175 | int m = y - lastY;
176 | doMovement(m);
177 | //记录下此刻y坐标
178 | this.lastY = y;
179 | break;
180 |
181 | case MotionEvent.ACTION_UP:
182 | moveUp();
183 | break;
184 |
185 | case MotionEvent.ACTION_CANCEL:
186 | moveUp();
187 | break;
188 | }
189 | return true;
190 | }
191 |
192 |
193 | /**
194 | * 下拉move事件处理
195 | *
196 | * @param moveY 移动的Y值
197 | */
198 | private void doMovement(float moveY) {
199 | if (refreshState == REFRESHING
200 | || refreshState == REFRESHING_SUCCESS
201 | || refreshState == REFRESHING_FAILED
202 | || anim.isRunning()) {
203 | return;
204 | }
205 | LayoutParams lp = (LayoutParams) refreshView.getLayoutParams();
206 | lastTop += moveY * 0.5;
207 | if (lastTop < refreshTargetTop) {
208 | return;
209 | }
210 | if (onRefreshMoveEvent != null) {
211 | onRefreshMoveEvent.onMove(lastTop);
212 | }
213 | doMovement(lp, lastTop);
214 | refreshView.setLayoutParams(lp);
215 | refreshView.invalidate();
216 | invalidate();
217 | }
218 |
219 | /**
220 | * up事件处理
221 | */
222 | private void moveUp() {
223 | if (refreshState == REFRESHING
224 | || refreshState == REFRESHING_SUCCESS
225 | || refreshState == REFRESHING_FAILED
226 | || anim.isRunning()) {
227 | return;
228 | }
229 | LayoutParams lp = (LayoutParams) refreshView.getLayoutParams();
230 | if (onRefreshMoveEvent != null) {
231 | onRefreshMoveEvent.onMoveUp();
232 | }
233 | doMoveUp(lp);
234 | }
235 |
236 |
237 | /**
238 | * 结束刷新事件
239 | *
240 | * @param isOK 是否刷新成功
241 | */
242 | public void finishRefresh(boolean isOK) {
243 | if (isOK) {
244 | refreshOK();
245 | } else {
246 | refreshFailed();
247 | }
248 | new Handler().postDelayed(new Runnable() {
249 | public void run() {
250 | animRefreshView(500, TAKEBACK_RESET);
251 | }
252 | }, 300);
253 | }
254 |
255 |
256 | @Override
257 | public boolean onInterceptTouchEvent(MotionEvent e) {
258 | if (!isRefreshEnabled) {
259 | return false;
260 | }
261 | int action = e.getAction();
262 | int y = (int) e.getRawY();
263 | switch (action) {
264 | case MotionEvent.ACTION_DOWN:
265 | lastY = y;
266 | break;
267 |
268 | case MotionEvent.ACTION_MOVE:
269 | if (y > lastY && canScroll()) {
270 | return true;
271 | }
272 | //记录下此刻y坐标
273 | this.lastY = y;
274 | break;
275 | }
276 | return false;
277 | }
278 |
279 | /**
280 | * 判断第二个子View是否可以向下滑动
281 | *
282 | * @return 是否可以滑动
283 | */
284 | public boolean canScroll() {
285 | View childView;
286 | if (getChildCount() > 1) {
287 | childView = this.getChildAt(1);
288 | return !ViewCompat.canScrollVertically(childView, -1);
289 | }
290 | return true;
291 | }
292 |
293 |
294 | /**
295 | * 从开始位置滑动到结束位置
296 | *
297 | * @param duration 滑动事件
298 | * @param takeBackState 收回状态
299 | */
300 | protected void animRefreshView(int duration, int takeBackState) {
301 | this.takeBackState = takeBackState;
302 | if (!anim.isRunning()) {
303 | anim.start();
304 | anim.setDuration(duration);
305 | }
306 | }
307 |
308 | /**
309 | * 初始化
310 | */
311 | protected void resetRefreshView() {
312 | lastTop = refreshTargetTop;
313 | takeBackState = TAKEBACK_RESET;
314 | LayoutParams lp = (LayoutParams) refreshView.getLayoutParams();
315 | lp.height = -refreshTargetTop;
316 | refreshView.setLayoutParams(lp);
317 | refreshView.invalidate();
318 | pullDownToRefresh();
319 | }
320 |
321 | @Override
322 | public boolean dispatchTouchEvent(MotionEvent ev) {
323 | return refreshState == REFRESHING || super.dispatchTouchEvent(ev);
324 | }
325 |
326 | /**
327 | * 下拉刷新状态
328 | */
329 | protected void pullDownToRefresh() {
330 | setRefreshState(REFRESH_BY_PULLDOWN);
331 | if (refreshStateChanged != null) {
332 | refreshStateChanged.pullDownToRefresh();
333 | }
334 | }
335 |
336 | /**
337 | * 松开刷新状态
338 | */
339 | protected void pullUpToRefresh() {
340 | setRefreshState(REFRESH_BY_RELEASE);
341 | if (refreshStateChanged != null) {
342 | refreshStateChanged.pullUpToRefresh();
343 | }
344 | }
345 |
346 | /**
347 | * 正在刷新状态
348 | */
349 | protected void refreshing() {
350 | setRefreshState(REFRESHING);
351 | if (refreshStateChanged != null) {
352 | refreshStateChanged.refreshing();
353 | }
354 | }
355 |
356 | /**
357 | * 刷新成功状态
358 | */
359 | protected void refreshOK() {
360 | setRefreshState(REFRESHING_SUCCESS);
361 | }
362 |
363 | /**
364 | * 刷新失败状态
365 | */
366 | protected void refreshFailed() {
367 | setRefreshState(REFRESHING_FAILED);
368 | }
369 |
370 | /**
371 | * 设置是否可以刷新
372 | *
373 | * @param b 是否可以刷新
374 | */
375 | public void setRefreshEnabled(boolean b) {
376 | this.isRefreshEnabled = b;
377 | }
378 |
379 | /**
380 | * 设置刷新回调
381 | *
382 | * @param listener 刷新回调
383 | */
384 | public void setRefreshListener(RefreshListener listener) {
385 | this.refreshListener = listener;
386 | }
387 |
388 | /**
389 | * 获取当前刷新状态
390 | *
391 | * @return 刷新状态
392 | */
393 | public int getRefreshState() {
394 | return refreshState;
395 | }
396 |
397 | /**
398 | * 设置当前刷新状态
399 | *
400 | * @param refreshState 设置刷新状态
401 | */
402 | protected void setRefreshState(int refreshState) {
403 | this.refreshState = refreshState;
404 | }
405 |
406 | public int dp(int dp) {
407 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
408 | dp, getContext().getResources().getDisplayMetrics());
409 | }
410 |
411 | /**
412 | * 获得屏幕宽度
413 | *
414 | * @return 屏幕宽度
415 | */
416 | public int getScreenWidth() {
417 | WindowManager wm = (WindowManager) getContext()
418 | .getSystemService(Context.WINDOW_SERVICE);
419 | DisplayMetrics outMetrics = new DisplayMetrics();
420 | wm.getDefaultDisplay().getMetrics(outMetrics);
421 | return outMetrics.widthPixels;
422 | }
423 |
424 |
425 | /**
426 | * 刷新监听接口
427 | *
428 | * @author Nono
429 | */
430 | public interface RefreshListener {
431 | /**
432 | * 刷新回调
433 | */
434 | void onRefresh();
435 | }
436 |
437 | public void setRefreshStateChanged(onRefreshStateChanged refreshStateChanged) {
438 | this.refreshStateChanged = refreshStateChanged;
439 | }
440 |
441 | public onRefreshMoveEvent getOnRefreshMoveEvent() {
442 | return onRefreshMoveEvent;
443 | }
444 |
445 | public void setOnRefreshMoveEvent(onRefreshMoveEvent onRefreshMoveEvent) {
446 | this.onRefreshMoveEvent = onRefreshMoveEvent;
447 | }
448 |
449 | public interface onRefreshStateChanged {
450 | void pullDownToRefresh();
451 |
452 | void pullUpToRefresh();
453 |
454 | void refreshing();
455 | }
456 |
457 | public interface onRefreshMoveEvent {
458 | void onMove(int move);
459 |
460 | void onMoveUp();
461 | }
462 |
463 | }
464 |
--------------------------------------------------------------------------------
/YPX_RefreshLayout_demo/refreshlayout/src/main/java/com/ypx/refreshlayout/simple/YPXNormalRefreshView.java:
--------------------------------------------------------------------------------
1 | package com.ypx.refreshlayout.simple;
2 |
3 |
4 | import android.content.Context;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.animation.RotateAnimation;
9 | import android.widget.ImageView;
10 | import android.widget.LinearLayout;
11 | import android.widget.ProgressBar;
12 | import android.widget.TextView;
13 |
14 | import com.ypx.refreshlayout.R;
15 | import com.ypx.refreshlayout.YPXRefreshBaseView;
16 | import com.ypx.refreshlayout.util.TimeSPUtil;
17 |
18 | import java.text.SimpleDateFormat;
19 | import java.util.Date;
20 |
21 |
22 | /**
23 | * 刷新控制view
24 | *
25 | * @author yangpeixing
26 | */
27 | public class YPXNormalRefreshView extends YPXRefreshBaseView {
28 |
29 | //下拉刷新相关布局
30 | private LinearLayout ll_ok;
31 | private LinearLayout ll_refresh;
32 | private ImageView iv_refresh, iv_ok;
33 | private TextView tv_tip, tv_time, tv_ok;
34 | private ProgressBar pb_refresh;
35 |
36 | public YPXNormalRefreshView(Context context) {
37 | super(context);
38 | }
39 |
40 | public YPXNormalRefreshView(Context context, AttributeSet attrs) {
41 | super(context, attrs);
42 | }
43 |
44 | @Override
45 | protected View getRefreshHeaderView() {
46 | View refreshView = LayoutInflater.from(mContext).inflate(R.layout.layout_refresh_header, null);
47 | ll_ok = (LinearLayout) refreshView.findViewById(R.id.ll_ok);
48 | ll_refresh = (LinearLayout) refreshView.findViewById(R.id.ll_refresh);
49 | iv_refresh = (ImageView) refreshView.findViewById(R.id.iv_refresh);
50 | iv_ok = (ImageView) refreshView.findViewById(R.id.iv_ok);
51 | tv_tip = (TextView) refreshView.findViewById(R.id.tv_tip);
52 | tv_time = (TextView) refreshView.findViewById(R.id.tv_time);
53 | tv_ok = (TextView) refreshView.findViewById(R.id.tv_ok);
54 | pb_refresh = (ProgressBar) refreshView.findViewById(R.id.pb_refresh);
55 | return refreshView;
56 | }
57 |
58 | @Override
59 | protected void doMovement(LinearLayout.LayoutParams lp, int lastTop) {
60 | lp.topMargin = lastTop;
61 | if (lastTop < 0) {//下拉刷新状态
62 | if (refreshState != REFRESH_BY_PULLDOWN) {
63 | pullDownToRefresh();
64 | }
65 | } else {//松开刷新状态
66 | if (refreshState != REFRESH_BY_RELEASE) {
67 | pullUpToRefresh();
68 | }
69 | }
70 | }
71 |
72 | @Override
73 | protected void doMoveUp(LinearLayout.LayoutParams lp) {
74 | //未拉到触发可刷新事件的状态,则直接收回
75 | if (refreshState == REFRESH_BY_PULLDOWN) {
76 | animRefreshView(500, TAKEBACK_ALL);
77 | } else {//松开刷新
78 | animRefreshView(300, TAKEBACK_REFRESH);
79 | refreshing();
80 | if (refreshListener != null) {
81 | refreshListener.onRefresh();
82 | setRefreshState(REFRESHING);
83 | }
84 | }
85 | }
86 |
87 | /**
88 | * 下拉刷新状态
89 | */
90 | @Override
91 | public void pullDownToRefresh() {
92 | setRefreshState(REFRESH_BY_PULLDOWN);
93 | ll_refresh.setVisibility(View.VISIBLE);
94 | ll_ok.setVisibility(View.GONE);
95 | tv_tip.setText("下拉刷新");
96 | getRefreshTime();
97 | RotateAnimation anim1 = new RotateAnimation(0, 180,
98 | RotateAnimation.RELATIVE_TO_SELF, 0.5f,
99 | RotateAnimation.RELATIVE_TO_SELF, 0.5f);
100 | anim1.setDuration(300);
101 | anim1.setFillAfter(true);
102 | iv_refresh.clearAnimation();
103 | iv_refresh.startAnimation(anim1);
104 | pb_refresh.setVisibility(View.GONE);
105 | iv_refresh.setVisibility(View.VISIBLE);
106 | }
107 |
108 | /**
109 | * 松开刷新状态
110 | */
111 | @Override
112 | public void pullUpToRefresh() {
113 | setRefreshState(REFRESH_BY_RELEASE);
114 | ll_refresh.setVisibility(View.VISIBLE);
115 | ll_ok.setVisibility(View.GONE);
116 | tv_tip.setText("松开刷新");
117 | getRefreshTime();
118 | iv_refresh.setImageDrawable(mContext.getResources().getDrawable(R.mipmap.pull_up));
119 | RotateAnimation anim1 = new RotateAnimation(180, 0,
120 | RotateAnimation.RELATIVE_TO_SELF, 0.5f,
121 | RotateAnimation.RELATIVE_TO_SELF, 0.5f);
122 | anim1.setDuration(300);
123 | anim1.setFillAfter(true);
124 | iv_refresh.clearAnimation();
125 | iv_refresh.startAnimation(anim1);
126 | pb_refresh.setVisibility(View.GONE);
127 | iv_refresh.setVisibility(View.VISIBLE);
128 | }
129 |
130 | /**
131 | * 正在刷新状态
132 | */
133 | @Override
134 | public void refreshing() {
135 | setRefreshState(REFRESHING);
136 | ll_refresh.setVisibility(View.VISIBLE);
137 | ll_ok.setVisibility(View.GONE);
138 | tv_tip.setText("正在刷新...");
139 | getRefreshTime();
140 | TimeSPUtil.getInstance(mContext).setRefreshTime("MyMobile", "" +
141 | getDate( "MM-dd HH:mm", System.currentTimeMillis()));
142 | iv_refresh.clearAnimation();
143 | iv_refresh.setVisibility(View.GONE);
144 | pb_refresh.setVisibility(View.VISIBLE);
145 | }
146 |
147 | /**
148 | * 刷新成功状态
149 | */
150 | @Override
151 | public void refreshOK() {
152 | setRefreshState(REFRESHING_SUCCESS);
153 | ll_refresh.setVisibility(View.GONE);
154 | ll_ok.setVisibility(View.VISIBLE);
155 | tv_ok.setText("刷新成功");
156 | iv_ok.setImageDrawable(getResources().getDrawable(R.mipmap.pull_ok));
157 | }
158 |
159 | /**
160 | * 刷新失败状态
161 | */
162 | @Override
163 | public void refreshFailed() {
164 | setRefreshState(REFRESHING_FAILED);
165 | ll_refresh.setVisibility(View.GONE);
166 | ll_ok.setVisibility(View.VISIBLE);
167 | tv_ok.setText("刷新失败");
168 | iv_ok.setImageDrawable(getResources().getDrawable(R.mipmap.pull_failure));
169 | }
170 |
171 |
172 | public void getRefreshTime() {
173 | String time = TimeSPUtil.getInstance(mContext).getRefreshTime("MyMobile");
174 | if (time == null || "".equals(time)) {
175 | tv_time.setVisibility(View.GONE);
176 | } else {
177 | tv_time.setVisibility(View.VISIBLE);
178 | tv_time.setText(String.format("上次刷新:%s", time));
179 | }
180 | }
181 |
182 | public static String getDate(String formatStr, long times) {
183 | Date date = new Date(times);// 获取当前日期对象
184 | SimpleDateFormat format = new SimpleDateFormat(formatStr);// 设置格式
185 | return format.format(date);
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/YPX_RefreshLayout_demo/refreshlayout/src/main/java/com/ypx/refreshlayout/simple/qq/YPXBezierHeaderView.java:
--------------------------------------------------------------------------------
1 | package com.ypx.refreshlayout.simple.qq;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.animation.ValueAnimator;
6 | import android.annotation.SuppressLint;
7 | import android.content.Context;
8 | import android.graphics.Bitmap;
9 | import android.graphics.BitmapFactory;
10 | import android.graphics.Canvas;
11 | import android.graphics.Color;
12 | import android.graphics.Paint;
13 | import android.graphics.Path;
14 | import android.util.AttributeSet;
15 | import android.util.DisplayMetrics;
16 | import android.util.TypedValue;
17 | import android.view.MotionEvent;
18 | import android.view.View;
19 | import android.view.WindowManager;
20 |
21 | import com.ypx.refreshlayout.R;
22 |
23 | /**
24 | * 作者:yangpeixing on 16/9/6 15:14
25 | * 博客主页:http://blog.csdn.net/qq_16674697?viewmode=list
26 | */
27 | public class YPXBezierHeaderView extends View {
28 | float offset = 1.0f;
29 | float delayY;
30 | float lastY;
31 | int color = Color.parseColor("#999999");
32 | int drawableID = R.mipmap.refresh;
33 | OnAnimResetListener listener;
34 | ObjectAnimator anim;
35 | /**
36 | * 圆的画笔
37 | */
38 | private Paint circlePaint;
39 | /**
40 | * 画笔的路径
41 | */
42 | private Path circlePath;
43 | /**
44 | * 可拖动的最远距离
45 | */
46 | private int maxHeight;
47 | /**
48 | * 刷新图标
49 | */
50 | protected Bitmap bt;
51 | private float topCircleRadius;//默认上面圆形半径
52 | private float topCircleX;//默认上面圆形x
53 | private float topCircleY;//默认上面圆形y
54 | private float bottomCircleRadius;//默认上面圆形半径
55 | private float bottomCircleX;//默认下面圆形x
56 | private float bottomCircleY;//默认下面圆形y
57 | private float defaultRadius;//默认上面圆形半径
58 |
59 |
60 | public YPXBezierHeaderView(Context context) {
61 | this(context, null);
62 | }
63 |
64 | public YPXBezierHeaderView(Context context, AttributeSet attrs) {
65 | this(context, attrs, 0);
66 | }
67 |
68 | public YPXBezierHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
69 | super(context, attrs, defStyleAttr);
70 | init();
71 | }
72 |
73 | public void setOnAnimResetListener(OnAnimResetListener lt) {
74 | this.listener = lt;
75 | }
76 |
77 | protected void init() {
78 | maxHeight = dp(300);
79 | topCircleX = getScreenWidth() / 2;
80 | topCircleY = dp(100);
81 | topCircleRadius = dp(30);
82 | resetBottomCricle();
83 |
84 | circlePath = new Path();
85 | circlePaint = new Paint();
86 | circlePaint.setAntiAlias(true);
87 | circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
88 | circlePaint.setStrokeWidth(1);
89 | circlePaint.setColor(color);
90 | }
91 |
92 | @Override
93 | public boolean dispatchTouchEvent(MotionEvent event) {
94 | switch (event.getAction()) {
95 | case MotionEvent.ACTION_DOWN:
96 | lastY = event.getRawY();
97 | break;
98 | case MotionEvent.ACTION_MOVE:
99 | delayY = event.getRawY() - lastY;//滑动高度的偏移量
100 | if (delayY < 0) {
101 | return true;
102 | }
103 | offset = 1 - delayY / maxHeight;//滑动的偏移量offset 范围 offset∈(1,0)
104 | //如果偏移量大于等于0.2的时候我们就让它开始重绘,
105 | // 这样可以给下面的圆留下一点可见半径,要不然offset为0的时候下面的圆就成了点
106 | if (offset >= 0.2) {
107 | bottomCircleRadius = defaultRadius * offset;
108 | bottomCircleX = topCircleX;
109 | bottomCircleY = topCircleY + delayY;
110 | topCircleRadius = (float) (defaultRadius * (Math.pow(offset, 1 / 3.0)));
111 | postInvalidate();
112 | }
113 | break;
114 | case MotionEvent.ACTION_UP:
115 | animToReset(false);
116 | break;
117 | case MotionEvent.ACTION_CANCEL:
118 | animToReset(false);
119 | break;
120 | }
121 | return true;
122 | }
123 |
124 | public void setOffset(float offset) {
125 | this.offset = offset;
126 | }
127 |
128 | public void animToReset(boolean lock) {
129 | if (!lock) {
130 | anim = ObjectAnimator.ofFloat(offset, "ypx", 0.0F, 1.0F).setDuration(200);
131 | //使用反弹算法插值器,貌似没有什么太大的效果 - -!
132 | // anim.setInterpolator(new BounceInterpolator());
133 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
134 | @Override
135 | public void onAnimationUpdate(ValueAnimator animation) {
136 | float cVal = (Float) animation.getAnimatedValue();
137 | reset(cVal);
138 | }
139 | });
140 | anim.addListener(new Animator.AnimatorListener() {
141 | @Override
142 | public void onAnimationStart(Animator animator) {
143 |
144 | }
145 |
146 | @Override
147 | public void onAnimationEnd(Animator animator) {
148 | if (listener != null) {
149 | listener.onReset();
150 | }
151 | }
152 |
153 | @Override
154 | public void onAnimationCancel(Animator animator) {
155 |
156 | }
157 |
158 | @Override
159 | public void onAnimationRepeat(Animator animator) {
160 |
161 | }
162 | });
163 | anim.start();
164 | }
165 | }
166 |
167 | /**
168 | * 收回
169 | *
170 | * @param moffset 收回偏移量
171 | */
172 | public void reset(float moffset) {
173 | if (moffset < 0.2) {
174 | return;
175 | }
176 | offset = moffset;
177 | bottomCircleRadius = defaultRadius * offset;
178 | bottomCircleY = topCircleY + delayY * (1 - offset);
179 | topCircleRadius = (float) (defaultRadius * (Math.pow(offset, 1 / 3.0)));
180 | postInvalidate();
181 |
182 | }
183 |
184 | @SuppressLint("DrawAllocation")
185 | @Override
186 | protected void onDraw(Canvas canvas) {
187 | drawPath();
188 | float left = topCircleX - topCircleRadius;
189 | float top = topCircleY - topCircleRadius;
190 |
191 | canvas.drawPath(circlePath, circlePaint);
192 | canvas.drawCircle(bottomCircleX, bottomCircleY, bottomCircleRadius, circlePaint);
193 | canvas.drawCircle(topCircleX, topCircleY, topCircleRadius, circlePaint);
194 |
195 | int btWidth = (int) topCircleRadius * 2 - dp(6);
196 | if ((btWidth) > 0) {
197 | bt = BitmapFactory.decodeResource(getResources(), drawableID);
198 | if (bt != null) {
199 | bt = Bitmap.createScaledBitmap(bt, btWidth, btWidth, true);
200 | }
201 | canvas.drawBitmap(bt, left + dp(3), top + dp(2), null);
202 | bt.recycle();
203 | }
204 | super.onDraw(canvas);
205 |
206 | }
207 |
208 | private void drawPath() {
209 |
210 | float p1X = topCircleX - topCircleRadius;
211 | float p1Y = topCircleY;
212 | float p2X = topCircleX + topCircleRadius;
213 | float p2Y = topCircleY;
214 | float p3X = bottomCircleX - bottomCircleRadius;
215 | float p3Y = bottomCircleY;
216 | float p4X = bottomCircleX + bottomCircleRadius;
217 | float p4Y = bottomCircleY;
218 |
219 |
220 | float anchorX = (p1X + p4X) / 2 - topCircleRadius * offset;
221 | float anchorY = (p1Y + p4Y) / 2;
222 |
223 | float anchorX2 = (p2X + p3X) / 2 + topCircleRadius * offset;
224 | float anchorY2 = (p2Y + p3Y) / 2;
225 |
226 | /* 画粘连体 */
227 | circlePath.reset();
228 | circlePath.moveTo(p1X, p1Y);
229 | circlePath.quadTo(anchorX, anchorY, p3X, p3Y);
230 | circlePath.lineTo(p4X, p4Y);
231 | circlePath.quadTo(anchorX2, anchorY2, p2X, p2Y);
232 | circlePath.lineTo(p1X, p1Y);
233 |
234 | }
235 |
236 | public float getTopCircleRadius() {
237 | return topCircleRadius;
238 | }
239 |
240 | public void setTopCircleRadius(float topCircleRadius) {
241 | this.topCircleRadius = topCircleRadius;
242 | }
243 |
244 | public float getTopCircleX() {
245 | return topCircleX;
246 | }
247 |
248 | public void setTopCircleX(float topCircleX) {
249 | this.topCircleX = topCircleX;
250 | }
251 |
252 | public float getTopCircleY() {
253 | return topCircleY;
254 | }
255 |
256 | public void setTopCircleY(float topCircleY) {
257 | this.topCircleY = topCircleY;
258 | }
259 |
260 | public float getBottomCircleRadius() {
261 | return bottomCircleRadius;
262 | }
263 |
264 | public void setBottomCircleRadius(float bottomCircleRadius) {
265 | this.bottomCircleRadius = bottomCircleRadius;
266 | }
267 |
268 | public float getBottomCircleX() {
269 | return bottomCircleX;
270 | }
271 |
272 | public void setBottomCircleX(float bottomCircleX) {
273 | this.bottomCircleX = bottomCircleX;
274 | }
275 |
276 | public float getBottomCircleY() {
277 | return bottomCircleY;
278 | }
279 |
280 | public void setBottomCircleY(float bottomCircleY) {
281 | this.bottomCircleY = bottomCircleY;
282 | }
283 |
284 | public int getMaxHeight() {
285 | return maxHeight;
286 | }
287 |
288 | public void setMaxHeight(int maxHeight) {
289 | this.maxHeight = maxHeight;
290 | }
291 |
292 | public float getDefaultRadius() {
293 | return defaultRadius;
294 | }
295 |
296 | public int getColor() {
297 | return color;
298 | }
299 |
300 | public void setColor(int color) {
301 | this.color = color;
302 | circlePaint.setColor(color);
303 | invalidate();
304 | }
305 |
306 | public int getDrawableID() {
307 | return drawableID;
308 | }
309 |
310 | public void setDrawableID(int drawableID) {
311 | this.drawableID = drawableID;
312 | invalidate();
313 | }
314 |
315 | /**
316 | * 设置小圆为大圆的大小
317 | */
318 | public void resetBottomCricle() {
319 | bottomCircleRadius = topCircleRadius;
320 | bottomCircleY = topCircleY;
321 | bottomCircleX = topCircleX;
322 | defaultRadius = topCircleRadius;
323 | }
324 |
325 | public int dp(int dp) {
326 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
327 | dp, getContext().getResources().getDisplayMetrics());
328 | }
329 |
330 | /**
331 | * 获得屏幕宽度
332 | *
333 | * @return 屏幕宽度
334 | */
335 | public int getScreenWidth() {
336 | WindowManager wm = (WindowManager) getContext()
337 | .getSystemService(Context.WINDOW_SERVICE);
338 | DisplayMetrics outMetrics = new DisplayMetrics();
339 | wm.getDefaultDisplay().getMetrics(outMetrics);
340 | return outMetrics.widthPixels;
341 | }
342 |
343 | public interface OnAnimResetListener {
344 | void onReset();
345 | }
346 |
347 | }
348 |
--------------------------------------------------------------------------------
/YPX_RefreshLayout_demo/refreshlayout/src/main/java/com/ypx/refreshlayout/simple/qq/YPXQQRefreshView.java:
--------------------------------------------------------------------------------
1 | package com.ypx.refreshlayout.simple.qq;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.widget.ImageView;
8 | import android.widget.LinearLayout;
9 | import android.widget.ProgressBar;
10 | import android.widget.TextView;
11 |
12 | import com.ypx.refreshlayout.R;
13 | import com.ypx.refreshlayout.YPXRefreshBaseView;
14 |
15 |
16 | /**
17 | * 作者:yangpeixing on 17/1/23 14:39
18 | * 功能: QQ下拉刷新
19 | * 博客主页:http://blog.csdn.net/qq_16674697?viewmode=list
20 | */
21 | public class YPXQQRefreshView extends YPXRefreshBaseView {
22 | LinearLayout ll_ok;
23 | LinearLayout ll_refresh;
24 | ImageView iv_ok;
25 | TextView tv_ok;
26 | ProgressBar pb_refresh;
27 | YPXBezierHeaderView bezierView;
28 |
29 | private float topCircleRadius;//默认上面圆形半径
30 | private float topCircleX;//默认上面圆形x
31 | private float topCircleY;//默认上面圆形y
32 |
33 | private int refreshMaxHeight;//刷新小球可滑动的最大距离
34 | boolean bezierLock = false;//小球动画锁
35 |
36 | public YPXQQRefreshView(Context context) {
37 | super(context);
38 | }
39 |
40 | public YPXQQRefreshView(Context context, AttributeSet attrs) {
41 | super(context, attrs);
42 | }
43 |
44 |
45 | @Override
46 | protected View getRefreshHeaderView() {
47 | refreshView = LayoutInflater.from(mContext).inflate(R.layout.a_layout_qq_header, null);
48 | ll_ok = (LinearLayout) refreshView.findViewById(R.id.ll_ok);
49 | ll_refresh = (LinearLayout) refreshView.findViewById(R.id.ll_refresh);
50 | bezierView = (YPXBezierHeaderView) refreshView.findViewById(R.id.bview);
51 | iv_ok = (ImageView) refreshView.findViewById(R.id.iv_ok);
52 | tv_ok = (TextView) refreshView.findViewById(R.id.tv_ok);
53 | pb_refresh = (ProgressBar) refreshView.findViewById(R.id.pb_refresh);
54 | bezierView.setOnAnimResetListener(new YPXBezierHeaderView.OnAnimResetListener() {
55 | @Override
56 | public void onReset() {
57 | animRefreshView(200, TAKEBACK_REFRESH);
58 | if (refreshListener != null && refreshState == REFRESH_BY_RELEASE) {
59 | refreshing();
60 | refreshListener.onRefresh();
61 | setRefreshState(REFRESHING);
62 | }
63 | }
64 | });
65 | resetData();
66 | return refreshView;
67 | }
68 |
69 | @Override
70 | protected void doMovement(LinearLayout.LayoutParams lp, int lastTop) {
71 | if (lastTop < 0) {//下拉刷新状态
72 | lp.topMargin = lastTop;
73 | lp.height = -refreshTargetTop;
74 | pullDownToRefresh();
75 | } else {//松开刷新状态改为下拉贝塞尔小球
76 | lp.topMargin = 0;
77 | lp.height = lastTop - refreshTargetTop;
78 | float offset = 1 - (lastTop * 1.0f) / refreshMaxHeight;//1~0
79 | if (offset >= 0.2) {//没有触发小球最大拉动距离
80 | bezierView.setBottomCircleY(bezierView.getTopCircleY() + (lastTop));
81 | bezierView.setBottomCircleRadius(bezierView.getDefaultRadius() * offset);
82 | bezierView.setOffset(offset);
83 | bezierView.setTopCircleRadius((float) (bezierView.getDefaultRadius() * (Math.pow(offset, 1 / 3.0))));
84 | } else {//小球爆炸收回,触发刷新
85 | if (!bezierLock && takeBackState != TAKEBACK_ALL) {
86 | bezierView.animToReset(false);
87 | refreshState = REFRESH_BY_RELEASE;//松开刷新状态
88 | bezierLock = true;
89 | }
90 | }
91 | bezierView.postInvalidate();
92 | }
93 | }
94 |
95 | @Override
96 | protected void doMoveUp(LinearLayout.LayoutParams lp) {
97 | bezierView.setTopCircleRadius(topCircleRadius);
98 | bezierView.resetBottomCricle();
99 | animRefreshView(500, TAKEBACK_ALL);
100 | }
101 |
102 | private void resetData() {
103 | refreshMaxHeight = -refreshTargetTop;
104 | topCircleX = getScreenWidth() / 2;
105 | topCircleY = -refreshTargetTop / 2;
106 | topCircleRadius = -refreshTargetTop / 4;
107 | bezierView.setTopCircleX(topCircleX);
108 | bezierView.setTopCircleY(topCircleY);
109 | bezierView.setTopCircleRadius(topCircleRadius);
110 | bezierView.setMaxHeight(refreshMaxHeight);
111 | bezierView.resetBottomCricle();
112 | }
113 |
114 |
115 | @Override
116 | public void resetRefreshView() {
117 | super.resetRefreshView();
118 | bezierLock = false;
119 | bezierView.setTopCircleX(topCircleX);
120 | bezierView.setTopCircleY(topCircleY);
121 | bezierView.setTopCircleRadius(topCircleRadius);
122 | bezierView.setMaxHeight(refreshMaxHeight);
123 | bezierView.resetBottomCricle();
124 | }
125 |
126 | @Override
127 | public void pullDownToRefresh() {
128 | super.pullDownToRefresh();
129 | ll_refresh.setVisibility(View.VISIBLE);
130 | ll_ok.setVisibility(View.GONE);
131 | pb_refresh.setVisibility(View.GONE);
132 | bezierView.setVisibility(View.VISIBLE);
133 | }
134 |
135 | @Override
136 | public void pullUpToRefresh() {
137 | super.pullUpToRefresh();
138 | }
139 |
140 | @Override
141 | public void refreshing() {
142 | super.refreshing();
143 | ll_ok.setVisibility(View.GONE);
144 | bezierView.setVisibility(View.GONE);
145 | pb_refresh.setVisibility(View.VISIBLE);
146 |
147 | }
148 |
149 | @Override
150 | public void refreshFailed() {
151 | super.refreshFailed();
152 | ll_refresh.setVisibility(View.GONE);
153 | ll_ok.setVisibility(View.VISIBLE);
154 | tv_ok.setText("刷新失败");
155 | iv_ok.setImageDrawable(getResources().getDrawable(R.mipmap.pull_failure));
156 | }
157 |
158 | @Override
159 | public void refreshOK() {
160 | super.refreshOK();
161 | ll_refresh.setVisibility(View.GONE);
162 | ll_ok.setVisibility(View.VISIBLE);
163 | tv_ok.setText("刷新成功");
164 | iv_ok.setImageDrawable(getResources().getDrawable(R.mipmap.pull_ok));
165 | }
166 |
167 | public float getTopCircleY() {
168 | return topCircleY;
169 | }
170 |
171 | public void setTopCircleY(float topCircleY) {
172 | this.topCircleY = topCircleY;
173 | bezierView.setTopCircleY(topCircleY);
174 | bezierView.resetBottomCricle();
175 | }
176 |
177 | public float getTopCircleX() {
178 | return topCircleX;
179 |
180 | }
181 |
182 | public void setTopCircleX(float topCircleX) {
183 | this.topCircleX = topCircleX;
184 | bezierView.setTopCircleX(topCircleX);
185 | bezierView.resetBottomCricle();
186 | }
187 |
188 | public float getTopCircleRadius() {
189 | return topCircleRadius;
190 | }
191 |
192 | public void setTopCircleRadius(float topCircleRadius) {
193 | this.topCircleRadius = topCircleRadius;
194 | bezierView.setTopCircleRadius(topCircleRadius);
195 | bezierView.resetBottomCricle();
196 | }
197 |
198 | public int getRefreshMaxHeight() {
199 | return refreshMaxHeight;
200 | }
201 |
202 | public void setRefreshMaxHeight(int refreshMaxHeight) {
203 | this.refreshMaxHeight = refreshMaxHeight;
204 | bezierView.setMaxHeight(refreshMaxHeight);
205 | }
206 |
207 | public void setRefreshIcon(int drawableID) {
208 | bezierView.setDrawableID(drawableID);
209 | }
210 |
211 | public void setRefreshColor(int color) {
212 | bezierView.setColor(color);
213 | }
214 |
215 | public void setRefreshViewHeight(int height) {
216 | lastTop = -height;
217 | refreshTargetTop = -height;
218 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, this.refreshTargetTop);
219 | refreshView.setLayoutParams(lp);
220 | resetData();
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/YPX_RefreshLayout_demo/refreshlayout/src/main/java/com/ypx/refreshlayout/util/TimeSPUtil.java:
--------------------------------------------------------------------------------
1 | package com.ypx.refreshlayout.util;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | public class TimeSPUtil {
7 | private static final String PREFERENCE_NAME = "sp";
8 | private static SharedPreferences mSharedPreferences;
9 | private static TimeSPUtil mPreferenceUtils;
10 | private static SharedPreferences.Editor editor;
11 |
12 | private TimeSPUtil(Context cxt) {
13 | mSharedPreferences = cxt.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
14 | }
15 |
16 | /**
17 | * 单例模式,获取instance实例
18 | */
19 | public static TimeSPUtil getInstance(Context cxt) {
20 | if (mPreferenceUtils == null) {
21 | mPreferenceUtils = new TimeSPUtil(cxt);
22 | }
23 | editor = mSharedPreferences.edit();
24 | return mPreferenceUtils;
25 | }
26 |
27 | public void setRefreshTime(String str_name, String str_value) {
28 | editor.putString(str_name, str_value);
29 | editor.commit();
30 | }
31 |
32 |
33 | public String getRefreshTime(String str_name) {
34 | return mSharedPreferences.getString(str_name, "");
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/YPX_RefreshLayout_demo/refreshlayout/src/main/res/drawable/pulling.xml:
--------------------------------------------------------------------------------
1 |
2 |