Commit 96334b6b by 郑娜伟

添加 sobotwidget 控件库

parent 6a1bc21b
/build
\ No newline at end of file
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 14
}
}
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
compileOnly 'com.android.support:appcompat-v7:28.0.0'
compileOnly 'com.android.support:recyclerview-v7:28.0.0'
}
//添加发布到mavenCentral脚本
apply from: './sobot-widget-publish-mavencentral.gradle'
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
apply plugin: 'maven-publish'
apply plugin: 'signing'
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.source
exclude "**/R.class"
exclude "**/BuildConfig.class"
}
ext {
PUBLISH_GROUP_ID = "com.sobot.library" //项目包名
PUBLISH_ARTIFACT_ID = 'widget' //项目名
// PUBLISH_ARTIFACT_ID = 'widget_x' //项目名
PUBLISH_VERSION = '0.1' //版本号
}
ext["signing.keyId"] = ''
ext["signing.password"] = ''
ext["signing.secretKeyRingFile"] = ''
ext["ossrhUsername"] = ''
ext["ossrhPassword"] = ''
File secretPropsFile = project.rootProject.file('local.properties')
if (secretPropsFile.exists()) {
println "Found secret props file, loading props"
Properties p = new Properties()
p.load(new FileInputStream(secretPropsFile))
p.each { name, value ->
ext[name] = value
}
} else {
println "No props file, loading env vars"
}
publishing {
publications {
release(MavenPublication) {
// The coordinates of the library, being set from variables that
// we'll set up in a moment
groupId PUBLISH_GROUP_ID
artifactId PUBLISH_ARTIFACT_ID
version PUBLISH_VERSION
// Two artifacts, the `aar` and the sources
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
artifact androidSourcesJar
// Self-explanatory metadata for the most part
pom {
name = PUBLISH_ARTIFACT_ID
description = 'sobotcommon'
// If your project has a dedicated site, use its URL here
url = 'http://code.zhichidata.com/sobot_android/Sobot_module_Dev'
licenses {
license {
//协议类型,一般默认Apache License2.0的话不用改:
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'app_dev@sobot.com'
name = 'zhichi'
email = 'app_dev@sobot.com'
}
}
// Version control info, if you're using GitHub, follow the format as seen here
scm {
//修改成你的Git地址:
connection = 'git@code.zhichidata.com:sobot_android/Sobot_module_Dev.git'
developerConnection = 'git@code.zhichidata.com:sobot_android/Sobot_module_Dev.git'
//分支地址:
url = 'http://code.zhichidata.com/sobot_android/Sobot_module_Dev/master'
}
// A slightly hacky fix so that your POM will include any transitive dependencies
// that your library builds upon
// withXml {
// def dependenciesNode = asNode().appendNode('dependencies')
//
// project.configurations.implementation.allDependencies.each {
// def dependencyNode = dependenciesNode.appendNode('dependency')
// dependencyNode.appendNode('groupId', it.group)
// dependencyNode.appendNode('artifactId', it.name)
// dependencyNode.appendNode('version', it.version)
// }
// }
withXml {
def dependenciesNode = asNode().appendNode('dependencies')
//跳过“unspecified”的依赖项
//避免出现问题:
//Execution failed for task ':sample:checkDebugAarMetadata'.
//> Could not resolve all files for configuration ':sample:debugRuntimeClasspath'.
// > Could not find :unspecified:.
// Required by:
project.configurations.implementation.allDependencies.each {
if (it.name != 'unspecified') {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}
}
repositories {
// The repository to publish to, Sonatype/MavenCentral
maven {
// This is an arbitrary name, you may also use "mavencentral" or
// any other name that's descriptive for you
//项目名称
name = "sobotwidget"
def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
// You only need this if you want to publish snapshots, otherwise just set the URL
// to the release repo directly
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
// The username and password we've fetched earlier
credentials {
username ossrhUsername
password ossrhPassword
}
}
}
}
signing {
sign publishing.publications
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.sobot.widget">
<application>
</application>
</manifest>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
package com.sobot.widget.refresh.layout.api;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
import android.view.View;
import com.sobot.widget.refresh.layout.constant.SpinnerStyle;
import com.sobot.widget.refresh.layout.listener.OnStateChangedListener;
import static android.support.annotation.RestrictTo.Scope.LIBRARY;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static android.support.annotation.RestrictTo.Scope.SUBCLASSES;
/**
* 刷新内部组件
* Created by scwang on 2017/5/26.
*/
public interface RefreshComponent extends OnStateChangedListener {
/**
* 获取实体视图
* @return 实体视图
*/
@NonNull
View getView();
/**
* 获取变换方式 {@link SpinnerStyle} 必须返回 非空
* @return 变换方式
*/
@NonNull
SpinnerStyle getSpinnerStyle();
/**
* 【仅限框架内调用】设置主题颜色
* @param colors 对应Xml中配置的 srlPrimaryColor srlAccentColor
*/
@RestrictTo({LIBRARY,LIBRARY_GROUP,SUBCLASSES})
void setPrimaryColors(@ColorInt int... colors);
/**
* 【仅限框架内调用】尺寸定义完成 (如果高度不改变(代码修改:setHeader),只调用一次, 在RefreshLayout#onMeasure中调用)
* @param kernel RefreshKernel
* @param height HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
@RestrictTo({LIBRARY,LIBRARY_GROUP,SUBCLASSES})
void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight);
/**
* 【仅限框架内调用】手指拖动下拉(会连续多次调用,添加isDragging并取代之前的onPulling、onReleasing)
* @param isDragging true 手指正在拖动 false 回弹动画
* @param percent 下拉的百分比 值 = offset/footerHeight (0 - percent - (footerHeight+maxDragHeight) / footerHeight )
* @param offset 下拉的像素偏移量 0 - offset - (footerHeight+maxDragHeight)
* @param height 高度 HeaderHeight or FooterHeight (offset 可以超过 height 此时 percent 大于 1)
* @param maxDragHeight 最大拖动高度 offset 可以超过 height 参数 但是不会超过 maxDragHeight
*/
@RestrictTo({LIBRARY,LIBRARY_GROUP,SUBCLASSES})
void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight);
/**
* 【仅限框架内调用】释放时刻(调用一次,将会触发加载)
* @param refreshLayout RefreshLayout
* @param height 高度 HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
@RestrictTo({LIBRARY,LIBRARY_GROUP,SUBCLASSES})
void onReleased(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight);
/**
* 【仅限框架内调用】开始动画
* @param refreshLayout RefreshLayout
* @param height HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
@RestrictTo({LIBRARY,LIBRARY_GROUP,SUBCLASSES})
void onStartAnimator(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight);
/**
* 【仅限框架内调用】动画结束
* @param refreshLayout RefreshLayout
* @param success 数据是否成功刷新或加载
* @return 完成动画所需时间 如果返回 Integer.MAX_VALUE 将取消本次完成事件,继续保持原有状态
*/
@RestrictTo({LIBRARY,LIBRARY_GROUP,SUBCLASSES})
int onFinish(@NonNull RefreshLayout refreshLayout, boolean success);
/**
* 【仅限框架内调用】水平方向的拖动
* @param percentX 下拉时,手指水平坐标对屏幕的占比(0 - percentX - 1)
* @param offsetX 下拉时,手指水平坐标对屏幕的偏移(0 - offsetX - LayoutWidth)
* @param offsetMax 最大的偏移量
*/
@RestrictTo({LIBRARY,LIBRARY_GROUP,SUBCLASSES})
void onHorizontalDrag(float percentX, int offsetX, int offsetMax);
/**
* 是否支持水平方向的拖动(将会影响到onHorizontalDrag的调用)
* @return 水平拖动需要消耗更多的时间和资源,所以如果不支持请返回false
*/
boolean isSupportHorizontalDrag();
}
package com.sobot.widget.refresh.layout.api;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.support.annotation.NonNull;
import android.view.MotionEvent;
import android.view.View;
import com.sobot.widget.refresh.layout.listener.ScrollBoundaryDecider;
/**
* 刷新内容组件
* Created by scwang on 2017/5/26.
*/
public interface RefreshContent {
@NonNull
View getView();
@NonNull
View getScrollableView();
void onActionDown(MotionEvent e);
void setUpComponent(RefreshKernel kernel, View fixedHeader, View fixedFooter);
void setScrollBoundaryDecider(ScrollBoundaryDecider boundary);
void setEnableLoadMoreWhenContentNotFull(boolean enable);
void moveSpinner(int spinner, int headerTranslationViewId, int footerTranslationViewId);
boolean canRefresh();
boolean canLoadMore();
AnimatorUpdateListener scrollContentWhenFinished(int spinner);
}
package com.sobot.widget.refresh.layout.api;
import android.support.annotation.RestrictTo;
import static android.support.annotation.RestrictTo.Scope.LIBRARY;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static android.support.annotation.RestrictTo.Scope.SUBCLASSES;
/**
* 刷新底部
* Created by scwang on 2017/5/26.
*/
public interface RefreshFooter extends RefreshComponent {
/**
* 【仅限框架内调用】设置数据全部加载完成,将不能再次触发加载功能
* @param noMoreData 是否有更多数据
* @return true 支持全部加载完成的状态显示 false 不支持
*/
@RestrictTo({LIBRARY,LIBRARY_GROUP,SUBCLASSES})
boolean setNoMoreData(boolean noMoreData);
}
package com.sobot.widget.refresh.layout.api;
/**
* 刷新头部
* Created by scwang on 2017/5/26.
*/
public interface RefreshHeader extends RefreshComponent {
}
package com.sobot.widget.refresh.layout.api;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import com.sobot.widget.refresh.layout.constant.RefreshState;
/**
* 刷新布局核心功能接口
* 为功能复杂的 Header 或者 Footer 开放的接口
* Created by scwang on 2017/5/26.
*/
@SuppressWarnings({"unused", "UnusedReturnValue", "SameParameterValue"})
public interface RefreshKernel {
@NonNull
RefreshLayout getRefreshLayout();
@NonNull
RefreshContent getRefreshContent();
RefreshKernel setState(@NonNull RefreshState state);
//<editor-fold desc="视图位移 Spinner">
/**
* 开始执行二极刷新
* @param open 是否展开
* @return RefreshKernel
*/
RefreshKernel startTwoLevel(boolean open);
/**
* 结束关闭二极刷新
* @return RefreshKernel
*/
RefreshKernel finishTwoLevel();
/**
* 移动视图到指定位置
* moveSpinner 的取名来自 谷歌官方的 {@link android.support.v4.widget.SwipeRefreshLayout}
* @param spinner 位置 (px)
* @param isDragging true 手指正在拖动 false 回弹动画执行
* @return RefreshKernel
*/
RefreshKernel moveSpinner(int spinner, boolean isDragging);
/**
* 执行动画使视图位移到指定的 位置
* moveSpinner 的取名来自 谷歌官方的 {@link android.support.v4.widget.SwipeRefreshLayout}
* @param endSpinner 指定的结束位置 (px)
* @return ValueAnimator 如果没有执行动画 null
*/
ValueAnimator animSpinner(int endSpinner);
//</editor-fold>
//<editor-fold desc="请求事件">
/**
* 指定在下拉时候为 Header 或 Footer 绘制背景
* @param internal Header Footer 调用时传 this
* @param backgroundColor 背景颜色
* @return RefreshKernel
*/
RefreshKernel requestDrawBackgroundFor(@NonNull RefreshComponent internal, int backgroundColor);
/**
* 请求事件
* @param internal Header Footer 调用时传 this
* @param request 请求
* @return RefreshKernel
*/
RefreshKernel requestNeedTouchEventFor(@NonNull RefreshComponent internal, boolean request);
/**
* 请求设置默认内容滚动设置
* @param internal Header Footer 调用时传 this
* @param translation 移动
* @return RefreshKernel
*/
RefreshKernel requestDefaultTranslationContentFor(@NonNull RefreshComponent internal, boolean translation);
/**
* 请求重新测量 headerHeight 或 footerHeight , 要求 height 高度为 WRAP_CONTENT
* @param internal Header Footer 调用时传 this
* @return RefreshKernel
*/
RefreshKernel requestRemeasureHeightFor(@NonNull RefreshComponent internal);
/**
* 设置二楼回弹时长
* @param duration 二楼回弹时长
* @return RefreshKernel
*/
RefreshKernel requestFloorDuration(int duration);
/**
* 设置二楼底部上划关闭所占高度的比率
* @return RefreshKernel
*/
RefreshKernel requestFloorBottomPullUpToCloseRate(float rate);
//</editor-fold>
}
package com.sobot.widget.refresh.layout.constant;
/**
* 尺寸值的定义状态,用于在值覆盖的时候决定优先级
* 越往下优先级越高
*/
@SuppressWarnings("WeakerAccess")
public class DimensionStatus {
public static final DimensionStatus DefaultUnNotify = new DimensionStatus(0,false);//默认值,但是还没通知确认
public static final DimensionStatus Default = new DimensionStatus(1,true);//默认值
public static final DimensionStatus XmlWrapUnNotify = new DimensionStatus(2,false);//Xml计算,但是还没通知确认
public static final DimensionStatus XmlWrap = new DimensionStatus(3,true);//Xml计算
public static final DimensionStatus XmlExactUnNotify = new DimensionStatus(4,false);//Xml 的view 指定,但是还没通知确认
public static final DimensionStatus XmlExact = new DimensionStatus(5,true);//Xml 的view 指定
public static final DimensionStatus XmlLayoutUnNotify = new DimensionStatus(6,false);//Xml 的layout 中指定,但是还没通知确认
public static final DimensionStatus XmlLayout = new DimensionStatus(7,true);//Xml 的layout 中指定
public static final DimensionStatus CodeExactUnNotify = new DimensionStatus(8,false);//代码指定,但是还没通知确认
public static final DimensionStatus CodeExact = new DimensionStatus(9,true);//代码指定
public static final DimensionStatus DeadLockUnNotify = new DimensionStatus(10,false);//锁死,但是还没通知确认
public static final DimensionStatus DeadLock = new DimensionStatus(10,true);//锁死
public final int ordinal;
public final boolean notified;
public static final DimensionStatus[] values = new DimensionStatus[]{
DefaultUnNotify,
Default,
XmlWrapUnNotify,
XmlWrap,
XmlExactUnNotify,
XmlExact,
XmlLayoutUnNotify,
XmlLayout,
CodeExactUnNotify,
CodeExact,
DeadLockUnNotify,
DeadLock
};
private DimensionStatus(int ordinal,boolean notified) {
this.ordinal = ordinal;
this.notified = notified;
}
/**
* 转换为未通知状态
* @return 未通知状态
*/
public DimensionStatus unNotify() {
if (notified) {
DimensionStatus prev = values[ordinal - 1];
if (!prev.notified) {
return prev;
}
return DefaultUnNotify;
}
return this;
}
/**
* 转换为通知状态
* @return 通知状态
*/
public DimensionStatus notified() {
if (!notified) {
return values[ordinal + 1];
}
return this;
}
/**
* 是否可以被新的状态替换
* @param status 新转台
* @return 小于等于
*/
public boolean canReplaceWith(DimensionStatus status) {
return ordinal < status.ordinal || ((!notified || CodeExact == this) && ordinal == status.ordinal);
}
// /**
// * 是否没有达到新的状态
// * @param status 新转台
// * @return 大于等于 gte
// */
// public boolean gteStatusWith(DimensionStatus status) {
// return ordinal() >= status.ordinal();
// }
}
\ No newline at end of file
package com.sobot.widget.refresh.layout.constant;
/**
* 刷新状态
*/
@SuppressWarnings("unused")
public enum RefreshState {
None(0,false,false,false,false,false),
PullDownToRefresh(1,true,false,false,false,false), PullUpToLoad(2,true,false,false,false,false),
PullDownCanceled(1,false,false,false,false,false), PullUpCanceled(2,false,false,false,false,false),
ReleaseToRefresh(1,true,false,false,false,true), ReleaseToLoad(2,true,false,false,false,true),
ReleaseToTwoLevel(1,true,false,false,true,true), TwoLevelReleased(1,false,false,false,true,false),
RefreshReleased(1,false,false,false,false,false), LoadReleased(2,false,false,false,false,false),
Refreshing(1,false,true,false,false,false), Loading(2,false,true,false,false,false), TwoLevel(1, false, true,false,true,false),
RefreshFinish(1,false,false,true,false,false), LoadFinish(2,false,false,true,false,false), TwoLevelFinish(1,false,false,true,true,false);
public final boolean isHeader;
public final boolean isFooter;
public final boolean isTwoLevel;// 二级刷新 ReleaseToTwoLevel TwoLevelReleased TwoLevel
public final boolean isDragging;// 正在拖动状态:PullDownToRefresh PullUpToLoad ReleaseToRefresh ReleaseToLoad ReleaseToTwoLevel
public final boolean isOpening;// 正在刷新状态:Refreshing Loading TwoLevel
public final boolean isFinishing;//正在完成状态:RefreshFinish LoadFinish TwoLevelFinish
public final boolean isReleaseToOpening;// 释放立马打开 ReleaseToRefresh ReleaseToLoad ReleaseToTwoLevel
RefreshState(int role, boolean dragging, boolean opening, boolean finishing, boolean twoLevel, boolean releaseToOpening) {
this.isHeader = role == 1;
this.isFooter = role == 2;
this.isDragging = dragging;
this.isOpening = opening;
this.isFinishing = finishing;
this.isTwoLevel = twoLevel;
this.isReleaseToOpening = releaseToOpening;
}
public RefreshState toFooter() {
if (isHeader && !isTwoLevel) {
return values()[ordinal() + 1];
}
return this;
}
public RefreshState toHeader() {
if (isFooter && !isTwoLevel) {
return values()[ordinal()-1];
}
return this;
}
}
\ No newline at end of file
package com.sobot.widget.refresh.layout.constant;
/**
* 顶部和底部的组件在拖动时候的变换方式
* Created by scwang on 2017/5/26.
*/
@SuppressWarnings("DeprecatedIsStillUsed")
public class SpinnerStyle {
public static final SpinnerStyle Translate = new SpinnerStyle(0, true, false);
/**
* Scale 下拉过程中会动态 【测量】(header)和 【布局】(layout)降低app 性能,
* 官方自带的 Header 都已经从【Scale】转向【FixedBehind】来提高性能
* 自定义可以参考官方的 【飞机】【贝塞尔】【快递】等 Header
* @deprecated use {@link SpinnerStyle#FixedBehind}
*/
@Deprecated
public static final SpinnerStyle Scale = new SpinnerStyle(1, true, true);
public static final SpinnerStyle FixedBehind = new SpinnerStyle(2, false, false);
public static final SpinnerStyle FixedFront = new SpinnerStyle(3, true, false);
public static final SpinnerStyle MatchLayout = new SpinnerStyle(4, true, false);
public static final SpinnerStyle[] values = new SpinnerStyle[] {
Translate, //平行移动 特点: HeaderView高度不会改变,
Scale, //拉伸形变 特点:在下拉和上弹(HeaderView高度改变)时候,会自动触发OnDraw事件
FixedBehind, //固定在背后 特点:HeaderView高度不会改变,
FixedFront, //固定在前面 特点:HeaderView高度不会改变,
MatchLayout//填满布局 特点:HeaderView高度不会改变,尺寸充满 RefreshLayout
};
public final int ordinal;
public final boolean front;
public final boolean scale;
protected SpinnerStyle(int ordinal, boolean front, boolean scale) {
this.ordinal = ordinal;
this.front = front;
this.scale = scale;
}
}
package com.sobot.widget.refresh.layout.drawable;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
/**
* 画笔 Drawable
* Created by scwang on 2017/6/16.
*/
public abstract class PaintDrawable extends Drawable {
protected Paint mPaint = new Paint();
protected PaintDrawable() {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAntiAlias(true);
mPaint.setColor(0xffaaaaaa);
}
public void setColor(int color) {
mPaint.setColor(color);
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
package com.sobot.widget.refresh.layout.drawable;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
/**
* 旋转动画
* Created by scwang on 2017/6/16.
*/
@SuppressWarnings("WeakerAccess")
public class ProgressDrawable extends PaintDrawable implements Animatable , ValueAnimator.AnimatorUpdateListener{
protected int mWidth = 0;
protected int mHeight = 0;
protected int mProgressDegree = 0;
protected ValueAnimator mValueAnimator;
protected Path mPath = new Path();
public ProgressDrawable() {
mValueAnimator = ValueAnimator.ofInt(30, 3600);
mValueAnimator.setDuration(10000);
mValueAnimator.setInterpolator(null);
mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mValueAnimator.setRepeatMode(ValueAnimator.RESTART);
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mProgressDegree = 30 * (value / 30);
final Drawable drawable = ProgressDrawable.this;
drawable.invalidateSelf();
}
//<editor-fold desc="Drawable">
@Override
public void draw(@NonNull Canvas canvas) {
final Drawable drawable = ProgressDrawable.this;
final Rect bounds = drawable.getBounds();
final int width = bounds.width();
final int height = bounds.height();
final float r = Math.max(1f, width / 22f);
if (mWidth != width || mHeight != height) {
mPath.reset();
mPath.addCircle(width - r, height / 2f, r, Path.Direction.CW);
mPath.addRect(width - 5 * r, height / 2f - r, width - r, height / 2f + r, Path.Direction.CW);
mPath.addCircle(width - 5 * r, height / 2f, r, Path.Direction.CW);
mWidth = width;
mHeight = height;
}
canvas.save();
canvas.rotate(mProgressDegree, (width) / 2f, (height) / 2f);
for (int i = 0; i < 12; i++) {
mPaint.setAlpha((i+5) * 0x11);
canvas.rotate(30, (width) / 2f, (height) / 2f);
canvas.drawPath(mPath, mPaint);
}
canvas.restore();
}
//</editor-fold>
@Override
public void start() {
if (!mValueAnimator.isRunning()) {
mValueAnimator.addUpdateListener(this);
mValueAnimator.start();
}
}
@Override
public void stop() {
if (mValueAnimator.isRunning()) {
Animator animator = mValueAnimator;
animator.removeAllListeners();
mValueAnimator.removeAllUpdateListeners();
mValueAnimator.cancel();
}
}
@Override
public boolean isRunning() {
return mValueAnimator.isRunning();
}
}
package com.sobot.widget.refresh.layout.footer;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import com.sobot.widget.refresh.layout.drawable.PaintDrawable;
/**
* 箭头图像
* Created by scwang on 2018/2/5.
*/
public class ArrowDrawable extends PaintDrawable {
private int mWidth = 0;
private int mHeight = 0;
private Path mPath = new Path();
@Override
public void draw(@NonNull Canvas canvas) {
final Drawable drawable = ArrowDrawable.this;
final Rect bounds = drawable.getBounds();
final int width = bounds.width();
final int height = bounds.height();
if (mWidth != width || mHeight != height) {
int lineWidth = width * 30 / 225;
mPath.reset();
float vector1 = (lineWidth * 0.70710678118654752440084436210485f);//Math.sin(Math.PI/4));
float vector2 = (lineWidth / 0.70710678118654752440084436210485f);//Math.sin(Math.PI/4));
mPath.moveTo(width / 2f, height);
mPath.lineTo(0, height / 2f);
mPath.lineTo(vector1, height / 2f - vector1);
mPath.lineTo(width / 2f - lineWidth / 2f, height - vector2 - lineWidth / 2f);
mPath.lineTo(width / 2f - lineWidth / 2f, 0);
mPath.lineTo(width / 2f + lineWidth / 2f, 0);
mPath.lineTo(width / 2f + lineWidth / 2f, height - vector2 - lineWidth / 2f);
mPath.lineTo(width - vector1, height / 2f - vector1);
mPath.lineTo(width, height / 2f);
mPath.close();
mWidth = width;
mHeight = height;
}
canvas.drawPath(mPath, mPaint);
}
}
package com.sobot.widget.refresh.layout.listener;
public interface CoordinatorLayoutListener {
void onCoordinatorUpdate(boolean enableRefresh, boolean enableLoadMore);
}
\ No newline at end of file
package com.sobot.widget.refresh.layout.listener;
import android.content.Context;
import android.support.annotation.NonNull;
import com.sobot.widget.refresh.layout.api.RefreshFooter;
import com.sobot.widget.refresh.layout.api.RefreshLayout;
/**
* 默认Footer创建器
* Created by scwang on 2018/1/26.
*/
public interface DefaultRefreshFooterCreator {
@NonNull
RefreshFooter createRefreshFooter(@NonNull Context context, @NonNull RefreshLayout layout);
}
package com.sobot.widget.refresh.layout.listener;
import android.content.Context;
import android.support.annotation.NonNull;
import com.sobot.widget.refresh.layout.api.RefreshHeader;
import com.sobot.widget.refresh.layout.api.RefreshLayout;
/**
* 默认Header创建器
* Created by scwang on 2018/1/26.
*/
public interface DefaultRefreshHeaderCreator {
@NonNull
RefreshHeader createRefreshHeader(@NonNull Context context, @NonNull RefreshLayout layout);
}
package com.sobot.widget.refresh.layout.listener;
import android.content.Context;
import android.support.annotation.NonNull;
import com.sobot.widget.refresh.layout.api.RefreshLayout;
/**
* 默认全局初始化器
* Created by scwang on 2018/5/29 0029.
*/
public interface DefaultRefreshInitializer {
void initialize(@NonNull Context context, @NonNull RefreshLayout layout);
}
package com.sobot.widget.refresh.layout.listener;
import android.support.annotation.NonNull;
import com.sobot.widget.refresh.layout.api.RefreshLayout;
/**
* 加载更多监听器
* Created by scwang on 2017/5/26.
*/
public interface OnLoadMoreListener {
void onLoadMore(@NonNull RefreshLayout refreshLayout);
}
package com.sobot.widget.refresh.layout.listener;
import com.sobot.widget.refresh.layout.api.RefreshFooter;
import com.sobot.widget.refresh.layout.api.RefreshHeader;
/**
* 多功能监听器
* Created by scwang on 2017/5/26.
*/
public interface OnMultiListener extends OnRefreshLoadMoreListener, OnStateChangedListener {
/**
* 手指拖动下拉(会连续多次调用,添加isDragging并取代之前的onPulling、onReleasing)
* @param header 头部
* @param isDragging true 手指正在拖动 false 回弹动画
* @param percent 下拉的百分比 值 = offset/footerHeight (0 - percent - (footerHeight+maxDragHeight) / footerHeight )
* @param offset 下拉的像素偏移量 0 - offset - (footerHeight+maxDragHeight)
* @param headerHeight 高度 HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
void onHeaderMoving(RefreshHeader header, boolean isDragging, float percent, int offset, int headerHeight, int maxDragHeight);
void onHeaderReleased(RefreshHeader header, int headerHeight, int maxDragHeight);
void onHeaderStartAnimator(RefreshHeader header, int headerHeight, int maxDragHeight);
void onHeaderFinish(RefreshHeader header, boolean success);
/**
* 手指拖动上拉(会连续多次调用,添加isDragging并取代之前的onPulling、onReleasing)
* @param footer 尾部
* @param isDragging true 手指正在拖动 false 回弹动画
* @param percent 下拉的百分比 值 = offset/footerHeight (0 - percent - (footerHeight+maxDragHeight) / footerHeight )
* @param offset 下拉的像素偏移量 0 - offset - (footerHeight+maxDragHeight)
* @param footerHeight 高度 HeaderHeight or FooterHeight
* @param maxDragHeight 最大拖动高度
*/
void onFooterMoving(RefreshFooter footer, boolean isDragging, float percent, int offset, int footerHeight, int maxDragHeight);
void onFooterReleased(RefreshFooter footer, int footerHeight, int maxDragHeight);
void onFooterStartAnimator(RefreshFooter footer, int footerHeight, int maxDragHeight);
void onFooterFinish(RefreshFooter footer, boolean success);
}
package com.sobot.widget.refresh.layout.listener;
import android.support.annotation.NonNull;
import com.sobot.widget.refresh.layout.api.RefreshLayout;
/**
* 刷新监听器
* Created by scwang on 2017/5/26.
*/
public interface OnRefreshListener {
void onRefresh(@NonNull RefreshLayout refreshLayout);
}
package com.sobot.widget.refresh.layout.listener;
/**
* 刷新加载组合监听器
* Created by scwang on 2017/5/26.
*/
public interface OnRefreshLoadMoreListener extends OnRefreshListener, OnLoadMoreListener {
}
package com.sobot.widget.refresh.layout.listener;
import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
import com.sobot.widget.refresh.layout.api.RefreshLayout;
import com.sobot.widget.refresh.layout.constant.RefreshState;
import static android.support.annotation.RestrictTo.Scope.LIBRARY;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static android.support.annotation.RestrictTo.Scope.SUBCLASSES;
/**
* 刷新状态改变监听器
* Created by scwang on 2017/5/26.
*/
public interface OnStateChangedListener {
/**
* 【仅限框架内调用】状态改变事件 {@link RefreshState}
* @param refreshLayout RefreshLayout
* @param oldState 改变之前的状态
* @param newState 改变之后的状态
*/
@RestrictTo({LIBRARY,LIBRARY_GROUP,SUBCLASSES})
void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState);
}
package com.sobot.widget.refresh.layout.listener;
import android.view.View;
/**
* 滚动边界
* Created by scwang on 2017/7/8.
*/
public interface ScrollBoundaryDecider {
/**
* 根据内容视图状态判断是否可以开始下拉刷新
* @param content 内容视图
* @return true 将会触发下拉刷新
*/
boolean canRefresh(View content);
/**
* 根据内容视图状态判断是否可以开始上拉加载
* @param content 内容视图
* @return true 将会触发加载更多
*/
boolean canLoadMore(View content);
}
package com.sobot.widget.refresh.layout.simple;
import android.graphics.PointF;
import android.view.View;
import com.sobot.widget.refresh.layout.listener.ScrollBoundaryDecider;
import com.sobot.widget.refresh.layout.util.SmartUtil;
/**
* 滚动边界
* Created by scwang on 2017/7/8.
*/
public class SimpleBoundaryDecider implements ScrollBoundaryDecider {
//<editor-fold desc="Internal">
public PointF mActionEvent;
public ScrollBoundaryDecider boundary;
public boolean mEnableLoadMoreWhenContentNotFull = true;
//</editor-fold>
//<editor-fold desc="ScrollBoundaryDecider">
@Override
public boolean canRefresh(View content) {
if (boundary != null) {
return boundary.canRefresh(content);
}
//mActionEvent == null 时 canRefresh 不会动态递归搜索
return SmartUtil.canRefresh(content, mActionEvent);
}
@Override
public boolean canLoadMore(View content) {
if (boundary != null) {
return boundary.canLoadMore(content);
}
//mActionEvent == null 时 canLoadMore 不会动态递归搜索
return SmartUtil.canLoadMore(content, mActionEvent, mEnableLoadMoreWhenContentNotFull);
}
//</editor-fold>
}
package com.sobot.widget.refresh.layout.simple;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import com.sobot.widget.refresh.layout.SobotRefreshLayout;
import com.sobot.widget.refresh.layout.api.RefreshComponent;
import com.sobot.widget.refresh.layout.api.RefreshFooter;
import com.sobot.widget.refresh.layout.api.RefreshHeader;
import com.sobot.widget.refresh.layout.api.RefreshKernel;
import com.sobot.widget.refresh.layout.api.RefreshLayout;
import com.sobot.widget.refresh.layout.constant.RefreshState;
import com.sobot.widget.refresh.layout.constant.SpinnerStyle;
import com.sobot.widget.refresh.layout.listener.OnStateChangedListener;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
/**
* Component 初步实现
* 实现 Header 和 Footer 时,继承 ComponentAbstract 的话可以少写很多接口方法
* Created by scwang on 2018/2/6.
*/
public abstract class SimpleComponent extends RelativeLayout implements RefreshComponent {
protected View mWrappedView;
protected SpinnerStyle mSpinnerStyle;
protected RefreshComponent mWrappedInternal;
protected SimpleComponent(@NonNull View wrapped) {
this(wrapped, wrapped instanceof RefreshComponent ? (RefreshComponent) wrapped : null);
}
protected SimpleComponent(@NonNull View wrappedView, @Nullable RefreshComponent wrappedInternal) {
super(wrappedView.getContext(), null, 0);
this.mWrappedView = wrappedView;
this.mWrappedInternal = wrappedInternal;
if (this instanceof RefreshFooter && mWrappedInternal instanceof RefreshHeader && mWrappedInternal.getSpinnerStyle() == SpinnerStyle.MatchLayout) {
wrappedInternal.getView().setScaleY(-1);
} else if (this instanceof RefreshHeader && mWrappedInternal instanceof RefreshFooter && mWrappedInternal.getSpinnerStyle() == SpinnerStyle.MatchLayout) {
wrappedInternal.getView().setScaleY(-1);
}
}
protected SimpleComponent(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean equals(Object obj) {
if (!super.equals(obj)) {
if (obj instanceof RefreshComponent) {
final RefreshComponent thisView = this;
return thisView.getView() == ((RefreshComponent)obj).getView();
}
return false;
}
return true;
}
@NonNull
public View getView() {
return mWrappedView == null ? this : mWrappedView;
}
@Override
public int onFinish(@NonNull RefreshLayout refreshLayout, boolean success) {
if (mWrappedInternal != null && mWrappedInternal != this) {
return mWrappedInternal.onFinish(refreshLayout, success);
}
return 0;
}
@Override
public void setPrimaryColors(@ColorInt int ... colors) {
if (mWrappedInternal != null && mWrappedInternal != this) {
mWrappedInternal.setPrimaryColors(colors);
}
}
@NonNull
@Override
public SpinnerStyle getSpinnerStyle() {
if (mSpinnerStyle != null) {
return mSpinnerStyle;
}
if (mWrappedInternal != null && mWrappedInternal != this) {
return mWrappedInternal.getSpinnerStyle();
}
if (mWrappedView != null) {
ViewGroup.LayoutParams params = mWrappedView.getLayoutParams();
if (params instanceof SobotRefreshLayout.LayoutParams) {
mSpinnerStyle = ((SobotRefreshLayout.LayoutParams) params).spinnerStyle;
if (mSpinnerStyle != null) {
return mSpinnerStyle;
}
}
if (params != null) {
if (params.height == 0 || params.height == MATCH_PARENT) {
for (SpinnerStyle style : SpinnerStyle.values) {
if (style.scale) {
return mSpinnerStyle = style;
}
}
}
}
}
return mSpinnerStyle = SpinnerStyle.Translate;
}
@Override
public void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight) {
if (mWrappedInternal != null && mWrappedInternal != this) {
mWrappedInternal.onInitialized(kernel, height, maxDragHeight);
} else if (mWrappedView != null) {
ViewGroup.LayoutParams params = mWrappedView.getLayoutParams();
if (params instanceof SobotRefreshLayout.LayoutParams) {
kernel.requestDrawBackgroundFor(this, ((SobotRefreshLayout.LayoutParams) params).backgroundColor);
}
}
}
@Override
public boolean isSupportHorizontalDrag() {
return mWrappedInternal != null && mWrappedInternal != this && mWrappedInternal.isSupportHorizontalDrag();
}
@Override
public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) {
if (mWrappedInternal != null && mWrappedInternal != this) {
mWrappedInternal.onHorizontalDrag(percentX, offsetX, offsetMax);
}
}
@Override
public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) {
if (mWrappedInternal != null && mWrappedInternal != this) {
mWrappedInternal.onMoving(isDragging, percent, offset, height, maxDragHeight);
}
}
@Override
public void onReleased(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) {
if (mWrappedInternal != null && mWrappedInternal != this) {
mWrappedInternal.onReleased(refreshLayout, height, maxDragHeight);
}
}
@Override
public void onStartAnimator(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) {
if (mWrappedInternal != null && mWrappedInternal != this) {
mWrappedInternal.onStartAnimator(refreshLayout, height, maxDragHeight);
}
}
@Override
public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) {
if (mWrappedInternal != null && mWrappedInternal != this) {
if (this instanceof RefreshFooter && mWrappedInternal instanceof RefreshHeader) {
if (oldState.isFooter) {
oldState = oldState.toHeader();
}
if (newState.isFooter) {
newState = newState.toHeader();
}
} else if (this instanceof RefreshHeader && mWrappedInternal instanceof RefreshFooter) {
if (oldState.isHeader) {
oldState = oldState.toFooter();
}
if (newState.isHeader) {
newState = newState.toFooter();
}
}
final OnStateChangedListener listener = mWrappedInternal;
if (listener != null) {
listener.onStateChanged(refreshLayout, oldState, newState);
}
}
}
@SuppressLint("RestrictedApi")
public boolean setNoMoreData(boolean noMoreData) {
return mWrappedInternal instanceof RefreshFooter && ((RefreshFooter) mWrappedInternal).setNoMoreData(noMoreData);
}
}
package com.sobot.widget.refresh.layout.simple;
import android.support.annotation.NonNull;
import com.sobot.widget.refresh.layout.api.RefreshFooter;
import com.sobot.widget.refresh.layout.api.RefreshHeader;
import com.sobot.widget.refresh.layout.api.RefreshLayout;
import com.sobot.widget.refresh.layout.constant.RefreshState;
import com.sobot.widget.refresh.layout.listener.OnMultiListener;
/**
* 多功能监听器
* Created by scwang on 2017/5/26.
*/
public class SimpleMultiListener implements OnMultiListener {
@Override
public void onHeaderMoving(RefreshHeader header, boolean isDragging, float percent, int offset, int headerHeight, int maxDragHeight) {
}
@Override
public void onHeaderReleased(RefreshHeader header, int headerHeight, int maxDragHeight) {
}
@Override
public void onHeaderStartAnimator(RefreshHeader header, int footerHeight, int maxDragHeight) {
}
@Override
public void onHeaderFinish(RefreshHeader header, boolean success) {
}
@Override
public void onFooterMoving(RefreshFooter footer, boolean isDragging, float percent, int offset, int footerHeight, int maxDragHeight) {
}
@Override
public void onFooterReleased(RefreshFooter footer, int footerHeight, int maxDragHeight) {
}
@Override
public void onFooterStartAnimator(RefreshFooter footer, int headerHeight, int maxDragHeight) {
}
@Override
public void onFooterFinish(RefreshFooter footer, boolean success) {
}
@Override
public void onRefresh(@NonNull RefreshLayout refreshLayout) {
}
@Override
public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
}
@Override
public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) {
}
}
package com.sobot.widget.refresh.layout.wrapper;
import android.annotation.SuppressLint;
import android.view.View;
import com.sobot.widget.refresh.layout.api.RefreshFooter;
import com.sobot.widget.refresh.layout.simple.SimpleComponent;
/**
* 刷新底部包装
* Created by scwang on 2017/5/26.
*/
@SuppressLint("ViewConstructor")
public class RefreshFooterWrapper extends SimpleComponent implements RefreshFooter {
public RefreshFooterWrapper(View wrapper) {
super(wrapper);
}
}
package com.sobot.widget.refresh.layout.wrapper;
import android.annotation.SuppressLint;
import android.view.View;
import com.sobot.widget.refresh.layout.api.RefreshHeader;
import com.sobot.widget.refresh.layout.simple.SimpleComponent;
/**
* 刷新头部包装
* Created by scwang on 2017/5/26.
*/
@SuppressLint("ViewConstructor")
public class RefreshHeaderWrapper extends SimpleComponent implements RefreshHeader {
public RefreshHeaderWrapper(View wrapper) {
super(wrapper);
}
}
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/sobot_refresh_bg_color"
tools:background="@color/sobot_refresh_bg_color"
tools:paddingBottom="20dp"
tools:paddingTop="20dp"
tools:parentTag="android.widget.RelativeLayout">
<ImageView
android:id="@+id/srl_classics_arrow"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:layout_toStartOf="@+id/srl_classics_title"
android:layout_toLeftOf="@+id/srl_classics_title"
android:contentDescription="@android:string/untitled" />
<ImageView
android:id="@+id/srl_classics_progress"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:layout_toStartOf="@+id/srl_classics_title"
android:layout_toLeftOf="@+id/srl_classics_title"
android:contentDescription="@android:string/untitled"
tools:src="@android:drawable/stat_notify_sync"
tools:tint="#666" />
<TextView
android:id="@+id/srl_classics_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:maxLines="1"
android:text="@string/sobot_srl_footer_pulling"
android:textColor="@color/sobot_refresh_text_color"
android:textSize="15sp" />
</merge>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:background="@color/sobot_refresh_bg_color"
tools:paddingBottom="20dp"
tools:paddingTop="20dp"
tools:parentTag="android.widget.RelativeLayout">
<ImageView
android:id="@+id/srl_classics_arrow"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:layout_toStartOf="@+id/srl_classics_center"
android:layout_toLeftOf="@+id/srl_classics_center"
android:contentDescription="@android:string/untitled" />
<ImageView
android:id="@+id/srl_classics_progress"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:layout_toStartOf="@+id/srl_classics_center"
android:layout_toLeftOf="@+id/srl_classics_center"
android:contentDescription="@android:string/untitled"
tools:src="@android:drawable/stat_notify_sync"
tools:tint="#666666" />
<LinearLayout
android:id="@+id/srl_classics_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/srl_classics_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="@string/sobot_srl_header_pulling"
android:textColor="@color/sobot_refresh_text_color"
android:textSize="15sp" />
<TextView
android:id="@+id/srl_classics_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="@string/sobot_srl_header_update"
android:textColor="@color/sobot_refresh_text2_color"
android:textSize="12sp" />
</LinearLayout>
</merge>
\ No newline at end of file
<resources>
<string name="sobot_srl_content_empty">The content view in SmartRefreshLayout is empty. Do you forget to add it in xml layout file?</string>
<string name="sobot_srl_footer_pulling">Pull Up To Load More</string>
<string name="sobot_srl_footer_release">Release To Load More</string>
<string name="sobot_srl_footer_loading">Loading…</string>
<string name="sobot_srl_footer_refreshing">Wait For Refreshing…</string>
<string name="sobot_srl_footer_finish">Load Success</string>
<string name="sobot_srl_footer_failed">Load Failed</string>
<string name="sobot_srl_footer_nothing">No More Data</string>
<string name="sobot_srl_header_pulling">Pull Down To Refresh</string>
<string name="sobot_srl_header_release">Release To Refresh</string>
<string name="sobot_srl_header_refreshing">Refreshing…</string>
<string name="sobot_srl_header_loading">Wait For Loading…</string>
<string name="sobot_srl_header_finish">Refresh Success</string>
<string name="sobot_srl_header_failed">Refresh Failed</string>
<string name="sobot_srl_header_update">\'Last Update\' M-d HH:mm</string>
<string name="sobot_srl_header_secondary">Release To Second Floor</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--下拉刷新 背景颜色-->
<color name="sobot_refresh_bg_color">#1B1B1C</color>
<!--下拉刷新 标题文字颜色-->
<color name="sobot_refresh_text_color">#D1D1D6</color>
<!--下拉刷新 更新时间文字颜色-->
<color name="sobot_refresh_text2_color">#D1D1D6</color>
<!--下拉刷新 箭头控件颜色 -->
<color name="sobot_refresh_arrow_color">#D1D1D6</color>
<!--下拉刷新 加载中 旋转控件颜色-->
<color name="sobot_refresh_progress_color">#D1D1D6</color>
<!--下拉刷新 头部 箭头控件颜色 -->
<color name="sobot_refresh_header_arrow_color">@color/sobot_refresh_arrow_color</color>
<!--下拉刷新 头部 加载中 旋转控件颜色-->
<color name="sobot_refresh_header_progress_color">@color/sobot_refresh_progress_color</color>
<!--下拉刷新 尾部 箭头控件颜色 -->
<color name="sobot_refresh_footer_arrow_color">@color/sobot_refresh_arrow_color</color>
<!--下拉刷新 尾部 加载中 旋转控件颜色-->
<color name="sobot_refresh_footer_progress_color">@color/sobot_refresh_progress_color</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="sobotSrlStyle" format="reference" /><!--样式-->
<attr name="sobotSrlDrawableSize" format="dimension" /><!--图片尺寸-->
<attr name="sobotSrlDrawableArrowSize" format="dimension" /><!--箭头图片尺寸-->
<attr name="sobotSrlDrawableProgressSize" format="dimension" /><!--箭头图片尺寸-->
<attr name="sobotSrlDrawableMarginRight" format="dimension" /><!--图片和文字的间距-->
<attr name="sobotSrlTextSizeTitle" format="dimension" /><!--标题字体-->
<attr name="sobotSrlTextSizeTime" format="dimension" /><!--时间字体-->
<attr name="sobotSrlFinishDuration" format="integer" /><!--完成时停留时间-->
<attr name="sobotSrlPrimaryColor" format="color" /><!--主要颜色-->
<attr name="sobotSrlAccentColor" format="color" /><!--强调颜色-->
<attr name="sobotSrlDrawableArrow" format="reference" /><!--箭头图片-->
<attr name="sobotSrlColorArrow" format="color" /><!--强调颜色-->
<attr name="sobotSrlDrawableProgress" format="reference" /><!--转动图片-->
<attr name="sobotSrlColorProgress" format="color" /><!--强调颜色-->
<attr name="sobotSrlEnableHorizontalDrag" format="boolean" /><!--支持水平拖动-->
<attr name="sobotSrlTextPulling" format="string" />
<attr name="sobotSrlTextLoading" format="string" />
<attr name="sobotSrlTextRelease" format="string" />
<attr name="sobotSrlTextFinish" format="string" />
<attr name="sobotSrlTextFailed" format="string" />
<attr name="sobotSrlTextUpdate" format="string" />
<attr name="sobotSrlTextSecondary" format="string" />
<attr name="sobotSrlTextRefreshing" format="string" />
<attr name="sobotSrlTextNothing" format="string" />
<attr name="sobotSrlClassicsSpinnerStyle" format="enum">
<enum name="Translate" value="0" /><!--平行移动-->
<enum name="Scale" value="1" /><!--拉伸形变-->
<enum name="FixedBehind" value="2" /><!--固定在背后-->
</attr>
<attr name="sobot_layout_srlSpinnerStyle" format="enum">
<enum name="Translate" value="0" /><!--平行移动-->
<enum name="Scale" value="1" /><!--拉伸形变-->
<enum name="FixedBehind" value="2" /><!--固定在背后-->
<enum name="FixedFront" value="3" /><!--固定在前面-->
<enum name="MatchLayout" value="4" /><!--填满布局-->
</attr>
<declare-styleable name="SobotRefreshLayout">
<attr name="android:clipChildren" />
<attr name="android:clipToPadding" />
<attr name="sobotSrlPrimaryColor" />
<attr name="sobotSrlAccentColor" />
<attr name="srlReboundDuration" format="integer" />
<attr name="srlHeaderHeight" format="dimension" />
<attr name="srlFooterHeight" format="dimension" />
<attr name="srlHeaderInsetStart" format="dimension" />
<attr name="srlFooterInsetStart" format="dimension" />
<attr name="srlDragRate" format="float" />
<attr name="srlHeaderMaxDragRate" format="float" />
<attr name="srlFooterMaxDragRate" format="float" />
<attr name="srlHeaderTriggerRate" format="float" />
<attr name="srlFooterTriggerRate" format="float" />
<attr name="srlEnableRefresh" format="boolean" />
<attr name="srlEnableLoadMore" format="boolean" />
<attr name="srlEnableHeaderTranslationContent" format="boolean" />
<attr name="srlEnableFooterTranslationContent" format="boolean" />
<attr name="srlHeaderTranslationViewId" format="reference" />
<attr name="srlFooterTranslationViewId" format="reference" />
<attr name="srlEnablePreviewInEditMode" format="boolean" />
<attr name="srlEnableAutoLoadMore" format="boolean" />
<attr name="srlEnableOverScrollBounce" format="boolean" />
<attr name="srlEnablePureScrollMode" format="boolean" />
<attr name="srlEnableNestedScrolling" format="boolean" />
<attr name="srlEnableScrollContentWhenLoaded" format="boolean" />
<attr name="srlEnableScrollContentWhenRefreshed" format="boolean" />
<attr name="srlEnableLoadMoreWhenContentNotFull" format="boolean" />
<attr name="srlEnableFooterFollowWhenLoadFinished" format="boolean" />
<attr name="srlEnableFooterFollowWhenNoMoreData" format="boolean" />
<attr name="srlEnableClipHeaderWhenFixedBehind" format="boolean" />
<attr name="srlEnableClipFooterWhenFixedBehind" format="boolean" />
<attr name="srlEnableOverScrollDrag" format="boolean" />
<attr name="srlDisableContentWhenRefresh" format="boolean" />
<attr name="srlDisableContentWhenLoading" format="boolean" />
<attr name="srlFixedHeaderViewId" format="reference" />
<attr name="srlFixedFooterViewId" format="reference" />
</declare-styleable>
<declare-styleable name="SobotRefreshLayout_Layout">
<attr name="sobot_layout_srlSpinnerStyle" />
<attr name="layout_srlBackgroundColor" format="color" />
</declare-styleable>
<style name="SobotRefreshStyle">
<item name="sobotSrlPrimaryColor">@android:color/holo_blue_dark</item>
<item name="sobotSrlAccentColor">@android:color/white</item>
<item name="srlReboundDuration">300</item>
<item name="srlHeaderHeight">100dp</item>
<item name="srlFooterHeight">60dp</item>
<item name="srlHeaderInsetStart">0dp</item>
<item name="srlFooterInsetStart">0dp</item>
<item name="srlDragRate">0.5</item>
<item name="srlHeaderMaxDragRate">2.5</item>
<item name="srlFooterMaxDragRate">2.5</item>
<item name="srlHeaderTriggerRate">1</item>
<item name="srlFooterTriggerRate">1</item>
<!--<item name="srlEnableRefresh">true</item>-->
<!--<item name="srlEnableLoadMore">true</item>-->
<!--<item name="srlEnableHeaderTranslationContent">true</item>-->
<!--<item name="srlEnableFooterTranslationContent">true</item>-->
<!--<item name="srlHeaderTranslationViewId">-1</item>-->
<!--<item name="srlFooterTranslationViewId">-1</item>-->
<item name="srlEnablePreviewInEditMode">true</item>
<item name="srlEnableAutoLoadMore">true</item>
<item name="srlEnableOverScrollDrag">false</item>
<item name="srlEnableOverScrollBounce">true</item>
<item name="srlEnablePureScrollMode">false</item>
<item name="srlEnableNestedScrolling">true</item>
<item name="srlEnableScrollContentWhenLoaded">true</item>
<item name="srlEnableScrollContentWhenRefreshed">true</item>
<item name="srlEnableLoadMoreWhenContentNotFull">true</item>
<!--<item name="srlEnableFooterFollowWhenLoadFinished" format="boolean"/>-->
<item name="srlEnableFooterFollowWhenNoMoreData">false</item>
<item name="srlEnableClipHeaderWhenFixedBehind">true</item>
<item name="srlEnableClipFooterWhenFixedBehind">true</item>
<item name="srlDisableContentWhenRefresh">false</item>
<item name="srlDisableContentWhenLoading">false</item>
<!--<item name="srlFixedHeaderViewId" format="reference"/>-->
<!--<item name="srlFixedFooterViewId" format="reference"/>-->
</style>
<declare-styleable name="SobotClassicsFooter">
<attr name="sobotSrlClassicsSpinnerStyle" />
<attr name="sobotSrlPrimaryColor" />
<attr name="sobotSrlAccentColor" />
<attr name="sobotSrlFinishDuration" />
<attr name="sobotSrlTextSizeTitle" />
<attr name="sobotSrlColorArrow" />
<attr name="sobotSrlDrawableArrow" />
<attr name="sobotSrlDrawableProgress" />
<attr name="sobotSrlColorProgress" />
<attr name="sobotSrlDrawableMarginRight" />
<attr name="sobotSrlDrawableSize" />
<attr name="sobotSrlDrawableArrowSize" />
<attr name="sobotSrlDrawableProgressSize" />
<attr name="sobotSrlTextPulling" />
<attr name="sobotSrlTextRelease" />
<attr name="sobotSrlTextLoading" />
<attr name="sobotSrlTextRefreshing" />
<attr name="sobotSrlTextFinish" />
<attr name="sobotSrlTextFailed" />
<attr name="sobotSrlTextNothing" />
</declare-styleable>
<declare-styleable name="SobotClassicsHeader">
<attr name="sobotSrlClassicsSpinnerStyle" />
<attr name="sobotSrlPrimaryColor" />
<attr name="sobotSrlAccentColor" />
<attr name="sobotSrlFinishDuration" />
<attr name="sobotSrlDrawableArrow" />
<attr name="sobotSrlColorArrow" />
<attr name="sobotSrlColorProgress" />
<attr name="sobotSrlDrawableProgress" />
<attr name="sobotSrlDrawableMarginRight" />
<attr name="sobotSrlDrawableSize" />
<attr name="sobotSrlDrawableArrowSize" />
<attr name="sobotSrlDrawableProgressSize" />
<attr name="sobotSrlTextSizeTitle" />
<attr name="sobotSrlTextSizeTime" />
<attr name="sobotSrlTextTimeMarginTop" format="dimension" />
<attr name="sobotSrlEnableLastTime" format="boolean" />
<attr name="sobotSrlTextPulling" />
<attr name="sobotSrlTextLoading" />
<attr name="sobotSrlTextRelease" />
<attr name="sobotSrlTextFinish" />
<attr name="sobotSrlTextFailed" />
<attr name="sobotSrlTextUpdate" />
<attr name="sobotSrlTextSecondary" />
<attr name="sobotSrlTextRefreshing" />
</declare-styleable>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--下拉刷新 背景颜色-->
<color name="sobot_refresh_bg_color">#FFFFFF</color>
<!--下拉刷新 标题文字颜色-->
<color name="sobot_refresh_text_color">#ACB5C4</color>
<!--下拉刷新 更新时间文字颜色-->
<color name="sobot_refresh_text2_color">#ACB5C4</color>
<!--下拉刷新 箭头控件颜色 -->
<color name="sobot_refresh_arrow_color">#ACB5C4</color>
<!--下拉刷新 加载中 旋转控件颜色-->
<color name="sobot_refresh_progress_color">#ACB5C4</color>
<!--下拉刷新 头部 箭头控件颜色 -->
<color name="sobot_refresh_header_arrow_color">@color/sobot_refresh_arrow_color</color>
<!--下拉刷新 头部 加载中 旋转控件颜色-->
<color name="sobot_refresh_header_progress_color">@color/sobot_refresh_progress_color</color>
<!--下拉刷新 尾部 箭头控件颜色 -->
<color name="sobot_refresh_footer_arrow_color">@color/sobot_refresh_arrow_color</color>
<!--下拉刷新 尾部 加载中 旋转控件颜色-->
<color name="sobot_refresh_footer_progress_color">@color/sobot_refresh_progress_color</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="sobot_srl_tag" type="id" />
</resources>
\ No newline at end of file
<resources>
<string name="sobot_srl_content_empty">SmartRefreshLayout中没有找到内容视图。您是否忘记在xml布局文件中添加?</string>
<string name="sobot_srl_header_pulling">下拉可以刷新</string>
<string name="sobot_srl_header_refreshing">正在刷新…</string>
<string name="sobot_srl_header_loading">等待底部加载完成…</string>
<string name="sobot_srl_header_release">释放立即刷新</string>
<string name="sobot_srl_header_finish">刷新完成</string>
<string name="sobot_srl_header_failed">刷新失败</string>
<string name="sobot_srl_header_update">上次更新 M-d HH:mm</string>
<string name="sobot_srl_header_secondary">释放进入二楼</string>
<string name="sobot_srl_footer_pulling">上拉加载更多</string>
<string name="sobot_srl_footer_release">释放立即加载</string>
<string name="sobot_srl_footer_loading">正在加载…</string>
<string name="sobot_srl_footer_refreshing">等待头部刷新完成…</string>
<string name="sobot_srl_footer_finish">加载完成</string>
<string name="sobot_srl_footer_failed">加载失败</string>
<string name="sobot_srl_footer_nothing">没有更多数据了</string>
</resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment