extends Dialog implements View.OnClickListener {
/**
* 对话框 根视图
*/
protected View rootView;
/**
* dialog width scale(宽度比例)
*/
protected float scaleWidth = 1;
/**
* dialog height scale(高度比例)
*/
protected float scaleHeight;
/**
* max height(最大高度)
*/
protected float maxHeight;
/**
* (DisplayMetrics)设备密度
*/
protected DisplayMetrics displayMetrics;
protected Context context;
protected int gravity;
protected int animType;
/**
* Dialog的标记
*/
public Object tag;
public OnDialogClickListener onDialogClickListener;
/**
* Creates a dialog window that uses the default dialog theme.
*
* The supplied {@code context} is used to obtain the window manager and
* base theme used to present the dialog.
*
* @param context the context in which the dialog should run
* @see android.R.styleable#Theme_dialogTheme
*/
public BaseDialog(Context context) {
super(context);
this.context = context;
initBaseDialogTheme();
initDialog();
}
public BaseDialog(Context context, int theme) {
super(context, theme);
this.context = context;
initBaseDialogTheme();
initDialog();
}
protected void initDialog() {
displayMetrics = getContext().getResources().getDisplayMetrics();
maxHeight = displayMetrics.heightPixels - StatusBarUtils.getHeight(context);
rootView = LayoutInflater.from(getContext()).inflate(getLayoutRes(), null);
tag = getClass().getSimpleName();
setContentView(rootView);
setCanceledOnTouchOutside(false);
OnClickHelper.getInstance().onBindClickListener(rootView, this);
onCreateData();
}
/**
* 配置 对话框的 布局文件
*/
@LayoutRes
public abstract int getLayoutRes();
/**
* 在 这里 进行 findView 设置点击事件
*/
public abstract void onCreateData();
public D tag(@NonNull Object tag) {
if (tag != null) {
this.tag = tag;
}
return (D) this;
}
public Object tag() {
return tag;
}
/**
* 设置监听器
*/
public D setOnDialogClickListener(OnDialogClickListener onDialogClickListener) {
this.onDialogClickListener = onDialogClickListener;
return (D) this;
}
/**
* 销毁一切资源
*/
public void destody() {
super.dismiss();
context = null;
rootView.destroyDrawingCache();
rootView = null;
displayMetrics = null;
onDialogClickListener = null;
}
/**
* 设置 对话框 高度比 (0, 1]
*/
public D setScaleHeight(float scaleHeight) {
this.scaleHeight = scaleHeight;
return (D) this;
}
/**
* 设置 对话框 宽度比 (0, 1]
*/
public D setScaleWidth(float scaleWidth) {
this.scaleWidth = scaleWidth;
return (D) this;
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
int width;
if (scaleWidth == 0) {
width = ViewGroup.LayoutParams.WRAP_CONTENT;
} else {
width = (int) (displayMetrics.widthPixels * scaleWidth);
}
int height;
if (scaleHeight == 0) {
height = ViewGroup.LayoutParams.WRAP_CONTENT;
} else if (scaleHeight == 1) {
height = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
height = (int) (maxHeight * scaleHeight);
}
rootView.setLayoutParams(new FrameLayout.LayoutParams(width, height));
}
/**
* set dialog theme(设置对话框主题)
*/
protected void initBaseDialogTheme() {
animType = AnimType.CENTER_NORMAL;
/*android:windowNoTitle*/
requestWindowFeature(Window.FEATURE_NO_TITLE);
Window window = getWindow();
/* android:windowBackground*/
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
/*android:backgroundDimEnabled默认是true的*/
//window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
public T findView(@IdRes int id) {
return (T) rootView.findViewById(id);
}
public D setText(@IdRes int id, @NonNull String text) {
View view = findView(id);
setText(view, text);
return (D) this;
}
public D setText(@NonNull View view, @NonNull String text) {
if ((view == null) || (text == null)) {
return (D) this;
}
if (view instanceof TextView) {
((TextView) view).setText(text);
}
if (view instanceof EditText) {
((EditText) view).setSelection(text.length());
}
return (D) this;
}
public D setOnCilckListener(@IdRes int id) {
View view = findView(id);
if (view == null) {
return (D) this;
}
view.setOnClickListener(this);
return (D) this;
}
public D setOnCilckListener(@IdRes int... id) {
for (int i = 0; (id != null) && (i < id.length); i++) {
setOnCilckListener(id[i]);
}
return (D) this;
}
/**
* 处理按钮点击事件 并绑定 onDialogClickListener
*/
@Override
public void onClick(View v) {
onClick(v, v.getId());
}
/**
* 处理按钮点击事件 并绑定 onDialogClickListener
*/
public abstract void onClick(View v, int id);
public void onDialogClickListener(@ClickPosition String clickPosition) {
if (onDialogClickListener != null) {
onDialogClickListener.onDialogClick((D) this, clickPosition);
}
}
/**
* 设置 物理按键的 监听事件
*
* @param simpleOnKeyListener
*/
public D setOnKeyListener(SimpleOnKeyListener simpleOnKeyListener) {
super.setOnKeyListener(simpleOnKeyListener);
return (D) this;
}
/**
* 设置对话框的显示位置
*/
public D setGravity(int gravity) {
this.gravity = gravity;
Window dialogWindow = getWindow();
dialogWindow.setGravity(gravity);
return (D) this;
}
/**
* 设置对话框的显示位置,以及Y轴的向下偏移量(单位 dp)
*/
public D setGravity(int gravity, int yDP) {
this.gravity = gravity;
Window dialogWindow = getWindow();
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
lp.x = 0;
lp.y = (int) (yDP * context.getResources().getDisplayMetrics().density);
dialogWindow.setGravity(gravity);
return (D) this;
}
public D setAnimType(@AnimType int animType) {
this.animType = animType;
/*如果根据 AnimType 的类型,强制选择Dialog出现的位置*/
if (AnimType.BOTTOM_2_TOP == animType) {
setGravity(Gravity.BOTTOM);
} else if (AnimType.TOP_2_BOTTOM == animType) {
setGravity(Gravity.TOP);
} else if (AnimType.CENTER_SCALE == animType) {
setGravity(Gravity.CENTER);
} else if (AnimType.CENTER_NORMAL == animType) {
setGravity(Gravity.CENTER);
}
return (D) this;
}
private void attachView(View view) {
int[] position = new int[2];
view.getLocationInWindow(position);
Window window = getWindow();
WindowManager.LayoutParams params = window.getAttributes();
window.setGravity(Gravity.LEFT | Gravity.TOP);
params.x = position[0] + getViewWidth(view) / 2;
params.y = position[1] + getViewHeight(view) / 2;
window.setAttributes(params);
}
/**
* 测量这个view
* 最后通过getMeasuredWidth()获取宽度和高度.
*
* @param view 要测量的view
* @return 测量过的view
*/
private void measureView(View view) {
ViewGroup.LayoutParams p = view.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY);
} else {
childHeightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
}
view.measure(childWidthSpec, childHeightSpec);
}
/**
* 获得这个View的宽度
* 测量这个view,最后通过getMeasuredWidth()获取宽度.
*
* @param view 要测量的view
* @return 测量过的view的宽度
*/
protected int getViewWidth(View view) {
measureView(view);
return view.getMeasuredWidth();
}
/**
* 获得这个View的高度
* 测量这个view,最后通过getMeasuredHeight()获取高度.
*
* @param view 要测量的view
* @return 测量过的view的高度
*/
protected int getViewHeight(View view) {
measureView(view);
return view.getMeasuredHeight();
}
}
================================================
FILE: dialog/src/main/java/org/alex/dialog/callback/OnDialogClickListener.java
================================================
package org.alex.dialog.callback;
import android.app.Dialog;
import org.alex.dialog.annotation.ClickPosition;
/**
* 作者:Alex
* 时间:2016年09月03日 21:29
* 简述:
*/
@SuppressWarnings("all")
public interface OnDialogClickListener
{
public void onDialogClick(D dialog, @ClickPosition String clickPosition);
}
================================================
FILE: dialog/src/main/java/org/alex/dialog/callback/OnWait2DismissListener.java
================================================
package org.alex.dialog.callback;
import android.app.Dialog;
/**
* 作者:Alex
* 时间:2016年09月03日 21:29
* 简述:
*/
@SuppressWarnings("all")
public interface OnWait2DismissListener {
/**
* @param second 为零的时候 对话框结束
*/
public void onDismiss(D dialog, int second);
}
================================================
FILE: dialog/src/main/java/org/alex/dialog/callback/SimpleOnKeyListener.java
================================================
package org.alex.dialog.callback;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.DialogInterface.OnKeyListener;
import android.view.KeyEvent;
import org.alex.dialog.annotation.OnKeyType;
/**
* 作者:Alex
* 时间:2016年09月03日 21:29
* 简述:
*/
@SuppressWarnings("all")
public class SimpleOnKeyListener implements OnKeyListener {
private Activity activity;
private int onKeyType;
public SimpleOnKeyListener(Activity activity, @OnKeyType int onKeyType) {
this.onKeyType = onKeyType;
this.activity = activity;
}
public static SimpleOnKeyListener dismissNotKillActivity(Activity activity) {
return new SimpleOnKeyListener(activity, OnKeyType.DISMISS_NOT_KILL_ACTIVITY);
}
public static SimpleOnKeyListener dismissKillActivity(Activity activity) {
return new SimpleOnKeyListener(activity, OnKeyType.DISMISS_KILL_ACTIVITY);
}
public static SimpleOnKeyListener notDismissNotKillActivity(Activity activity) {
return new SimpleOnKeyListener(activity, OnKeyType.NOT_DISMISS_NOT_KILL_ACTIVITY);
}
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (OnKeyType.DISMISS_KILL_ACTIVITY == onKeyType) {
dialog.dismiss();
activity.finish();
} else if (OnKeyType.DISMISS_NOT_KILL_ACTIVITY == onKeyType) {
dialog.dismiss();
} else if (OnKeyType.NOT_DISMISS_NOT_KILL_ACTIVITY == onKeyType) {
}
}
return true;
}
}
================================================
FILE: dialog/src/main/java/org/alex/dialog/helper/OnClickHelper.java
================================================
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.alex.dialog.helper;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
public class OnClickHelper {
private static OnClickHelper OnClickHelper;
private OnClickHelper() {
}
public static OnClickHelper getInstance() {
if(OnClickHelper == null) {
Class var0 = OnClickHelper.class;
synchronized(OnClickHelper.class) {
OnClickHelper = OnClickHelper == null?new OnClickHelper():OnClickHelper;
}
}
return OnClickHelper;
}
public void onBindClickListener(@NonNull View contentView, @NonNull OnClickListener onClickListener) {
this.setOnClickListener(contentView, onClickListener);
}
private void setOnClickListener(@NonNull View view, @NonNull OnClickListener onClickListener) {
if(view == null) {
//LogUtil.w("控件为空");
} else {
view.setOnClickListener(onClickListener);
if(view instanceof ViewGroup) {
this.setOnClickListener((ViewGroup)view, onClickListener);
}
}
}
private void setOnClickListener(@NonNull ViewGroup viewGroup, @NonNull OnClickListener onClickListener) {
try {
viewGroup.setOnClickListener(onClickListener);
for(int e = 0; e < viewGroup.getChildCount(); ++e) {
View v = viewGroup.getChildAt(e);
if(v instanceof ViewGroup) {
this.setOnClickListener((ViewGroup)v, onClickListener);
} else if(v != null && v instanceof View) {
v.setOnClickListener(onClickListener);
}
}
} catch (Exception var5) {
//LogUtil.e(var5);
}
}
}
================================================
FILE: dialog/src/main/java/org/alex/dialog/util/StatusBarUtils.java
================================================
package org.alex.dialog.util;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
/**
* 作者:Alex
* 时间:2016年09月03日 21:29
* 简述:
*/
@SuppressWarnings("all")
public class StatusBarUtils {
public static int getHeight(Context context) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
Log.d(StatusBarUtils.class.getSimpleName(), "statusBarHeight--->" + statusBarHeight);
if (isFlymeOs4x()) {
return 2 * statusBarHeight;
}
return statusBarHeight;
}
public static boolean isFlymeOs4x() {
String sysVersion = android.os.Build.VERSION.RELEASE;
if ("4.4.4".equals(sysVersion)) {
String sysIncrement = android.os.Build.VERSION.INCREMENTAL;
String displayId = android.os.Build.DISPLAY;
if (!TextUtils.isEmpty(sysIncrement)) {
return sysIncrement.contains("Flyme_OS_4");
} else {
return displayId.contains("Flyme OS 4");
}
}
return false;
}
}
================================================
FILE: dialog/src/main/res/values/alex_theme_no_title_app.xml
================================================
================================================
FILE: dialog/src/main/res/values/strings.xml
================================================
Dialog
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app', ':dialog', ':sweeploadingview'
================================================
FILE: sweeploadingview/.gitignore
================================================
/build
================================================
FILE: sweeploadingview/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
Map libs = rootProject.ext.libs
compile libs.appcompat_v7
}
================================================
FILE: sweeploadingview/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in E:\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# 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 *;
#}
================================================
FILE: sweeploadingview/src/main/AndroidManifest.xml
================================================
================================================
FILE: sweeploadingview/src/main/java/org/alex/callback/SimpleAnimatorListener.java
================================================
package org.alex.callback;
import android.animation.Animator;
/**
* 作者:Alex
* 时间:2016年09月03日 10:06
* 简述:
*/
@SuppressWarnings("all")
public abstract class SimpleAnimatorListener implements Animator.AnimatorListener {
@Override
public void onAnimationStart(Animator animation){
}
@Override
public void onAnimationEnd(Animator animation){
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
}
================================================
FILE: sweeploadingview/src/main/java/org/alex/sweeploadingview/SweepLoadingView.java
================================================
package org.alex.sweeploadingview;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.RectF;
import android.support.annotation.ColorInt;
import android.support.annotation.FloatRange;
import android.util.AttributeSet;
import android.util.Property;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import org.alex.callback.SimpleAnimatorListener;
/**
* 作者:Alex
*
* 时间:2016/8/24 16:31
*
* 博客地址:http://www.jianshu.com/users/c3c4ea133871/subscriptions
*
*
*/
@SuppressWarnings("all")
public class SweepLoadingView extends View {
private RectF rectF;
private Paint paint;
private int circleColor;
private int circleWidth;
private int strokeCap;
/**
* 扫描一周 需要的时间
*/
private int swipeDuration;
/**
* 起点的角度
*/
private float startAngle;
/**
* 扫描的角度
*/
private float sweepAngle;
/**
* 终点 和 起点的间隔 角度
*/
private float gapAngle;
/**
* 起始点的偏移量
*/
private float startAngleOffset;
private ObjectAnimator sweepObjectAnimator;
private ObjectAnimator startAngleObjectAnimator;
/**
* 扫描一周
*/
private boolean isSweepRepeat;
public SweepLoadingView(Context context) {
super(context);
initView(null);
}
public SweepLoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(attrs);
}
private void initView(AttributeSet attrs) {
startAngleOffset = 0;
isSweepRepeat = false;
Context context = getContext();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SweepLoadingView);
circleWidth = typedArray.getDimensionPixelSize(R.styleable.SweepLoadingView_slv_circleWidth, 0);
circleColor = typedArray.getColor(R.styleable.SweepLoadingView_slv_circleColor, Color.parseColor("#FF5722"));
strokeCap = typedArray.getInt(R.styleable.SweepLoadingView_slv_strokeCap, 0);
swipeDuration = typedArray.getInt(R.styleable.SweepLoadingView_slv_duration, 1200);
gapAngle = typedArray.getFloat(R.styleable.SweepLoadingView_slv_gapAngle, 45F);
rectF = new RectF();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeCap((strokeCap == 0) ? Paint.Cap.ROUND : Paint.Cap.SQUARE);
paint.setStrokeWidth(circleWidth);
paint.setColor(circleColor);
initObjectAnimation();
}
private void initObjectAnimation() {
sweepObjectAnimator = ObjectAnimator.ofFloat(this, new AngleProperty(Float.class, "sweepAngle"), 360F - gapAngle * 2);
sweepObjectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
sweepObjectAnimator.setDuration(swipeDuration);
sweepObjectAnimator.setRepeatCount(Integer.MAX_VALUE);
sweepObjectAnimator.setRepeatMode(ValueAnimator.RESTART);
sweepObjectAnimator.addListener(new SweepAngleAnimatorListener());
startAngleObjectAnimator = ObjectAnimator.ofFloat(this, new AngleProperty(Float.class, "startAngle"), 360F);
startAngleObjectAnimator.setInterpolator(new LinearInterpolator());
startAngleObjectAnimator.setDuration(1500);
startAngleObjectAnimator.setRepeatCount(Integer.MAX_VALUE);
startAngleObjectAnimator.setRepeatMode(ValueAnimator.RESTART);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int min = Math.min(w, h);
int dWidth = circleWidth / 2;
rectF.left = dWidth;
rectF.right = min - dWidth;
rectF.top = dWidth;
rectF.bottom = min - dWidth;
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
float startAngle = this.startAngle - startAngleOffset;
float sweepAngle = this.sweepAngle;
if (isSweepRepeat) {
startAngle += sweepAngle;
sweepAngle = 360 - sweepAngle - gapAngle;
} else {
sweepAngle += gapAngle;
}
canvas.drawArc(rectF, startAngle, sweepAngle, false, paint);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
startSweepAnimation();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopSweepAnimation();
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (visibility == VISIBLE) {
startSweepAnimation();
} else {
stopSweepAnimation();
}
}
private void startSweepAnimation() {
if (!sweepObjectAnimator.isStarted()) {
//LogUtil.e("开始");
sweepObjectAnimator.start();
}
if (!startAngleObjectAnimator.isStarted()) {
startAngleObjectAnimator.start();
}
}
private void stopSweepAnimation() {
//LogUtil.e("停止");
sweepObjectAnimator.cancel();
startAngleObjectAnimator.cancel();
}
private final class SweepAngleAnimatorListener extends SimpleAnimatorListener {
@Override
public void onAnimationRepeat(Animator animation) {
handleSweepRepeat();
}
}
/**
* 处理扫描一周的 情况
*/
private void handleSweepRepeat() {
if (isSweepRepeat) {
startAngleOffset = (startAngleOffset + gapAngle * 2) % 360;
}
isSweepRepeat = !isSweepRepeat;
}
private final class AngleProperty extends Property {
private String name;
public AngleProperty(Class type, String name) {
super(type, name);
this.name = name;
}
@Override
public Float get(SweepLoadingView object) {
if ("sweepAngle".equals(name)) {
return object.getSweepAngle();
} else {
return object.getStartAngle();
}
}
@Override
public void set(SweepLoadingView object, Float value) {
if ("sweepAngle".equals(name)) {
object.setSweepAngle(value);
} else {
object.setStartAngle(value);
}
}
}
private float getSweepAngle() {
return sweepAngle;
}
private void setSweepAngle(float sweepAngle) {
this.sweepAngle = sweepAngle;
invalidate();
}
private float getStartAngle() {
return startAngle;
}
private void setStartAngle(float startAngle) {
this.startAngle = startAngle;
invalidate();
}
private void setCircleColor(@ColorInt int color) {
this.circleColor = color;
invalidate();
}
private void setCircleWidth(int width) {
if (width < 1) {
width = 4;
}
width = (int) dp2Px(width);
this.circleWidth = width;
invalidate();
}
private void setGapAngle(@FloatRange(from = 5F, to = 60F) float angle) {
if ((angle < 5) || (angle > 60)) {
angle = 45F;
}
this.gapAngle = angle;
invalidate();
}
/**
* 数据转换: dp---->px
*/
private float dp2Px(float dp) {
return dp * getContext().getResources().getDisplayMetrics().density;
}
}
================================================
FILE: sweeploadingview/src/main/res/values/alex_sweep_loading_view_attrs.xml
================================================
================================================
FILE: sweeploadingview/src/main/res/values/strings.xml
================================================
SweepLoadingView