前言

之前写的一个LoadingBar,这次把LoadingBar加到下拉刷新的头部。从头写一个下拉刷新,附赠上拉加载更多。下面话不多说了,来一起看看详细的介绍吧。

效果图:

实现过程

首先是自定义属性,attrs.xml中定义头部的高度和上下的padding。

####attrs.xml####

然后是头部的文件,里面放了一个TextView和一个图片

header_layout.xml####

android:layout_width="match_parent"

android:layout_height="50dp"

android:orientation="vertical">

android:src="@mipmap/down"

android:layout_centerVertical="true"

android:id="@+id/icon"

android:layout_width="20dp"

android:layout_height="20dp"

android:layout_toLeftOf="@+id/text"

android:layout_marginRight="5dp"/>

android:id="@+id/text"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerInParent="true"

android:text="下拉刷新" />

然后是布局文件,让PPRefreshView作为父布局,下面可以放AbsListView的子类。

activity_main.xml####

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@color/white" tools:context="top.greendami.greendami.MainActivity">

ppRefreshView:header_height="50dp"

ppRefreshView:header_padding="10dp"

android:id="@+id/swipeRefreshLayout"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="#d4d4d4">

android:background="@color/white"

android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent">

最后是重点,下拉刷新的控件。

"精心"准备了一张图

####PPRefreshView.java####

package top.greendami.greendami;

package com.allrun.arsmartelevatorformanager.widget;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Canvas;

import android.support.v7.widget.RecyclerView;

import android.util.AttributeSet;

import android.view.LayoutInflater;

import android.view.MotionEvent;

import android.view.View;

import android.view.ViewConfiguration;

import android.view.ViewGroup;

import android.view.animation.Animation;

import android.view.animation.RotateAnimation;

import android.widget.ImageView;

import android.widget.TextView;

import com.allrun.arsmartelevatorformanager.R;

import com.allrun.arsmartelevatorformanager.util.DPUnitUtil;

/**

* Created by GreendaMI on 2017/3/21.

*/

public class PPRefreshView extends ViewGroup {

Context context;

RecyclerView mListView;

PPView mPPView;

View header;

TextView title;

ImageView mImage;//箭头

int listTop = 0;

float headerHeight = 10 + 30 + 10;//header的高度,上留白 + 文字(PPVIew)高度 + 下留白

float headerpadding = 10;//上留白,下留白

private int mYDown, mLastY;

//最短滑动距离

int a = 0;

RotateAnimation mRotateAnimation;

int state = 0; //0,正常;1,下拉;2,松开

public void setPPRefreshViewListener(PPRefreshViewListener mPPRefreshViewListener) {

this.mPPRefreshViewListener = mPPRefreshViewListener;

}

PPRefreshViewListener mPPRefreshViewListener;

public PPRefreshView(Context context) {

super(context);

this.context = context;

}

public PPRefreshView(Context context, AttributeSet attrs) {

super(context, attrs);

this.context = context;

//px转dp

a = DPUnitUtil.px2dip(context,ViewConfiguration.get(context).getScaledDoubleTapSlop());

TypedArray b = context.obtainStyledAttributes(attrs, R.styleable.PPRefreshView_header);

headerHeight = b.getDimension(R.styleable.PPRefreshView_header_header_height, 100);

headerpadding = b.getDimension(R.styleable.PPRefreshView_header_header_padding, 10);

b.recycle();

initAnima();

}

private void initAnima() {

//箭头旋转

mRotateAnimation = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF,

0.5f, Animation.RELATIVE_TO_SELF, 0.5f);

mRotateAnimation.setFillAfter(true);

mRotateAnimation.setDuration(200);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

if (mPPView != null) {

mPPView.measure(widthMeasureSpec,(int) (headerHeight- 2 * headerpadding));

}

if (header != null) {

header.measure(widthMeasureSpec, (int) (headerHeight- 2 * headerpadding));

}

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

if (mListView == null && getChildCount() == 1) {

mListView = (RecyclerView)getChildAt(0);

mListView.setOnScrollListener(new RecyclerView.OnScrollListener() {

@Override

public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

super.onScrolled(recyclerView, dx, dy);

if(!recyclerView.canScrollVertically(1)){

//添加外部回调

if(mPPRefreshViewListener != null){ mPPRefreshViewListener.LoadMore();

}

}

}

});

}

if (mListView != null) {

mListView.layout(l, listTop, getMeasuredWidth(), b);

}

if (mPPView != null) {

//top:文字(PPVIew)高度 + 下留白

mPPView.layout(l, (int)(listTop - headerHeight + headerpadding), r, listTop);

}

if (header != null) {

//top:文字(PPView)高度 + 下留白

header.layout(l, (int)(listTop - headerHeight + headerpadding), r, listTop);

}

}

@Override

protected void dispatchDraw(Canvas canvas) {

super.dispatchDraw(canvas);

//松开手指,list回到顶部

if (state == 2) {

listTop = listTop - 25;

if (listTop < headerHeight) {

listTop = (int)headerHeight;

}

requestLayout();

}

//刷新完毕,关闭header

if (state == 0 && listTop > 0) {

listTop = listTop - 25;

if (listTop < 0) {

listTop = 0;

}

requestLayout();

}

}

@Override

public boolean dispatchTouchEvent(MotionEvent event) {

final int action = event.getAction();

switch (action) {

case MotionEvent.ACTION_DOWN:

// 按下

mYDown = (int) event.getRawY();

break;

case MotionEvent.ACTION_MOVE:

// 移动

mLastY = (int) event.getRawY();

if (!mListView.canScrollVertically(-1) && mLastY > mYDown &&(mLastY - mYDown) > a) {

state = 1;

listTop = mLastY - mYDown;

if (mPPView != null) {

removeView(mPPView);

mPPView = null;

}

if (header == null) {

header = LayoutInflater.from(context).inflate(R.layout.header_layout, null, false);

title = ((TextView) header.findViewById(R.id.text));

mImage = ((ImageView) header.findViewById(R.id.icon));

addView(header);

}

if (title != null && (mLastY - mYDown) > a * 2f) {

title.setText("松开刷新");

if (mImage.getAnimation() == null) {

mImage.startAnimation(mRotateAnimation);

}

}

if (title != null && (mLastY - mYDown) < a * 2f) {

title.setText("下拉刷新");

mImage.setImageResource(R.mipmap.down);

}

requestLayout();

//已经判断是下拉刷新,拦截手势

return false;

}

break;

case MotionEvent.ACTION_UP:

// 抬起

// if (canLoad()) {

// loadData();

// }

//松手的时候,把文字标题去掉

if (header != null) {

removeView(header);

header = null;

}

//如果之前是下拉状态,就添加PPVIew

if (mPPView == null && state == 1) {

//添加外部回调

if(mPPRefreshViewListener != null){

mPPRefreshViewListener.onRefresh();

}

mPPView = new PPView(context);

addView(mPPView);

mYDown = 0;

mLastY = 0;

state = 2;

requestLayout();

}

break;

default:

break;

}

return super.dispatchTouchEvent(event);

}

/**

* 收起下拉刷新的header,刷新结束

*/

public void RefreshOver() {

if (mPPView != null) {

removeView(mPPView);

mPPView = null;

}

if (header != null) {

removeView(header);

header = null;

title = null;

mImage = null;

}

state = 0;

}

public void setRefreshing(boolean b) {

if(!b){

state = 0;

postInvalidate();

}else{

state = 2;

postInvalidate();

}

}

public interface PPRefreshViewListener{

void onRefresh();

void LoadMore();

}

}

主要思路是监听手势的滑动距离,如果ListView已经划到顶部,则ListView跟随手指位置,并添加Header。放开手指后,ListView慢慢回到顶部。

外部回调。监听下拉和上拉。

mSwipeRefreshLayout.setPPRefreshViewListener(new PPRefreshView.PPRefreshViewListener() {

@Override

public void onRefresh() {

Toast.makeText(MainActivity.this,"亲,刷新了",Toast.LENGTH_SHORT).show();

data.add("测试数据100");

mAdapter.notifyDataSetChanged();

}

@Override

public void LoadMore() {

Toast.makeText(MainActivity.this,"加载更多",Toast.LENGTH_SHORT).show();

}

});

refreshLayout.setRefreshing(false);;//刷新完毕

差点忘了粘连小球的View。

####PPView.java####

package top.greendami.greendami;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.Path;

import android.support.annotation.Nullable;

import android.util.AttributeSet;

import android.view.View;

/**

* Created by GreendaMi on 2017/3/17.

*/

public class PPView extends View {

String TAG = "PPView";

//动画开关

boolean isLoading = true;

Context mContext;

private int mWidth = 100;

private int mheight = 100;

public int mColor;

public Paint mPaint = new Paint();

float time = 0;

//小球与中间打球的最远距离

float distance = 100;

public PPView(Context context) {

super(context);

mContext = context;

mColor = context.getResources().getColor(R.color.colorPrimary);

init();

}

public PPView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

mContext = context;

mColor = context.getResources().getColor(R.color.colorPrimary);

init();

}

private void init() {

mPaint.setAntiAlias(true);

mPaint.setColor(mColor);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

//宽度至少是高度的4倍

if (widthSpecSize < 4 * heightSpecSize) {

widthSpecSize = 4 * heightSpecSize;

}

mWidth = widthSpecSize;

mheight = heightSpecSize;

distance = 1.2f * mheight;

setMeasuredDimension(widthSpecSize, heightSpecSize);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (isLoading) {

//大圆半径

float bigR = mheight * 0.32f + mheight * 0.03f * Math.abs((float) Math.sin(Math.toRadians(time)));

float smallR = mheight * 0.17f + mheight * 0.03f * Math.abs((float) Math.cos(Math.toRadians(time)));

float bigx = (getWidth()) / 2;

//画中间大圆

canvas.drawCircle(bigx, mheight / 2, bigR, mPaint);

float smalx = getSmallCenterX();

//画小圆

canvas.drawCircle(smalx, mheight / 2, smallR, mPaint);

//画链接

//小球在右侧

if (smalx > bigx) {

Path path = new Path();

//上面的贝塞尔曲线的第一个点,在大圆身上

float x1 = bigx + bigR * (float) Math.cos(Math.toRadians(time));

float y1 = mheight / 2 - bigR * (float) Math.sin(Math.toRadians(time));

if (y1 > mheight / 2 - smallR) {

y1 = mheight / 2 - smallR;

x1 = bigx + (float) (Math.sqrt(bigR * bigR - smallR * smallR));

}

//上面的贝塞尔曲线的第三个点,在小圆身上

float x2 = smalx - smallR * (float) Math.cos(Math.toRadians(time));

float y2 = mheight / 2 - smallR * (float) Math.sin(Math.toRadians(time));

if (y2 > mheight / 2 - smallR * 0.8) {

y2 = mheight / 2 - smallR * 0.8f;

x2 = smalx - smallR * (float) (Math.sqrt(1-0.64f));

}

//下面的贝塞尔曲线的第三个点,在小圆身上

float x3 = smalx - smallR * (float) Math.cos(Math.toRadians(time));

float y3 = mheight / 2 + smallR * (float) Math.sin(Math.toRadians(time));

if (y3 < mheight / 2 + smallR * 0.8) {

y3 = mheight / 2 + smallR * 0.8f;

x3 = smalx - smallR * (float) (Math.sqrt(1-0.64f));

}

//下面的贝塞尔曲线的第一个点,在大圆身上

float x4 = bigx + bigR * (float) Math.cos(Math.toRadians(time));

float y4 = mheight / 2 + bigR * (float) Math.sin(Math.toRadians(time));

if (y4 < mheight / 2 + smallR) {

y4 = mheight / 2 + smallR;

x4 = bigx + (float) (Math.sqrt(bigR * bigR - smallR * smallR));

}

path.moveTo(x1, y1);

path.quadTo((bigx + smalx) / 2, mheight / 2, x2, y2);

// 绘制贝赛尔曲线(Path)

path.lineTo(x3, y3);

path.quadTo((bigx + smalx) / 2, mheight / 2, x4, y4);

canvas.drawPath(path, mPaint);

}

//小球在左侧

if (smalx < bigx) {

Path path = new Path();

float x1 = bigx + bigR * (float) Math.cos(Math.toRadians(time));

float y1 = mheight / 2 - bigR * (float) Math.sin(Math.toRadians(time));

if (y1 > mheight / 2 - smallR) {

y1 = mheight / 2 - smallR;

x1 = bigx - (float) (Math.sqrt(bigR * bigR - smallR * smallR));

}

float x2 = smalx - smallR * (float) Math.cos(Math.toRadians(time));

float y2 = mheight / 2 - smallR * (float) Math.sin(Math.toRadians(time));

if (y2 > mheight / 2 - smallR * 0.8) {

y2 = mheight / 2 - smallR * 0.8f;

x2 = smalx + smallR * (float) (Math.sqrt(1-0.64f));

}

float x3 = smalx - smallR * (float) Math.cos(Math.toRadians(time));

float y3 = mheight / 2 + smallR * (float) Math.sin(Math.toRadians(time));

if (y3 < mheight / 2 + smallR * 0.8) {

y3 = mheight / 2 + smallR * 0.8f;

x3 = smalx + smallR * (float) (Math.sqrt(1-0.64f));

}

float x4 = bigx + bigR * (float) Math.cos(Math.toRadians(time));

float y4 = mheight / 2 + bigR * (float) Math.sin(Math.toRadians(time));

if (y4 < mheight / 2 + smallR) {

y4 = mheight / 2 + smallR;

x4 = bigx - (float) (Math.sqrt(bigR * bigR - smallR * smallR));

}

path.moveTo(x1, y1);

path.quadTo((bigx + smalx) / 2, mheight / 2, x2, y2);

// 绘制贝赛尔曲线(Path)

path.lineTo(x3, y3);

path.quadTo((bigx + smalx) / 2, mheight / 2, x4, y4);

canvas.drawPath(path, mPaint);

}

postInvalidate();

}

}

//计算小球的X坐标

private float getSmallCenterX() {

//此处控制速度

time = time + 4f;

return mWidth / 2 + distance * (float) Math.cos(Math.toRadians(time));

}

}

两张素材

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

android 加载更多动画效果,Android实践之带加载效果的下拉刷新上拉加载更多相关推荐

  1. Android滑动冲突解决方式(下拉刷新上拉加载更多,适配RecyclerView/ListView/ScrollView)

    一.Android事件的分发机制 这里需要了解下Andorid事件的分发机制.事件分发一般是针对一组事件,即ACTION_DOWN > ACTION_UP 或 ACTION_DOWN > ...

  2. android 列表上拉加载更多,Android 下拉刷新,上拉加载更多控件–支持ListView,GridView和ScrollView...

    麦洛遇到这样一个需求,实现类似于IOS下拉刷新,上拉加载更多的控件.麦洛google,baidu了一番,网上有不少实现,比较常见的是国外牛人的实现,不过国外的实现基本上都是扩展于ListView,所以 ...

  3. Android ListView 实现下拉刷新上拉加载

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/39965327 1.简介 无疑,在Android开发中,ListView是使用非常频 ...

  4. Android自定义控件实战——实现仿IOS下拉刷新上拉加载 PullToRefreshLayout

    下拉刷新控件,网上有很多版本,有自定义Layout布局的,也有封装控件的,各种实现方式的都有.但是很少有人告诉你具体如何实现的,今天我们就来一步步实现自己封装的 PullToRefreshLayout ...

  5. Android 下拉刷新上拉加载 多种应用场景 超级大放送(上)

    转载请标明原文地址:http://blog.csdn.net/yalinfendou/article/details/47707017 关于Android下拉刷新上拉加载,网上的Demo太多太多了,这 ...

  6. Android 下拉刷新上拉加载可以左右滑动

    下面是下拉刷新上拉加载可以左右滑动的实例,下面是效果图: GitHub 下载地址:https://github.com/wuqingsen/MySlidingNested CSDN 下载地址:http ...

  7. 开启Fluter基础之旅五-------ListView 3D滚动、Flipper效果、ListView下拉刷新上拉加载、ListView重排序...

    继续来来操练Flutter的基础,对于Flutter的学习也有一段时间了,实操项目还木有做过,所以待这次基础学完之后就打算用一个项目对之前所学的进行一下巩固,不然光学这些零散的知识点最终还是不会Flu ...

  8. 下拉加载 实现 java_[Java教程]iscroll5实现一个下拉刷新上拉加载的效果

    [Java教程]iscroll5实现一个下拉刷新上拉加载的效果 0 2016-08-24 15:00:08 直接上代码!!! * { margin: 0; padding: 0; } ul, li { ...

  9. react-native 自定义 下拉刷新 / 上拉加载更多 组件

    1.封装 Scroller 组件 src/components/Scroller/index.js /*** 下拉刷新/上拉加载更多 组件(Scroller)*/ import React, {Com ...

  10. 使用MJRefresh自定义下拉刷新,上拉加载动画

    有时候我们需要自己设置下拉刷新,上拉加载动画的实现,这里主要是记录下使用MJRefresh自定义下拉刷新,上拉加载动画..... 下拉刷新我们只需要继承MJRefreshGifHeader即可: 实现 ...

最新文章

  1. telnet给服务器发消息,[摘抄]使用telnet命令直接发送
  2. python字符串补空格输出_python格式化打印字符串
  3. boost::units模块实现展示信息单元系统
  4. cheerio的小案例
  5. DelphiXE下的字符串变化
  6. RELL软件测试初学者化妆品,浅析软件测试流程在高校软件测试工作室中的应用...
  7. linux input输入子系统分析《一》:初识input输入子系统
  8. 高清壁纸|海贼王漫画名场面
  9. C语言和设计模式(策略模式)
  10. PLSQL Developer报“动态执行表不可访问,本会话的自动统计被禁止”的解决方案...
  11. `find -name`模式匹配多个模式
  12. Spring Boot + MyBatis + MySQL 实现读写分离!
  13. 尼恩Java面试宝典
  14. js控制5秒后页面自动跳转
  15. plc c语言做报警,wincc声音报警C脚本版
  16. 正版Windows7 OEM初级版升级到各版本(Lenovo/ThinkPad/ThinkCentre品牌)的密钥
  17. 2的负x次幂图像_函数Y等于2的X次方图像怎么画?求过程
  18. JSP报刊订阅管理系统
  19. 《AlignedReID: Surpassing Human-Level Performance in Person Re-Identification》论文解读
  20. 计算机---之程序的装入

热门文章

  1. Unity超基础学习笔记(二)
  2. 计算机科学基础内容摘抄,科学网-上计算机课,不接触计算机----日记摘抄(161)-武夷山的博文...
  3. OpenShift 4 - Knative教程 (5) Eventing之Source和Sink
  4. (一)带有图像到图像转换的移动风格迁移
  5. 记录MS SQL Server数据库
  6. WPF实现Win10汉堡菜单
  7. java初级系统项目_JavaSE-初级阶段项目-基于excel表格的学生管理系统
  8. protobuf3 自定义option_Protobuf3语法详解
  9. mysql json 引号 双引号_关于JSON字符串key缺少双引号的解决方法 的讲解
  10. mingus python_【Python图像特征的音乐序列生成】关于mingus一个bug的修复,兼改进情感模型...