diff --git a/src/main/java/xyz/wbsite/dbtool/javafx/manger/ProjectManager.java b/src/main/java/xyz/wbsite/dbtool/javafx/manger/ProjectManager.java index 2ed19428..1190a0a3 100644 --- a/src/main/java/xyz/wbsite/dbtool/javafx/manger/ProjectManager.java +++ b/src/main/java/xyz/wbsite/dbtool/javafx/manger/ProjectManager.java @@ -4,6 +4,7 @@ import com.sun.org.apache.xpath.internal.operations.And; import org.springframework.boot.system.ApplicationHome; import xyz.wbsite.dbtool.javafx.enumeration.DataBase; import xyz.wbsite.dbtool.javafx.enumeration.FieldType; +import xyz.wbsite.dbtool.javafx.manger.callable.AndroidCallable; import xyz.wbsite.dbtool.javafx.manger.callable.SDKCallable; import xyz.wbsite.dbtool.javafx.manger.callable.SpringBootCallable; import xyz.wbsite.dbtool.javafx.po.*; @@ -306,8 +307,21 @@ public class ProjectManager { } } - public void generateAndroid(final String path, AndroidOption option){ + public void generateAndroid(final String path, AndroidOption option) { + AndroidCallable androidCallable = new AndroidCallable(path, option); + Future submit = service.submit(androidCallable); + try { + Boolean b = (Boolean) submit.get(); + if (!b) { + Dialog.showError("请确认目录结构是否存在或正确!"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + Dialog.showSuccess("Android生成完成."); } public void invalidate() { diff --git a/src/main/java/xyz/wbsite/dbtool/javafx/manger/callable/AndroidCallable.java b/src/main/java/xyz/wbsite/dbtool/javafx/manger/callable/AndroidCallable.java new file mode 100644 index 00000000..a352824c --- /dev/null +++ b/src/main/java/xyz/wbsite/dbtool/javafx/manger/callable/AndroidCallable.java @@ -0,0 +1,199 @@ +package xyz.wbsite.dbtool.javafx.manger.callable; + +import xyz.wbsite.dbtool.javafx.manger.FreeMarkerManager; +import xyz.wbsite.dbtool.javafx.manger.ManagerFactory; +import xyz.wbsite.dbtool.javafx.manger.ProjectManager; +import xyz.wbsite.dbtool.javafx.po.AbstractDBmapper; +import xyz.wbsite.dbtool.javafx.po.AndroidOption; +import xyz.wbsite.dbtool.javafx.tool.Tool; + +import javax.validation.constraints.NotNull; +import java.io.File; +import java.util.HashMap; +import java.util.concurrent.Callable; +import java.util.regex.Matcher; + +import static xyz.wbsite.dbtool.javafx.tool.Tool.clear; + +public class AndroidCallable implements Callable { + + private Tool tool = new Tool(); + private String path; + private AndroidOption option; + + private FreeMarkerManager freeMarkerManager; + + public AndroidCallable(@NotNull String path, AndroidOption option) { + this.path = path; + this.option = option; + this.freeMarkerManager = ManagerFactory.getFreeMarkerManager(); + } + + private AbstractDBmapper dBmapper; + + public Boolean call() throws Exception { + File project = new File(path, option.projectName); + if (!project.exists()) { + project.mkdirs(); + } else { + clear(project); + } + + dBmapper = ProjectManager.dBmapper; + + // 目录生成 + File app = new File(project, "app"); + app.mkdirs(); + File src = new File(app, "src"); + src.mkdirs(); + File main = new File(src, "main"); + main.mkdirs(); + File java = new File(main, "java"); + java.mkdirs(); + File res = new File(src, "res"); + res.mkdirs(); + File domain = new File(java.getAbsolutePath() + File.separator + option.domain.replaceAll("\\.", Matcher.quoteReplacement(File.separator))); + domain.mkdirs(); + File base = new File(domain, "base"); + base.mkdirs(); + File activity = new File(base, "activity"); + activity.mkdirs(); + File service = new File(base, "service"); + service.mkdirs(); + File util = new File(base, "util"); + util.mkdirs(); + File fragment = new File(domain, "fragment"); + fragment.mkdirs(); + + { + Tool.outputResource("Android/.gitignore", new File(project, ".gitignore")); + Tool.outputResource("Android/build.gradles", new File(project, "build.gradle")); + Tool.outputResource("Android/settings.gradles", new File(project, "settings.gradle")); + } + + { + HashMap ctx = new HashMap(); + ctx.put("package", option.packages); + freeMarkerManager.outputTemp(new File(app, "build.gradle"), "Android/app/build.gradles", ctx); + Tool.outputResource("Android/app/proguard-rules.pro", new File(app, "build.gradle")); + } + + { + HashMap ctx = new HashMap(); + ctx.put("package", option.packages); + ctx.put("domain", option.domain); + freeMarkerManager.outputTemp(new File(main, "AndroidManifest.xml"), "Android/app/src/main/AndroidManifest.xml", ctx); + } + + + { + HashMap ctx = new HashMap(); + ctx.put("package", option.packages); + ctx.put("domain", option.domain); + freeMarkerManager.outputTemp(new File(base, "BaseActivity.java"), "Android/app/src/main/java/base/BaseActivity.java", ctx); + freeMarkerManager.outputTemp(new File(base, "BaseFragment.java"), "Android/app/src/main/java/base/BaseFragment.java", ctx); + freeMarkerManager.outputTemp(new File(base, "BaseSPAActivity.java"), "Android/app/src/main/java/base/BaseSPAActivity.java", ctx); + freeMarkerManager.outputTemp(new File(base, "BaseSPAFragment.java"), "Android/app/src/main/java/base/BaseSPAFragment.java", ctx); + freeMarkerManager.outputTemp(new File(base, "BaseSPATakePhotoFragment.java"), "Android/app/src/main/java/base/BaseSPATakePhotoFragment.java", ctx); + freeMarkerManager.outputTemp(new File(base, "IActivityResult.java"), "Android/app/src/main/java/base/IActivityResult.java", ctx); + + freeMarkerManager.outputTemp(new File(activity, "FilePickerActivity.java"), "Android/app/src/main/java/base/activity/FilePickerActivity.java", ctx); + freeMarkerManager.outputTemp(new File(activity, "QRcodeActivity.java"), "Android/app/src/main/java/base/activity/QRcodeActivity.java", ctx); + + + freeMarkerManager.outputTemp(new File(service, "RestartService.java"), "Android/app/src/main/java/base/service/RestartService.java", ctx); + + freeMarkerManager.outputTemp(new File(util, "AnimationUtil.java"), "Android/app/src/main/java/base/util/AnimationUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "ApkUtil.java"), "Android/app/src/main/java/base/util/ApkUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "Base64Util.java"), "Android/app/src/main/java/base/util/Base64Util.java", ctx); + freeMarkerManager.outputTemp(new File(util, "BitmapUtil.java"), "Android/app/src/main/java/base/util/BitmapUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "BluetoothUtil.java"), "Android/app/src/main/java/base/util/BluetoothUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "Consant.java"), "Android/app/src/main/java/base/util/Consant.java", ctx); + freeMarkerManager.outputTemp(new File(util, "DataBaseUtil.java"), "Android/app/src/main/java/base/util/DataBaseUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "DensityUtil.java"), "Android/app/src/main/java/base/util/DensityUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "DESUtil.java"), "Android/app/src/main/java/base/util/DESUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "DialogUtil.java"), "Android/app/src/main/java/base/util/DialogUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "FastBlur.java"), "Android/app/src/main/java/base/util/FastBlur.java", ctx); + freeMarkerManager.outputTemp(new File(util, "FileUtil.java"), "Android/app/src/main/java/base/util/FileUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "GPSUtil.java"), "Android/app/src/main/java/base/util/GPSUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "IdCardUtil.java"), "Android/app/src/main/java/base/util/IdCardUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "Logger.java"), "Android/app/src/main/java/base/util/Logger.java", ctx); + freeMarkerManager.outputTemp(new File(util, "NetUtil.java"), "Android/app/src/main/java/base/util/NetUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "RSAUtil.java"), "Android/app/src/main/java/base/util/RSAUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "SizeUtil.java"), "Android/app/src/main/java/base/util/SizeUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "SPUtil.java"), "Android/app/src/main/java/base/util/SPUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "StorageUtil.java"), "Android/app/src/main/java/base/util/StorageUtil.java", ctx); + freeMarkerManager.outputTemp(new File(util, "Tasker.java"), "Android/app/src/main/java/base/util/Tasker.java", ctx); + freeMarkerManager.outputTemp(new File(util, "Toaster.java"), "Android/app/src/main/java/base/util/Toaster.java", ctx); + } + + { + HashMap ctx = new HashMap(); + ctx.put("package", option.packages); + ctx.put("domain", option.domain); + freeMarkerManager.outputTemp(new File(domain, "WBUIApplication.java"), "Android/app/src/main/java/WBUIApplication.java", ctx); + freeMarkerManager.outputTemp(new File(domain, "WBUIMainActivity.java"), "Android/app/src/main/java/WBUIMainActivity.java", ctx); + } + + { + HashMap ctx = new HashMap(); + ctx.put("package", option.packages); + ctx.put("domain", option.domain); + freeMarkerManager.outputTemp(new File(fragment, "LoginFragment.java"), "Android/app/src/main/java/fragment/LoginFragment.java", ctx); + freeMarkerManager.outputTemp(new File(fragment, "MainFragment.java"), "Android/app/src/main/java/fragment/MainFragment.java", ctx); + } + + + System.out.println("finish"); + return true; + } + + + public class Method { + private String request; + private String stringMethod; + private String manager; + private String method; + private String target; + + public String getRequest() { + return request; + } + + public void setRequest(String request) { + this.request = request; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public String getStringMethod() { + return stringMethod; + } + + public void setStringMethod(String stringMethod) { + this.stringMethod = stringMethod; + } + + public String getManager() { + return manager; + } + + public void setManager(String manager) { + this.manager = manager; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + } +} diff --git a/src/main/java/xyz/wbsite/dbtool/javafx/tool/Dialog.java b/src/main/java/xyz/wbsite/dbtool/javafx/tool/Dialog.java index e5b19548..1bc1b1cf 100644 --- a/src/main/java/xyz/wbsite/dbtool/javafx/tool/Dialog.java +++ b/src/main/java/xyz/wbsite/dbtool/javafx/tool/Dialog.java @@ -558,6 +558,13 @@ public class Dialog { androidOption.packages = controller.getPackages().getText(); androidOption.domain = controller.getDomain().getText(); dBmanger.generateAndroid(file.getAbsolutePath(), androidOption); + Dialog.stopPopup(); + Platform.runLater(new Runnable() { + @Override + public void run() { + stage.close(); + } + }); } }); TextField path = controller.getPath(); diff --git a/src/main/resources/modules/Android/.gitignore b/src/main/resources/modules/Android/.gitignore new file mode 100644 index 00000000..6dfe0a65 --- /dev/null +++ b/src/main/resources/modules/Android/.gitignore @@ -0,0 +1,29 @@ +# Built application files +*.apk +*.ap_ + +# Files for the Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +#Log Files +*.log + +*.iml + diff --git a/src/main/resources/modules/Android/app/build.gradles b/src/main/resources/modules/Android/app/build.gradles new file mode 100644 index 00000000..ad876a13 --- /dev/null +++ b/src/main/resources/modules/Android/app/build.gradles @@ -0,0 +1,46 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + defaultConfig { + applicationId "${package}" + minSdkVersion 23 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:26.1.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + + //QMUI + implementation 'com.qmuiteam:qmui:1.1.3' + implementation "com.qmuiteam:arch:0.1.4" + //依赖注入框架 + implementation 'com.jakewharton:butterknife:8.8.1' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' + //拍照,相册 + implementation 'com.jph.takephoto:takephoto_library:4.0.3' + //图片查看 + implementation 'com.github.chrisbanes:PhotoView:1.2.6' + //权限申请框架 + implementation 'com.yanzhenjie:permission:2.0.0-rc12' + //二维码 + implementation 'com.google.zxing:core:3.3.2' + implementation'com.journeyapps:zxing-android-embedded:3.6.0' + //文件选择 + implementation 'com.leon:lfilepickerlibrary:1.7.0' + //Picker + implementation 'com.github.gzu-liyujiang.AndroidPicker:WheelPicker:1.5.6' +} diff --git a/src/main/resources/modules/Android/app/proguard-rules.pro b/src/main/resources/modules/Android/app/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/src/main/resources/modules/Android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# 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 diff --git a/src/main/resources/modules/Android/app/src/main/AndroidManifest.xml b/src/main/resources/modules/Android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..26da84ce --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/modules/Android/app/src/main/java/WBUIApplication.java b/src/main/resources/modules/Android/app/src/main/java/WBUIApplication.java new file mode 100644 index 00000000..1bf31ed9 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/WBUIApplication.java @@ -0,0 +1,201 @@ +package xyz.wbsite.webclient; + +import android.app.Activity; +import android.app.Application; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.location.Location; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.Gravity; +import android.widget.TextView; +import android.widget.Toast; + +import com.qmuiteam.qmui.util.QMUIDisplayHelper; +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import xyz.wbsite.wbui.base.utils.DataBaseUtil; + + +/** + * Application 入口。 + */ +public class WBUIApplication extends Application implements Thread.UncaughtExceptionHandler { + + private static WBUIApplication instance; + private WeakReference currentActivity; + + private DataBaseUtil db; + private Map map; + private Location mLocation; + + private Toast toast; + protected Handler handler = new Handler(); + + public Location getLocation() { + return mLocation; + } + + public Handler getHandler() { + return handler; + } + + public static WBUIApplication getInstance() { + return instance; + } + + public static Map getMap() { + return instance.map; + } + + public DataBaseUtil getDb() { + return db; + } + + @Override + public void onCreate() { + super.onCreate(); +// if (LeakCanary.isInAnalyzerProcess(this)) { +// return; +// } +// LeakCanary.install(this); + instance = this; + initExceptionHandler(); + initMap(); + initLife(); + } + + public void lozyInit() { + initDB(); + } + + private void initDB() { + db = new DataBaseUtil(this, new DataBaseUtil.Register() { + @Override + public List run() { + return new ArrayList<>(); + } + }); + } + + private void initMap() { + map = new HashMap(); + } + + private void initLife() { + registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + + } + + @Override + public void onActivityStarted(Activity activity) { + + } + + @Override + public void onActivityResumed(Activity activity) { + currentActivity = new WeakReference(activity); + } + + @Override + public void onActivityPaused(Activity activity) { + + } + + @Override + public void onActivityStopped(Activity activity) { + + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + + } + + @Override + public void onActivityDestroyed(Activity activity) { + + } + }); + } + + private void initExceptionHandler() { + // 程序的默认处理器 + Thread.setDefaultUncaughtExceptionHandler(this); + } + + // 判断是否安装插件 + public boolean hasInstallPlugin(String packageName) { + try { + PackageInfo info = getPackageManager().getPackageInfo(packageName, 0); + return (info != null); + } catch (PackageManager.NameNotFoundException e) { + } + return false; + } + + /** + * 展示Toast消息。 + * + * @param message 消息内容 + */ + public synchronized void showToast(final String message) { + if (toast == null) { + toast = new Toast(this); + toast.setGravity(Gravity.CENTER, 0, 0); + TextView textView = new TextView(this); + textView.setTextColor(Color.parseColor("#ffffff")); + int padding = QMUIDisplayHelper.dp2px(this, 20); + textView.setPadding(padding, padding, padding, padding); + textView.setBackgroundColor(Color.parseColor("#aa131313")); + toast.setView(textView); + } + ((TextView) toast.getView()).setText(message); + toast.show(); + } + + @Override + public void uncaughtException(Thread thread, Throwable throwable) { + Log.e("error--->", "error : ", throwable); + exit(); + } + + public void confirm(String message, QMUIDialogAction.ActionListener actionListener) { + if (currentActivity != null) { + Activity activity = currentActivity.get(); + QMUIDialog qmuiDialog = new QMUIDialog.MessageDialogBuilder(activity) + .setMessage(message) + .addAction("取消", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .addAction("确定", actionListener) + .create(com.qmuiteam.qmui.R.style.QMUI_Dialog); + qmuiDialog.show(); + } + } + + public void exit() { + confirm("确认退出?", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + android.os.Process.killProcess(android.os.Process.myPid()); + } + }); + } + +} diff --git a/src/main/resources/modules/Android/app/src/main/java/WBUIMainActivity.java b/src/main/resources/modules/Android/app/src/main/java/WBUIMainActivity.java new file mode 100644 index 00000000..00548275 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/WBUIMainActivity.java @@ -0,0 +1,70 @@ +package xyz.wbsite.webclient; + +import android.Manifest; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.widget.Toast; + +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; +import com.yanzhenjie.permission.Action; +import com.yanzhenjie.permission.AndPermission; + +import java.util.List; + +import xyz.wbsite.wbui.base.BaseSPAActivity; +import xyz.wbsite.wbui.base.Consant; +import xyz.wbsite.webclient.fragment.LoginFragment; + +public class WBUIMainActivity extends BaseSPAActivity { + private static final String KEY_FRAGMENT = "key_fragment"; + private static final int VALUE_FRAGMENT_HOME = 0; + private static final int VALUE_FRAGMENT_NOTCH_HELPER = 1; + + @Override + protected int getContextViewId() { + return R.id.main_id; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + AndPermission.with(this).runtime().permission(Manifest.permission.READ_EXTERNAL_STORAGE).onGranted(new Action>() { + @Override + public void onAction(List data) { + startFirstFragment(new LoginFragment()); + } + }).onDenied(new Action>() { + @Override + public void onAction(List data) { + startFirstFragment(new LoginFragment()); + } + }).start(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == Consant.REQUESTCODE_FROM_FRAGMENT) { + Toast.makeText(getApplicationContext(), "选中了个文件", Toast.LENGTH_SHORT).show(); + }else { + IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); + if (result != null) { + if (result.getContents() == null) { + Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show(); + } + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + } +} + diff --git a/src/main/resources/modules/Android/app/src/main/java/base/BaseActivity.java b/src/main/resources/modules/Android/app/src/main/java/base/BaseActivity.java new file mode 100644 index 00000000..41a9ebe2 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/BaseActivity.java @@ -0,0 +1,28 @@ +package xyz.wbsite.wbui.base; + +import android.app.Activity; +import android.content.Intent; + +import java.util.Hashtable; + +public abstract class BaseActivity extends Activity { + private Hashtable resultHashtable = new Hashtable<>(); + private int mRequestCode = 1; + + public void startForResult(Intent intent, IActivityResult activityResult) { + int requestCode = mRequestCode++; + resultHashtable.put(requestCode, activityResult); + startActivityForResult(intent, requestCode); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultHashtable.containsKey(requestCode)) { + IActivityResult i = resultHashtable.remove(requestCode); + if (i != null) { + i.onResult(resultCode, data); + } + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/BaseFragment.java b/src/main/resources/modules/Android/app/src/main/java/base/BaseFragment.java new file mode 100644 index 00000000..88d9da21 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/BaseFragment.java @@ -0,0 +1,37 @@ +package xyz.wbsite.wbui.base; + +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import butterknife.ButterKnife; +import butterknife.Unbinder; + +public abstract class BaseFragment extends Fragment { + private Unbinder unbinder; + protected Handler handler = new Handler(); + + protected abstract void initView(); + + protected abstract int getFragmnetLayout(); + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View inflate = LayoutInflater.from(getActivity()).inflate(getFragmnetLayout(), null); + unbinder = ButterKnife.bind(this, inflate); + initView(); + return inflate; + } + + @Override + public void onDestroy() { + super.onDestroy(); + unbinder.unbind(); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/BaseSPAActivity.java b/src/main/resources/modules/Android/app/src/main/java/base/BaseSPAActivity.java new file mode 100644 index 00000000..f898911b --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/BaseSPAActivity.java @@ -0,0 +1,71 @@ +package xyz.wbsite.wbui.base; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; + +import com.qmuiteam.qmui.arch.QMUIFragmentActivity; +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog; + +import java.util.Hashtable; + +import xyz.wbsite.webclient.fragment.LoginFragment; + + +public abstract class BaseSPAActivity extends QMUIFragmentActivity { + + protected QMUITipDialog loading; + + private Hashtable resultHashtable = new Hashtable<>(); + private int mRequestCode = 1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + loading = new QMUITipDialog.Builder(this).setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING).setTipWord("加载中...").create(); + } + + public void showLoading() { + loading.show(); + } + + public void dismissLoading() { + loading.dismiss(); + } + + public synchronized void confirmDialog(String messgae, QMUIDialogAction.ActionListener actionListener) { + new QMUIDialog.MessageDialogBuilder(this) + .setTitle("消息") + .setMessage(messgae) + .addAction("确定", actionListener) + .setCanceledOnTouchOutside(false) + .create(com.qmuiteam.qmui.R.style.QMUI_Dialog).show(); + } + + public void startForResult(Intent intent, IActivityResult activityResult) { + int requestCode = mRequestCode++; + resultHashtable.put(requestCode, activityResult); + startActivityForResult(intent, requestCode); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultHashtable.containsKey(requestCode)) { + IActivityResult i = resultHashtable.remove(requestCode); + if (i != null) { + i.onResult(resultCode, data); + } + } + } + + protected void startFirstFragment(BaseSPAFragment fragment) { + getSupportFragmentManager() + .beginTransaction() + .add(getContextViewId(), fragment, fragment.getClass().getSimpleName()) + .addToBackStack(fragment.getClass().getSimpleName()) + .commit(); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/BaseSPAFragment.java b/src/main/resources/modules/Android/app/src/main/java/base/BaseSPAFragment.java new file mode 100644 index 00000000..22e880ad --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/BaseSPAFragment.java @@ -0,0 +1,135 @@ +package xyz.wbsite.wbui.base; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.view.LayoutInflater; +import android.view.View; + +import com.qmuiteam.qmui.arch.QMUIFragment; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; + +import java.util.Hashtable; + +import butterknife.ButterKnife; +import butterknife.Unbinder; + +public abstract class BaseSPAFragment extends QMUIFragment { + private Unbinder unbinder; + protected Handler handler = new Handler(); + private Hashtable mResultListerner = new Hashtable<>(); + private int mRequestCode = 1; + + public interface IFragmentResultListerner { + void onResult(int resultCode, Intent data); + } + + public void startForResult(BaseSPAFragment intent, IFragmentResultListerner listerner) { + int requestCode = mRequestCode++; + mResultListerner.put(requestCode, listerner); + startFragmentForResult(intent, requestCode); + } + + @Override + protected void onFragmentResult(int requestCode, int resultCode, Intent data) { + super.onFragmentResult(requestCode, resultCode, data); + if (mResultListerner.containsKey(requestCode)) { + IFragmentResultListerner l = mResultListerner.remove(requestCode); + if (l != null) { + l.onResult(resultCode, data); + } + } + } + + @Override + protected int backViewInitOffset() { + return QMUIDisplayHelper.dp2px(getContext(), 100); + } + + protected abstract int getFragmnetLayout(); + + @Override + protected View onCreateView() { + View inflate = LayoutInflater.from(getActivity()).inflate(getFragmnetLayout(), null); + unbinder = ButterKnife.bind(this, inflate); + onViewInit(); + if (getArguments() == null) { + setArguments(new Bundle()); + } + onDataRecovery(getArguments()); + return inflate; + } + + @Override + public void onDestroy() { + super.onDestroy(); + unbinder.unbind(); + } + + /** + * 初始化View,事件等 + */ + protected abstract void onViewInit(); + + /** + * 保存数据 + * + * @param data + */ + protected void onDataSave(Bundle data) { + } + + /** + * 恢复数据 + * + * @param data + */ + protected void onDataRecovery(Bundle data) { + + } + + public void showLoading() { + BaseSPAActivity activity = (BaseSPAActivity) getActivity(); + activity.showLoading(); + } + + public void closeLoading() { + BaseSPAActivity activity = (BaseSPAActivity) getActivity(); + activity.dismissLoading(); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + onDataSave(outState); + } + + @Override + public void onViewStateRestored(@Nullable Bundle savedInstanceState) { + super.onViewStateRestored(savedInstanceState); + onDataRecovery(getArguments()); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + onDataSave(getArguments()); + } + + public void startForResult(Intent intent, IActivityResult activityResult) { + FragmentActivity activity = getActivity(); + if (activity instanceof BaseSPAActivity) { + BaseSPAActivity baseSPAActivity = (BaseSPAActivity) activity; + baseSPAActivity.startForResult(intent, activityResult); + } + } + + @Override + protected boolean canDragBack() { + return false; + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/BaseSPATakePhotoFragment.java b/src/main/resources/modules/Android/app/src/main/java/base/BaseSPATakePhotoFragment.java new file mode 100644 index 00000000..f51512d4 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/BaseSPATakePhotoFragment.java @@ -0,0 +1,178 @@ +package xyz.wbsite.wbui.base; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; + +import com.jph.takephoto.app.TakePhoto; +import com.jph.takephoto.app.TakePhotoImpl; +import com.jph.takephoto.compress.CompressConfig; +import com.jph.takephoto.model.CropOptions; +import com.jph.takephoto.model.InvokeParam; +import com.jph.takephoto.model.TContextWrap; +import com.jph.takephoto.model.TResult; +import com.jph.takephoto.permission.InvokeListener; +import com.jph.takephoto.permission.PermissionManager; +import com.jph.takephoto.permission.TakePhotoInvocationHandler; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import xyz.wbsite.wbui.base.utils.StorageUtil; + +import static xyz.wbsite.wbui.base.Consant.DIR_IMG; + + +public abstract class BaseSPATakePhotoFragment extends BaseSPAFragment implements TakePhoto.TakeResultListener, InvokeListener { + private static final String TAG = BaseSPATakePhotoFragment.class.getName(); + + //TakePhoto + private TakePhoto takePhoto; + private CropOptions cropOptions; //裁剪参数 + private CompressConfig compressConfig; //压缩参数 + private InvokeParam invokeParam; + + private TakePhtotListener listener; + + public void requestTakePhoto(TakePhtotListener listener) { + this.listener = listener; + + //获取TakePhoto实例 + takePhoto = getTakePhoto(); + //设置压缩、裁剪参数 + //设置裁剪参数 + cropOptions = new CropOptions.Builder().setWithOwnCrop(true).create(); + //设置压缩参数 + compressConfig = new CompressConfig.Builder().setMaxSize(512 * 1024).setMaxPixel(800).create(); + takePhoto.onEnableCompress(compressConfig, true); //设置为需要压缩 + //拍照并裁剪 + takePhoto.onPickFromCapture(getImageCropUri()); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + getTakePhoto().onSaveInstanceState(outState); + super.onSaveInstanceState(outState); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + getTakePhoto().onActivityResult(requestCode, resultCode, data); + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + PermissionManager.TPermissionType type = PermissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults); + PermissionManager.handlePermissionsResult(getActivity(), type, invokeParam, this); + } + + @Override + public void takeSuccess(TResult result) { + Log.i(TAG, "takeSuccess:" + result.getImage().getCompressPath()); + String compressPath = result.getImage().getCompressPath(); + if (compressPath != null) { + File file = new File(compressPath); + File file1 = new File(StorageUtil.getOwnCacheDirectory(getContext(), DIR_IMG), "min_" + file.getName()); + copyFileUsingFileStreams(file, file1); + + if (listener != null) { + Bitmap bitmap = BitmapFactory.decodeFile(file1.getAbsolutePath()); + listener.takeSuccess(bitmap); + listener = null; + } + } + } + + private static void copyFileUsingFileStreams(File source, File dest) { + InputStream input = null; + OutputStream output = null; + try { + input = new FileInputStream(source); + output = new FileOutputStream(dest); + byte[] buf = new byte[1024]; + int bytesRead; + while ((bytesRead = input.read(buf)) > 0) { + output.write(buf, 0, bytesRead); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + input.close(); + } catch (IOException e) { + e.printStackTrace(); + } + try { + output.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Override + public void takeFail(TResult result, String msg) { + Log.i(TAG, "takeFail:" + msg); + + if (listener != null) { + listener.takeFail(); + listener = null; + } + } + + @Override + public void takeCancel() { + if (listener != null) { + listener.takeCancel(); + listener = null; + } + } + + @Override + public PermissionManager.TPermissionType invoke(InvokeParam invokeParam) { + PermissionManager.TPermissionType type = PermissionManager.checkPermission(TContextWrap.of(this), invokeParam.getMethod()); + if (PermissionManager.TPermissionType.WAIT.equals(type)) { + this.invokeParam = invokeParam; + } + return type; + } + + /** + * 获取TakePhoto实例 + * + * @return + */ + public TakePhoto getTakePhoto() { + if (takePhoto == null) { + takePhoto = (TakePhoto) TakePhotoInvocationHandler.of(this).bind(new TakePhotoImpl(this, this)); + } + return takePhoto; + } + + //获得照片的输出保存Uri + private Uri getImageCropUri() { + File file = new File(StorageUtil.getOwnCacheDirectory(getContext(), DIR_IMG), System.currentTimeMillis() + ".jpg"); + if (!file.getParentFile().exists()) file.getParentFile().mkdirs(); + return Uri.fromFile(file); + } + + public interface TakePhtotListener { + void takeSuccess(Bitmap bitmap); + + void takeFail(); + + void takeCancel(); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/IActivityResult.java b/src/main/resources/modules/Android/app/src/main/java/base/IActivityResult.java new file mode 100644 index 00000000..fc30011a --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/IActivityResult.java @@ -0,0 +1,8 @@ +package xyz.wbsite.wbui.base; + + +import android.content.Intent; + +public interface IActivityResult { + void onResult(int resultCode, Intent data); +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/activity/FilePickerActivity.java b/src/main/resources/modules/Android/app/src/main/java/base/activity/FilePickerActivity.java new file mode 100644 index 00000000..4fcf1a06 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/activity/FilePickerActivity.java @@ -0,0 +1,40 @@ +package xyz.wbsite.wbui.base.activity; + + +import android.content.Intent; +import android.os.Bundle; + +import com.leon.lfilepickerlibrary.LFilePicker; +import com.leon.lfilepickerlibrary.utils.Constant; + +import xyz.wbsite.wbui.base.BaseSPAActivity; + +public class FilePickerActivity extends BaseSPAActivity { + + private static final int REQUESTCODE_FROM_ACTIVITY = 0; + + + @Override + protected int getContextViewId() { + return 0; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + new LFilePicker() + .withActivity(this) + .withTitle("选择附件") + .withBackIcon(Constant.BACKICON_STYLETHREE) + .withRequestCode(REQUESTCODE_FROM_ACTIVITY) + .start(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + setResult(resultCode,data); + finish(); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/activity/QRcodeActivity.java b/src/main/resources/modules/Android/app/src/main/java/base/activity/QRcodeActivity.java new file mode 100644 index 00000000..db499b78 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/activity/QRcodeActivity.java @@ -0,0 +1,71 @@ +package xyz.wbsite.wbui.base.activity; + +import android.app.Activity; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.view.KeyEvent; + +import com.journeyapps.barcodescanner.CaptureManager; +import com.journeyapps.barcodescanner.DecoratedBarcodeView; + +import xyz.wbsite.webclient.R; + + +public class QRcodeActivity extends Activity implements DecoratedBarcodeView.TorchListener { + + private CaptureManager captureManager; + private DecoratedBarcodeView mDBV; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_custom_capture); + + mDBV = (DecoratedBarcodeView) findViewById(R.id.dbv_custom); + mDBV.setTorchListener(this); + + //重要代码,初始化捕获 + captureManager = new CaptureManager(this, mDBV); + captureManager.initializeFromIntent(getIntent(), savedInstanceState); + captureManager.decode(); + } + + @Override + protected void onPause() { + super.onPause(); + captureManager.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + captureManager.onResume(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + captureManager.onDestroy(); + } + + @Override + public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { + super.onSaveInstanceState(outState, outPersistentState); + captureManager.onSaveInstanceState(outState); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return mDBV.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + } + + @Override + public void onTorchOn() { + + } + + @Override + public void onTorchOff() { + + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/service/RestartService.java b/src/main/resources/modules/Android/app/src/main/java/base/service/RestartService.java new file mode 100644 index 00000000..b7ef5757 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/service/RestartService.java @@ -0,0 +1,62 @@ +package xyz.wbsite.wbui.base.services; + + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; + +public class RestartService extends Service { + + /** + * 重启整个APP + * + * @param context + * @param Delayed 延迟多少毫秒 + */ + public static void restartAPP(Context context, long Delayed) { + + // 开启服务,用来重启 + Intent intent1 = new Intent(context, RestartService.class); + intent1.putExtra("PackageName", context.getPackageName()); + intent1.putExtra("Delayed", Delayed); + context.startService(intent1); + + // 杀死进程 + android.os.Process.killProcess(android.os.Process.myPid()); + } + + /** + * 应用关闭重启时间 + */ + private static long stopDelayed = 1000; + + private Handler handler; + + private String PackageName; + + public RestartService() { + handler = new Handler(); + } + + @Override + public int onStartCommand(final Intent intent, int flags, int startId) { + stopDelayed = intent.getLongExtra("Delayed", 1000); + PackageName = intent.getStringExtra("PackageName"); + handler.postDelayed(new Runnable() { + @Override + public void run() { + Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage(PackageName); + startActivity(LaunchIntent); + RestartService.this.stopSelf(); + } + }, stopDelayed); + return super.onStartCommand(intent, flags, startId); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUICustomDialog.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUICustomDialog.java new file mode 100644 index 00000000..00a4e8b5 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUICustomDialog.java @@ -0,0 +1,123 @@ +package xyz.wbsite.wbui.base.ui; + +import android.app.Dialog; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; + +public class WBUICustomDialog extends Dialog { + public WBUICustomDialog(Context context) { + super(context); + this.requestWindowFeature(Window.FEATURE_NO_TITLE); + this.getWindow().getDecorView().setPadding(0, 0, 0, 0); + this.getWindow().getDecorView().setBackground(null); + } + + @Override + public void setContentView(View view, ViewGroup.LayoutParams params) { + if (params.width == ViewGroup.LayoutParams.MATCH_PARENT) { + params.width = this.getWindow().getContext().getResources().getDisplayMetrics().widthPixels; + } + if (params.height == ViewGroup.LayoutParams.MATCH_PARENT) { + params.height = this.getWindow().getContext().getResources().getDisplayMetrics().heightPixels; + } + + super.setContentView(view, params); + } + + public void setContentView(View view, ViewGroup.LayoutParams params, double widthScale, double heightScale) { + if (widthScale < 0) { + widthScale = 0; + } + if (heightScale > 1) { + heightScale = 1; + } + if (params.width == ViewGroup.LayoutParams.MATCH_PARENT) { + params.width = (int) (widthScale * this.getWindow().getContext().getResources().getDisplayMetrics().widthPixels); + } + if (params.height == ViewGroup.LayoutParams.MATCH_PARENT) { + params.height = (int) (heightScale * this.getWindow().getContext().getResources().getDisplayMetrics().heightPixels); + } + super.setContentView(view, params); + } + + @Override + public void setContentView(int layoutResID) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + View inflate = inflater.inflate(layoutResID, null); + if (inflater == null) { + throw new AssertionError("layout not find"); + } + ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + this.setContentView(inflate, layoutParams); + } + + public void setContentView(int layoutResID, double widthScale, double heightScale) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + View inflate = inflater.inflate(layoutResID, null); + if (inflater == null) { + throw new AssertionError("layout not find"); + } + ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + this.setContentView(inflate, layoutParams, widthScale, heightScale); + } + + /** + * 对齐方式 + * + * @param gravity + */ + public void setGravity(int gravity) { + WindowManager.LayoutParams attributes = getWindow().getAttributes(); + attributes.gravity = gravity; + getWindow().setAttributes(attributes); + } + + /** + * 设置背景屏幕透明度 + */ + public void setDimAmount(float amount) { + this.getWindow().setDimAmount(amount); + } + + /** + * 自定义按钮点击事件 + * + * @param viewId 自定义Dialog viewId + * @param clickListener 点击事件监听器 + * @param dismiss 是否消失 + */ + public void addOnclickListener(int viewId, final View.OnClickListener clickListener, final boolean dismiss) { + this.findViewById(viewId).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clickListener.onClick(v); + if (dismiss) { + dismiss(); + } + } + }); + } + + /** + * 自定义按钮点击事件 + * + * @param view 自定义Dialog view + * @param clickListener 点击事件监听器 + * @param dismiss 是否消失 + */ + public void addOnclickListener(View view, final View.OnClickListener clickListener, final boolean dismiss) { + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clickListener.onClick(v); + if (dismiss) { + dismiss(); + } + } + }); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUIGridView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUIGridView.java new file mode 100644 index 00000000..0d5896fa --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUIGridView.java @@ -0,0 +1,51 @@ +package xyz.wbsite.wbui.base.ui; + + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Shader; +import android.util.AttributeSet; +import android.view.View; +import android.widget.GridView; + +import xyz.wbsite.webclient.R; + +public class WBUIGridView extends GridView { + public WBUIGridView(Context context) { + super(context); + } + + public WBUIGridView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public WBUIGridView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + int cols = getNumColumns(); //获取列数 + + Paint localPaint; //设置画笔 + localPaint = new Paint(); + localPaint.setStyle(Paint.Style.FILL); //画笔实心 + + for (int i = 0; i < getChildCount(); i++) { + View childAt = getChildAt(i); + localPaint.setShader(new LinearGradient(childAt.getLeft(), childAt.getBottom(), childAt.getRight(), childAt.getBottom(), new int[]{ + getResources().getColor(R.color.colorGray_09), getResources().getColor(R.color.colorGray_09), getResources().getColor(R.color.colorGray_09)}, null, Shader.TileMode.REPEAT)); + canvas.drawLine(childAt.getLeft(), childAt.getBottom() - 1, childAt.getRight(), childAt.getBottom() - 1, localPaint); + + if ((i + 1) % cols != 0) { + localPaint.setShader(new LinearGradient(childAt.getRight(), childAt.getTop(), childAt.getRight(), childAt.getBottom(), new int[]{ + getResources().getColor(R.color.colorGray_09), getResources().getColor(R.color.colorGray_09), getResources().getColor(R.color.colorGray_09)}, null, Shader.TileMode.MIRROR)); + canvas.drawLine(childAt.getRight() - 1, childAt.getTop(), childAt.getRight() - 1, childAt.getBottom(), localPaint); + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUITextView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUITextView.java new file mode 100644 index 00000000..99eeac6f --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUITextView.java @@ -0,0 +1,53 @@ +package xyz.wbsite.wbui.base.ui; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.util.AttributeSet; + +public class WBUITextView extends android.support.v7.widget.AppCompatTextView { + private int mOffsetY = 0; + public Bitmap lastBitmap; + + public WBUITextView(Context context) { + super(context); + } + + public WBUITextView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDraw(Canvas canvas) { + if (lastBitmap != null) { + canvas.drawBitmap(lastBitmap, 0, mOffsetY - getHeight(), null); + canvas.translate(0, mOffsetY); + } + super.onDraw(canvas); + + if (mOffsetY == 0) { + lastBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); + super.onDraw(new Canvas(lastBitmap)); + } + } + + @Override + public void setText(CharSequence text, BufferType type) { + mOffsetY = getHeight(); + post(new Runnable() { + @Override + public void run() { + if (mOffsetY > 0) { + mOffsetY -= 5; + if (mOffsetY < 0) mOffsetY = 0; + postDelayed(this, 10); + } else { + mOffsetY = 0; + } + System.out.println(mOffsetY); + invalidate(); + } + }); + super.setText(text, type); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUITipTextView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUITipTextView.java new file mode 100644 index 00000000..9a00352d --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/WBUITipTextView.java @@ -0,0 +1,41 @@ +package xyz.wbsite.wbui.base.ui; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; + +public class WBUITipTextView extends android.support.v7.widget.AppCompatTextView { + private long time = 3000; + private String lastValue = ""; + private Runnable runnable = new Runnable() { + @Override + public void run() { + time-=1000; + if (time <= 0){ + WBUITipTextView.this.setText(""); + }else { + postDelayed(runnable,1000); + } + } + }; + + public WBUITipTextView(Context context) { + super(context); + } + + public WBUITipTextView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public WBUITipTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void setText(CharSequence text, BufferType type) { + this.time = 3000; + postDelayed(runnable,1000); + + super.setText(text, type); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbButton.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbButton.java new file mode 100644 index 00000000..4d6b6681 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbButton.java @@ -0,0 +1,85 @@ +package xyz.wbsite.wbui.base.ui.button; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.RectF; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.util.AttributeSet; +import android.widget.Button; + +public class WbButton extends Button { + private int mColor; + private float mScale; + + public WbButton(Context context) { + super(context); + init(); + } + + public WbButton(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + mScale = getResources().getDisplayMetrics().density; + ColorStateList textColors = this.getTextColors(); + mColor = textColors.getDefaultColor(); + + StateListDrawable stateListDrawable = new StateListDrawable(); + stateListDrawable.setEnterFadeDuration(200); + stateListDrawable.setExitFadeDuration(200); + int radius = (int) (5 * mScale); + int width = (int) (1 * mScale); + + //不可按时的drawable + { + RoundRectShape roundRectShape = new RoundRectShape( + new float[]{radius, radius, radius, radius, radius, radius, radius, radius}, + new RectF(width, width, width, width), + new float[]{radius - width, radius - width, radius - width, radius - width, radius - width, radius - width, radius - width, radius - width} + ); + ShapeDrawable shapeDrawable = new ShapeDrawable(roundRectShape); + shapeDrawable.getPaint().setColor(Color.parseColor("#aaaaaa")); + stateListDrawable.addState(new int[]{-android.R.attr.state_enabled}, shapeDrawable); + } + //正常的drawable + { + RoundRectShape roundRectShape = new RoundRectShape( + new float[]{radius, radius, radius, radius, radius, radius, radius, radius}, + new RectF(width, width, width, width), + new float[]{radius - width, radius - width, radius - width, radius - width, radius - width, radius - width, radius - width, radius - width} + ); + ShapeDrawable shapeDrawable = new ShapeDrawable(roundRectShape); + shapeDrawable.getPaint().setColor(mColor); + + stateListDrawable.addState(new int[]{-android.R.attr.state_pressed}, shapeDrawable); + } + //按下去的drawable + { + RoundRectShape roundRectShape = new RoundRectShape( + new float[]{radius, radius, radius, radius, radius, radius, radius, radius}, + null, + null + ); + ShapeDrawable shapeDrawable = new ShapeDrawable(roundRectShape); + shapeDrawable.getPaint().setColor(mColor); + stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, shapeDrawable); + } + this.setBackground(stateListDrawable); + + //各状态对应的字体颜色 + int[][] satate = { + {-android.R.attr.state_enabled}, + {-android.R.attr.state_pressed}, + {android.R.attr.state_pressed} + }; + int[] colors = {Color.parseColor("#aaaaaa"), mColor, Color.WHITE,}; + + ColorStateList colorStateList = new ColorStateList(satate, colors); + this.setTextColor(colorStateList); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbImageButton.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbImageButton.java new file mode 100644 index 00000000..51db550d --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbImageButton.java @@ -0,0 +1,48 @@ +package xyz.wbsite.wbui.base.ui.button; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.ImageButton; + +public class WbImageButton extends ImageButton { + private int mSrcWitdh; + private int mSrcHeight; + private int mExactlyWidth; + private int mEactlyHeight; + + public WbImageButton(Context context) { + super(context); + } + + public WbImageButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + Drawable drawable = getDrawable(); + Rect bounds = drawable.getBounds(); + mSrcWitdh = bounds.width(); + mSrcHeight = bounds.height(); + + System.out.println(mSrcWitdh); + System.out.println(mSrcHeight); + if (mSrcWitdh != 0 && mSrcHeight != 0) { + if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) { + //宽度确定,高度自适应 + mExactlyWidth = MeasureSpec.getSize(widthMeasureSpec); + mEactlyHeight = mExactlyWidth * mSrcHeight / mSrcWitdh; + setMeasuredDimension(mExactlyWidth, mEactlyHeight); + } else if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { + //高度确定,宽度自适应 + mEactlyHeight = MeasureSpec.getSize(heightMeasureSpec); + mExactlyWidth = mEactlyHeight * mSrcWitdh / mSrcHeight; + setMeasuredDimension(mExactlyWidth, mEactlyHeight); + } + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbMultiToggleButton.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbMultiToggleButton.java new file mode 100644 index 00000000..4dbdd2cc --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbMultiToggleButton.java @@ -0,0 +1,114 @@ +package xyz.wbsite.wbui.base.ui.button; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; + +/** + * + */ +public class WbMultiToggleButton extends LinearLayout implements View.OnClickListener { + private Button b1; + private Button b2; + + + public WbMultiToggleButton(Context context) { + super(context); + init(); + } + + public WbMultiToggleButton(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + this.setOrientation(HORIZONTAL); + float density = this.getResources().getDisplayMetrics().density; + LayoutParams layoutParams = new LayoutParams(0, LayoutParams.MATCH_PARENT); + layoutParams.weight = 1; + + { + // 边框 + ShapeDrawable shapeDrawable = new ShapeDrawable(new RoundRectShape(new float[]{density * 5, density * 5, density * 5, density * 5, density * 5, density * 5, density * 5, density * 5}, null, null)); + shapeDrawable.getPaint().setColor(Color.parseColor("#aaaaaa")); + this.setPadding((int) (density * 1), (int) (density * 1), (int) (density * 1), (int) (density * 1)); + this.setBackground(shapeDrawable); + } + + { + b1 = new Button(getContext()); + b1.setText("aa"); + b1.setTag("b1"); + b1.setOnClickListener(this); + int[][] satate = { + {-android.R.attr.state_selected}, + {android.R.attr.state_selected} + }; + int[] colors = {Color.BLACK, Color.WHITE}; + ColorStateList colorStateList = new ColorStateList(satate, colors); + b1.setTextColor(colorStateList); + + ShapeDrawable unselected = new ShapeDrawable(new RoundRectShape(new float[]{density * 4, density * 4, 0, 0, 0, 0, density * 4, density * 4}, null, null)); + unselected.getPaint().setColor(Color.WHITE); + ShapeDrawable selected = new ShapeDrawable(new RoundRectShape(new float[]{density * 4, density * 4, 0, 0, 0, 0, density * 4, density * 4}, null, null)); + selected.getPaint().setColor(Color.parseColor("#66b3ff")); + StateListDrawable listDrawable = new StateListDrawable(); + listDrawable.addState(new int[]{-android.R.attr.state_selected}, unselected); + listDrawable.addState(new int[]{android.R.attr.state_selected}, selected); + b1.setBackground(listDrawable); + this.addView(b1, layoutParams); + } + + { + b2 = new Button(getContext()); + b2.setText("bb"); + b2.setTag("b2"); + b2.setOnClickListener(this); + int[][] satate = { + {-android.R.attr.state_selected}, + {android.R.attr.state_selected} + }; + int[] colors = {Color.BLACK, Color.WHITE}; + ColorStateList colorStateList = new ColorStateList(satate, colors); + b2.setTextColor(colorStateList); + ShapeDrawable unselected = new ShapeDrawable(new RoundRectShape(new float[]{0, 0, density * 4, density * 4, density * 4, density * 4, 0, 0}, null, null)); + unselected.getPaint().setColor(Color.WHITE); + ShapeDrawable selected = new ShapeDrawable(new RoundRectShape(new float[]{0, 0, density * 4, density * 4, density * 4, density * 4, 0, 0}, null, null)); + selected.getPaint().setColor(Color.parseColor("#66b3ff")); + StateListDrawable listDrawable = new StateListDrawable(); + listDrawable.addState(new int[]{-android.R.attr.state_selected}, unselected); + listDrawable.addState(new int[]{android.R.attr.state_selected}, selected); + b2.setBackground(listDrawable); + this.addView(b2, layoutParams); + } + + b1.setSelected(true); + b2.setSelected(false); + } + + @Override + public void onClick(View v) { + Object tag = v.getTag(); + if (tag instanceof String) { + String t = (String) tag; + switch (t) { + case "b1": + b1.setSelected(true); + b2.setSelected(false); + break; + case "b2": + b1.setSelected(false); + b2.setSelected(true); + break; + } + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbRadioButton.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbRadioButton.java new file mode 100644 index 00000000..a1279cf3 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbRadioButton.java @@ -0,0 +1,64 @@ +package xyz.wbsite.wbui.base.ui.button; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.StateListDrawable; +import android.util.AttributeSet; +import android.widget.RadioButton; + + +/** + * Created by bingwang on 2016/7/17. + */ +public class WbRadioButton extends RadioButton { + + public WbRadioButton(Context context) { + super(context); + initial(); + } + + public WbRadioButton(Context context, AttributeSet attrs) { + super(context, attrs); + initial(); + } + + /** + * 构造自定义radio + */ + public void initial() { + float density = getResources().getDisplayMetrics().density; + StateListDrawable stateListDrawable = new StateListDrawable(); + + Bitmap unchecked = Bitmap.createBitmap((int) (30 * density), (int) (30 * density), Bitmap.Config.ARGB_8888); + Bitmap checked = Bitmap.createBitmap((int) (30 * density), (int) (30 * density), Bitmap.Config.ARGB_8888); + + {//未选中状态 + Canvas canvas = new Canvas(unchecked); + Paint paint = new Paint(); + paint.setColor(Color.DKGRAY); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(5 * density); + canvas.drawRect(4.5f * density, 4.5f * density, 25.5f * density, 25.5f * density, paint); + } + + {//选中状态 + Canvas canvas = new Canvas(checked); + Paint paint = new Paint(); + paint.setColor(Color.DKGRAY); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(5 * density); + canvas.drawRect(4.5f * density, 4.5f * density, 25.5f * density, 25.5f * density, paint); + paint.setColor(Color.argb(255, 00, 94, 00)); + paint.setStyle(Paint.Style.FILL); + canvas.drawRect(9 * density, 9 * density, 21 * density, 21 * density, paint); + } + + stateListDrawable.addState(new int[]{-android.R.attr.state_checked}, new BitmapDrawable(getResources(), unchecked)); + stateListDrawable.addState(new int[]{android.R.attr.state_checked}, new BitmapDrawable(getResources(), checked)); + this.setButtonDrawable(stateListDrawable); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbToggleButton.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbToggleButton.java new file mode 100644 index 00000000..5ae59944 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/button/WbToggleButton.java @@ -0,0 +1,114 @@ +package xyz.wbsite.wbui.base.ui.button; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; + +/** + * + */ +public class WbToggleButton extends LinearLayout implements View.OnClickListener { + private Button b1; + private Button b2; + + + public WbToggleButton(Context context) { + super(context); + init(); + } + + public WbToggleButton(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + this.setOrientation(HORIZONTAL); + float density = this.getResources().getDisplayMetrics().density; + LayoutParams layoutParams = new LayoutParams(0, LayoutParams.MATCH_PARENT); + layoutParams.weight = 1; + + { + // 边框 + ShapeDrawable shapeDrawable = new ShapeDrawable(new RoundRectShape(new float[]{density * 5, density * 5, density * 5, density * 5, density * 5, density * 5, density * 5, density * 5}, null, null)); + shapeDrawable.getPaint().setColor(Color.parseColor("#aaaaaa")); + this.setPadding((int) (density * 1), (int) (density * 1), (int) (density * 1), (int) (density * 1)); + this.setBackground(shapeDrawable); + } + + { + b1 = new Button(getContext()); + b1.setText("aa"); + b1.setTag("b1"); + b1.setOnClickListener(this); + int[][] satate = { + {-android.R.attr.state_selected}, + {android.R.attr.state_selected} + }; + int[] colors = {Color.BLACK, Color.WHITE}; + ColorStateList colorStateList = new ColorStateList(satate, colors); + b1.setTextColor(colorStateList); + + ShapeDrawable unselected = new ShapeDrawable(new RoundRectShape(new float[]{density * 4, density * 4, 0, 0, 0, 0, density * 4, density * 4}, null, null)); + unselected.getPaint().setColor(Color.WHITE); + ShapeDrawable selected = new ShapeDrawable(new RoundRectShape(new float[]{density * 4, density * 4, 0, 0, 0, 0, density * 4, density * 4}, null, null)); + selected.getPaint().setColor(Color.parseColor("#66b3ff")); + StateListDrawable listDrawable = new StateListDrawable(); + listDrawable.addState(new int[]{-android.R.attr.state_selected}, unselected); + listDrawable.addState(new int[]{android.R.attr.state_selected}, selected); + b1.setBackground(listDrawable); + this.addView(b1, layoutParams); + } + + { + b2 = new Button(getContext()); + b2.setText("bb"); + b2.setTag("b2"); + b2.setOnClickListener(this); + int[][] satate = { + {-android.R.attr.state_selected}, + {android.R.attr.state_selected} + }; + int[] colors = {Color.BLACK, Color.WHITE}; + ColorStateList colorStateList = new ColorStateList(satate, colors); + b2.setTextColor(colorStateList); + ShapeDrawable unselected = new ShapeDrawable(new RoundRectShape(new float[]{0, 0, density * 4, density * 4, density * 4, density * 4, 0, 0}, null, null)); + unselected.getPaint().setColor(Color.WHITE); + ShapeDrawable selected = new ShapeDrawable(new RoundRectShape(new float[]{0, 0, density * 4, density * 4, density * 4, density * 4, 0, 0}, null, null)); + selected.getPaint().setColor(Color.parseColor("#66b3ff")); + StateListDrawable listDrawable = new StateListDrawable(); + listDrawable.addState(new int[]{-android.R.attr.state_selected}, unselected); + listDrawable.addState(new int[]{android.R.attr.state_selected}, selected); + b2.setBackground(listDrawable); + this.addView(b2, layoutParams); + } + + b1.setSelected(true); + b2.setSelected(false); + } + + @Override + public void onClick(View v) { + Object tag = v.getTag(); + if (tag instanceof String) { + String t = (String) tag; + switch (t) { + case "b1": + b1.setSelected(true); + b2.setSelected(false); + break; + case "b2": + b1.setSelected(false); + b2.setSelected(true); + break; + } + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/ConfirmDialog.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/ConfirmDialog.java new file mode 100644 index 00000000..aa5a17e2 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/ConfirmDialog.java @@ -0,0 +1,70 @@ +package xyz.wbsite.wbui.base.ui.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.support.annotation.NonNull; +import android.view.Gravity; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class ConfirmDialog extends Dialog { + private LinearLayout mLinearLayout; + + public ConfirmDialog(@NonNull Context context) { + super(context); + } + + public ConfirmDialog(@NonNull Context context, int themeResId) { + super(context, themeResId); + } + + public ConfirmDialog(Context context, String message) { + super(context); + //px与dp比例 + float mScale = getContext().getResources().getDisplayMetrics().density; + + //设置基本参数 + this.requestWindowFeature(Window.FEATURE_NO_TITLE); +// this.getWindow().setBackgroundDrawable(new ColorDrawable(Color.argb(0, 0, 0, 0))); +// this.getWindow().setDimAmount(0); + this.setCanceledOnTouchOutside(false); + + //父容器 + { + mLinearLayout = new LinearLayout(getContext()); + mLinearLayout.setGravity(Gravity.CENTER); + mLinearLayout.setOrientation(LinearLayout.VERTICAL); + mLinearLayout.setPadding(10, 10, 10, 10); + mLinearLayout.setBackground(new ColorDrawable(Color.WHITE)); + ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, (int) (mScale * 50)); + this.setContentView(mLinearLayout, layoutParams); + } + + + //消息显示框 + TextView mTextView = new TextView(context); + mTextView.setText(message); + mTextView.setGravity(Gravity.CENTER); + mTextView.setTextColor(Color.BLACK); + mTextView.setMinWidth((int) (mScale * 150)); + mLinearLayout.addView(mTextView); + + { + ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, (int) (mScale * 50)); + + Button button = new Button(getContext()); + button.setText("确认"); + button.setTextColor(Color.parseColor("#7AECC4")); + button.setBackground(null); + button.setGravity(Gravity.RIGHT); + mLinearLayout.addView(button, layoutParams); + } + + } + +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/CustomDialog.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/CustomDialog.java new file mode 100644 index 00000000..a544196f --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/CustomDialog.java @@ -0,0 +1,123 @@ +package xyz.wbsite.wbui.base.ui.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; + +public class CustomDialog extends Dialog { + public CustomDialog(Context context) { + super(context); + this.requestWindowFeature(Window.FEATURE_NO_TITLE); + this.getWindow().getDecorView().setPadding(0, 0, 0, 0); +// this.getWindow().setBackgroundDrawable(new ColorDrawable(Color.argb(0, 0, 0, 0))); + } + + @Override + public void setContentView(View view, ViewGroup.LayoutParams params) { + if (params.width == ViewGroup.LayoutParams.MATCH_PARENT) { + params.width = this.getWindow().getContext().getResources().getDisplayMetrics().widthPixels; + } + if (params.height == ViewGroup.LayoutParams.MATCH_PARENT) { + params.height = this.getWindow().getContext().getResources().getDisplayMetrics().heightPixels; + } + + super.setContentView(view, params); + } + + public void setContentView(View view, ViewGroup.LayoutParams params, double widthScale, double heightScale) { + if (widthScale < 0) { + widthScale = 0; + } + if (heightScale > 1) { + heightScale = 1; + } + if (params.width == ViewGroup.LayoutParams.MATCH_PARENT) { + params.width = (int) (widthScale * this.getWindow().getContext().getResources().getDisplayMetrics().widthPixels); + } + if (params.height == ViewGroup.LayoutParams.MATCH_PARENT) { + params.height = (int) (heightScale * this.getWindow().getContext().getResources().getDisplayMetrics().heightPixels); + } + super.setContentView(view, params); + } + + @Override + public void setContentView(int layoutResID) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + View inflate = inflater.inflate(layoutResID, null); + if (inflater == null) { + throw new AssertionError("layout not find"); + } + ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + this.setContentView(inflate, layoutParams); + } + + public void setContentView(int layoutResID, double widthScale, double heightScale) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + View inflate = inflater.inflate(layoutResID, null); + if (inflater == null) { + throw new AssertionError("layout not find"); + } + ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + this.setContentView(inflate, layoutParams, widthScale, heightScale); + } + + /** + * 对齐方式 + * + * @param gravity + */ + public void setGravity(int gravity) { + WindowManager.LayoutParams attributes = getWindow().getAttributes(); + attributes.gravity = gravity; + getWindow().setAttributes(attributes); + } + + /** + * 设置背景屏幕透明度 + */ + public void setDimAmount(float amount) { + this.getWindow().setDimAmount(amount); + } + + /** + * 自定义按钮点击事件 + * + * @param viewId 自定义Dialog viewId + * @param clickListener 点击事件监听器 + * @param dismiss 是否消失 + */ + public void addOnclickListener(int viewId, final View.OnClickListener clickListener, final boolean dismiss) { + this.findViewById(viewId).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clickListener.onClick(v); + if (dismiss) { + dismiss(); + } + } + }); + } + + /** + * 自定义按钮点击事件 + * + * @param view 自定义Dialog view + * @param clickListener 点击事件监听器 + * @param dismiss 是否消失 + */ + public void addOnclickListener(View view, final View.OnClickListener clickListener, final boolean dismiss) { + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clickListener.onClick(v); + if (dismiss) { + dismiss(); + } + } + }); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/MessageDialog.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/MessageDialog.java new file mode 100644 index 00000000..6f2833c4 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/MessageDialog.java @@ -0,0 +1,113 @@ +package xyz.wbsite.wbui.base.ui.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.os.AsyncTask; +import android.view.Gravity; +import android.view.ViewGroup; +import android.view.Window; +import android.view.animation.Animation; +import android.view.animation.ScaleAnimation; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * Created by wangbing on 2016/07/01. + */ +public class MessageDialog extends Dialog { + private LinearLayout mLinearLayout; + + private MessageDialog(Context context) { + super(context); + } + + public MessageDialog(Context context, String message) { + super(context); + //px与dp比例 + float mScale = getContext().getResources().getDisplayMetrics().density; + + //设置基本参数 + this.requestWindowFeature(Window.FEATURE_NO_TITLE); + this.getWindow().setBackgroundDrawable(new ColorDrawable(Color.argb(0, 0, 0, 0))); + this.getWindow().setDimAmount(0); + this.setCanceledOnTouchOutside(false); + + //父容器 + mLinearLayout = new LinearLayout(getContext()); + mLinearLayout.setGravity(Gravity.CENTER); + mLinearLayout.setOrientation(LinearLayout.HORIZONTAL); + mLinearLayout.setPadding(10, 10, 10, 10); + ShapeDrawable shapeDrawable = new ShapeDrawable(); + shapeDrawable.setShape(new RoundRectShape(new float[]{8, 8, 8, 8, 8, 8, 8, 8}, null, null)); + shapeDrawable.getPaint().setColor(Color.argb(180, 20, 20, 20)); + mLinearLayout.setBackground(shapeDrawable); + ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, (int) (mScale * 50)); + this.setContentView(mLinearLayout, layoutParams); + +// LoadingView1 loadingView1 = new LoadingView1(getContext()); +// LinearLayout.LayoutParams layoutParams1 = new LinearLayout.LayoutParams((int)(mScale*35),(int)(mScale*35)); +// mLinearLayout.addView(loadingView1, layoutParams1); + + //消息显示框 + TextView mTextView = new TextView(context); + mTextView.setText(message); + mTextView.setGravity(Gravity.CENTER); + mTextView.setTextColor(Color.WHITE); + mTextView.setMinWidth((int) (mScale * 150)); + mLinearLayout.addView(mTextView); + } + + @Override + public void show() { + super.show(); + + //启动异步任务,1秒之后自动关闭 + new AsyncTask() { + @Override + protected Boolean doInBackground(String... params) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return true; + } + + @Override + protected void onPostExecute(Boolean aBoolean) { + if (aBoolean) { + doAnimation(); + } + } + }.execute(); + } + + public void doAnimation() { + //启动消失动画 + ScaleAnimation scaleAnimation = new ScaleAnimation(1f, 0f, 1f, 0f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f); + scaleAnimation.setDuration(400); + scaleAnimation.setRepeatCount(0); + scaleAnimation.setFillAfter(true); + scaleAnimation.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + + } + + @Override + public void onAnimationEnd(Animation animation) { + dismiss(); + } + + @Override + public void onAnimationRepeat(Animation animation) { + + } + }); + mLinearLayout.startAnimation(scaleAnimation); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/WBUIDialog.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/WBUIDialog.java new file mode 100644 index 00000000..95f56724 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/WBUIDialog.java @@ -0,0 +1,22 @@ +package xyz.wbsite.wbui.base.ui.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.support.annotation.NonNull; +import android.widget.FrameLayout; + +public class WBUIDialog extends Dialog { + public WBUIDialog(@NonNull Context context) { + super(context); + } + + public void show() { + super.show(); +// FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mContentView +// .getLayoutParams(); +// layoutParams.width = display.getWidth(); +// layoutParams.height = display.getHeight(); +// mContentView.setLayoutParams(layoutParams); + + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/WbConfirmDialog.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/WbConfirmDialog.java new file mode 100644 index 00000000..48549852 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/dialog/WbConfirmDialog.java @@ -0,0 +1,155 @@ +package xyz.wbsite.wbui.base.ui.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.view.Gravity; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.TranslateAnimation; +import android.widget.Button; +import android.widget.LinearLayout; + + +/** + * Created by bingwang on 2016/5/17. + */ +public class WbConfirmDialog extends Dialog { + LinearLayout mLinearLayout; + private float demsity = 1; + private Button mPositiveButton; + private Button mNegativeButton; + private OnPostiveListener mOnPostiveListener = null; + private OnNegativeListener mOnNegativeListener = null; + + public WbConfirmDialog(Context context) { + super(context); + this.requestWindowFeature(Window.FEATURE_NO_TITLE); + this.getWindow().setGravity(Gravity.FILL_HORIZONTAL | Gravity.BOTTOM); + this.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + this.getWindow().setDimAmount(0.2f); + this.getWindow().setWindowAnimations(-1); + this.demsity = context.getResources().getDisplayMetrics().density; + + mLinearLayout = new LinearLayout(context); + mLinearLayout.setOrientation(LinearLayout.VERTICAL); + mLinearLayout.setPadding((int) (5 * demsity), 0, (int) (5 * demsity), (int) (5 * demsity)); + WindowManager.LayoutParams lp = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT); + lp.width = context.getResources().getDisplayMetrics().widthPixels; + this.setContentView(mLinearLayout, lp); + + { + mNegativeButton = new Button(context); + mNegativeButton.setText("取消"); + mNegativeButton.setGravity(Gravity.CENTER); + mNegativeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onNegative(); + } + }); + + float[] out = {5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity}; + RoundRectShape roundRectShape = new RoundRectShape(out, null, null); + ShapeDrawable shapeDrawable = new ShapeDrawable(roundRectShape); + shapeDrawable.getPaint().setColor(Color.argb(255, 255, 255, 255)); + + StateListDrawable stateListDrawable = new StateListDrawable(); + stateListDrawable.addState(new int[]{-android.R.attr.state_pressed}, shapeDrawable); + + ShapeDrawable shapeDrawable1 = new ShapeDrawable(roundRectShape); + shapeDrawable1.getPaint().setColor(Color.argb(255, 240, 240, 240)); + stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, shapeDrawable1); + + mNegativeButton.setBackground(stateListDrawable); + + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (45 * demsity)); + layoutParams.setMargins(0, (int) (5 * demsity), 0, 0); + mLinearLayout.addView(mNegativeButton, layoutParams); + } + + + { + mPositiveButton = new Button(context); + mPositiveButton.setText("确认"); + mPositiveButton.setGravity(Gravity.CENTER); + float[] out = {5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity, 5 * demsity}; + RoundRectShape roundRectShape = new RoundRectShape(out, null, null); + ShapeDrawable shapeDrawable = new ShapeDrawable(roundRectShape); + shapeDrawable.getPaint().setColor(Color.argb(255, 255, 255, 255)); + + StateListDrawable stateListDrawable = new StateListDrawable(); + stateListDrawable.addState(new int[]{-android.R.attr.state_pressed}, shapeDrawable); + + ShapeDrawable shapeDrawable1 = new ShapeDrawable(roundRectShape); + shapeDrawable1.getPaint().setColor(Color.argb(255, 240, 240, 240)); + stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, shapeDrawable1); + mPositiveButton.setBackground(stateListDrawable); + + mPositiveButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onPositive(); + } + }); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (45 * demsity)); + mLinearLayout.addView(mPositiveButton, 0, layoutParams); + } + + } + + @Override + public void show() { + super.show(); + TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, + Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0); + translateAnimation.setDuration(200); + + mLinearLayout.startAnimation(translateAnimation); + } + + public void onPositive() { + dismiss(); + if (mOnPostiveListener != null) { + mOnPostiveListener.onPostive(); + } + } + + public void onNegative() { + dismiss(); + if (mOnNegativeListener != null) { + mOnNegativeListener.onNegative(); + } + } + + public OnNegativeListener getOnNegativeListener() { + return mOnNegativeListener; + } + + public void setOnNegativeListener(OnNegativeListener mOnNegativeListener) { + this.mOnNegativeListener = mOnNegativeListener; + } + + public OnPostiveListener getOnPostiveListener() { + return mOnPostiveListener; + } + + public void setOnPostiveListener(OnPostiveListener mOnPostiveListener) { + this.mOnPostiveListener = mOnPostiveListener; + } + + public interface OnPostiveListener { + void onPostive(); + } + + public interface OnNegativeListener { + void onNegative(); + } + +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/image/AnimationView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/AnimationView.java new file mode 100644 index 00000000..056587ff --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/AnimationView.java @@ -0,0 +1,168 @@ +package xyz.wbsite.wbui.base.ui.image; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RadialGradient; +import android.graphics.RectF; +import android.graphics.Shader; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.CycleInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.RotateAnimation; + + +public class AnimationView extends View { + + private int fullDegrees = 300; + + private float progress = 0; + + private int offset = 0; + private int indicatorRadius; + private int indicatorWidth; + + private int arrowWidth; + + private int shadowRadius; + + private int shadowWidth; + + private Animation rotateAnimation; + + public AnimationView(Context context) { + super(context); + init(); + } + + public AnimationView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + indicatorWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getContext().getResources().getDisplayMetrics()); + shadowWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getContext().getResources().getDisplayMetrics()); + rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + rotateAnimation.setDuration(1000); + rotateAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); + rotateAnimation.setRepeatCount(Animation.INFINITE); + arrowWidth = indicatorWidth * 3; + } + + private int getOffset() { + int o = 0; + if (getPaddingTop() > o) { + o = getPaddingTop(); + } + + if (getPaddingRight() > o) { + o = getPaddingRight(); + } + + if (getPaddingBottom() > o) { + o = getPaddingBottom(); + } + + if (getPaddingLeft() > o) { + o = getPaddingLeft(); + } + + if (getPaddingStart() > o) { + o = getPaddingStart(); + } + + if (getPaddingEnd() > o) { + o = getPaddingEnd(); + } + + if (o == 0) { + o = shadowWidth + 10; + } + + return o; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + int width = getWidth(); + int height = getHeight(); + + {//阴影部分 + shadowRadius = Math.min(width, height) / 2; + Paint paint = new Paint(); + paint.setColor(Color.RED); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(shadowWidth); + RadialGradient radialGradient = new RadialGradient( + width / 2, + height / 2, + shadowRadius, + Color.parseColor("#888888"), + Color.TRANSPARENT, + Shader.TileMode.REPEAT); + paint.setShader(radialGradient); + canvas.drawCircle(getWidth() / 2, getHeight() / 2, shadowRadius - shadowWidth / 2 - 1, paint); + } + + {//指示器部分 + offset = getOffset(); + indicatorRadius = shadowRadius - offset - shadowWidth; + int currentDegress = (int) (fullDegrees * progress); + int currentArrowWidth = (int) (arrowWidth * progress); + + Path path = new Path(); + path.moveTo(width / 2, height / 2 - indicatorRadius); + RectF outer = new RectF( + width / 2 - indicatorRadius, + height / 2 - indicatorRadius, + width / 2 + indicatorRadius, + height / 2 + indicatorRadius + ); + path.addArc(outer, -90, currentDegress); + Paint paint1 = new Paint(); + paint1.setStyle(Paint.Style.STROKE); + paint1.setStrokeWidth(indicatorWidth); + paint1.setAntiAlias(true); + canvas.drawPath(path, paint1); + + Path arrowPath = new Path(); + arrowPath.moveTo(width / 2, height / 2 - indicatorRadius - currentArrowWidth / 2); + arrowPath.lineTo(width / 2, height / 2 - indicatorRadius + currentArrowWidth / 2); + arrowPath.lineTo(width / 2 + currentArrowWidth, height / 2 - indicatorRadius); + arrowPath.close(); + paint1.setStyle(Paint.Style.FILL); + canvas.rotate(currentDegress, width / 2, height / 2); + canvas.drawPath(arrowPath, paint1); + } + } + + public void startAnimation() { + progress = 1; + this.startAnimation(rotateAnimation); + } + + public void stopAnimation() { + this.clearAnimation(); + } + + + public float getProgress() { + return progress; + } + + public void setProgress(float progress) { + this.progress = progress; + invalidate(); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/image/BlurImage.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/BlurImage.java new file mode 100644 index 00000000..479bcb18 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/BlurImage.java @@ -0,0 +1,31 @@ +package xyz.wbsite.wbui.base.ui.image; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.AttributeSet; + +import xyz.wbsite.wbui.base.utils.FastBlur; + + +public class BlurImage extends android.support.v7.widget.AppCompatImageView { + public BlurImage(Context context) { + super(context); + } + + public BlurImage(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDraw(Canvas canvas) { + //获取当前显示的位图 + Bitmap bitMap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); + super.onDraw(new Canvas(bitMap)); + //模糊处理 + FastBlur.doBlur(bitMap, 10, true); + //绘图 + canvas.drawBitmap(bitMap, 0, 0, new Paint()); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/image/CircleImageView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/CircleImageView.java new file mode 100644 index 00000000..d96bf498 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/CircleImageView.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.wbsite.wbui.base.ui.image; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RadialGradient; +import android.graphics.Shader; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.OvalShape; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewCompat; +import android.view.View; +import android.view.animation.Animation; +import android.widget.ImageView; + +/** + * Private class created to work around issues with AnimationListeners being + * called before the animation is actually complete and support shadows on older + * platforms. + */ +public class CircleImageView extends ImageView { + + private static final int KEY_SHADOW_COLOR = 0x1E000000; + private static final int FILL_SHADOW_COLOR = 0x3D000000; + // PX + private static final float X_OFFSET = 0f; + private static final float Y_OFFSET = 1.75f; + private static final float SHADOW_RADIUS = 3.5f; + private static final int SHADOW_ELEVATION = 4; + + private Animation.AnimationListener mListener; + int mShadowRadius; + + public CircleImageView(Context context, int color) { + super(context); + final float density = getContext().getResources().getDisplayMetrics().density; + final int shadowYOffset = (int) (density * Y_OFFSET); + final int shadowXOffset = (int) (density * X_OFFSET); + + mShadowRadius = (int) (density * SHADOW_RADIUS); + + ShapeDrawable circle; + if (elevationSupported()) { + circle = new ShapeDrawable(new OvalShape()); + ViewCompat.setElevation(this, SHADOW_ELEVATION * density); + } else { + OvalShape oval = new OvalShadow(mShadowRadius); + circle = new ShapeDrawable(oval); + setLayerType(View.LAYER_TYPE_SOFTWARE, circle.getPaint()); + circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, + KEY_SHADOW_COLOR); + final int padding = mShadowRadius; + // set padding so the inner image sits correctly within the shadow. + setPadding(padding, padding, padding, padding); + } + circle.getPaint().setColor(color); + ViewCompat.setBackground(this, circle); + } + + private boolean elevationSupported() { + return android.os.Build.VERSION.SDK_INT >= 21; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (!elevationSupported()) { + setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight() + + mShadowRadius * 2); + } + } + + public void setAnimationListener(Animation.AnimationListener listener) { + mListener = listener; + } + + @Override + public void onAnimationStart() { + super.onAnimationStart(); + if (mListener != null) { + mListener.onAnimationStart(getAnimation()); + } + } + + @Override + public void onAnimationEnd() { + super.onAnimationEnd(); + if (mListener != null) { + mListener.onAnimationEnd(getAnimation()); + } + } + + /** + * Update the background color of the circle image view. + * + * @param colorRes Id of a color resource. + */ + public void setBackgroundColorRes(int colorRes) { + setBackgroundColor(ContextCompat.getColor(getContext(), colorRes)); + } + + @Override + public void setBackgroundColor(int color) { + if (getBackground() instanceof ShapeDrawable) { + ((ShapeDrawable) getBackground()).getPaint().setColor(color); + } + } + + private class OvalShadow extends OvalShape { + private RadialGradient mRadialGradient; + private Paint mShadowPaint; + + OvalShadow(int shadowRadius) { + super(); + mShadowPaint = new Paint(); + mShadowRadius = shadowRadius; + updateRadialGradient((int) rect().width()); + } + + @Override + protected void onResize(float width, float height) { + super.onResize(width, height); + updateRadialGradient((int) width); + } + + @Override + public void draw(Canvas canvas, Paint paint) { + final int viewWidth = CircleImageView.this.getWidth(); + final int viewHeight = CircleImageView.this.getHeight(); + canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2, mShadowPaint); + canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2 - mShadowRadius, paint); + } + + private void updateRadialGradient(int diameter) { + mRadialGradient = new RadialGradient(diameter / 2, diameter / 2, + mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT }, + null, Shader.TileMode.CLAMP); + mShadowPaint.setShader(mRadialGradient); + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/image/RoundImageView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/RoundImageView.java new file mode 100644 index 00000000..cb6e8f44 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/RoundImageView.java @@ -0,0 +1,55 @@ +package xyz.wbsite.wbui.base.ui.image; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.util.AttributeSet; +import android.widget.ImageView; + +public class RoundImageView extends ImageView { + public RoundImageView(Context context) { + super(context); + } + + public RoundImageView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDraw(Canvas canvas) { + + //获取原图 + int width = getWidth(); + int height = getHeight(); + Bitmap srcBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + super.onDraw(new Canvas(srcBitmap)); + + //绘制原图 + { + Bitmap shapeBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas shapeCanvas = new Canvas(shapeBitmap); + shapeCanvas.drawCircle(width / 2, height / 2, Math.min(width, height) / 2 - 4, new Paint()); + + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + shapeCanvas.drawBitmap(srcBitmap, 0, 0, paint); + + canvas.drawBitmap(shapeBitmap, 0, 0, new Paint()); + } + + //绘制边框 + { + Paint paint = new Paint(); + paint.setStyle(Paint.Style.STROKE); + paint.setColor(Color.GREEN); + paint.setStrokeWidth(4); + paint.setAntiAlias(true); + canvas.drawCircle(width / 2, height / 2, Math.min(width, height) / 2 - 4, paint); + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/image/WBUISquareImageView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/WBUISquareImageView.java new file mode 100644 index 00000000..113e29ea --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/WBUISquareImageView.java @@ -0,0 +1,55 @@ +package xyz.wbsite.wbui.base.ui.image; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; + +import xyz.wbsite.webclient.R; + +public class WBUISquareImageView extends android.support.v7.widget.AppCompatImageView { + + private int weight_width = 1; + private int weight_height = 1; + + public WBUISquareImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public WBUISquareImageView(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.uiSquare); + weight_width = typedArray.getInt(R.styleable.uiSquare_weight_width,1); + weight_height = typedArray.getInt(R.styleable.uiSquare_weight_height,1); + } + + public WBUISquareImageView(Context context) { + super(context); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec)); + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); + + int expectWidth = height * weight_width / weight_height; + int expectHeght = width * weight_height / weight_width; + + if (expectWidth == 0){ + expectWidth = expectHeght; + }else if (expectHeght == 0){ + expectHeght = expectWidth; + }else { + if ((double) expectWidth / width <= (double) expectHeght / height) { + expectHeght = expectWidth * weight_height / weight_width; + } else { + expectWidth = expectHeght * weight_width / weight_height; + } + } + + widthMeasureSpec = MeasureSpec.makeMeasureSpec(expectWidth, MeasureSpec.EXACTLY); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(expectHeght, MeasureSpec.EXACTLY); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/image/WbRoundImageView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/WbRoundImageView.java new file mode 100644 index 00000000..36f61411 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/image/WbRoundImageView.java @@ -0,0 +1,48 @@ +package xyz.wbsite.wbui.base.ui.image; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.util.AttributeSet; + +import xyz.wbsite.wbui.base.utils.FastBlur; + + +public class WbRoundImageView extends android.support.v7.widget.AppCompatImageView { + public WbRoundImageView(Context context) { + super(context); + } + + public WbRoundImageView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDraw(Canvas canvas) { + + //获取原图 + int width = getWidth(); + int height = getHeight(); + Bitmap srcBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + super.onDraw(new Canvas(srcBitmap)); + + canvas.drawBitmap(FastBlur.doBlur(srcBitmap, 20, false), 0, 0, new Paint()); + + //绘制原图 + { + Bitmap shapeBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas shapeCanvas = new Canvas(shapeBitmap); + shapeCanvas.drawCircle(width / 2, height / 2, Math.min(width, height) / 2 - 4, new Paint()); + + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + shapeCanvas.drawBitmap(srcBitmap, 0, 0, paint); + + canvas.drawBitmap(shapeBitmap, 0, 0, new Paint()); + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/layout/WBUIPaternalLayout.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/layout/WBUIPaternalLayout.java new file mode 100644 index 00000000..e36812a6 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/layout/WBUIPaternalLayout.java @@ -0,0 +1,515 @@ +package xyz.wbsite.wbui.base.ui.layout; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.support.v4.view.NestedScrollingParent; +import android.support.v4.view.NestedScrollingParentHelper; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.Scroller; + +import xyz.wbsite.wbui.base.utils.DensityUtil; + +public class WBUIPaternalLayout extends ViewGroup implements NestedScrollingParent, AbsListView.OnScrollListener { + private static final String TAG = "WBUIPaternalLayout"; + private final NestedScrollingParentHelper mNestedScrollingParentHelper; + + private View mTargetView; + private View mPullView; + private View mPushView; + + private int mPullViewIndex = -1; + private int mPushViewIndex = -1; + private boolean mNestedScrollInProgress; + private float mInitialDownY; + private float mInitialDownX; + private float mLastMotionY; + private float mDragRate = 0.8f; + private int mTheshold = 20; + private Scroller mScroller; + + private int firstVisibleItem = 0; + private int visibleItemCount = 0; + private int totalItemCount = 0; + + private IPullViewBuilder pullViewBuilder; + private IPushViewBuilder pushViewBuilder; + + private int pullOffset = 0; + private int pullHeight = DensityUtil.dp2px(getContext(), 100); + private int pushOffset = 0; + private int pushHeight = DensityUtil.dp2px(getContext(), 100); + + private boolean mIsPull; + private boolean mIsPush; + boolean isTop = false; + boolean isBottom = false; + + int width = 0; + int height = 0; + + int mTouchSlop = 2; + + @Override + public void onViewAdded(View child) { + int maxChild = 1 + (mPullView != null ? 1 : 0) + (mPushView != null ? 1 : 0); + if (getChildCount() > maxChild) { + throw new IllegalStateException("WBUIPaternalLayout can host only one child"); + } + + if (child instanceof AbsListView) { + AbsListView listView = (AbsListView) child; + listView.setOnScrollListener(this); + } + super.onViewAdded(child); + } + + public WBUIPaternalLayout(Context context) { + this(context, null); + } + + public WBUIPaternalLayout(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + + mScroller = new Scroller(getContext()); + mScroller.setFriction(ViewConfiguration.getScrollFriction()); + + ViewCompat.setChildrenDrawingOrderEnabled(this, true); + mNestedScrollingParentHelper = new NestedScrollingParentHelper(this); + } + + public interface IPullViewBuilder { + View createView(); + + void onChange(View view, int currentHeight, int pullHeight, boolean isNearHeight); + + void onAction(View view, int pullHeight, WBUIPaternalLayout layout); + + void onFinish(View view); + } + + public interface IPushViewBuilder { + View createView(); + + void onChange(View view, int currentHeight, int pushHeight, boolean isNearHeight); + + void onAction(View view, int pushHeight, WBUIPaternalLayout layout); + + void onFinish(View view); + } + + public void setPullViewBuilder(IPullViewBuilder pullViewBuilder) { + this.pullViewBuilder = pullViewBuilder; + if (mPullView == null) { + mPullView = pullViewBuilder.createView(); + addView(mPullView); + } + } + + public void setPushViewBuilder(IPushViewBuilder pushViewBuilder) { + this.pushViewBuilder = pushViewBuilder; + if (mPushView == null) { + mPushView = pushViewBuilder.createView(); + addView(mPushView); + } + } + + @Override + protected int getChildDrawingOrder(int childCount, int i) { + if (mPullViewIndex < 0 || mPushViewIndex < 0) { + return i; + } + if (i == mPullViewIndex || i == mPushViewIndex) { + return childCount - 1; + } + if (i > mPullViewIndex || i > mPushViewIndex) { + return i - 1; + } + return i; + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean b) { + if ((android.os.Build.VERSION.SDK_INT < 21 && mTargetView instanceof AbsListView) + || (mTargetView != null && !ViewCompat.isNestedScrollingEnabled(mTargetView))) { + } else { + super.requestDisallowInterceptTouchEvent(b); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + ensureTargetView(); + if (mTargetView == null) { + Log.d(TAG, "onMeasure: mTargetView == null"); + return; + } + int targetMeasureWidthSpec = MeasureSpec.makeMeasureSpec( + getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY); + int targetMeasureHeightSpec = MeasureSpec.makeMeasureSpec( + getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY); + mTargetView.measure(targetMeasureWidthSpec, targetMeasureHeightSpec); + if (mPullView != null) { + measureChild(mPullView, widthMeasureSpec, heightMeasureSpec); + } + if (mPushView != null) { + measureChild(mPushView, widthMeasureSpec, heightMeasureSpec); + } + mPullViewIndex = -1; + for (int i = 0; i < getChildCount(); i++) { + if (getChildAt(i) == mPullView && mPullView != null) { + mPullViewIndex = i; + break; + } + if (getChildAt(i) == mPushView && mPushView != null) { + mPushViewIndex = i; + break; + } + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + width = getMeasuredWidth(); + height = getMeasuredHeight(); + if (getChildCount() == 0) { + return; + } + ensureTargetView(); + if (mTargetView == null) { + Log.d(TAG, "onLayout: mTargetView == null"); + return; + } + + final int childWidth = width - getPaddingLeft() - getPaddingRight(); + final int childHeight = height - getPaddingTop() - getPaddingBottom(); + mTargetView.layout(0, 0, 0 + childWidth, 0 + childHeight); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + ensureTargetView(); + + if (pullOffset > 0 || pushOffset > 0) { + return true; + } + + if (canChildScrollUp(mTargetView)) { + isTop = false; + } else { + isTop = true; + } + + if (canChildScrollDown(mTargetView)) { + isBottom = false; + } else { + isBottom = true; + } + + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mInitialDownX = ev.getX(); + mInitialDownY = ev.getY(); + mLastMotionY = ev.getY(); + break; + case MotionEvent.ACTION_MOVE: + final float x = ev.getX(); + final float y = ev.getY(); + final float dx = x - mInitialDownX; + final float dy = y - mInitialDownY; + boolean isYDrag = Math.abs(dy) > Math.abs(dx); + + if (isYDrag && isTop && dy > 0) {//pull + System.out.println(); + mLastMotionY = y; + mIsPull = true; + return true; + } + + if (isYDrag && isBottom && dy < 0) {//push + System.out.println(); + mLastMotionY = y; + mIsPush = true; + return true; + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + reset(); + break; + } + + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); + + switch (action) { + case MotionEvent.ACTION_DOWN: + if (!mScroller.isFinished()) { + mScroller.abortAnimation(); + } + break; + case MotionEvent.ACTION_MOVE: { + final float x = ev.getX(); + final float y = ev.getY(); + float dy = (y - mLastMotionY) * mDragRate; + + if (Math.abs(dy) < mTouchSlop) { + return false; + } + + if (mIsPull && pullViewBuilder != null && mPullView != null) { + pullOffset += dy; + if (pullOffset > pullHeight) { + pullOffset = pullHeight; + } + pullViewBuilder.onChange(mPullView, pullOffset, pullHeight, pullOffset >= pullHeight - mTheshold); + mPullView.setLayoutParams(new LayoutParams(width, pullHeight)); + mPullView.layout(0, pullOffset - pullHeight, width, pullOffset); + postInvalidate(); + } + if (mIsPush && pushViewBuilder != null && mPushView != null) { + if (dy > 0) { + pushOffset -= Math.abs(dy); + } else { + pushOffset += Math.abs(dy); + } + + if (pushOffset > pushHeight) { + pushOffset = pushHeight; + } + final int width = getMeasuredWidth(); + final int height = getMeasuredHeight(); + pushViewBuilder.onChange(mPushView, pushOffset, pushHeight, pushOffset >= pushHeight - mTheshold); + mPushView.setLayoutParams(new LayoutParams(width, pushHeight)); + mPushView.layout(0, height - pushOffset, width, height - pushOffset + pushHeight); + postInvalidate(); + } + break; + } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { + reset(); + return false; + } + } + mLastMotionY = ev.getY(); + return true; + } + + private void ensureTargetView() { + if (mTargetView == null) { + for (int i = 0; i < getChildCount(); i++) { + View view = getChildAt(i); + if (!view.equals(mPullView) && !view.equals(mPushView)) { + mTargetView = view; + break; + } + } + } + } + + public void reset() { + mIsPull = mIsPush = false; + + if (!mScroller.isFinished()) { + mScroller.abortAnimation(); + } + + if (pullOffset >= pullHeight - mTheshold) { + pullViewBuilder.onAction(mPullView, pullHeight, this); + } else if (pullOffset > 0) { + mScroller.startScroll(0, 0, 0, pullOffset, 800); + postInvalidate(); + } + + if (pushOffset >= pushHeight - mTheshold) { + pushViewBuilder.onAction(mPushView, pushHeight, this); + } else if (pushOffset > 0) { + mScroller.startScroll(0, 0, 0, pushOffset, 800); + postInvalidate(); + } + } + + public void finish() { + if (pullOffset > 0) { + pullViewBuilder.onFinish(mPullView); + mScroller.startScroll(0, 0, 0, pullOffset, 800); + postInvalidate(); + } else if (pushOffset > 0) { + pushViewBuilder.onFinish(mPullView); + mScroller.startScroll(0, 0, 0, pushOffset, 800); + postInvalidate(); + } + } + + @Override + public void computeScroll() { + if (mIsPull || mIsPush) { + return; + } + if (mScroller.computeScrollOffset()) { + if (pullOffset > 0) { + pullOffset = pullOffset - mScroller.getCurrY(); + if (pullOffset <= 0) { + pullOffset = 0; + mScroller.abortAnimation(); + } + mPullView.setLayoutParams(new LayoutParams(width, pullOffset)); + mPullView.layout(0, 0, width, pullOffset); + postInvalidate(); + } else if (pushOffset > 0) { + pushOffset = pushOffset - mScroller.getCurrY(); + if (pushOffset <= 0) { + pushOffset = 0; + mScroller.abortAnimation(); + } + mPushView.setLayoutParams(new LayoutParams(width, pushOffset)); + mPushView.layout(0, height - pushOffset, width, height); + postInvalidate(); + } else { + postInvalidate(); + } + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + reset(); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + if (!enabled) { + reset(); + postInvalidate(); + } + } + + public boolean canChildScrollUp(View mTargetView) { + if (mTargetView == null) { + return false; + } + if (android.os.Build.VERSION.SDK_INT < 14) { + if (mTargetView instanceof AbsListView) { + final AbsListView absListView = (AbsListView) mTargetView; + return absListView.getChildCount() > 0 + && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) + .getTop() < absListView.getPaddingTop()); + } else { + return ViewCompat.canScrollVertically(mTargetView, -1) || mTargetView.getScrollY() > 0; + } + } else { + return ViewCompat.canScrollVertically(mTargetView, -1); + } + } + + public boolean canChildScrollDown(View mTargetView) { + if (mTargetView == null) { + return false; + } + if (android.os.Build.VERSION.SDK_INT < 14) { + if (mTargetView instanceof AbsListView) { + final AbsListView absListView = (AbsListView) mTargetView; + View lastChild = absListView.getChildAt(absListView.getChildCount() - 1); + return this.firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0 && lastChild.getBottom() >= absListView.getPaddingBottom(); + } else { + return ViewCompat.canScrollVertically(mTargetView, 1) || mTargetView.getScrollY() > 0; + } + } else { + return ViewCompat.canScrollVertically(mTargetView, 1); + } + } + + @Override + public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { + return isEnabled() && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; + } + + @Override + public void onNestedScrollAccepted(View child, View target, int axes) { + mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); + mNestedScrollInProgress = true; + } + + @Override + public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { + if (dy > 0) { + if (dy >= 0) { + consumed[1] = 0; + pullOffset = 0; + } else { + consumed[1] = dy; + pullOffset += dy; + } + } + } + + @Override + public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { + if (dyUnconsumed < 0 && !canChildScrollUp(mTargetView)) { + pullOffset += dyUnconsumed; + } + } + + @Override + public int getNestedScrollAxes() { + return mNestedScrollingParentHelper.getNestedScrollAxes(); + } + + @Override + public void onStopNestedScroll(View child) { + mNestedScrollingParentHelper.onStopNestedScroll(child); + if (mNestedScrollInProgress) { + mNestedScrollInProgress = false; + reset(); + } + } + + @Override + public boolean onNestedPreFling(View target, float velocityX, float velocityY) { + mNestedScrollInProgress = false; + reset(); + return true; + } + + @Override + public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { + try { + return super.onNestedFling(target, velocityX, velocityY, consumed); + } catch (Throwable e) { + // android 24及以上ViewGroup会继续往上派发, 23以及以下直接返回false + // 低于5.0的机器和RecyclerView配合工作时,部分机型会调用这个方法,但是ViewGroup并没有实现这个方法,会报错,这里catch一下 + } + return false; + } + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + this.firstVisibleItem = firstVisibleItem; + this.visibleItemCount = visibleItemCount; + this.totalItemCount = totalItemCount; + } + + public interface OnChildScrollUpCallback { + boolean canChildScrollUp(WBUIPaternalLayout parent, @Nullable View child); + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/layout/WBUISquareLayout.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/layout/WBUISquareLayout.java new file mode 100644 index 00000000..692f2987 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/layout/WBUISquareLayout.java @@ -0,0 +1,90 @@ +package xyz.wbsite.wbui.base.ui.layout; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.widget.RelativeLayout; + +import xyz.wbsite.webclient.R; + +public class WBUISquareLayout extends RelativeLayout { + + private int weight_width = 1; + private int weight_height = 1; + private int mWidth; + private int mHeight; + + private View targetView; + + public WBUISquareLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public WBUISquareLayout(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.uiSquare); + weight_width = typedArray.getInt(R.styleable.uiSquare_weight_width, weight_width); + weight_height = typedArray.getInt(R.styleable.uiSquare_weight_height, weight_height); + } + + public WBUISquareLayout(Context context) { + super(context); + } + + private void ensureView() { + int childCount = getChildCount(); + if (childCount > 1) { + throw new IllegalStateException("WBUISquareLayout can host only one child"); + } else if (childCount == 1) { + targetView = getChildAt(0); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + int wMode = MeasureSpec.getMode(widthMeasureSpec); + int wSize = MeasureSpec.getSize(widthMeasureSpec); + int hMode = MeasureSpec.getMode(heightMeasureSpec); + int hSize = MeasureSpec.getSize(heightMeasureSpec); + + if (wMode == MeasureSpec.EXACTLY && hMode == MeasureSpec.EXACTLY) { + mWidth = wSize; + mHeight = hSize; + } else if (wMode == MeasureSpec.EXACTLY && hMode != MeasureSpec.EXACTLY) { + mWidth = wSize; + mHeight = wSize * weight_height / weight_width; + } else if (wMode != MeasureSpec.EXACTLY && hMode == MeasureSpec.EXACTLY) { + mWidth = hSize * weight_width / weight_height; + mHeight = hSize; + } else if (wMode != MeasureSpec.EXACTLY && hMode != MeasureSpec.EXACTLY) { + measureChildren(widthMeasureSpec, heightMeasureSpec); + ensureView(); + if (targetView != null) { + int measuredWidth = targetView.getMeasuredWidth(); + int measuredHeight = targetView.getMeasuredHeight(); + mWidth = mHeight = Math.max(measuredWidth, measuredHeight); + } + } + measureChildren(MeasureSpec.makeMeasureSpec(mWidth,MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(mHeight,MeasureSpec.AT_MOST)); + setMeasuredDimension(mWidth, mHeight); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + ensureView(); + if (targetView != null) { + int measuredWidth = targetView.getMeasuredWidth(); + int measuredHeight = targetView.getMeasuredHeight(); + if (measuredWidth > measuredHeight) { + int dy = measuredWidth - measuredHeight; + targetView.layout(0, dy / 2, measuredWidth, measuredHeight + dy / 2); + } else { + int dx = measuredHeight - measuredWidth; + targetView.layout(dx / 2, 0, measuredWidth + dx / 2, measuredHeight); + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/layout/WbViewPager.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/layout/WbViewPager.java new file mode 100644 index 00000000..05a71d4f --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/layout/WbViewPager.java @@ -0,0 +1,104 @@ +package xyz.wbsite.wbui.base.ui.layout; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +/** + * Created by bingwang on 2016/5/16. + */ +public class WbViewPager extends RelativeLayout { + + private LinearLayout mIndexView; + private ViewPager mViewPager; + + public WbViewPager(Context context) { + super(context); + } + + public WbViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + + mViewPager = new ViewPager(context); + this.addView(mViewPager, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + mIndexView = new LinearLayout(context); + mIndexView.setGravity(Gravity.CENTER); + mIndexView.setOrientation(LinearLayout.HORIZONTAL); + LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 50); + layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL); + layoutParams.setMargins(0, 0, 0, 10); + this.addView(mIndexView, layoutParams); + } + + public void setAdapter(PagerAdapter adapter) { + mViewPager.setAdapter(adapter); + + for (int i = 0; i < adapter.getCount(); i++) { + Circle circle = new Circle(getContext()); + if (i == 0) { + circle.setColor(Color.argb(255, 100, 100, 100)); + } + mIndexView.addView(circle, new ViewGroup.LayoutParams(40, 20)); + } + + + mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + for (int i = 0; i < mIndexView.getChildCount(); i++) { + Circle circle = (Circle) mIndexView.getChildAt(i); + circle.setColor(Color.argb(255, 200, 200, 200)); + circle.invalidate(); + } + Circle circle = (Circle) mIndexView.getChildAt(position); + circle.setColor(Color.argb(255, 100, 100, 100)); + circle.invalidate(); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + } + + private class Circle extends View { + private int mColor = Color.argb(255, 200, 200, 200); + private Paint mPaint = new Paint(); + + public Circle(Context context) { + super(context); + } + + @Override + protected void onDraw(Canvas canvas) { + mPaint.setColor(mColor); + canvas.drawCircle(getWidth() / 2, getHeight() / 2, Math.min(getWidth() / 2, getHeight() / 2), mPaint); + } + + public int getColor() { + return mColor; + } + + public void setColor(int mColor) { + this.mColor = mColor; + } + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/list/BoundListView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/list/BoundListView.java new file mode 100644 index 00000000..afde016d --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/list/BoundListView.java @@ -0,0 +1,144 @@ +package xyz.wbsite.wbui.base.ui.list; + +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.ListView; + +public class BoundListView extends ListView { + public static int OVERSCROLL_STATE_NORMAL = 0;//正常 + public static int OVERSCROLL_STATE_PULL = -1;//拉 + public static int OVERSCROLL_STATE_PUSH = 1;//推 + + private int mOverScrollState = OVERSCROLL_STATE_NORMAL; + private float lastY = -1;//最近一次Y轴位置 + + /** + * 小于0-->pull,大于0-->push + */ + private int mOverY = 0; + + public BoundListView(Context context) { + super(context); + } + + public BoundListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void setOverScrollMode(int mode) { + //去掉边缘发光效果 + super.setOverScrollMode(OVER_SCROLL_NEVER); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + if (mOverY < 0) { + mOverScrollState = OVERSCROLL_STATE_PULL; + } else if (mOverY > 0) { + mOverScrollState = OVERSCROLL_STATE_PUSH; + } + } + if (mOverScrollState != OVERSCROLL_STATE_NORMAL) { + switch (ev.getAction()) { + case MotionEvent.ACTION_UP: + mOverScrollState = OVERSCROLL_STATE_NORMAL; + onBound(); + return true; + } + + if (lastY < 0) { + lastY = ev.getY(); + } else { + if (Math.abs(ev.getY() - lastY) > 30) { + lastY = ev.getY(); + return true; + } + if (ev.getY() - lastY != 0) { + if (mOverScrollState == OVERSCROLL_STATE_PULL) { + mOverY -= (ev.getY() - lastY); + if (mOverY > 0) { + mOverY = 0; + mOverScrollState = OVERSCROLL_STATE_NORMAL; + ev.setAction(MotionEvent.ACTION_DOWN); + super.onTouchEvent(ev); + } + } else if (mOverScrollState == OVERSCROLL_STATE_PUSH) { + mOverY -= (ev.getY() - lastY); + if (mOverY < 0) { + mOverY = 0; + mOverScrollState = OVERSCROLL_STATE_NORMAL; + ev.setAction(MotionEvent.ACTION_DOWN); + super.onTouchEvent(ev); + } + } + } + lastY = ev.getY(); + invalidate(); + } + return true; + } else { + return super.onTouchEvent(ev); + } + } + + @Override + protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { +// if (isTouchEvent) { +// if (deltaY < 0) { +// mOverScrollState = OVERSCROLL_STATE_PULL; +// } else { +// mOverScrollState = OVERSCROLL_STATE_PUSH; +// } +// mOverY += deltaY; +// invalidate(); +// } + return true; + } + + @Override + public void draw(Canvas canvas) { + canvas.translate(0, -mOverY / 2); + super.draw(canvas); + } + + /** + * 回弹归位 + */ + private void onBound() { + final int during = 1000; + final int d = 10; + if (mOverY != 0) { + new Thread() { + @Override + public void run() { + int flag = 1; + if (mOverY < 0) { + flag = -1; + mOverY = -mOverY; + } + int temp = mOverY; + + int i = 0; + while (i < during && Math.abs(temp) >= 5 && mOverScrollState == OVERSCROLL_STATE_NORMAL) { + try { + Thread.sleep(d); + } catch (InterruptedException e) { + e.printStackTrace(); + } + i += d; + temp = (int) (Math.pow((double) i / during - 1, 2) * temp); + mOverY = flag * temp; + postInvalidate(); + } + mOverY = 0; + postInvalidate(); + } + }.start(); + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/list/NoScrollListView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/list/NoScrollListView.java new file mode 100644 index 00000000..9ee2b4cf --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/list/NoScrollListView.java @@ -0,0 +1,33 @@ +package xyz.wbsite.wbui.base.ui.list; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ListView; + +public class NoScrollListView extends ListView { + + public NoScrollListView(Context context) { + super(context); + } + + public NoScrollListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NoScrollListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * 设置listView不可滑动 + * + * @param widthMeasureSpec + * @param heightMeasureSpec + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, + MeasureSpec.AT_MOST); + super.onMeasure(widthMeasureSpec, expandSpec); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/list/OverScrollView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/list/OverScrollView.java new file mode 100644 index 00000000..1e778524 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/list/OverScrollView.java @@ -0,0 +1,178 @@ +package xyz.wbsite.wbui.base.ui.list; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.ScrollView; + +import java.lang.reflect.Field; + +public class OverScrollView extends ScrollView { + LinearLayout mLinearLayout; + LinearLayout mHeader; + LinearLayout mFooter; + private int mLastScrollY = -1; + private int mOverY = 0; + private int mLastMotionY = 0; + + public static int OVERSCROLL_STATE_NORMAL = 0;//正常 + public static int OVERSCROLL_STATE_PULL = 1;//拉 + public static int OVERSCROLL_STATE_PUSH = -1;//推 + + private int mOverScrollState = OVERSCROLL_STATE_NORMAL; + + public OverScrollView(Context context) { + super(context); + } + + public OverScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void setOverScrollMode(int mode) { + //去掉边缘发光效果 + super.setOverScrollMode(OVER_SCROLL_NEVER); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + Log.i("--->", "addView"); + + mLinearLayout = new LinearLayout(getContext()); + mLinearLayout.setOrientation(LinearLayout.VERTICAL); + + mHeader = new LinearLayout(getContext()); + mFooter = new LinearLayout(getContext()); + mLinearLayout.addView(mHeader, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + mLinearLayout.addView(child, params); + mLinearLayout.addView(mFooter, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + super.addView(mLinearLayout, index, layoutParams); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_UP: + mOverScrollState = OVERSCROLL_STATE_NORMAL; + onBound(); + } + + if (mOverScrollState == OVERSCROLL_STATE_PULL) { + if (ev.getY() > mLastMotionY) { + mOverY = (int) (ev.getY() - mLastMotionY); + this.fullScroll(ScrollView.FOCUS_UP); + mHeader.setMinimumHeight(mOverY); + Log.i("-->", "mOverY=" + mOverY); + } else { + mOverScrollState = OVERSCROLL_STATE_NORMAL; + } + return true; + } else if (mOverScrollState == OVERSCROLL_STATE_PUSH) { + if (mLastMotionY > ev.getY()) { + mOverY = (int) (ev.getY() - mLastMotionY); + mFooter.setMinimumHeight(-mOverY); + this.fullScroll(ScrollView.FOCUS_DOWN); + Log.i("-->", "mOverY=" + mOverY); + } else { + mOverScrollState = OVERSCROLL_STATE_NORMAL; + } + return true; + } + return super.onTouchEvent(ev); + } + + /** + * 回弹归位 + */ + private void onBound() { + final Handler handler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (mOverY > 0) { + OverScrollView.this.fullScroll(ScrollView.FOCUS_UP); + mHeader.setMinimumHeight(mOverY); + } else if (mOverY < 0) { + OverScrollView.this.fullScroll(ScrollView.FOCUS_DOWN); + mFooter.setMinimumHeight(-mOverY); + } else { + mHeader.setMinimumHeight(0); + mFooter.setMinimumHeight(0); + } + } + }; + new Thread() { + int during = 1000; + int d = 10; + + @Override + public void run() { + int flag = 1; + if (mOverY < 0) { + flag = -1; + mOverY = -mOverY; + } + int temp = mOverY; + + int i = 0; + while (i < during && Math.abs(temp) >= 5 && mOverScrollState == OVERSCROLL_STATE_NORMAL) { + try { + Thread.sleep(d); + } catch (InterruptedException e) { + e.printStackTrace(); + } + i += d; + temp = (int) (Math.pow((double) i / during - 1, 2) * temp); + mOverY = flag * temp; + handler.sendEmptyMessage(1); + } + mOverY = 0; + } + }.start(); + } + + @Override + protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { + + //只捕捉边界值 + if (mOverScrollState == OVERSCROLL_STATE_NORMAL && (scrollY == 0 || scrollY == scrollRangeY)) { + if (scrollY == 0) { + mOverScrollState = OVERSCROLL_STATE_PULL; + } else { + mOverScrollState = OVERSCROLL_STATE_PUSH; + } + try { + Class aClass = this.getClass().getSuperclass(); + Field mLastMotionYField = aClass.getDeclaredField("mLastMotionY"); + mLastMotionYField.setAccessible(true); + mLastMotionY = (int) mLastMotionYField.get(this); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } +// switch (mOverScrollState){ +// case -1: +// Log.i("-->","PUSH"); +// break; +// case 1: +// Log.i("-->","PULL"); +// break; +// case 0: +// Log.i("-->","NORMAL"); +// break; +// } + +// Log.i("-->-->","deltaY-->"+getScaleY()+"/"+scrollY+"---"+scrollRangeY); + return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/list/OverView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/list/OverView.java new file mode 100644 index 00000000..f0d4ca27 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/list/OverView.java @@ -0,0 +1,92 @@ +package xyz.wbsite.wbui.base.ui.list; + +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.widget.LinearLayout; + +/** + * Created by wangbing on 2016/4/17. + */ +public class OverView extends LinearLayout { + private int mOverY = 0; + private int mLastY = -1; + + public OverView(Context context) { + super(context); + } + + public OverView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + Log.i("-->", "Y" + event.getY()); + Log.i("-->", "mLastY" + mLastY); + if (mLastY == -1) { + mLastY = (int) event.getY(); + } else { + mOverY = (int) (event.getY() - mLastY); + invalidate(); + } + switch (event.getAction()) { + case MotionEvent.ACTION_UP: + mLastY = -1; + onBound(); + return true; + } + return true; + } + + private void onBound() { + final int during = 1000; + final int d = 10; + if (mOverY != 0) { + new Thread() { + @Override + public void run() { + int flag = 1; + if (mOverY < 0) { + flag = -1; + mOverY = -mOverY; + } + int temp = mOverY; + + int i = 0; + while (i < during && Math.abs(temp) >= 5) { + try { + Thread.sleep(d); + } catch (InterruptedException e) { + e.printStackTrace(); + } + i += d; + temp = (int) (Math.pow((double) i / during - 1, 2) * temp); + mOverY = flag * temp; + postInvalidate(); + } + mOverY = 0; + postInvalidate(); + } + }.start(); + } + } + + @Override + public void draw(Canvas canvas) { + canvas.translate(0, mOverY / 2); + super.draw(canvas); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_MOVE) { + return true; + } + return false; + } + + +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingProgressBar.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingProgressBar.java new file mode 100644 index 00000000..41e003a3 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingProgressBar.java @@ -0,0 +1,142 @@ +package xyz.wbsite.wbui.base.ui.other; + + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RadialGradient; +import android.graphics.RectF; +import android.graphics.Shader; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.LinearInterpolator; + +import xyz.wbsite.wbui.base.utils.DensityUtil; + +public class LoadingProgressBar extends View { + private ValueAnimator mAnimator; + + private Paint mShadowPaint = new Paint(); + private Paint mPaint = new Paint(); + private int shadowWidth = DensityUtil.dp2px(getContext(), 4); + private int backgroundWidth = DensityUtil.dp2px(getContext(), 4); + private int indicatorWidth = DensityUtil.dp2px(getContext(), 3); + + private int startAngle = -90; + private int sweepAngle = 100; + private int mDuration = 1000; + + private int defaultSize = DensityUtil.dp2px(getContext(), 50); + + private boolean isAnimating = false; + + public LoadingProgressBar(Context context) { + super(context); + } + + public LoadingProgressBar(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int wMode = MeasureSpec.getMode(widthMeasureSpec); + int hMode = MeasureSpec.getMode(heightMeasureSpec); + + if (wMode == MeasureSpec.AT_MOST) { + setMeasuredDimension(defaultSize, MeasureSpec.getSize(heightMeasureSpec)); + return; + } else if (hMode == MeasureSpec.AT_MOST) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), defaultSize); + return; + } else if (wMode == MeasureSpec.AT_MOST && hMode == MeasureSpec.AT_MOST) { + setMeasuredDimension(defaultSize, defaultSize); + return; + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onDraw(Canvas canvas) { + int width = getWidth(); + int height = getHeight(); + int radius = Math.min(width, height) / 2; + if (radius >= shadowWidth + backgroundWidth) { + RadialGradient radialGradient = new RadialGradient(width / 2, height / 2, radius, new int[]{Color.GRAY, Color.TRANSPARENT}, null, Shader.TileMode.CLAMP); + mShadowPaint.setShader(radialGradient); + canvas.drawCircle(width / 2, height / 2, radius, mShadowPaint); + + mPaint.setColor(Color.WHITE); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setAntiAlias(true); + canvas.drawCircle(width / 2, height / 2, radius - shadowWidth, mPaint); + } + + + if (radius >= shadowWidth + backgroundWidth + indicatorWidth) { + mPaint.setColor(Color.RED); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(indicatorWidth); + + RectF rectF = new RectF( + width / 2 - radius + shadowWidth + backgroundWidth + indicatorWidth / 2, + height / 2 - radius + shadowWidth + backgroundWidth + indicatorWidth / 2, + width / 2 + radius - shadowWidth - backgroundWidth - indicatorWidth / 2, + height / 2 + radius - shadowWidth - backgroundWidth - indicatorWidth / 2); + canvas.drawArc(rectF, startAngle, sweepAngle, false, mPaint); + } + } + + public void setProgress(float process) { + startAngle = (int) (process * 360 - 90); + sweepAngle = (int) (Math.abs(startAngle + 90 - 180) * 1.0f / 180 * 120 + 45); + invalidate(); + } + + public void startAnimation() { + mAnimator = ValueAnimator.ofInt(-90, 270); + mAnimator.setDuration(mDuration); + mAnimator.setRepeatCount(ValueAnimator.INFINITE); + mAnimator.setRepeatMode(ValueAnimator.RESTART); + mAnimator.setInterpolator(new LinearInterpolator()); + mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + public void onAnimationUpdate(ValueAnimator animation) { + startAngle = (int) animation.getAnimatedValue(); + sweepAngle = (int) (Math.abs(startAngle + 90 - 180) * 1.0f / 180 * 120 + 45); + invalidate(); + } + }); + mAnimator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + isAnimating = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + isAnimating = false; + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + }); + mAnimator.start(); + } + + public void stopAnimation() { + if (isAnimating) { + mAnimator.cancel(); + startAngle = -90; + sweepAngle = 100; + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingView.java new file mode 100644 index 00000000..c8121d78 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingView.java @@ -0,0 +1,114 @@ +package xyz.wbsite.wbui.base.ui.other; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.SweepGradient; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.WindowManager; + +/** + * Created by bingwang on 2016/5/14. + */ +public class LoadingView extends View { + /** + * 周期 + */ + private int mDuring = 1000; + /** + * 旋转步长 + */ + private int mStep = 10; + /** + * 当前旋转角度 + */ + private int mSweep = 0; + /** + * 前景色 + */ + private int mColor = Color.BLACK; + + public LoadingView(Context context) { + super(context); + } + + public LoadingView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = 0; + int height = 0; + + DisplayMetrics displayMetrics = new DisplayMetrics(); + WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + wm.getDefaultDisplay().getMetrics(displayMetrics); + + int widhtMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + if (widhtMode == MeasureSpec.EXACTLY) { + width = widthSize; + } else { + width = (int) (50 * displayMetrics.density); + } + + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + if (heightMode == MeasureSpec.EXACTLY) { + height = heightSize; + } else { + height = (int) (50 * displayMetrics.density); + } + setMeasuredDimension(width, height); + } + + @Override + public void onDraw(Canvas canvas) { + int scale = 0; + int width = getWidth(); + int height = getHeight(); + if (width > height) { + scale = height; + } else { + scale = width; + } + //画圈 + Paint paint = new Paint(); + //设置画笔 + paint.setColor(Color.BLUE); + paint.setAntiAlias(true); + //设置角度渐变 + SweepGradient radialGradient = new SweepGradient(width / 2, height / 2, mColor, Color.argb(0, 0, 0, 255)); + paint.setShader(radialGradient); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(10); + + canvas.rotate(mSweep, width / 2, height / 2); + canvas.drawCircle(width / 2, height / 2, scale / 2 - 10, paint); + mSweep += mStep; + if (mSweep >= 360) { + mSweep = 0; + } + postInvalidateDelayed(360 / mStep); + } + + public int getDuring() { + return mDuring; + } + + public void setDuring(int mDuring) { + this.mDuring = mDuring; + } + + public int getColor() { + return mColor; + } + + public void setColor(int mColor) { + this.mColor = mColor; + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingView1.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingView1.java new file mode 100644 index 00000000..82b4076f --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingView1.java @@ -0,0 +1,113 @@ +package xyz.wbsite.wbui.base.ui.other; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.WindowManager; + +/** + * Created by bingwang on 2016/5/14. + */ +public class LoadingView1 extends View { + /** + * 周期 + */ + private int mDuring = 1000; + + private int firstFlag; + /** + * 前景色 + */ + private int mColor = Color.BLACK; + + public LoadingView1(Context context) { + super(context); + } + + public LoadingView1(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = 0; + int height = 0; + + DisplayMetrics displayMetrics = new DisplayMetrics(); + WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + wm.getDefaultDisplay().getMetrics(displayMetrics); + + int widhtMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + if (widhtMode == MeasureSpec.EXACTLY) { + width = widthSize; + } else { + width = (int) (50 * displayMetrics.density); + } + + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + if (heightMode == MeasureSpec.EXACTLY) { + height = heightSize; + } else { + height = (int) (50 * displayMetrics.density); + } + setMeasuredDimension(width, height); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + int scale = 0; + int width = getWidth(); + int height = getHeight(); + if (width > height) { + scale = height; + } else { + scale = width; + } + + Paint p = new Paint(); + p.setAntiAlias(true); + p.setColor(mColor); + + int centerX = getWidth() / 2; + int centerY = getHeight() / 2; + int blockWidth = scale / 20; + int blockHeight = scale / 4; + int radiou = blockWidth / 2; + RectF rect = new RectF(centerX - blockWidth / 2, centerY - 3 * blockHeight / 2, centerX + blockWidth / 2, centerY - blockHeight / 2); + canvas.rotate(firstFlag, centerX, centerY); + firstFlag += 30; + if (firstFlag == 360) firstFlag = 0; + canvas.drawRoundRect(rect, radiou, radiou, p); + + for (int i = 0; i < 11; i++) { + canvas.rotate(30, centerX, centerY); + p.setAlpha(i * (255 / 12)); + canvas.drawRoundRect(rect, radiou, radiou, p); + } + postInvalidateDelayed(mDuring / 12); + } + + public int getDuring() { + return mDuring; + } + + public void setDuring(int mDuring) { + this.mDuring = mDuring; + } + + public int getColor() { + return mColor; + } + + public void setColor(int mColor) { + this.mColor = mColor; + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingView2.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingView2.java new file mode 100644 index 00000000..5312e8b3 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/LoadingView2.java @@ -0,0 +1,63 @@ +package xyz.wbsite.wbui.base.ui.other; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; + +/** + * Created by bingwang on 2016/5/14. + */ +public class LoadingView2 extends View { + /** + * 周期 + */ + private int mDuring = 600; + private int timeStamp = 0; + private int currentAngle = 0; + + public LoadingView2(Context context) { + super(context); + } + + public LoadingView2(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDraw(Canvas canvas) { + int width = getWidth(); + int height = getHeight(); + + AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator(); + currentAngle = (int) (360 * interpolator.getInterpolation(((float) (timeStamp % mDuring) / (mDuring + 1)))); + canvas.rotate(currentAngle, width / 2, height / 2); + + Paint paint = new Paint(); + paint.setAntiAlias(true); + canvas.drawCircle(width / 2, height / 2, 3, paint); + + int abs = Math.abs((currentAngle % 360) % 180 - 180 * ((currentAngle % 360) / 180)); + for (int i = 0; i < 5; i++) { + canvas.rotate(-abs * 30 / 180, width / 2, height / 2); + paint.setAlpha(255 - 255 * i / 5); + canvas.drawCircle(width / 2, height / 2 - 50, 10, paint); + } + +// for (int i = 0; i < 200; i++) { +// float interpolation = interpolator.getInterpolation(i / 100f); +// +// StringBuffer stringBuffer = new StringBuffer(); +// stringBuffer.append(i/100f); +// for (int j = 0; j < interpolation*100; j++) { +// stringBuffer.append("."); +// } +// System.out.println(stringBuffer.toString()); +// } + + timeStamp += 10; + postInvalidateDelayed(30); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/other/RefreshView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/RefreshView.java new file mode 100644 index 00000000..6347a430 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/RefreshView.java @@ -0,0 +1,347 @@ +package xyz.wbsite.wbui.base.ui.other; + +import android.content.Context; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.DecelerateInterpolator; +import android.widget.AbsListView; +import android.widget.FrameLayout; +import android.widget.ListView; +import android.widget.RelativeLayout; + +public class RefreshView extends RelativeLayout { + private FrameLayout mHeader; + private FrameLayout mFooter; + private View target = null; + + private int mLastY = 0; + private Handler mHandler = new Handler(); + + + private PullActionListener mPullActionListener = null; + private PushActionListener mPushActionListener = null; + + + private int mRefreshHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50.0f, getContext().getResources().getDisplayMetrics()); + + + public RefreshView(Context context) { + super(context); + } + + public RefreshView(Context context, AttributeSet attrs) { + super(context, attrs); + mHeader = new FrameLayout(getContext()); + mHeader.setTag("mHeader"); + LayoutParams mHeaderParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); + mHeaderParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); + this.addView(mHeader, mHeaderParams); + mFooter = new FrameLayout(getContext()); + mFooter.setTag("mFooter"); + LayoutParams mFooterParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); + mFooterParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + this.addView(mFooter, mFooterParams); + + } + + @Override + public void onViewAdded(View child) { + if ("mHeader".equals(child.getTag()) || "mFooter".equals(child.getTag())) { + super.onViewAdded(child); + } else if (target == null) { + target = child; + super.onViewAdded(child); + } else { + throw new RuntimeException("只能有一个子VIEW"); + } + } + + @Override + public void addView(View child) { + super.addView(child); + } + + public int getRefreshHeight() { + return mRefreshHeight; + } + + public void setRefreshHeight(int mRefreshHeight) { + this.mRefreshHeight = mRefreshHeight; + } + + public boolean isListViewReachTopEdge(AbsListView listView) { + boolean result = false; + if (listView.getFirstVisiblePosition() == 0) { + final View topChildView = listView.getChildAt(0); + result = topChildView.getTop() == 0; + } + return result; + } + + public boolean isListViewReachBottomEdge(AbsListView listView) { + boolean result = false; + if (listView.getLastVisiblePosition() == (listView.getCount() - 1)) { + final View bottomChildView = listView.getChildAt(listView.getLastVisiblePosition() - listView.getFirstVisiblePosition()); + result = (listView.getHeight() >= bottomChildView.getBottom()); + } + ; + return result; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + boolean b = super.dispatchTouchEvent(ev); + + Log.i("------",""+mOverY); + + AbsListView target = (AbsListView) this.target; + + if (isListViewReachTopEdge(target)) { + int y = (int) ev.getY(); + if (y > mLastY && mLastY != 0) { + mOverScrollState = OVERSCROLL_STATE_PULL; + } + } else if (isListViewReachBottomEdge(target)) { + int y = (int) ev.getY(); + if (y < mLastY && mLastY != 0) { + mOverScrollState = OVERSCROLL_STATE_PUSH; + } + } + + if (ev.getAction() == MotionEvent.ACTION_UP) { + mOverScrollState = OVERSCROLL_STATE_NORMAL; + onBound(); + } + + if (mOverScrollState != OVERSCROLL_STATE_NORMAL) { + mOverY += mLastY - (int) ev.getY(); + if (Math.abs(mOverY) > mOverYLimit) { + mOverYLimit = Math.abs(mOverY); + } + mLastY = (int) ev.getY(); + + if ((mOverY > 0 && mOverScrollState == OVERSCROLL_STATE_PULL) || (mOverY < 0 && mOverScrollState == OVERSCROLL_STATE_PUSH)) { + mOverY = 0; + mOverScrollState = OVERSCROLL_STATE_NORMAL; + } + if (mOverScrollState == OVERSCROLL_STATE_PULL) { + ViewGroup.LayoutParams layoutParams = mHeader.getLayoutParams(); + layoutParams.height = Math.abs(mOverY) / 2; + mHeader.setLayoutParams(layoutParams); + } else if (mOverScrollState == OVERSCROLL_STATE_PUSH) { + ViewGroup.LayoutParams layoutParams = mFooter.getLayoutParams(); + layoutParams.height = Math.abs(mOverY) / 2; + mFooter.setLayoutParams(layoutParams); + } + onOverHeightChangeListener(mOverY / 2); + return true; + } + mLastY = (int) ev.getY(); + return true; + } +// +// @Override +// public boolean onInterceptTouchEvent(MotionEvent ev) { +// if (mOverScrollState != OVERSCROLL_STATE_NORMAL){ +// return true; +// } +// return super.onInterceptTouchEvent(ev); +// } + + /** + * 基本参数 + */ + private static int OVERSCROLL_STATE_NORMAL = 0;//正常 + private static int OVERSCROLL_STATE_PULL = -1;//拉 + private static int OVERSCROLL_STATE_PUSH = 1;//推 + private int mOverScrollState = OVERSCROLL_STATE_NORMAL; + + /** + * 越界值 mOverY<0是pull mOverY>0是push + */ + private int mOverY = 0; + private boolean isRefreshing = false; + + private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator(2); + + private int timestamp = 0; + + + /** + * 从越界位置返回正常位置的周期 + */ + private int mDuring = 450; + /** + * 帧画面步值,越小画面越流畅,但应为系统配置性能各有差异,不是越低越好 + */ + private int mStep = 30; + + /** + * 下拉或上推时的峰值,用于恢复时阻尼效果计算 + */ + private int mOverYLimit = 0; + + private Runnable runnable = new Runnable() { + @Override + public void run() { + + if (mOverScrollState != OVERSCROLL_STATE_NORMAL || mOverY == 0) { + timestamp = 0; + mOverYLimit = 0; + return; + } + + if (!isRefreshing || (isRefreshing && mOverScrollState == OVERSCROLL_STATE_NORMAL && Math.abs(mOverY) > 2 * mRefreshHeight)) { + timestamp += mStep; + + if (Math.abs(mOverY) > 2 * mRefreshHeight && + Math.abs(mOverYLimit - (int) (mOverYLimit * decelerateInterpolator.getInterpolation(timestamp * 1.0f / mDuring))) < 2 * mRefreshHeight) { + if (mOverY < 0) { + mOverY = -2 * mRefreshHeight; + } else { + mOverY = 2 * mRefreshHeight; + } + onRefresh(); + } else { + mOverY = (Math.abs(mOverY) / mOverY) * (mOverYLimit - (int) (mOverYLimit * decelerateInterpolator.getInterpolation(timestamp * 1.0f / mDuring))); + } + if (Math.abs(mOverY) <= 5) { + mOverY = 0; + } + + if (mOverY < 0) { + ViewGroup.LayoutParams layoutParams = mHeader.getLayoutParams(); + layoutParams.height = Math.abs(mOverY) / 2; + mHeader.setLayoutParams(layoutParams); + } else if (mOverY > 0) { + ViewGroup.LayoutParams layoutParams = mFooter.getLayoutParams(); + layoutParams.height = Math.abs(mOverY) / 2; + mFooter.setLayoutParams(layoutParams); + } else { + ViewGroup.LayoutParams layoutParams = mHeader.getLayoutParams(); + layoutParams.height = 0; + mHeader.setLayoutParams(layoutParams); + layoutParams = mFooter.getLayoutParams(); + layoutParams.height = 0; + mFooter.setLayoutParams(layoutParams); + } + onOverHeightChangeListener(mOverY / 2); + } + + mHandler.postDelayed(runnable, mStep); + } + }; + + + /** + * 释放时,回弹方法 + */ + private void onBound() { + mHandler.postDelayed(runnable, mStep); + } + + /** + * 当有越界事件发生时调用该方法 + * + * @param mOverY mOverY<0是pull mOverY>0是push + */ + public void onOverHeightChangeListener(int mOverY) { + if (mOverY < 0 && mPullActionListener != null) { + mPullActionListener.onHeightChange(Math.abs(mOverY), mRefreshHeight, mHeader); + } else if (mOverY > 0 && mPushActionListener != null) { + mPushActionListener.onHeightChange(mOverY, mRefreshHeight, mFooter); + } else { + //do nothing + } + } + + /** + * 尝试刷新动作 + */ + private void onRefresh() { + if (isRefreshing) { + return; + } + isRefreshing = true; + if (mOverY < 0 && mPullActionListener != null) { + mOverY = -2 * mRefreshHeight; + mPullActionListener.onRefresh(mRefreshHeight, mHeader, notify); + } else if (mOverY < 0 && mPullActionListener == null) { + isRefreshing = false; + } else if (mOverY > 0 && mPushActionListener != null) { + mOverY = 2 * mRefreshHeight; + mPushActionListener.onRefresh(mRefreshHeight, mFooter, notify); + } else if (mOverY > 0 && mPushActionListener == null) { + isRefreshing = false; + } + } + + private Notify notify = new Notify() { + + @Override + public void refreshNotify() { + if (mOverY < 0 && mPullActionListener != null) { + mPullActionListener.onCompleted(Math.abs(mOverY), mHeader, notify); + } else if (mOverY > 0 && mPushActionListener != null) { + mPushActionListener.onCompleted(mOverY, mFooter, notify); + } else { + //do nothing + } + } + + @Override + public void completedNotify() { + isRefreshing = false; + } + + }; + + /** + * pull事件的listener接口 + */ + public interface PullActionListener { + void onHeightChange(int currentHeight, int refreshHeight, ViewGroup rootView); + + void onRefresh(int mRefreshHeight, ViewGroup viewGroup, Notify notify); + + void onCompleted(int mRefreshHeight, ViewGroup viewGroup, Notify notify); + } + + /** + * push事件的listener接口 + */ + public interface PushActionListener { + void onHeightChange(int currentHeight, int refreshHeight, ViewGroup rootView); + + void onRefresh(int mRefreshHeight, ViewGroup viewGroup, Notify notify); + + void onCompleted(int mRefreshHeight, ViewGroup viewGroup, Notify notify); + } + + public interface Notify { + void refreshNotify(); + + void completedNotify(); + } + + public PullActionListener getPullActionListener() { + return mPullActionListener; + } + + public void setPullActionListener(PullActionListener mPullActionListener) { + this.mPullActionListener = mPullActionListener; + } + + public PushActionListener getPushActionListener() { + return mPushActionListener; + } + + public void setPushActionListener(PushActionListener mPushActionListener) { + this.mPushActionListener = mPushActionListener; + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/other/WBUIKeyboardView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/WBUIKeyboardView.java new file mode 100644 index 00000000..f04c14ba --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/other/WBUIKeyboardView.java @@ -0,0 +1,101 @@ +package xyz.wbsite.wbui.base.witget.other; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.inputmethodservice.Keyboard.Key; +import android.inputmethodservice.KeyboardView; +import android.util.AttributeSet; + +import java.util.List; + +import xyz.wbsite.webclient.R; + +public class WBUIKeyboardView extends KeyboardView { + + private Drawable mKeybgDrawable; + private Resources res; + + public WBUIKeyboardView(Context context, AttributeSet attrs) { + super(context, attrs); + initResources(context); + } + + public WBUIKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initResources(context); + } + + private void initResources(Context context) { + res = context.getResources(); + mKeybgDrawable = res.getDrawable(R.drawable.ui_keyboard_key); + } + + @Override + public void onDraw(Canvas canvas) { + List keys = getKeyboard().getKeys(); + for (Key key : keys) { + canvas.save(); + + int offsety = 0; + if (key.y == 0) { + offsety = 1; + } + + int initdrawy = key.y + offsety; + + Rect rect = new Rect(key.x, initdrawy, key.x + key.width, key.y + + key.height); + + canvas.clipRect(rect); + + int primaryCode = -1; + if (null != key.codes && key.codes.length != 0) { + primaryCode = key.codes[0]; + } + + Drawable dr = mKeybgDrawable; + + if (null != dr) { + int[] state = key.getCurrentDrawableState(); + + dr.setState(state); + dr.setBounds(rect); + dr.draw(canvas); + } + + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setTextAlign(Paint.Align.CENTER); + paint.setTextSize(50); + paint.setColor(Color.BLACK); + + if (key.label != null) { + canvas.drawText( + key.label.toString(), + key.x + (key.width / 2), + initdrawy + (key.height + paint.getTextSize() - paint.descent()) / 2, + paint); + } else if (key.icon != null) { + int intriWidth = key.icon.getIntrinsicWidth(); + int intriHeight = key.icon.getIntrinsicHeight(); + + final int drawableX = key.x + (key.width - intriWidth) / 2; + final int drawableY = initdrawy + (key.height - intriHeight) / 2; + + key.icon.setBounds( + drawableX, drawableY, drawableX + intriWidth, + drawableY + intriHeight); + + key.icon.draw(canvas); + } + + canvas.restore(); + } + } + +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/spinner/WbSpinner.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/spinner/WbSpinner.java new file mode 100644 index 00000000..ce2145be --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/spinner/WbSpinner.java @@ -0,0 +1,97 @@ +package xyz.wbsite.wbui.base.ui.spinner; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.RectF; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.PopupWindow; +import android.widget.TextView; + +import java.util.Date; + +public class WbSpinner extends TextView { + private String[] arrs = { + "AAAAAAAAAAAA", "BBBBBBBBBBB", "CCCCCCCCCCC", "DDDDDDDDDDD" + }; + private float scale; + private ListView optionList; + private Context context; + private int width; + private boolean isShow = false; + private PopupWindow popupWindow; + private TextView itemTxtView; + + public WbSpinner(Context context) { + super(context); + } + + public WbSpinner(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + width = getWidth(); + } + + private void init() { + context = getContext(); + scale = getResources().getDisplayMetrics().density; + this.setTextColor(Color.BLACK); + + this.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (isShow) { + popupWindow.dismiss(); + isShow = false; + return; + } + isShow = true; + optionList = new ListView(context); + optionList.setDivider(new ColorDrawable(Color.parseColor("#efefef"))); + optionList.setDividerHeight(1); + optionList.setFadingEdgeLength(0); + + ShapeDrawable shapeDrawable = new ShapeDrawable(new RoundRectShape(new float[]{0, 0, 0, 0, 5, 5, 5, 5}, new RectF((int) (1 * scale), (int) (1 * scale), (int) (1 * scale), (int) (1 * scale)), new float[]{0, 0, 0, 0, 5, 5, 5, 5})); + shapeDrawable.getPaint().setColor(Color.parseColor("#efefef")); + shapeDrawable.setPadding((int) (1 * scale), 0, (int) (1 * scale), (int) (1 * scale)); + + ColorDrawable colorDrawable = new ColorDrawable(Color.WHITE); + LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{colorDrawable, shapeDrawable}); + optionList.setBackground(layerDrawable); + + itemTxtView = new TextView(getContext()); + itemTxtView.setBackgroundColor(Color.BLUE); + int id = (int) (new Date().getTime()); + itemTxtView.setId(id); + + optionList.setAdapter(new ArrayAdapter(context, id, arrs)); + optionList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + WbSpinner.this.setText(arrs[position]); + popupWindow.dismiss(); + isShow = false; + } + }); + + popupWindow = new PopupWindow(width, ViewGroup.LayoutParams.WRAP_CONTENT); + popupWindow.setContentView(optionList); + popupWindow.showAsDropDown(WbSpinner.this, 0, 0); + } + }); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/AverageTextView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/AverageTextView.java new file mode 100644 index 00000000..741c6af3 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/AverageTextView.java @@ -0,0 +1,59 @@ +package xyz.wbsite.wbui.base.ui.textview; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.support.annotation.Nullable; +import android.text.TextPaint; +import android.util.AttributeSet; + +public class AverageTextView extends android.support.v7.widget.AppCompatTextView { + + public AverageTextView(Context context) { + super(context); + } + + public AverageTextView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDraw(Canvas canvas) { + TextPaint paint = getPaint(); + paint.setTextAlign(Paint.Align.CENTER); + paint.setColor(getCurrentTextColor()); + + String mText = getText().toString(); + int width = getWidth() - getPaddingLeft() - getPaddingRight(); + int height = getHeight() - getPaddingTop() - getPaddingBottom(); + int x_offset = getPaddingLeft(); + int y_offset = getPaddingTop(); + + Paint.FontMetrics fontMetrics = paint.getFontMetrics(); + float offset = (Math.abs(fontMetrics.top) - fontMetrics.bottom) / 2; + + if (mText.length() >= 2) { + String firstF = String.valueOf(mText.charAt(0)); + String lastF = String.valueOf(mText.charAt(mText.length() - 1)); + + float firstF_W = paint.measureText(firstF); + float lastF_W = paint.measureText(lastF); + + {// 绘制首位字符 + canvas.drawText(firstF, firstF_W / 2 + x_offset, height / 2 + offset + y_offset, paint); + canvas.drawText(lastF, width - lastF_W / 2 + x_offset, height / 2 + offset + y_offset, paint); + } + + // 如果大于2个才均分 + if (mText.length() > 2) { + float remainingWidth = width - firstF_W / 2 - lastF_W / 2;//剩余宽度 + float averageW = remainingWidth / (mText.length() - 2 + 1);//等分距离 + + for (int i = 1; i < mText.length() - 1; i++) {//循环绘文字 + String f = String.valueOf(mText.charAt(i)); + canvas.drawText(f, firstF_W/2 + i * averageW + x_offset, height / 2 + offset + y_offset, paint); + } + } + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/TipTextView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/TipTextView.java new file mode 100644 index 00000000..31a59fb6 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/TipTextView.java @@ -0,0 +1,46 @@ +package xyz.wbsite.wbui.base.ui.textview; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.widget.TextView; + +/** + * Created by wangbing on 2018/4/7. + */ + +public class TipTextView extends TextView { + private long time = 3000; + private String lastValue = ""; + private Runnable runnable = new Runnable() { + @Override + public void run() { + time -= 1000; + if (time <= 0) { + TipTextView.this.setText(""); + } else { + postDelayed(runnable, 1000); + } + } + }; + + public TipTextView(Context context) { + super(context); + } + + public TipTextView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public TipTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void setText(CharSequence text, BufferType type) { + this.time = 3000; + postDelayed(runnable, 1000); + + super.setText(text, type); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbEditText.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbEditText.java new file mode 100644 index 00000000..615c9f62 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbEditText.java @@ -0,0 +1,68 @@ +package xyz.wbsite.wbui.base.ui.textview; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.StateListDrawable; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.MotionEvent; +import android.widget.EditText; + +public class WbEditText extends EditText { + private float density; + private Paint paint; + + public WbEditText(Context context) { + super(context); + init(); + } + + public WbEditText(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + density = getResources().getDisplayMetrics().density; + + StateListDrawable stateListDrawable = new StateListDrawable(); + + { + Bitmap bitmap = Bitmap.createBitmap((int) (density * 20), (int) (density * 20), Bitmap.Config.ARGB_4444); + Canvas canvas = new Canvas(bitmap); + paint = new Paint(); + canvas.drawCircle(density * 20 / 2, density * 20 / 2, density * 20 / 2, paint); + + BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bitmap); + bitmapDrawable.setBounds((int) (density * 20), (int) (density * 20), (int) (density * 20), (int) (density * 20)); + bitmapDrawable.setGravity(Gravity.RIGHT); + + stateListDrawable.addState(new int[]{android.R.attr.state_focused}, bitmapDrawable); + } + + + this.setBackground(stateListDrawable); + this.setPadding(20, 20, 50, 20); + this.setMinimumWidth(200); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + float x = event.getX(); + float y = event.getY(); + + int width = getWidth(); + int height = getHeight(); + + if (x > (width - density * 20) && y > height / 2 - density * 10 && y < (height / 2 + density * 10)) { + paint.setColor(Color.BLUE); + invalidate(); + return true; + } + return super.onTouchEvent(event); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbTextView.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbTextView.java new file mode 100644 index 00000000..0ecaa012 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbTextView.java @@ -0,0 +1,57 @@ +package xyz.wbsite.wbui.base.ui.textview; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.widget.TextView; + +/** + * Created by wangbing on 16/6/2. + */ +public class WbTextView extends TextView { + private int mOffsetY = 0; + public Bitmap lastBitmap; + + public WbTextView(Context context) { + super(context); + } + + public WbTextView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDraw(Canvas canvas) { + if (lastBitmap != null) { + canvas.drawBitmap(lastBitmap, 0, mOffsetY - getHeight(), null); + canvas.translate(0, mOffsetY); + } + super.onDraw(canvas); + + if (mOffsetY == 0) { + lastBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); + super.onDraw(new Canvas(lastBitmap)); + } + } + + @Override + public void setText(CharSequence text, BufferType type) { + mOffsetY = getHeight(); + post(new Runnable() { + @Override + public void run() { + if (mOffsetY > 0) { + mOffsetY -= 5; + if (mOffsetY < 0) mOffsetY = 0; + postDelayed(this, 10); + } else { + mOffsetY = 0; + } + System.out.println(mOffsetY); + invalidate(); + } + }); + super.setText(text, type); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbTextView1.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbTextView1.java new file mode 100644 index 00000000..f786e7d0 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbTextView1.java @@ -0,0 +1,64 @@ +package xyz.wbsite.wbui.base.ui.textview; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.widget.TextView; + +/** + * Created by bingwang on 2016/6/4. + */ +public class WbTextView1 extends TextView { + private int r = 250; + + public void setR(int r) { + this.r = r; + } + + public WbTextView1(Context context) { + super(context); + } + + public WbTextView1(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDraw(Canvas canvas) { + int width = getWidth(); + int height = getHeight(); + int meshWidth = 5; + int meshHeight = 5; + + //获得原始位图 + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + super.onDraw(new Canvas(bitmap)); + + //初始化网格坐标 + float[] vers = new float[(meshWidth + 1) * (meshHeight + 1) * 2]; + for (int y = 0; y <= meshHeight; y++) { + for (int x = 0; x <= meshWidth; x++) { + vers[2 * x + y * (2 * (meshWidth + 1))] = (float) width * x / meshWidth; + vers[2 * x + y * (2 * (meshWidth + 1)) + 1] = (float) height * y / meshWidth; + } + } + + //扭曲坐标 + for (int i = 0; i < vers.length; i += 2) { + if (vers[i] != width / 2) { + //获取周长 + float l = Math.abs(vers[i] - width / 2); + //获取角度 + double v = Math.toRadians((l / (2 * Math.PI * r)) * 360); + //转换后的长度 + double v1 = Math.sin(v) * r; + //转成实际坐标 + vers[i] = (float) ((Math.abs(vers[i] - width / 2) / (vers[i] - width / 2)) * v1 + width / 2); + } + } + + canvas.drawBitmapMesh(bitmap, meshWidth, meshHeight, vers, 0, null, 0, null); + } + +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbTextView2.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbTextView2.java new file mode 100644 index 00000000..9ec80ee3 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/textview/WbTextView2.java @@ -0,0 +1,55 @@ +package xyz.wbsite.wbui.base.ui.textview; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.util.AttributeSet; +import android.view.Gravity; +import android.widget.TextView; + +/** + * TextView 自带右箭头 + */ +public class WbTextView2 extends TextView { + public WbTextView2(Context context) { + super(context); + init(); + } + + public WbTextView2(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + int drawableWidth = 20; + int drawableColor = Color.parseColor("#aaaaaa"); + + float density = getResources().getDisplayMetrics().density; + Bitmap bitmap = Bitmap.createBitmap((int) (1 * drawableWidth * density), (int) (1 * drawableWidth * density), Bitmap.Config.ARGB_4444); + + Canvas canvas = new Canvas(bitmap); + Path path = new Path(); + path.moveTo(0.775f * drawableWidth * density, 0.5f * drawableWidth * density); + path.lineTo(0.4f * drawableWidth * density, 0.875f * drawableWidth * density); + path.arcTo(new RectF(0.35f * drawableWidth * density, 0.725f * drawableWidth * density, 0.45f * drawableWidth * density, 0.875f * drawableWidth * density), 90, 90, false); + path.lineTo(0.65f * drawableWidth * density, 0.5f * drawableWidth * density); + path.lineTo(0.35f * drawableWidth * density, 0.2f * drawableWidth * density); + path.arcTo(new RectF(0.35f * drawableWidth * density, 0.125f * drawableWidth * density, 0.45f * drawableWidth * density, 0.275f * drawableWidth * density), 180, 90, false); + path.lineTo(0.775f * drawableWidth * density, 0.5f * drawableWidth * density); + + Paint paint = new Paint(); + paint.setColor(drawableColor); + canvas.drawPath(path, paint); + + BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bitmap); + bitmapDrawable.setGravity(Gravity.RIGHT); + this.setPadding(this.getPaddingLeft(), this.getPaddingTop(), (int) (drawableWidth * density + this.getPaddingRight()), this.getPaddingBottom()); + this.setBackground(bitmapDrawable); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/ui/toast/MessageToast.java b/src/main/resources/modules/Android/app/src/main/java/base/ui/toast/MessageToast.java new file mode 100644 index 00000000..d646c2e4 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/ui/toast/MessageToast.java @@ -0,0 +1,55 @@ +package xyz.wbsite.wbui.base.ui.toast; + +import android.content.Context; +import android.graphics.Color; +import android.util.DisplayMetrics; +import android.view.Gravity; +import android.view.WindowManager; +import android.view.animation.TranslateAnimation; +import android.widget.FrameLayout; +import android.widget.TextView; +import android.widget.Toast; + +public class MessageToast extends Toast { + private Context mContext; + private FrameLayout mRootView; + private String message; + + private MessageToast(Context context) { + super(context); + } + + public MessageToast(Context context, String message) { + super(context); + this.mContext = context; + this.message = message; + this.mRootView = new FrameLayout(context); + this.setView(mRootView); + } + + @Override + public void show() { + //加入控件 + TextView tvMessage = new TextView(mContext); + tvMessage.setText(message); + tvMessage.setGravity(Gravity.CENTER); + tvMessage.setBackgroundColor(Color.argb(100, 100, 100, 100)); + mRootView.addView(tvMessage); + + //高度 + WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics metrics = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(metrics); + tvMessage.setHeight((int) (45 * metrics.density)); + + //动画 + TranslateAnimation translateAnimation = new TranslateAnimation(0f, 0f, 45 * metrics.density, 0.0f); + translateAnimation.setDuration(400); + tvMessage.startAnimation(translateAnimation); + + //位置 + this.setGravity(Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0); + + super.show(); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/AnimationUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/AnimationUtil.java new file mode 100644 index 00000000..3ef745e8 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/AnimationUtil.java @@ -0,0 +1,134 @@ +package xyz.wbsite.wbui.base.utils; + + +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.CycleInterpolator; +import android.view.animation.TranslateAnimation; + +public class AnimationUtil { + + public static void shake(View view, int counts, Animation.AnimationListener listener) { + Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0); + translateAnimation.setInterpolator(new CycleInterpolator(counts)); + translateAnimation.setDuration(500); + translateAnimation.setAnimationListener(listener); + view.startAnimation(translateAnimation); + } + + public static void shake(View view, int counts) { + shake(view, counts, null); + } + + public static void toBottom(final View view, long duration, Animation.AnimationListener listener) { + TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, + Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, + 0.0f, Animation.RELATIVE_TO_SELF, 1.0f); + animation.setDuration(duration); + animation.setAnimationListener(listener); + view.clearAnimation(); + view.startAnimation(animation); + } + + public static void toBottom(View view, long duration) { + toBottom(view, duration, null); + } + + public static void toTop(View view, long duration, Animation.AnimationListener listener) { + TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, + Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, + 0.0f, Animation.RELATIVE_TO_SELF, -1.0f); + animation.setDuration(duration); + animation.setAnimationListener(listener); + view.clearAnimation(); + view.startAnimation(animation); + } + + public static void toTop(View view, long duration) { + toTop(view, duration, null); + } + + public static void toLeft(View view, long duration, Animation.AnimationListener listener) { + TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, + Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, + 0.0f, Animation.RELATIVE_TO_SELF, 0.0f); + animation.setDuration(duration); + animation.setAnimationListener(listener); + view.clearAnimation(); + view.startAnimation(animation); + } + + public static void toLeft(View view, long duration) { + toLeft(view, duration, null); + } + + public static void toRight(final View view, long duration, Animation.AnimationListener listener) { + TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, + Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, + 0.0f, Animation.RELATIVE_TO_SELF, 0.0f); + animation.setDuration(duration); + animation.setAnimationListener(listener); + view.clearAnimation(); + view.startAnimation(animation); + } + + public static void toRight(final View view, long duration) { + toRight(view, duration, null); + } + + public static void fromBottom(View view, long duration, Animation.AnimationListener listener) { + TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, + Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, + 1.0f, Animation.RELATIVE_TO_SELF, 0.0f); + animation.setDuration(duration); + animation.setAnimationListener(listener); + view.clearAnimation(); + view.startAnimation(animation); + } + + public static void fromBottom(View view, long duration) { + fromBottom(view, duration, null); + } + + public static void fromTop(View view, long duration, Animation.AnimationListener listener) { + TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, + Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, + -1.0f, Animation.RELATIVE_TO_SELF, 0.0f); + animation.setDuration(duration); + animation.setAnimationListener(listener); + view.clearAnimation(); + view.startAnimation(animation); + } + + public static void fromTop(View view, long duration) { + fromTop(view, duration, null); + } + + public static void fromLeft(View view, long duration, Animation.AnimationListener listener) { + TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -1.0f, + Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, + 0.0f, Animation.RELATIVE_TO_SELF, 0.0f); + animation.setDuration(duration); + animation.setAnimationListener(listener); + view.clearAnimation(); + view.startAnimation(animation); + } + + public static void fromLeft(View view, long duration) { + fromLeft(view, duration, null); + } + + public static void fromRight(View view, long duration, Animation.AnimationListener listener) { + TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 1.0f, + Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, + 0.0f, Animation.RELATIVE_TO_SELF, 0.0f); + animation.setDuration(duration); + animation.setAnimationListener(listener); + view.clearAnimation(); + view.startAnimation(animation); + } + + public static void fromRight(View view, long duration) { + fromRight(view, duration, null); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/ApkUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/ApkUtil.java new file mode 100644 index 00000000..eec98e21 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/ApkUtil.java @@ -0,0 +1,191 @@ +package xyz.wbsite.wbui.base.utils; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; + +import com.yanzhenjie.permission.Action; +import com.yanzhenjie.permission.AndPermission; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class ApkUtil { + + /** + * 安装assets中的APK文件 + * + * @param context + * @param fileName + * @param tempDir + */ + public static void installAssetsApk(Context context, String fileName, File tempDir) { + + boolean b = copyAssetsFile(context, fileName, tempDir); + if (b) { + File apkFile = new File(tempDir, fileName); + AndPermission.with(context) + .install() + .file(apkFile) + .onGranted(new Action() { + @Override + public void onAction(File data) { + + } + }) + .onDenied(new Action() { + @Override + public void onAction(File data) { + + } + }) + .start(); + } + } + + /** + * 复制文件到SD卡 + * * @param context + * * @param fileName 复制的文件名 + * * @param path 保存的目录路径 + * * @return + */ + private static boolean copyAssetsFile(Context context, String fileName, File directory) { + try { + InputStream mInputStream = context.getAssets().open(fileName); + if (!directory.exists()) { + directory.mkdir(); + } + File mFile = new File(directory, fileName); + if (!mFile.exists()) mFile.createNewFile(); + FileOutputStream mFileOutputStream = new FileOutputStream(mFile); + byte[] mbyte = new byte[1024]; + int i = 0; + while ((i = mInputStream.read(mbyte)) > 0) { + mFileOutputStream.write(mbyte, 0, i); + } + mInputStream.close(); + mFileOutputStream.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } catch (Exception e) { + return false; + } + } + + // 判断是APP + public static boolean hasInstall(Context context, String packageName) { + try { + PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); + return (info != null); + } catch (PackageManager.NameNotFoundException e) { + } + return false; + } + + /** + * 通过包名启动第三方app + * + * @param context + * @param packageName + */ + public static void startThridApp(Context context, String packageName) { + try { + Intent minIntent = context.getPackageManager() + .getLaunchIntentForPackage(packageName); + context.startActivity(minIntent); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 查询手机内非系统应用 + * + * @param context + * @return + */ + public static List getAllApps(Context context) { + List apps = new ArrayList(); + PackageManager pManager = context.getPackageManager(); + //获取手机内所有应用 + List paklist = pManager.getInstalledPackages(0); + for (int i = 0; i < paklist.size(); i++) { + PackageInfo pak = (PackageInfo) paklist.get(i); + //判断是否为非系统预装的应用程序 + if ((pak.applicationInfo.flags & pak.applicationInfo.FLAG_SYSTEM) <= 0) { + // customs applications + apps.add(pak); + } + } + return apps; + } + + /** + * 查询手机内所有支持分享的应用 + * + * @param context + * @return + */ + public static List getShareApps(Context context) { + List mApps = new ArrayList(); + Intent intent = new Intent(Intent.ACTION_SEND, null); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setType("text/plain"); + PackageManager pManager = context.getPackageManager(); + mApps = pManager.queryIntentActivities(intent, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); + + return mApps; + } + + /** + * 已知包名和类名启动应用程序 + * + * @param context + * @param packageName + * @param className + */ + public static void startThridApp(Context context, String packageName, String className) { + try { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + ComponentName cn = new ComponentName(packageName, className); + intent.setComponent(cn); + context.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 已知第三方应用的包名和指定类的action启动,可以启动第三方应用的指定Activity, + * 并且传递参数,指定Activity必须设置Action; + * + * @param context + * @param packageName + * @param action + * @param type + * @param count + */ + public static void startThridApp(Context context, String packageName, String action, String type, int count) { + try { + Intent mIntent = new Intent(); + mIntent.setPackage(packageName); + mIntent.setAction(action);//action + mIntent.putExtra("a", type); + mIntent.putExtra("c", count); + context.startActivity(mIntent); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/Base64Util.java b/src/main/resources/modules/Android/app/src/main/java/base/util/Base64Util.java new file mode 100644 index 00000000..c3e3fdae --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/Base64Util.java @@ -0,0 +1,507 @@ +package xyz.wbsite.wbui.base.utils; + +import java.util.Arrays; + +/** + * Base64Util + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class Base64Util { + private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + private static final int[] IA = new int[256]; + + static { + Arrays.fill(IA, -1); + for(int i = 0, iS = CA.length; i < iS; i++) + IA[CA[i]] = i; + IA['='] = 0; + } + + public final static char[] encodeToChar(byte[] sArr, boolean lineSep) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if(sLen == 0) + return new char[0]; + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + char[] dArr = new char[dLen]; + + // Encode even 24-bits + for(int s = 0, d = 0, cc = 0; s < eLen; ) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = CA[(i >>> 18) & 0x3f]; + dArr[d++] = CA[(i >>> 12) & 0x3f]; + dArr[d++] = CA[(i >>> 6) & 0x3f]; + dArr[d++] = CA[i & 0x3f]; + + // Add optional line separator + if(lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't even 24 bits. + int left = sLen - eLen; // 0 - 2. + if(left > 0) { + // Prepare the int + int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = CA[i >> 12]; + dArr[dLen - 3] = CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + public final static byte[] decode(char[] sArr) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if(sLen == 0) + return new byte[0]; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for(int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if(IA[sArr[i]] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if((sLen - sepCnt) % 4 != 0) + return null; + + int pad = 0; + for(int i = sLen; i > 1 && IA[sArr[--i]] <= 0; ) + if(sArr[i] == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for(int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for(int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[s++]]; + if(c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if(d < len) { + dArr[d++] = (byte) (i >> 8); + if(d < len) + dArr[d++] = (byte) i; + } + } + return dArr; + } + + /** + * Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as + * fast as {@link =#=decode(char[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(char[] sArr) { + // Check special case + int sLen = sArr.length; + if(sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while(sIx < eIx && IA[sArr[sIx]] < 0) + sIx++; + + // Trim illegal chars from end + while(eIx > 0 && IA[sArr[eIx]] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for(int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if(sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if(d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for(int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for(int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + /** + * Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if(sLen == 0) + return new byte[0]; + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + byte[] dArr = new byte[dLen]; + + // Encode even 24-bits + for(int s = 0, d = 0, cc = 0; s < eLen; ) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[d++] = (byte) CA[i & 0x3f]; + + // Add optional line separator + if(lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't an even 24 bits. + int left = sLen - eLen; // 0 - 2. + if(left > 0) { + // Prepare the int + int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = (byte) CA[i >> 12]; + dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + /** + * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(byte[] sArr) { + // Check special case + int sLen = sArr.length; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for(int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if(IA[sArr[i] & 0xff] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if((sLen - sepCnt) % 4 != 0) + return null; + + int pad = 0; + for(int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0; ) + if(sArr[i] == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for(int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for(int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[s++] & 0xff]; + if(c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if(d < len) { + dArr[d++] = (byte) (i >> 8); + if(d < len) + dArr[d++] = (byte) i; + } + } + + return dArr; + } + + + /** + * Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as + * fast as {@link =#=decode(byte[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(byte[] sArr) { + // Check special case + int sLen = sArr.length; + if(sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while(sIx < eIx && IA[sArr[sIx] & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while(eIx > 0 && IA[sArr[eIx] & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for(int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if(sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if(d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for(int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for(int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + // **************************************************************************************** + // * String version + // **************************************************************************************** + + /** + * Encodes a raw byte array into a BASE64 String representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static String encodeToString(byte[] sArr, boolean lineSep) { + // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower. + return new String(encodeToChar(sArr, lineSep)); + } + + /** + * Decodes a BASE64 encoded String. All illegal characters will be ignored and can handle both strings with + * and without line separators.
+ * Note! It can be up to about 2x the speed to call decode(str.toCharArray()) instead. That + * will create a temporary array though. This version will use str.charAt(i) to iterate the string. + * + * @param str The source string. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(String str) { + // Check special case + int sLen = str != null ? str.length() : 0; + if(sLen == 0) + return new byte[0]; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for(int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if(IA[str.charAt(i)] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if((sLen - sepCnt) % 4 != 0) + return null; + + // Count '=' at end + int pad = 0; + for(int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0; ) + if(str.charAt(i) == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for(int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for(int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[str.charAt(s++)]; + if(c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if(d < len) { + dArr[d++] = (byte) (i >> 8); + if(d < len) + dArr[d++] = (byte) i; + } + } + return dArr; + } + + /** + * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as + * fast as {@link =#=decode(String)}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param s The source string. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(String s) { + // Check special case + int sLen = s.length(); + if(sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while(sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while(eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for(int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if(sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if(d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for(int j = 0; sIx <= eIx - pad; j++) + i |= IA[s.charAt(sIx++)] << (18 - j * 6); + + for(int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + public static void main(String[] args) { + String s = Base64Util.encodeToString("我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1我搜搜1".getBytes(), false); + System.out.println(s); + + byte[] decode = Base64Util.decode(s); + System.out.println(new String(decode)); + + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/BitmapUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/BitmapUtil.java new file mode 100644 index 00000000..27b7b4b3 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/BitmapUtil.java @@ -0,0 +1,135 @@ + +package xyz.wbsite.wbui.base.utils; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Matrix; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.util.DisplayMetrics; +import android.util.TypedValue; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +public final class BitmapUtil { + + + public static Bitmap scaleBitmap(Bitmap src, int destWidth, int destHeigth) { + if (src == null) return null; + + int width = src.getWidth(); + int height = src.getHeight(); + + float scaleWidth = ((float) destWidth) / width; + float scaleHeight = ((float) destHeigth) / height; + + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true); + } + + public static Bitmap scaleBitmap(Bitmap src, float scale) { + if (src == null) return null; + + int width = src.getWidth(); + int height = src.getHeight(); + + Matrix matrix = new Matrix(); + matrix.postScale(scale, scale); + return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true); + } + + public static Drawable getDrawableFromAssetFile(Resources res, String fileName) { + InputStream is = null; + Drawable icon = null; + try { + is = res.getAssets().open(fileName); + TypedValue typedValue = new TypedValue(); + typedValue.density = TypedValue.DENSITY_DEFAULT; + icon = Drawable.createFromResourceStream(res, typedValue, is, null); + } catch (Exception e) { + } finally { + try { + if (is != null) { + is.close(); + is = null; + } + } catch (final IOException e) { + } + } + return icon; + } + + public static Bitmap getBitmapFromAssetFile(Resources res, String fileName) { + InputStream is = null; + Bitmap bmp = null; + try { + is = res.getAssets().open(fileName); + + DisplayMetrics metrics = res.getDisplayMetrics(); + Options opts = new Options(); + opts.inDensity = (int) (metrics.densityDpi / metrics.scaledDensity); + bmp = BitmapFactory.decodeStream(is, null, opts); + is.close(); + is = null; + } catch (Exception e) { + } finally { + try { + if (is != null) is.close(); + } catch (final IOException e) { + } + } + return bmp; + } + + public static Drawable getDrawableFromFile(Resources res, String fileName) { + FileInputStream fis = null; + Drawable icon = null; + try { + fis = new FileInputStream(fileName); + DisplayMetrics metrics = res.getDisplayMetrics(); + Options opts = new Options(); + opts.inDensity = (int) (metrics.densityDpi / metrics.scaledDensity); + Bitmap bmp = BitmapFactory.decodeStream(fis, null, opts); + + fis.close(); + fis = null; + if (bmp != null) { + icon = new BitmapDrawable(bmp); + } + } catch (Exception e) { + } finally { + try { + if (fis != null) fis.close(); + } catch (final IOException e) { + } + } + return icon; + } + + public static Bitmap getBitmapFromFile(Resources res, String fileName) { + FileInputStream fis = null; + Bitmap bmp = null; + try { + fis = new FileInputStream(fileName); + + DisplayMetrics metrics = res.getDisplayMetrics(); + Options opts = new Options(); + opts.inDensity = (int) (metrics.densityDpi / metrics.scaledDensity); + bmp = BitmapFactory.decodeStream(fis, null, opts); + fis.close(); + fis = null; + } catch (Exception e) { + } finally { + try { + if (fis != null) fis.close(); + } catch (final IOException e) { + } + } + return bmp; + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/BluetoothUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/BluetoothUtil.java new file mode 100644 index 00000000..d54c9845 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/BluetoothUtil.java @@ -0,0 +1,124 @@ +package xyz.wbsite.wbui.base.utils; + +import android.bluetooth.BluetoothDevice; +import android.util.Log; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class BluetoothUtil { + + + /** + * 与设备配对 参考源码:platform/packages/apps/Settings.git + * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java + */ + static public boolean createBond(Class btClass, BluetoothDevice btDevice) throws Exception { + Method createBondMethod = btClass.getMethod("createBond"); + Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice); + return returnValue.booleanValue(); + } + + /** + * 与设备解除配对 参考源码:platform/packages/apps/Settings.git + * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java + */ + static public boolean removeBond(Class btClass, BluetoothDevice btDevice) throws Exception { + Method removeBondMethod = btClass.getMethod("removeBond"); + Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice); + return returnValue.booleanValue(); + } + + static public boolean setPin(Class btClass, BluetoothDevice btDevice, String str) throws Exception { + try { + Method removeBondMethod = btClass.getDeclaredMethod("setPin", new Class[]{byte[].class}); + Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice, new Object[]{str.getBytes()}); + Log.e("returnValue", "" + returnValue); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (Exception e) { + } + return true; + } + + static public boolean setPassKey(Class btClass, BluetoothDevice btDevice, String str) throws Exception { + + try { + Method removeBondMethod = btClass.getDeclaredMethod("setPasskey", new Class[]{byte[].class}); + Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice, new Object[]{str.getBytes()}); + Log.e("returnValue", "" + returnValue); + + } catch (SecurityException e) { + // throw new RuntimeException(e.getMessage()); + e.printStackTrace(); + } catch (IllegalArgumentException e) { + // throw new RuntimeException(e.getMessage()); + e.printStackTrace(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return true; + } + + // 取消用户输入 + static public boolean cancelPairingUserInput(Class btClass, BluetoothDevice device) throws Exception { + Method createBondMethod = btClass.getMethod("cancelPairingUserInput"); + // cancelBondProcess() + Boolean returnValue = (Boolean) createBondMethod.invoke(device); + return returnValue.booleanValue(); + + } + + // 取消配对 + static public boolean cancelBondProcess(Class btClass, BluetoothDevice device) throws Exception { + + Method createBondMethod = btClass.getMethod("cancelBondProcess"); + Boolean returnValue = (Boolean) createBondMethod.invoke(device); + return returnValue.booleanValue(); + } + + /** + * @param clsShow + */ + static public void printAllInform(Class clsShow) { + + try { + + // 取得所有方法 + Method[] hideMethod = clsShow.getMethods(); + int i = 0; + for (; i < hideMethod.length; i++) { + Log.e("method name", hideMethod[i].getName() + ";and the i is:" + i); + } + // 取得所有常量 + Field[] allFields = clsShow.getFields(); + for (i = 0; i < allFields.length; i++) { + Log.e("Field name", allFields[i].getName()); + } + + } catch (SecurityException e) { + // throw new RuntimeException(e.getMessage()); + e.printStackTrace(); + } catch (IllegalArgumentException e) { + // throw new RuntimeException(e.getMessage()); + e.printStackTrace(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + public static boolean isWantedMac(String macStr) { + if (macStr == null || macStr.isEmpty()) return false; + if (macStr.equals("55:44:33:22:11:00")) { + return true; + } else { + return false; + } + } + +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/Consant.java b/src/main/resources/modules/Android/app/src/main/java/base/util/Consant.java new file mode 100644 index 00000000..00aa8170 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/Consant.java @@ -0,0 +1,21 @@ +package xyz.wbsite.wbui.base; + +/** + * 内容辅助类 + */ +public class Consant { + + //是否记住密码 + public static String REMEMBER_PASSWORD = "remember_password"; + + //是否自动登录 + public static String AUTO_LOGIN = "auto_login"; + + //文件存储目录 + public static String DIR = "WBUI"; + + //图片存储目录 + public static String DIR_IMG = "WBUI/temp"; + + public static int REQUESTCODE_FROM_FRAGMENT = 1; +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/DESUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/DESUtil.java new file mode 100644 index 00000000..e7f32f15 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/DESUtil.java @@ -0,0 +1,45 @@ +package xyz.wbsite.wbui.base.utils; + +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +public class DESUtil { + + public static final String ALGORITHM_DES = "DES"; + + public static Key generateKey(String algorithm) throws NoSuchAlgorithmException { + KeyGenerator generator = KeyGenerator.getInstance(algorithm); + return generator.generateKey(); + } + + public static Key getKey(byte[] key, String algorithm) throws NoSuchAlgorithmException { + return new SecretKeySpec(key, algorithm); + } + + public static byte[] encrypt(String transformation, Key key, String content) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException { + Cipher cipher = Cipher.getInstance(transformation); + // 加密模式 + cipher.init(Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(content.getBytes()); + } + + public static byte[] decrypt(String transformation, Key key, byte[] content) + throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException { + Cipher cipher = Cipher.getInstance(transformation); + // 解密模式 + cipher.init(Cipher.DECRYPT_MODE, key); + return cipher.doFinal(content); + } + +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/DataBaseUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/DataBaseUtil.java new file mode 100644 index 00000000..01a2132a --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/DataBaseUtil.java @@ -0,0 +1,435 @@ +package xyz.wbsite.wbui.base.utils; + +import android.content.ContentValues; +import android.content.Context; +import android.content.ContextWrapper; +import android.database.Cursor; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteOpenHelper; + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import xyz.wbsite.webclient.WBUIApplication; +import xyz.wbsite.wbui.base.Consant; + +public class DataBaseUtil { + private String defaultPath = ""; + private String defaultDBName = "data.db"; + private SQLiteOpenHelper sqLiteOpenHelper; + private SQLiteDatabase writableDatabase; + private List entityList; + + private DataBaseUtil() { + } + + public DataBaseUtil(Context context, Register register) { + entityList = register.run(); + File ownCacheDirectory = StorageUtil.getOwnCacheDirectory(context, Consant.DIR); + defaultPath = ownCacheDirectory.getAbsolutePath(); + sqLiteOpenHelper = new SQLiteOpenHelperImpl(context, defaultDBName, null, 1); + writableDatabase = sqLiteOpenHelper.getWritableDatabase(); + } + + public List query(T t) { + return query(t, 0, 0); + } + + public List query(T t, int start, int size) { + ArrayList list = new ArrayList<>(); + try { + Class a = findEntity(t); + //1、获取表名 + String table = a.getSimpleName(); + //2、获取字段列表 + List fs = new ArrayList<>(); + for (Field f : a.getDeclaredFields()) { + if (f.isAnnotationPresent(DBField.class)) { + fs.add(f); + } + } + String[] columns = new String[fs.size()]; + int where = 0; + for (int i = 0; i < columns.length; i++) { + fs.get(i).setAccessible(true); + columns[i] = fs.get(i).getName().toUpperCase(); + try { + Object o = fs.get(i).get(t); + if (o != null) { + where++; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + } + //3、获取where条件 + StringBuilder selectionSB = new StringBuilder(""); + String[] selectionArgs = null; + if (where > 0) { + selectionArgs = new String[where]; + where = 0; + for (int i = 0; i < columns.length; i++) { + columns[i] = fs.get(i).getName().toUpperCase(); + try { + Object o = fs.get(i).get(t); + if (o != null) { + if ("".equals(selectionSB.toString())) { + selectionSB.append(" "); + } else { + selectionSB.append(" and "); + } + selectionSB.append(columns[i]).append(" like ?"); + selectionArgs[where] = o.toString(); + where++; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + //4、分页 + String limit = size != 0 ? (start + "," + size) : ""; + + //5、其他暂未支持 + + Cursor query = null; + try { + query = writableDatabase.query(table, columns, selectionSB.toString(), selectionArgs, null, null, "", limit); + } catch (SQLiteException e) { + e.printStackTrace(); + return null; + } + + while (query.moveToNext()) { + try { + T o = (T) a.newInstance(); + for (int i = 0; i < fs.size(); i++) { + Field field = fs.get(i); + String string = query.getString(i); + field.set(o, string); + } + list.add(o); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return list; + } + + public void delete(T t) { + try { + Class aClass = findEntity(t); + //1、获取表名 + String table = aClass.getSimpleName(); + //2、获取字段列表 + List fs = new ArrayList<>(); + for (Field f : aClass.getDeclaredFields()) { + if (f.isAnnotationPresent(DBField.class)) { + fs.add(f); + } + } + String[] columns = new String[fs.size()]; + int where = 0; + for (int i = 0; i < columns.length; i++) { + columns[i] = fs.get(i).getName().toUpperCase(); + try { + fs.get(i).setAccessible(true); + Object o = fs.get(i).get(t); + if (o != null) { + where++; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + } + //3、获取where条件 + StringBuilder selectionSB = new StringBuilder(""); + String[] selectionArgs = null; + if (where > 0) { + selectionArgs = new String[where]; + where = 0; + for (int i = 0; i < columns.length; i++) { + columns[i] = fs.get(i).getName().toUpperCase(); + try { + fs.get(i).setAccessible(true); + Object o = fs.get(i).get(t); + if (o != null) { + if ("".equals(selectionSB.toString())) { + selectionSB.append(" "); + } else { + selectionSB.append(" and "); + } + selectionSB.append(columns[i]).append(" = ?"); + selectionArgs[where] = o.toString(); + where++; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + writableDatabase.delete(table, selectionSB.toString(), selectionArgs); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void insert(T t) { + try { + Class aClass = findEntity(t); + StringBuffer sql = new StringBuffer(); + String tableName = aClass.getSimpleName(); + sql.append("INSERT INTO "); + sql.append(tableName.toUpperCase()); + sql.append("("); + + ContentValues contentValues = new ContentValues(); + StringBuffer fieldsSql = new StringBuffer(); + StringBuffer valueSql = new StringBuffer(); + for (Field f : aClass.getDeclaredFields()) { + if (f.isAnnotationPresent(DBField.class)) { + + f.setAccessible(true); + try { + String value = ""; + Object o = f.get(t); + if (o instanceof String) { + value = (String) o; + valueSql.append(value); + } else { + valueSql.append("null"); + } + valueSql.append(","); + contentValues.put(f.getName().toUpperCase(), value); + } catch (IllegalAccessException e) { + e.printStackTrace(); + valueSql.append("null,"); + } + + fieldsSql.append(f.getName().toUpperCase()); + fieldsSql.append(","); + } + } + + if (",".equals(fieldsSql.substring(fieldsSql.length() - 1, fieldsSql.length()))) { + fieldsSql.replace(fieldsSql.length() - 1, fieldsSql.length(), ""); + } + if (",".equals(valueSql.substring(valueSql.length() - 1, valueSql.length()))) { + valueSql.replace(valueSql.length() - 1, valueSql.length(), ""); + } + sql.append(fieldsSql); + sql.append(") VALUES ("); + sql.append(valueSql); + sql.append(")"); + + writableDatabase.insert(tableName, null, contentValues); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void insertBitch(List data) { + if (data == null || data.size() == 0) { + return; + } + writableDatabase.beginTransaction(); + for (Object datum : data) { + insert(datum); + } + writableDatabase.setTransactionSuccessful(); + writableDatabase.endTransaction(); + } + + /** + * 查找注册的实体 + * + * @param t + * @param + * @return + * @throws Exception + */ + private Class findEntity(T t) throws Exception { + for (Class aClass : entityList) { + if (t.getClass() == aClass) { + return aClass; + } + } + throw new Exception("can not find Entity:" + t.getClass().getName() + ", make sure it is registered"); + } + + /** + * 数据库字段注解 + */ + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.RUNTIME) + public @interface DBField { + String value() default ""; + } + + /** + * 利用字段注解拼接sql + * + * @param mClass + * @param sql + */ + private void fillSql(Class mClass, StringBuffer sql) { + Field[] fields = mClass.getDeclaredFields(); + for (Field f : fields) { + if (f.isAnnotationPresent(DBField.class)) { + DBField bind = f.getAnnotation(DBField.class); + String type = bind.value(); + sql.append(f.getName().toUpperCase()); + sql.append(" "); + sql.append(type); + sql.append(","); + } + } + } + + private class SQLiteOpenHelperImpl extends SQLiteOpenHelper { + + public SQLiteOpenHelperImpl(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { + super(new DatabaseContext(context), name, factory, version); + } + + public SQLiteOpenHelperImpl(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) { + super(context, name, factory, version, errorHandler); + } + + @Override + public void onCreate(SQLiteDatabase sqLiteDatabase) { + for (Class object : entityList) { + StringBuffer sql = new StringBuffer(); + String name = object.getSimpleName(); + + sql.append("CREATE TABLE "); + sql.append(name); + sql.append(" ("); + fillSql(object, sql); + + sql.replace(sql.length() - 1, sql.length(), ""); + sql.append(")"); + sqLiteDatabase.execSQL(sql.toString()); + } + } + + @Override + public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { + + } + } + + /** + * 创建数据库辅助类,可以自定义数据库存放路径 + */ + class DatabaseContext extends ContextWrapper { + + public DatabaseContext(Context base) { + super(base); + } + + @Override + public File getDatabasePath(String name) { + if ("".equals(defaultPath)) {//defaultPath如果为""则放在程序默认位置 + defaultPath = getApplicationContext().getDatabasePath("database").getAbsolutePath(); + } + + File path = new File(defaultPath); + if (!path.exists()) { + path.mkdirs(); + } + + File dbfile = new File(path, defaultDBName); + + if (!dbfile.exists()) { + try { + dbfile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return dbfile; + } + + @Override + public SQLiteDatabase openOrCreateDatabase(String name, int mode, + SQLiteDatabase.CursorFactory factory, + DatabaseErrorHandler errorHandler) { + return openOrCreateDatabase(name, mode, factory); + } + + @Override + public SQLiteDatabase openOrCreateDatabase(String name, int mode, + SQLiteDatabase.CursorFactory factory) { + SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase( + getDatabasePath(name), null); + + return result; + } + } + + public interface Register { + List run(); + } + + /** + * 以下为Demo + * @param args + */ + public static void main(String[] args) { + //Demo + DataBaseUtil db = new DataBaseUtil(WBUIApplication.getInstance(), new Register() { + @Override + public List run() { + List objects = new ArrayList<>(); + objects.add(User.class); + return objects; + } + }); + //插入数据 + db.insert(new User("Test")); + //删除数据 + db.delete(new User("Test")); + //查询数据(不分页) + db.query(new User("Test")); + //查询数据(分页) + db.query(new User("Test"),0,10); + } + + /** + * Demo Entity + */ + public static class User { + @DataBaseUtil.DBField("VARCHAR(20)") + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public User(String name) { + this.name = name; + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/DensityUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/DensityUtil.java new file mode 100644 index 00000000..d0ef71bc --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/DensityUtil.java @@ -0,0 +1,59 @@ +package xyz.wbsite.wbui.base.utils; + +import android.content.Context; +import android.util.TypedValue; + +public class DensityUtil { + private DensityUtil() { + /* cannot be instantiated */ + throw new UnsupportedOperationException("cannot be instantiated"); + } + + /** + * dp转px + * + * @param context + * @param dpVal + * @return + */ + public static int dp2px(Context context, float dpVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + dpVal, context.getResources().getDisplayMetrics()); + } + + /** + * sp转px + * + * @param context + * @param spVal + * @return + */ + public static int sp2px(Context context, float spVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, + spVal, context.getResources().getDisplayMetrics()); + } + + /** + * px转dp + * + * @param context + * @param pxVal + * @return + */ + public static float px2dp(Context context, float pxVal) { + final float scale = context.getResources().getDisplayMetrics().density; + return (pxVal / scale); + } + + /** + * px转sp + * + * @param context + * @param pxVal + * @return + */ + public static float px2sp(Context context, float pxVal) { + return (pxVal / context.getResources().getDisplayMetrics().scaledDensity); + } + +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/DialogUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/DialogUtil.java new file mode 100644 index 00000000..ee4c8f16 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/DialogUtil.java @@ -0,0 +1,71 @@ +package xyz.wbsite.wbui.base.utils; + +import android.content.Context; + +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; + + +public class DialogUtil { + + /** + * 展示一个需要确认的消息提示 + * + * @param context + * @param title 标题 + * @param message 消息内容 + */ + public static void showConfirmMessage(Context context, String title, String message) { + QMUIDialog qmuiDialog = new QMUIDialog.MessageDialogBuilder(context) + .setMessage(message) + .addAction("确定", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .create(com.qmuiteam.qmui.R.style.QMUI_Dialog); + qmuiDialog.show(); + } + + /** + * 展示一个需要确认的消息提示(带回调) + * + * @param context + * @param title 标题 + * @param message 消息内容 + */ + public static void showConfirmMessage(Context context, String title, String message, QMUIDialogAction.ActionListener actionListener) { + QMUIDialog qmuiDialog = new QMUIDialog.MessageDialogBuilder(context) + .setMessage(message) + .addAction("取消", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .addAction("确定", actionListener) + .create(com.qmuiteam.qmui.R.style.QMUI_Dialog); + qmuiDialog.show(); + } + + /** + * 展示一个信息弹窗 + * + * @param context + * @param title 标题 + * @param message 消息内容 + */ + public static void showMessage(Context context, String title, String message) { + new QMUIDialog.MessageDialogBuilder(context) + .setTitle(title) + .setMessage(message) + .addAction("确认", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .create(com.qmuiteam.qmui.R.style.QMUI_Dialog).show(); + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/FastBlur.java b/src/main/resources/modules/Android/app/src/main/java/base/util/FastBlur.java new file mode 100644 index 00000000..0b5c1414 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/FastBlur.java @@ -0,0 +1,217 @@ +package xyz.wbsite.wbui.base.utils; + +import android.graphics.Bitmap; + +/** + * Created by wangbing on 16/6/7. + */ +public class FastBlur { + + public static Bitmap doBlur(Bitmap sentBitmap, int radius, + boolean canReuseInBitmap) { + Bitmap bitmap; + if (canReuseInBitmap) { + bitmap = sentBitmap; + } else { + bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); + } + + if (radius < 1) { + return (null); + } + + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + + int[] pix = new int[w * h]; + bitmap.getPixels(pix, 0, w, 0, 0, w, h); + + int wm = w - 1; + int hm = h - 1; + int wh = w * h; + int div = radius + radius + 1; + + int r[] = new int[wh]; + int g[] = new int[wh]; + int b[] = new int[wh]; + int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; + int vmin[] = new int[Math.max(w, h)]; + + int divsum = (div + 1) >> 1; + divsum *= divsum; + int dv[] = new int[256 * divsum]; + for (i = 0; i < 256 * divsum; i++) { + dv[i] = (i / divsum); + } + + yw = yi = 0; + + int[][] stack = new int[div][3]; + int stackpointer; + int stackstart; + int[] sir; + int rbs; + int r1 = radius + 1; + int routsum, goutsum, boutsum; + int rinsum, ginsum, binsum; + + for (y = 0; y < h; y++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + for (i = -radius; i <= radius; i++) { + p = pix[yi + Math.min(wm, Math.max(i, 0))]; + sir = stack[i + radius]; + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + rbs = r1 - Math.abs(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < w; x++) { + + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) { + vmin[x] = Math.min(x + radius + 1, wm); + } + p = pix[yw + vmin[x]]; + + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[(stackpointer) % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += w; + } + for (x = 0; x < w; x++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + yp = -radius * w; + for (i = -radius; i <= radius; i++) { + yi = Math.max(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - Math.abs(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) { + yp += w; + } + } + yi = x; + stackpointer = radius; + for (y = 0; y < h; y++) { + // Preserve alpha channel: ( 0xff000000 & pix[yi] ) + pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) + | (dv[gsum] << 8) | dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) { + vmin[y] = Math.min(y + r1, hm) * w; + } + p = x + vmin[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += w; + } + } + + bitmap.setPixels(pix, 0, w, 0, 0, w, h); + + return (bitmap); + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/FileUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/FileUtil.java new file mode 100644 index 00000000..7fdf6780 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/FileUtil.java @@ -0,0 +1,112 @@ +package xyz.wbsite.wbui.base.utils; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * 文件及文件夹工具类 + */ +public class FileUtil { + + public static byte[] File2byte(File file) { + byte[] buffer = null; + try { + FileInputStream fis = new FileInputStream(file); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] b = new byte[1024]; + int n; + while ((n = fis.read(b)) != -1) { + bos.write(b, 0, n); + } + fis.close(); + bos.close(); + buffer = bos.toByteArray(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return buffer; + } + + public static void byte2File(byte[] buf, File file) { + BufferedOutputStream bos = null; + FileOutputStream fos = null; + try { + file.getParentFile().mkdirs(); + file.createNewFile(); + fos = new FileOutputStream(file); + bos = new BufferedOutputStream(fos); + bos.write(buf); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (bos != null) { + try { + bos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static long sizeOf(File file) { + + if (!file.exists()) { + String message = file + " does not exist"; + throw new IllegalArgumentException(message); + } + + if (file.isDirectory()) { + return sizeOfDirectory(file); + } else { + return file.length(); + } + + } + + public static long sizeOfDirectory(File directory) { + checkDirectory(directory); + + final File[] files = directory.listFiles(); + if (files == null) { // null if security restricted + return 0L; + } + long size = 0; + + for (final File file : files) { + + size += sizeOf(file); + if (size < 0) { + break; + + } + + } + + return size; + } + + private static void checkDirectory(File directory) { + if (!directory.exists()) { + throw new IllegalArgumentException(directory + " does not exist"); + } + if (!directory.isDirectory()) { + throw new IllegalArgumentException(directory + + " is not a directory"); + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/GPSUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/GPSUtil.java new file mode 100644 index 00000000..5dd61512 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/GPSUtil.java @@ -0,0 +1,319 @@ +package xyz.wbsite.wbui.base.utils; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.location.Criteria; +import android.location.GpsSatellite; +import android.location.GpsStatus; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * GPS定位服务类,封装了GPS类的细节,只向用户暴露简单的start()和stop()两个方法。需要用户实现{@link IGPSCallback}接口中的方法 + *

+ * 使用方法:
+ * GPSUtil GPSUtil = new GPSUtil(gpsCallback, MainActivity.this, true); + *
+ * GPSUtil.start(); + * + * @author 优化设计,QQ:50199907 Email:yw1530@126.com + * @see IGPSCallback + */ +public class GPSUtil { + private String TAG = GPSUtil.class.getSimpleName(); + /** + * GPS函数回调接口 + */ + private IGPSCallback gpsCallback; + + private LocationManager loctionManager; + + private String provider; + + /** + * GPSUtil是否运行 + */ + private boolean isRun; + + /** + * @return GPS状态,true为正在运行,false已停止。 + */ + public boolean getIsRun() { + return isRun; + } + + /** + * 自动停止 + */ + private boolean isAutoStop = false; + + /** + * 是否调试 + */ + private boolean isTest = false; + + /** + * 超时时间(秒) + */ + private int outTime = 120; + + /** + * 精度 + */ + private final float accuracy = 100.0f; + + /** + * GPS配置参数 + */ + private Criteria criteria; + + /** + * @return 获取criteria + */ + public Criteria getCriteria() { + initCriteria(); + return criteria; + } + + /** + * 初始化GPS参数 + */ + private void initCriteria() { + if (criteria == null) { + criteria = new Criteria(); + criteria.setAccuracy(Criteria.ACCURACY_FINE); // 高精度 + criteria.setAltitudeRequired(false); // 不要求海拔 + criteria.setBearingRequired(false); // 不要求方位 + criteria.setCostAllowed(true); // 允许有花费 + criteria.setPowerRequirement(Criteria.POWER_LOW);// 设置低功耗模式 + } + } + + /** + * 最后一次错误描述 + */ + private String lastErrorDescription = ""; + + /** + * @return 获取GPSSerivce最后一次出错的描述 + */ + public String getError() { + return lastErrorDescription; + } + + /** + * 设置最后一次错误描述,该描述可以通过getError()方法获取。 + * + * @param error 错误说明 + * @see GPSUtil#getError() + */ + private void setError(String error) { + if (error == null) + return; + this.lastErrorDescription = error; + } + + /** + * GPSUtil构造函数 + * + * @param gpsCallback 回调函数接口 + * @param context Context + */ + public GPSUtil(IGPSCallback gpsCallback, Context context) { + this(gpsCallback, context, true); + } + + public GPSUtil(IGPSCallback gpsCallback, Context context, boolean isAutoStop) { + + this(gpsCallback, context, isAutoStop, false); + } + + public GPSUtil(IGPSCallback gpsCallback, Context context, boolean isAutoStop, boolean isTest) { + this.gpsCallback = gpsCallback; + this.isAutoStop = isAutoStop; + this.isTest = isTest; + loctionManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + + initCriteria(); + + // 从可用的位置提供器中,匹配以上标准的最佳提供器 + provider = loctionManager.getBestProvider(criteria, true); + } + + /** + * 返回超时时间(单位:秒) + * + * @return + */ + public int getOutTime() { + return outTime; + } + + /** + * 设置超时时间 + * + * @param outTime 超时时间(单位:秒,范围:10—600),只可在Start()方法调用前使用,默认180秒,如果小于10秒则超时无效, + * 只能手动调用Stop() 方法停止GPS。 + */ + public void setOutTime(int outTime) { + if (outTime >= 0 && outTime <= 600) { + this.outTime = outTime; + } + } + + /** + * 开始GPS定位 + * + * @return 返回false表示GPS打开失败,可能没有硬件打开(由手机用户控制硬件开关)。 + */ + @SuppressLint("MissingPermission") + public boolean start() { + try { + if (!loctionManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)) { + // GPS没有打开 + setError("GPS未打开"); + gpsCallback.failCallBack(IGPSCallback.ERROR_CLOSE); + return false; + } + + // 注册监听函数 + if (locationListener != null) { + loctionManager.requestLocationUpdates(provider, 1000 * 10, accuracy, locationListener); + } + + // 调试 + if (isTest) { + loctionManager.addGpsStatusListener(new GpsStatus.Listener() { + private List numSatelliteList = new ArrayList(); + + @Override + public void onGpsStatusChanged(int event) { + GpsStatus status = loctionManager.getGpsStatus(null); //取当前状态 + + StringBuilder sb = new StringBuilder(""); + if (status == null) { + sb.append("搜索到卫星个数:" + 0); + } else if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) { + int maxSatellites = status.getMaxSatellites(); + Iterator it = status.getSatellites().iterator(); + numSatelliteList.clear(); + int count = 0; + while (it.hasNext() && count <= maxSatellites) { + GpsSatellite s = it.next(); + numSatelliteList.add(s); + count++; + } + sb.append("搜索到卫星个数:" + numSatelliteList.size()); + } + Log.d(TAG, sb.toString()); + } + }); + } + + isRun = true; + return true; + } catch (Exception e) { + setError(e.getMessage()); + e.printStackTrace(); + return false; + } + } + + /** + * 停止GPS定位 + * + * @return + */ + public boolean stop() { + try { + if (locationListener != null) { + loctionManager.removeUpdates(locationListener); + } + isRun = false; + return true; + } catch (Exception e) { + setError(e.getMessage()); + e.printStackTrace(); + return false; + } + } + + /** + * 位置监听器 + */ + private final LocationListener locationListener = new LocationListener() { + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + } + + @Override + public void onProviderEnabled(String provider) { + } + + @Override + public void onProviderDisabled(String provider) { + } + + // 当位置变化时触发 + @Override + public void onLocationChanged(Location location) { + if (location != null) { + if (location.hasAccuracy() && location.getAccuracy() <= accuracy) { + // 是否自动停止 + if (isAutoStop) { + stop(); + } + gpsCallback.gpsCallback(location); + } + } + } + }; + + /** + * GPS定位服务回调函数接口,需要实现{@link IGPSCallback#gpsCallback(Location location)}和 + * {@link IGPSCallback#failCallBack(String error)}方法 + * + * @author 优化设计,QQ:50199907 Email:yw1530@126.com + * @see GPSUtil + */ + public interface IGPSCallback { + /** + * GPS无信号 + */ + public final static String ERROR_NO_SIGNAL = "GPS无信号"; + + /** + * GPS超时退出 + */ + public final static String ERROR_OUTTIME = "GPS超时退出"; + + /** + * GPS硬件没有打开 + */ + public final static String ERROR_CLOSE = "GPS硬件关闭"; + + /** + * GPS执行成功,返回Location时的回调函数 + * + * @param location 位置信息 + */ + void gpsCallback(Location location); + + /** + * GPS错误时的回调函数 包括GPS无信号、GPS超时退出、GPS硬件没有打开 + * + * @param error 错误描述,一般为{@link IGPSCallback#ERROR_NO_SIGNAL}、 + * {@link IGPSCallback#ERROR_OUTTIME}、 + * {@link IGPSCallback#ERROR_CLOSE}。也可以由GPSUtil类自定义 + */ + void failCallBack(String error); + } + +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/IdCardUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/IdCardUtil.java new file mode 100644 index 00000000..4d087da7 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/IdCardUtil.java @@ -0,0 +1,144 @@ +package xyz.wbsite.wbui.base.utils; + +import java.util.Calendar; +import java.util.Locale; + +/** + * 身份证号码工具类 + */ +public class IdCardUtil { + /** + * 中国公民身份证号码最小长度。 + */ + public final int CHINA_ID_MIN_LENGTH = 15; + + /** + * 中国公民身份证号码最大长度。 + */ + public final int CHINA_ID_MAX_LENGTH = 18; + + /** + * 根据身份编号获取年龄 + * + * @param idCard 身份编号 + * @return 年龄 + */ + public static int getAgeByIdCard(String idCard) { + int iAge = 0; + Calendar cal = Calendar.getInstance(); + String year = idCard.substring(6, 10); + int iCurrYear = cal.get(Calendar.YEAR); + iAge = iCurrYear - Integer.valueOf(year); + return iAge; + } + + /** + * 根据身份编号获取生日 + * + * @param idCard 身份编号 + * @return 生日(yyyyMMdd) + */ + public static String getBirthByIdCard(String idCard) { + return idCard.substring(6, 14); + } + + /** + * 根据身份编号获取生日年 + * + * @param idCard 身份编号 + * @return 生日(yyyy) + */ + public static String getYearByIdCard(String idCard) { + return idCard.substring(6, 10); + } + + public static String getYearString(String idCard) { + return idCard.substring(6, 10); + } + + /** + * 根据身份编号获取生日月 + * + * @param idCard 身份编号 + * @return 生日(MM) + */ + public static String getMonthByIdCard(String idCard) { + return idCard.substring(10, 12); + } + + /** + * 根据身份编号获取生日天 + * + * @param idCard 身份编号 + * @return 生日(dd) + */ + public static String getDateByIdCard(String idCard) { + return idCard.substring(12, 14); + } + + /** + * 根据身份编号获取性别 + * + * @param idCard 身份编号 + * @return 性别(M-男,F-女,N-未知) + */ + public static String getGenderByIdCard(String idCard) { + String sGender = "未知"; + + String sCardNum = idCard.substring(16, 17); + if (Integer.parseInt(sCardNum) % 2 != 0) { + sGender = "1";//男 + } else { + sGender = "2";//女 + } + return sGender; + } + + public static String getCSRQ(String idCard) { + String nian = getYearByIdCard(idCard); + String yue = getMonthByIdCard(idCard); + String ri = getDateByIdCard(idCard); + + return String.format(Locale.CHINA, "%s%s%s", nian, yue, ri); + } + + public static String getXzqh(String idCard) { + return idCard.substring(0, 6); + } + + + public static void main(String[] a) { + String idcard = "460200199209275127"; + String sex = getGenderByIdCard(idcard); + System.out.println("性别:" + sex); + int age = getAgeByIdCard(idcard); + System.out.println("年龄:" + age); + String nian = getYearByIdCard(idcard); + String yue = getMonthByIdCard(idcard); + String ri = getDateByIdCard(idcard); + System.out.print(nian + "年" + yue + "月" + ri + "日"); + + String sr = getBirthByIdCard(idcard); + System.out.println("生日:" + sr); + } + + private static int[] w = {7, 9, 10, 5, 8, 4, 2, 1, 6, + 3, 7, 9, 10, 5, 8, 4, 2}; + + public static boolean isValidate(String id) { + if (id == null || id.length() != 18) { + return false; + } + char[] c = id.toCharArray(); + int sum = 0; + for (int i = 0; i < w.length; i++) { + sum += (c[i] - '0') * w[i]; + } + System.out.println(sum); + char[] verifyCode = "10X98765432".toCharArray(); + char ch = verifyCode[sum % 11]; + System.out.println(ch); + return c[17] == ch; + } + +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/Logger.java b/src/main/resources/modules/Android/app/src/main/java/base/util/Logger.java new file mode 100644 index 00000000..23fc6180 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/Logger.java @@ -0,0 +1,104 @@ +package xyz.wbsite.wbui.base.utils; + +import android.util.Log; + +public class Logger { + private static StackTraceElement[] currentThread; + private static String tagName; + private static String msgT; + private static String msgC; + private static String callTraceStack; + private static int curLogLevel = Log.VERBOSE; + + public static int getCurLogLevel() { + return curLogLevel; + } + + public static void setCurLogLevel(int curLogLevel) { + Logger.curLogLevel = curLogLevel; + } + + public synchronized static void initTrace(String msg, int... isPrintStack) { + int isPrintStackOne = isPrintStack.length > 0 ? isPrintStack[0] : 10; + currentThread = Thread.currentThread().getStackTrace(); + // vm调用栈中此方法所在index:2:VMStack.java:-2:getThreadStackTrace()<--Thread.java:737:getStackTrace()<-- + int curentIndex = 4; + String className = currentThread[curentIndex].getFileName(); + int endIndex = className.lastIndexOf("."); + tagName = endIndex < 0 ? className : className.substring(0, endIndex); + msgT = "[" + className + ":" + currentThread[curentIndex].getLineNumber() + ":" + + currentThread[curentIndex].getMethodName() + "()]---"; + msgC = "msg:[" + msg + "]"; + if (isPrintStackOne > 0) { + StringBuilder sb = new StringBuilder(); + sb.append("callTraceStack:["); + for (int i = curentIndex; i < curentIndex + isPrintStackOne && i < currentThread.length; i++) { + sb.append(currentThread[i].getFileName() + ":" + currentThread[i].getLineNumber() + ":" + + currentThread[i].getMethodName() + "()" + "<--"); + } + sb.append("]"); + callTraceStack = sb.toString(); + msgC += callTraceStack; + } + } + + public static void e(String msg, boolean printStack) { + e(msg, printStack ? 105 : 0); + } + + public static void w(String msg, boolean printStackNum) { + w(msg, printStackNum ? 105 : 0); + } + + public static void d(String msg, boolean printStackNum) { + d(msg, printStackNum ? 105 : 0); + } + + public static void v(String msg, boolean printStackNum) { + v(msg, printStackNum ? 105 : 0); + } + + public static void i(String msg, boolean printStackNum) { + i(msg, printStackNum ? 105 : 0); + } + + public static void e(String msg, int... printStackNum) { + if (curLogLevel > Log.ERROR) { + return; + } + initTrace(msg, printStackNum.length > 0 ? printStackNum[0] : 0); + Log.e(tagName, msgT + msgC); + } + + public static void w(String msg, int... printStackNum) { + if (curLogLevel > Log.WARN) { + return; + } + initTrace(msg, printStackNum.length > 0 ? printStackNum[0] : 0); + Log.w(tagName, msgT + msgC); + } + + public static void d(String msg, int... printStackNum) { + if (curLogLevel > Log.DEBUG) { + return; + } + initTrace(msg, printStackNum.length > 0 ? printStackNum[0] : 0); + Log.d(tagName, msgT + msgC); + } + + public static void v(String msg, int... printStackNum) { + if (curLogLevel > Log.VERBOSE) { + return; + } + initTrace(msg, printStackNum.length > 0 ? printStackNum[0] : 0); + Log.v(tagName, msgT + msgC); + } + + public static void i(String msg, int... printStackNum) { + if (curLogLevel > Log.INFO) { + return; + } + initTrace(msg, printStackNum.length > 0 ? printStackNum[0] : 0); + Log.i(tagName, msgT + msgC); + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/NetUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/NetUtil.java new file mode 100644 index 00000000..0b1455ff --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/NetUtil.java @@ -0,0 +1,85 @@ +package xyz.wbsite.wbui.base.utils; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +/** + * 网络工具类 + */ +public class NetUtil { + public static final int NO_NETWORK = 0; + public static final int NETWORK_WIFI = 1; + public static final int NETWORK_MOBILE = 2; + + /** + * 判断网络是否连接 + */ + public static boolean isConnected(Context context) { + ConnectivityManager connectivity = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + + if (null != connectivity) { + NetworkInfo info = connectivity.getActiveNetworkInfo(); + if (null != info && info.isConnected()) { + if (info.getState() == NetworkInfo.State.CONNECTED) { + return true; + } + } + } + return false; + } + + /** + * 判断是否是wifi连接 + */ + public static boolean isWifi(Context context) { + ConnectivityManager cm = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + + if (cm == null) + return false; + return cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI; + + } + + /** + * 打开网络设置界面 + */ + public static void openSetting(Activity activity) { + Intent intent = new Intent("/"); + ComponentName cm = new ComponentName("com.android.settings", + "com.android.settings.WirelessSettings"); + intent.setComponent(cm); + intent.setAction("android.intent.action.VIEW"); + activity.startActivityForResult(intent, 0); + } + + /** + * 检查网络连接 + * + * @param context + * @return 0为无网络状态,1为wifi连接状态,2为移动网络连接状态 + */ + public static int checkNetwork(Context context) { + if (context == null) { + return NETWORK_WIFI; + + } + ConnectivityManager cm = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo wifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + if (wifi != null && wifi.isConnected()) { + return NETWORK_WIFI; + } + NetworkInfo mobile = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + if (mobile != null && mobile.isConnected()) { + return NETWORK_MOBILE; + } + return NO_NETWORK; + } + +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/RSAUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/RSAUtil.java new file mode 100644 index 00000000..72e85251 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/RSAUtil.java @@ -0,0 +1,156 @@ +package xyz.wbsite.wbui.base.utils; + +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +public class RSAUtil { + + public static final String PUBLIC_KEY = "RSAPublicKey"; + public static final String PRIVATE_KEY = "RSAPrivateKey"; + private static final String KEY_ALGORITHM = "RSA"; + private static int KEY_SIZE = 512; + + private static PublicKey MOBILE_PUBLIC_KEY = null; + private static PrivateKey MOBILE_PRIVATE_KEY = null; + + static { + try { + + // 初始化公钥 + X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64Util.decode("MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJChCOcEFsKdG9Ibwi23b3UreERc1D5+GhEjvByxmFZ2Q+mqco8V9CDsIZUfpb6pLQA8x32G17YHEyj34etpmBUCAwEAAQ==")); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + MOBILE_PUBLIC_KEY = keyFactory.generatePublic(spec); + + // 初始化私钥 + PKCS8EncodedKeySpec specPri = new PKCS8EncodedKeySpec(Base64Util.decode("MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAkKEI5wQWwp0b0hvCLbdvdSt4RFzUPn4aESO8HLGYVnZD6apyjxX0IOwhlR+lvqktADzHfYbXtgcTKPfh62mYFQIDAQABAkBhuZ4lUxr592TEDOOhNnCGkI/cSYlUjKqaaDYEgW/5Azom4VLya/JXFQVMVeMKQoiwH8aXn8od6+l5O7m+mYVlAiEA5yPFXhM4GXY0Oxe7l0uHKRlLkHIz44kvnd4Fj2xNfK8CIQCgL0SljzvL+iilHb+5PKk1wmpLPaDKajZqInSOhkhQewIgFpGAkOnxfVL0UJzFnUUrolCs9yKffGUFuDVYd6OMgVMCICh2CBbxqR8K3z1l2EnH4s3rf8HlnTnDvl7suRhPHvEFAiAxvjwQl6yHoDH6IGB6jeE9+hYK/xvuGT2TAyztZhfVRA==")); + + keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + MOBILE_PRIVATE_KEY = keyFactory.generatePrivate(specPri); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeySpecException e) { + e.printStackTrace(); + } + } + + public static Map generateKeys() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_ALGORITHM); + generator.initialize(KEY_SIZE); + KeyPair keyPair = generator.generateKeyPair(); + PublicKey publicKey = keyPair.getPublic(); + PrivateKey privateKey = keyPair.getPrivate(); + + Map keys = new HashMap(); + keys.put(PUBLIC_KEY, publicKey); + keys.put(PRIVATE_KEY, privateKey); + return keys; + } + + public static PublicKey getPublicKey(byte[] content) throws Exception { + PublicKey publicKey = null; + try { + X509EncodedKeySpec spec = new X509EncodedKeySpec(content); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + publicKey = keyFactory.generatePublic(spec); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + throw e; + } catch (InvalidKeySpecException e) { + e.printStackTrace(); + throw e; + } + + return publicKey; + } + + public static PrivateKey getPrivateKey(byte[] content) throws Exception { + PrivateKey privateKey = null; + try { + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(content); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + privateKey = keyFactory.generatePrivate(spec); + } catch (InvalidKeySpecException e) { + e.printStackTrace(); + throw e; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + throw e; + } + + return privateKey; + } + + public static byte[] encryptWithPublicKey(byte[] data, PublicKey key) throws Exception { + Cipher cipher; + try { + cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(data); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + throw e; + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + throw e; + } catch (InvalidKeyException e) { + e.printStackTrace(); + throw e; + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + throw e; + } catch (BadPaddingException e) { + e.printStackTrace(); + throw e; + } + + } + + public static byte[] decryptWithPrivateKey(byte[] data, PrivateKey key) throws Exception { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.DECRYPT_MODE, key); + + return cipher.doFinal(data); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + throw e; + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + throw e; + } catch (InvalidKeyException e) { + e.printStackTrace(); + throw e; + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + throw e; + } catch (BadPaddingException e) { + e.printStackTrace(); + throw e; + } + } + + public static byte[] encryptMobile(byte[] data) throws Exception { + return encryptWithPublicKey(data, MOBILE_PUBLIC_KEY); + } + + public static byte[] decryptMobile(byte[] data) throws Exception { + return decryptWithPrivateKey(data, MOBILE_PRIVATE_KEY); + } + + +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/SPUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/SPUtil.java new file mode 100644 index 00000000..6f8c6206 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/SPUtil.java @@ -0,0 +1,166 @@ +package xyz.wbsite.wbui.base.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.text.TextUtils; + +import xyz.wbsite.webclient.WBUIApplication; + +/** + * Created by Administrator on 2018/3/11 0011. + */ + +public class SPUtil { + + public static final String FILE_DEFAULT = "default"; + + public static int getInt(String key) { + SharedPreferences sharedPreferences = getSharePreferences(FILE_DEFAULT); + return sharedPreferences.getInt(key, 0); + } + + public static boolean getBoolean(String key) { + SharedPreferences sharedPreferences = getSharePreferences(FILE_DEFAULT); + return sharedPreferences.getBoolean(key, false); + } + + public static float getFloat(String key) { + SharedPreferences sharedPreferences = getSharePreferences(FILE_DEFAULT); + return sharedPreferences.getFloat(key, 0); + } + + public static boolean getBoolean(String key, boolean defValue) { + SharedPreferences sharedPreferences = getSharePreferences(FILE_DEFAULT); + return sharedPreferences.getBoolean(key, defValue); + } + + public static String getString(String key) { + SharedPreferences sharedPreferences = getSharePreferences(FILE_DEFAULT); + return sharedPreferences.getString(key, ""); + } + + public static String getStringWithDefault(String key, String defStr) { + SharedPreferences sharedPreferences = getSharePreferences(FILE_DEFAULT); + return sharedPreferences.getString(key, defStr); + } + + public static void putInt(String key, int value) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(FILE_DEFAULT)); + editor.putInt(key, value); + editor.commit(); + } + + public static void putBoolean(String key, boolean value) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(FILE_DEFAULT)); + editor.putBoolean(key, value); + editor.commit(); + } + + public static void putString(String key, String value) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(FILE_DEFAULT)); + editor.putString(key, value); + editor.commit(); + } + + public static void putFloat(String key, float value) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(FILE_DEFAULT)); + + editor.putFloat(key, value); + editor.commit(); + } + + public static void putLong(String key, long value) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(FILE_DEFAULT)); + + editor.putLong(key, value); + editor.commit(); + } + + + public static long getLong(String key) { + SharedPreferences sharedPreferences = getSharePreferences(FILE_DEFAULT); + return sharedPreferences.getLong(key, 0); + } + + public static int getInt(String key, int defValue) { + SharedPreferences sharedPreferences = getSharePreferences(FILE_DEFAULT); + return sharedPreferences.getInt(key, defValue); + } + + public static int getInt(String key, String fileName) { + + SharedPreferences sharedPreferences = getSharePreferences(getFileName(fileName)); + return sharedPreferences.getInt(key, 0); + } + + public static boolean getBoolean(String key, String fileName) { + SharedPreferences sharedPreferences = getSharePreferences(getFileName(fileName)); + return sharedPreferences.getBoolean(key, false); + } + + public static String getString(String key, String fileName) { + SharedPreferences sharedPreferences = getSharePreferences(getFileName(fileName)); + return sharedPreferences.getString(key, null); + } + + public static float getFloat(String key, String fileName) { + SharedPreferences sharedPreferences = getSharePreferences(getFileName(fileName)); + return sharedPreferences.getFloat(key, 0); + } + + public static long getLong(String key, String fileName) { + SharedPreferences sharedPreferences = getSharePreferences(getFileName(fileName)); + return sharedPreferences.getLong(key, 0); + } + + public static void putInt(String key, int value, String fileName) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(getFileName(fileName))); + editor.putInt(key, value); + editor.commit(); + + } + + public static void putBoolean(String key, boolean value, String fileName) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(getFileName(fileName))); + + editor.putBoolean(key, value); + editor.commit(); + } + + public static void putString(String key, String value, String fileName) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(getFileName(fileName))); + + editor.putString(key, value); + editor.commit(); + } + + public static void putFloat(String key, float value, String fileName) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(getFileName(fileName))); + + editor.putFloat(key, value); + editor.commit(); + } + + public static void putLong(String key, long value, String fileName) { + SharedPreferences.Editor editor = getEditor(getSharePreferences(getFileName(fileName))); + + editor.putLong(key, value); + editor.commit(); + } + + private static SharedPreferences getSharePreferences(String fileName) { + return WBUIApplication.getInstance().getSharedPreferences(fileName, Context.MODE_PRIVATE); + } + + private static SharedPreferences.Editor getEditor(SharedPreferences sharedPreferences) { + return sharedPreferences.edit(); + } + + public static String getFileName(String fileName) { + if (!TextUtils.isEmpty(fileName)) { + return fileName; + } else { + return FILE_DEFAULT; + } + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/ScreenUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/ScreenUtil.java new file mode 100644 index 00000000..f22bf5ee --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/ScreenUtil.java @@ -0,0 +1,106 @@ +package xyz.wbsite.wbui.base.utils; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.WindowManager; + +/** + * 屏幕相关工具类 + */ +public class ScreenUtil { + + /** + * 获得屏幕高度 + * + * @param context + * @return + */ + public static int getScreenWidth(Context context) { + WindowManager wm = (WindowManager) context + .getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics outMetrics = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(outMetrics); + return outMetrics.widthPixels; + } + + /** + * 获得屏幕宽度 + * + * @param context + * @return + */ + public static int getScreenHeight(Context context) { + WindowManager wm = (WindowManager) context + .getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics outMetrics = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(outMetrics); + return outMetrics.heightPixels; + } + + /** + * 获得状态栏的高度 + * + * @param context + * @return + */ + public static int getStatusHeight(Context context) { + int statusHeight = -1; + try { + Class clazz = Class.forName("com.android.internal.R$dimen"); + Object object = clazz.newInstance(); + int height = Integer.parseInt(clazz.getField("status_bar_height") + .get(object).toString()); + statusHeight = context.getResources().getDimensionPixelSize(height); + } catch (Exception e) { + e.printStackTrace(); + } + return statusHeight; + } + + /** + * 获取当前屏幕截图,包含状态栏 + * + * @param activity + * @return + */ + public static Bitmap snapShotWithStatusBar(Activity activity) { + View view = activity.getWindow().getDecorView(); + view.setDrawingCacheEnabled(true); + view.buildDrawingCache(); + Bitmap bmp = view.getDrawingCache(); + int width = getScreenWidth(activity); + int height = getScreenHeight(activity); + Bitmap bp = null; + bp = Bitmap.createBitmap(bmp, 0, 0, width, height); + view.destroyDrawingCache(); + return bp; + } + + /** + * 获取当前屏幕截图,不包含状态栏 + * + * @param activity + * @return + */ + public static Bitmap snapShotWithoutStatusBar(Activity activity) { + View view = activity.getWindow().getDecorView(); + view.setDrawingCacheEnabled(true); + view.buildDrawingCache(); + Bitmap bmp = view.getDrawingCache(); + Rect frame = new Rect(); + activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); + int statusBarHeight = frame.top; + + int width = getScreenWidth(activity); + int height = getScreenHeight(activity); + Bitmap bp = null; + bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height + - statusBarHeight); + view.destroyDrawingCache(); + return bp; + } +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/SizeUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/SizeUtil.java new file mode 100644 index 00000000..f55da56e --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/SizeUtil.java @@ -0,0 +1,58 @@ +package xyz.wbsite.wbui.base.utils; + +import android.content.Context; +import android.util.TypedValue; + +/** + * 尺寸单位转换 + */ +public class SizeUtil { + + /** + * dp转px + * + * @param context + * @param dpVal + * @return + */ + public static int dp2px(Context context, float dpVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + dpVal, context.getResources().getDisplayMetrics()); + } + + /** + * sp转px + * + * @param context + * @param spVal + * @return + */ + public static int sp2px(Context context, float spVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, + spVal, context.getResources().getDisplayMetrics()); + } + + /** + * px转dp + * + * @param context + * @param pxVal + * @return + */ + public static float px2dp(Context context, float pxVal) { + final float scale = context.getResources().getDisplayMetrics().density; + return (pxVal / scale); + } + + /** + * px转sp + * + * @param context + * @param pxVal + * @return + */ + public static float px2sp(Context context, float pxVal) { + return (pxVal / context.getResources().getDisplayMetrics().scaledDensity); + } + +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/StorageUtil.java b/src/main/resources/modules/Android/app/src/main/java/base/util/StorageUtil.java new file mode 100644 index 00000000..bee70b8c --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/StorageUtil.java @@ -0,0 +1,231 @@ +package xyz.wbsite.wbui.base.utils; + + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Environment; +import android.os.StatFs; +import android.provider.MediaStore; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import static android.os.Environment.MEDIA_MOUNTED; + +public class StorageUtil { + private static final String EXTERNAL_STORAGE_PERMISSION = "android.permission.WRITE_EXTERNAL_STORAGE"; + private static final String INDIVIDUAL_DIR_NAME = "uil-images"; + private static final String TAG = "StorageUtils"; + + private StorageUtil() { + } + + /** + * 获取缓存目录 + * context 上下文 + */ + public static File getCacheDirectory(Context context) { + return getCacheDirectory(context, true); + } + + /** + * 获取缓存目录 + * context 上下文 + * preferExternal 是否外部优先 + */ + public static File getCacheDirectory(Context context, boolean preferExternal) { + File appCacheDir = null; + String externalStorageState; + try { + externalStorageState = Environment.getExternalStorageState(); + } catch (NullPointerException e) { // (sh)it happens (Issue #660) + externalStorageState = ""; + } + if (preferExternal && MEDIA_MOUNTED.equals(externalStorageState) && hasExternalStoragePermission(context)) { + appCacheDir = getExternalCacheDir(context); + } + if (appCacheDir == null) { + appCacheDir = context.getCacheDir(); + } + if (appCacheDir == null) { + String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache/"; + Log.d(TAG, "Can't define system cache directory! " + cacheDirPath + " will be used."); + appCacheDir = new File(cacheDirPath); + } + return appCacheDir; + } + + /** + * Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be + * created on SD card ("/Android/data/[app_package_name]/cache/uil-images") if card is mounted and app has + * appropriate permission. Else - Android defines cache directory on device's file system. + * + * @param context Application context + * @return Cache {@link File directory} + */ + public static File getIndividualCacheDirectory(Context context) { + File cacheDir = getCacheDirectory(context); + File individualCacheDir = new File(cacheDir, INDIVIDUAL_DIR_NAME); + if (!individualCacheDir.exists()) { + if (!individualCacheDir.mkdir()) { + individualCacheDir = cacheDir; + } + } + return individualCacheDir; + } + + /** + * Returns specified application cache directory. Cache directory will be created on SD card by defined path if card + * is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system. + * + * @param context Application context + * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images") + * @return Cache {@link File directory} + */ + public static File getOwnCacheDirectory(Context context, String cacheDir) { + File appCacheDir = null; + if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) { + appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir); + } + if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) { + appCacheDir = context.getCacheDir(); + } + return appCacheDir; + } + + private static File getExternalCacheDir(Context context) { + File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"); + File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache"); + if (!appCacheDir.exists()) { + if (!appCacheDir.mkdirs()) { + Log.d(TAG, "Unable to create external cache directory"); + return null; + } + try { + new File(appCacheDir, ".nomedia").createNewFile(); + } catch (IOException e) { + Log.d(TAG, "Can't create \".nomedia\" file in application external cache directory"); + } + } + return appCacheDir; + } + + private static boolean hasExternalStoragePermission(Context context) { + int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION); + return perm == PackageManager.PERMISSION_GRANTED; + } + + /** + * 判断SDCard是否可用 + * + * @return + */ + public static boolean isSDCardEnable() { + return Environment.getExternalStorageState().equals( + Environment.MEDIA_MOUNTED); + + } + + /** + * 获取SD卡路径 + * + * @return + */ + public static String getSDCardPath() { + return Environment.getExternalStorageDirectory().getAbsolutePath() + + File.separator; + } + + /** + * 获取SD卡的剩余容量 单位byte + * + * @return + */ + public static long getSDCardAllSize() { + if (isSDCardEnable()) { + StatFs stat = new StatFs(getSDCardPath()); + // 获取空闲的数据块的数量 + long availableBlocks = (long) stat.getAvailableBlocks() - 4; + // 获取单个数据块的大小(byte) + long freeBlocks = stat.getAvailableBlocks(); + return freeBlocks * availableBlocks; + } + return 0; + } + + /** + * 获取指定路径所在空间的剩余可用容量字节数,单位byte + * + * @param filePath + * @return 容量字节 SDCard可用空间,内部存储可用空间 + */ + public static long getFreeBytes(String filePath) { + // 如果是sd卡的下的路径,则获取sd卡可用容量 + if (filePath.startsWith(getSDCardPath())) { + filePath = getSDCardPath(); + } else {// 如果是内部存储的路径,则获取内存存储的可用容量 + filePath = Environment.getDataDirectory().getAbsolutePath(); + } + StatFs stat = new StatFs(filePath); + long availableBlocks = (long) stat.getAvailableBlocks() - 4; + return stat.getBlockSize() * availableBlocks; + } + + /** + * @param bmp 获取的bitmap数据 + * @param picName 自定义的图片名 + */ + public static void saveBmp2Gallery(Context context, Bitmap bmp, String picName) { + + String fileName = null; + //系统相册目录 + String galleryPath = Environment.getExternalStorageDirectory() + + File.separator + Environment.DIRECTORY_DCIM + + File.separator + "Camera" + File.separator; + + + // 声明文件对象 + File file = null; + // 声明输出流 + FileOutputStream outStream = null; + + try { + // 如果有目标文件,直接获得文件对象,否则创建一个以filename为名称的文件 + file = new File(galleryPath, picName + ".jpg"); + + // 获得文件相对路径 + fileName = file.toString(); + // 获得输出流,如果文件中有内容,追加内容 + outStream = new FileOutputStream(fileName); + if (null != outStream) { + bmp.compress(Bitmap.CompressFormat.JPEG, 100, outStream); + } + + } catch (Exception e) { + e.getStackTrace(); + } finally { + try { + if (outStream != null) { + outStream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + //通知相册更新 + MediaStore.Images.Media.insertImage(context.getContentResolver(), + bmp, fileName, null); + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + Uri uri = Uri.fromFile(file); + intent.setData(uri); + context.sendBroadcast(intent); + + Toaster.showToast("图片保存成功"); + } + +} \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/Tasker.java b/src/main/resources/modules/Android/app/src/main/java/base/util/Tasker.java new file mode 100644 index 00000000..6e3b3dfe --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/Tasker.java @@ -0,0 +1,51 @@ +package xyz.wbsite.wbui.base.utils; + +import android.os.AsyncTask; + +/** + * 任务执行器 + */ +public class Tasker { + + /** + * 执行任务 + * + * @param task + */ + public static void exec(Task task) { + task.execute(); + } + + public static abstract class Task extends AsyncTask { + + /** + * 异步运行任务 + * + * @return 任务结果 true:达到预期目的,false:未达到预期目的 + */ + protected abstract boolean run(); + + /** + * 任务处理完毕后执行 + * + * @param result + */ + protected void post(boolean result) { + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + return run(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + protected void onPostExecute(Boolean result) { + post(result == null ? false : result); + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/base/util/Toaster.java b/src/main/resources/modules/Android/app/src/main/java/base/util/Toaster.java new file mode 100644 index 00000000..f60abe80 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/base/util/Toaster.java @@ -0,0 +1,33 @@ +package xyz.wbsite.wbui.base.utils; + +import android.view.View; + +import xyz.wbsite.webclient.WBUIApplication; + +/** + * 消息发生器 + */ +public class Toaster { + /** + * 展示Toast消息。 + * + * @param message 消息内容 + */ + public static void showToast(String message) { + WBUIApplication.getInstance().showToast(message); + } + + /** + * 引起注意或聚焦的消息提示 + * + * @param message + * @param view + */ + public static void showError(String message, View view) { + showToast(message); + if (view != null) { + view.requestFocus(); + AnimationUtil.shake(view, 3); + } + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/fragment/LoginFragment.java b/src/main/resources/modules/Android/app/src/main/java/fragment/LoginFragment.java new file mode 100644 index 00000000..aaa71045 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/fragment/LoginFragment.java @@ -0,0 +1,57 @@ +package xyz.wbsite.webclient.fragment; + +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.LinearLayout; + +import butterknife.BindView; +import xyz.wbsite.wbui.base.BaseSPAFragment; +import xyz.wbsite.webclient.R; + +public class LoginFragment extends BaseSPAFragment { + + @BindView(R.id.edtUsername) + EditText edtUsername; + @BindView(R.id.llyUsername) + LinearLayout llyUsername; + @BindView(R.id.edtPassword) + EditText edtPassword; + @BindView(R.id.llyPassword) + LinearLayout llyPassword; + @BindView(R.id.btnLogin) + Button btnLogin; + @BindView(R.id.chkPwd) + CheckBox chkPwd; + @BindView(R.id.chkAuto) + CheckBox chkAuto; + + @Override + protected int getFragmnetLayout() { + return R.layout.fragment_login; + } + + @Override + protected void onViewInit() { + btnLogin.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showLoading(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + closeLoading(); + startFragment(new MainFragment()); + } + }, 500); + } + }); + + } + + @Override + protected boolean canDragBack() { + return false; + } +} diff --git a/src/main/resources/modules/Android/app/src/main/java/fragment/MainFragment.java b/src/main/resources/modules/Android/app/src/main/java/fragment/MainFragment.java new file mode 100644 index 00000000..bb969fd0 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/java/fragment/MainFragment.java @@ -0,0 +1,34 @@ +package xyz.wbsite.webclient.fragment; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewPager; + +import com.qmuiteam.qmui.widget.QMUITabSegment; +import butterknife.BindView; +import xyz.wbsite.wbui.base.BaseSPAFragment; +import xyz.wbsite.webclient.R; + +public class MainFragment extends BaseSPAFragment { + + @BindView(R.id.pager) + ViewPager pager; + @BindView(R.id.tabs) + QMUITabSegment tabs; + + @Override + protected int getFragmnetLayout() { + return R.layout.fragment_main; + } + + @Override + protected void onViewInit() { + } + + @Override + protected boolean canDragBack() { + return false; + } +} diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ic_launcher_background.xml b/src/main/resources/modules/Android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..d5fccc53 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background.xml b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background.xml new file mode 100644 index 00000000..d6daa596 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background_form_input.xml b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background_form_input.xml new file mode 100644 index 00000000..47341470 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background_form_input.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background_login.xml b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background_login.xml new file mode 100644 index 00000000..fda18af2 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background_login.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background_login_button.xml b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background_login_button.xml new file mode 100644 index 00000000..dd524888 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_background_login_button.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ui_border_bottom.xml b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_border_bottom.xml new file mode 100644 index 00000000..a1bf163d --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_border_bottom.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ui_drawable_click.xml b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_drawable_click.xml new file mode 100644 index 00000000..0f6c46b1 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_drawable_click.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ui_icon_down.png b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_icon_down.png new file mode 100644 index 00000000..94a73247 Binary files /dev/null and b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_icon_down.png differ diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ui_icon_up.png b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_icon_up.png new file mode 100644 index 00000000..47fc91b5 Binary files /dev/null and b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_icon_up.png differ diff --git a/src/main/resources/modules/Android/app/src/main/res/drawable/ui_keyboard_key.xml b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_keyboard_key.xml new file mode 100644 index 00000000..f700b85b --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/drawable/ui_keyboard_key.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/res/layout/activity_custom_capture.xml b/src/main/resources/modules/Android/app/src/main/res/layout/activity_custom_capture.xml new file mode 100644 index 00000000..e00add0a --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/layout/activity_custom_capture.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/modules/Android/app/src/main/res/layout/fragment.xml b/src/main/resources/modules/Android/app/src/main/res/layout/fragment.xml new file mode 100644 index 00000000..62bdd0f6 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/layout/fragment.xml @@ -0,0 +1,23 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/modules/Android/app/src/main/res/layout/fragment_login.xml b/src/main/resources/modules/Android/app/src/main/res/layout/fragment_login.xml new file mode 100644 index 00000000..5fb71119 --- /dev/null +++ b/src/main/resources/modules/Android/app/src/main/res/layout/fragment_login.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +