();
}
activityStack.add(activity);
}
/**
* 获取当前Activity(堆栈中最后一个压入的)
*/
public Activity currentActivity() {
Activity activity = activityStack.lastElement();
return activity;
}
/**
* 结束当前Activity(堆栈中最后一个压入的)
*/
public void finishActivity() {
Activity activity = activityStack.lastElement();
finishActivity(activity);
}
/**
* 结束指定的Activity
*/
public void finishActivity(Activity activity) {
if (activity != null) {
activityStack.remove(activity);
activity.finish();
activity = null;
}
}
/**
* 结束指定类名的Activity
*/
public void finishActivity(Class> cls) {
for (Activity activity : activityStack) {
if (activity.getClass().equals(cls)) {
finishActivity(activity);
}
}
}
/**
* 结束所有Activity
*/
public void finishAllActivity() {
for (int i = 0, size = activityStack.size(); i < size; i++) {
if (null != activityStack.get(i)) {
activityStack.get(i).finish();
}
}
activityStack.clear();
}
/**
* 退出应用程序
*/
public void AppExit(Context context) {
try {
finishAllActivity();
ActivityManager activityMgr =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityMgr.killBackgroundProcesses(context.getPackageName());
System.exit(0);
} catch (Exception e) {
}
}
public boolean isAppExit() {
return activityStack == null || activityStack.isEmpty();
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/global/GlobalApplication.java
================================================
package com.lxm.module_library.global;
import android.app.Application;
import android.content.Context;
import android.os.Handler;
import com.lxm.module_library.utils.PreferencesUtil;
/**
* Created by Horrarndoo on 2017/9/1.
*
* 全局Application
*/
public class GlobalApplication extends Application {
protected static Context context;
protected static Handler handler;
protected static int mainThreadId;
private static GlobalApplication globalApplication;
public static GlobalApplication getInstance() {
return globalApplication;
}
@Override
public void onCreate() {
super.onCreate();
globalApplication = this;
context = getApplicationContext();
handler = new Handler();
mainThreadId = android.os.Process.myTid();
PreferencesUtil.Companion.get(this);
}
/**
* 获取上下文对象
*
* @return context
*/
public static Context getContext() {
return context;
}
/**
* 获取全局handler
*
* @return 全局handler
*/
public static Handler getHandler() {
return handler;
}
/**
* 获取主线程id
*
* @return 主线程id
*/
public static int getMainThreadId() {
return mainThreadId;
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/helper/RetrofitCreateHelper.java
================================================
package com.lxm.module_library.helper;
import com.lxm.module_library.utils.AppUtils;
import com.lxm.module_library.helper.okhttp.TrustManager;
import com.lxm.module_library.helper.okhttp.cache.CacheInterceptor;
import com.lxm.module_library.helper.okhttp.cache.HttpCache;
import com.lxm.module_library.helper.okhttp.cookies.CookieManger;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import java.util.concurrent.TimeUnit;
/**
* Created by Horrarndoo on 2017/9/7.
*
*/
public class RetrofitCreateHelper {
private static final int TIMEOUT_READ = 5;
private static final int TIMEOUT_CONNECTION = 5;
private static final HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY);
private static CacheInterceptor cacheInterceptor = new CacheInterceptor();
private static OkHttpClient okHttpClient = new OkHttpClient.Builder()
//SSL证书
.sslSocketFactory(TrustManager.getUnsafeOkHttpClient())
.hostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
//打印日志
.addInterceptor(interceptor)
//设置Cache拦截器
.addNetworkInterceptor(cacheInterceptor)
.addInterceptor(cacheInterceptor)
.cache(HttpCache.getCache())
// 设置 Cookie
.cookieJar(new CookieManger(AppUtils.INSTANCE.getContext()))
//time out
.connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)
.readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
.writeTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
//失败重连
.retryOnConnectionFailure(true)
.build();
public static T createApi(Class clazz, String url) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(clazz);
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/helper/RxHelper.java
================================================
package com.lxm.module_library.helper;
import io.reactivex.*;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
/**
* Created by Horrarndoo on 2017/9/12.
*
*/
public class RxHelper {
/**
* 统一线程处理
*
* 发布事件io线程,接收事件主线程
*/
public static ObservableTransformer rxSchedulerHelper() {//compose处理线程
return new ObservableTransformer() {
@Override
public ObservableSource apply(Observable upstream) {
return upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
/**
* 生成Flowable
*
* @param t
* @return Flowable
*/
public static Flowable createFlowable(final T t) {
return Flowable.create(new FlowableOnSubscribe() {
@Override
public void subscribe(FlowableEmitter emitter) throws Exception {
try {
emitter.onNext(t);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
}, BackpressureStrategy.BUFFER);
}
/**
* 生成Observable
*
* @param t
* @return Flowable
*/
public static Observable createObservable(final T t) {
return Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
try {
emitter.onNext(t);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
});
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/helper/okhttp/TrustManager.java
================================================
package com.lxm.module_library.helper.okhttp;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* Created by Horrarndoo on 2017/9/12.
*
*/
public class TrustManager {
public static SSLSocketFactory getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final X509TrustManager[] trustAllCerts = new X509TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts,
new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext
.getSocketFactory();
return sslSocketFactory;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/helper/okhttp/cache/CacheInterceptor.java
================================================
package com.lxm.module_library.helper.okhttp.cache;
import com.lxm.module_library.utils.AppUtils;
import com.lxm.module_library.utils.HttpUtils;
import com.lxm.module_library.utils.NetworkConnectionUtils;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
/**
* Created by Horrarndoo on 2017/9/12.
*
* CacheInterceptor
*/
public class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (NetworkConnectionUtils.INSTANCE.isNetworkConnected(AppUtils.INSTANCE.getContext())) {
// 有网络时, 缓存60s
int maxAge = 10 ;
request = request.newBuilder()
.removeHeader("User-Agent")
.header("User-Agent", HttpUtils.INSTANCE.getUserAgent())
.build();
Response response = chain.proceed(request);
return response.newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-Control")
.header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else {
// 无网络时,缓存为4周
int maxStale = 60 * 60 * 24 * 28;
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.removeHeader("User-Agent")
.header("User-Agent", HttpUtils.INSTANCE.getUserAgent())
.build();
Response response = chain.proceed(request);
return response.newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-Control")
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/helper/okhttp/cache/HttpCache.java
================================================
package com.lxm.module_library.helper.okhttp.cache;
import com.lxm.module_library.utils.AppUtils;
import okhttp3.Cache;
import java.io.File;
/**
* Created by Horrarndoo on 2017/9/12.
*
*/
public class HttpCache {
private static final int HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 50 * 1024 * 1024;
public static Cache getCache() {
return new Cache(new File(AppUtils.INSTANCE.getContext().getExternalCacheDir().getAbsolutePath() + File
.separator + "data/NetCache"),
HTTP_RESPONSE_DISK_CACHE_MAX_SIZE);
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/helper/okhttp/cookies/CookieManger.java
================================================
package com.lxm.module_library.helper.okhttp.cookies;
import android.content.Context;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import java.util.List;
/**
* Created by CoderLengary
*/
public class CookieManger implements CookieJar {
private static Context mContext;
private static PersistentCookieStore cookieStore;
public CookieManger(Context context) {
mContext = context;
if (cookieStore == null) {
cookieStore = new PersistentCookieStore(mContext);
}
}
@Override
public void saveFromResponse(HttpUrl url, List cookies) {
if (cookies != null && cookies.size() > 0) {
for (Cookie item : cookies) {
cookieStore.add(url, item);
}
}
}
@Override
public List loadForRequest(HttpUrl url) {
List cookies = cookieStore.get(url);
return cookies;
}
public static void clearAllCookies() {
cookieStore.removeAll();
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/helper/okhttp/cookies/OkHttpCookies.java
================================================
package com.lxm.module_library.helper.okhttp.cookies;
import okhttp3.Cookie;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class OkHttpCookies implements Serializable {
private transient final Cookie cookies;
private transient Cookie clientCookies;
public OkHttpCookies(Cookie cookies) {
this.cookies = cookies;
}
public Cookie getCookies() {
Cookie bestCookies = cookies;
if (clientCookies != null) {
bestCookies = clientCookies;
}
return bestCookies;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(cookies.name());
out.writeObject(cookies.value());
out.writeLong(cookies.expiresAt());
out.writeObject(cookies.domain());
out.writeObject(cookies.path());
out.writeBoolean(cookies.secure());
out.writeBoolean(cookies.httpOnly());
out.writeBoolean(cookies.hostOnly());
out.writeBoolean(cookies.persistent());
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
String name = (String) in.readObject();
String value = (String) in.readObject();
long expiresAt = in.readLong();
String domain = (String) in.readObject();
String path = (String) in.readObject();
boolean secure = in.readBoolean();
boolean httpOnly = in.readBoolean();
boolean hostOnly = in.readBoolean();
Cookie.Builder builder = new Cookie.Builder();
builder = builder.name(name);
builder = builder.value(value);
builder = builder.expiresAt(expiresAt);
builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain);
builder = builder.path(path);
builder = secure ? builder.secure() : builder;
builder = httpOnly ? builder.httpOnly() : builder;
clientCookies = builder.build();
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/helper/okhttp/cookies/PersistentCookieStore.java
================================================
package com.lxm.module_library.helper.okhttp.cookies;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Log;
import okhttp3.Cookie;
import okhttp3.HttpUrl;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class PersistentCookieStore {
private static final String LOG_TAG = "PersistentCookieStore";
private static final String COOKIE_PREFS = "Cookies_Prefs";
private final Map> cookies;
private final SharedPreferences cookiePrefs;
public PersistentCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
cookies = new HashMap<>();
//将持久化的cookies缓存到内存中 即map cookies
Map prefsMap = cookiePrefs.getAll();
for (Map.Entry entry : prefsMap.entrySet()) {
String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
for (String name : cookieNames) {
String encodedCookie = cookiePrefs.getString(name, null);
if (encodedCookie != null) {
Cookie decodedCookie = decodeCookie(encodedCookie);
if (decodedCookie != null) {
if (!cookies.containsKey(entry.getKey())) {
cookies.put(entry.getKey(), new ConcurrentHashMap());
}
cookies.get(entry.getKey()).put(name, decodedCookie);
}
}
}
}
}
protected String getCookieToken(Cookie cookie) {
return cookie.name() + "@" + cookie.domain();
}
public void add(HttpUrl url, Cookie cookie) {
String name = getCookieToken(cookie);
if (!cookies.containsKey(url.host())) {
cookies.put(url.host(), new ConcurrentHashMap());
}
cookies.get(url.host()).put(name, cookie);
//cookies持久化到本地
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
prefsWriter.putString(name, encodeCookie(new OkHttpCookies(cookie)));
prefsWriter.apply();
}
public List get(HttpUrl url) {
ArrayList ret = new ArrayList<>();
if (cookies.containsKey(url.host())) {
ret.addAll(cookies.get(url.host()).values());
}
return ret;
}
public boolean removeAll() {
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.clear();
prefsWriter.apply();
cookies.clear();
return true;
}
/**
* cookies 序列化成 string
*
* @param cookie 要序列化的cookie
* @return 序列化之后的string
*/
protected String encodeCookie(OkHttpCookies cookie) {
if (cookie == null)
return null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(cookie);
} catch (IOException e) {
Log.d(LOG_TAG, "IOException in encodeCookie", e);
return null;
}
return byteArrayToHexString(os.toByteArray());
}
/**
* 将字符串反序列化成cookies
*
* @param cookieString cookies string
* @return cookie object
*/
protected Cookie decodeCookie(String cookieString) {
byte[] bytes = hexStringToByteArray(cookieString);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Cookie cookie = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
cookie = ((OkHttpCookies) objectInputStream.readObject()).getCookies();
} catch (IOException e) {
Log.d(LOG_TAG, "IOException in decodeCookie", e);
} catch (ClassNotFoundException e) {
Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);
}
return cookie;
}
/**
* 二进制数组转十六进制字符串
*
* @param bytes byte array to be converted
* @return string containing hex values
*/
protected String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte element : bytes) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase(Locale.US);
}
/**
* 十六进制字符串转二进制数组
*
* @param hexString string of hex-encoded values
* @return decoded byte array
*/
protected byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/materialLogin/DefaultLoginView.java
================================================
package com.lxm.module_library.materialLogin;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.support.design.widget.TextInputLayout;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.lxm.module_library.R;
/**
* Created by shem on 1/15/16.
*/
public class DefaultLoginView extends FrameLayout {
public interface DefaultLoginViewListener {
void onLogin(TextInputLayout loginUser, TextInputLayout loginPass);
}
private DefaultLoginViewListener listener;
public DefaultLoginView(Context context) {
this(context, null);
}
public DefaultLoginView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DefaultLoginView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DefaultLoginView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.login_layout, this, true);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.DefaultLoginView,
0, 0);
TextView loginTitle = (TextView) findViewById(R.id.login_title);
final TextInputLayout loginUser = (TextInputLayout) findViewById(R.id.login_user);
final TextInputLayout loginPass = (TextInputLayout) findViewById(R.id.login_pass);
TextView loginBtn = (TextView) findViewById(R.id.login_btn);
findViewById(R.id.login_btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onLogin(loginUser, loginPass);
}
}
});
try {
String string = a.getString(R.styleable.DefaultLoginView_loginTitle);
if (string != null) {
loginTitle.setText(string);
}
string = a.getString(R.styleable.DefaultLoginView_loginHint);
if (string != null) {
loginUser.setHint(string);
}
string = a.getString(R.styleable.DefaultLoginView_loginPasswordHint);
if (string != null) {
loginPass.setHint(string);
}
string = a.getString(R.styleable.DefaultLoginView_loginActionText);
if (string != null) {
loginBtn.setText(string);
}
int color = a.getColor(R.styleable.DefaultLoginView_loginTextColor, ContextCompat.getColor(getContext(), R.color.material_login_login_text_color));
loginUser.getEditText().setTextColor(color);
loginPass.getEditText().setTextColor(color);
} finally {
a.recycle();
}
}
public void setListener(DefaultLoginViewListener listener) {
this.listener = listener;
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/materialLogin/DefaultRegisterView.java
================================================
package com.lxm.module_library.materialLogin;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.support.design.widget.TextInputLayout;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.lxm.module_library.R;
/**
* Created by shem on 1/15/16.
*/
public class DefaultRegisterView extends FrameLayout implements RegisterView {
public interface DefaultRegisterViewListener {
void onRegister(TextInputLayout registerUser, TextInputLayout registerPass, TextInputLayout registerPassRep);
}
private DefaultRegisterViewListener listener;
private View registerCancel;
public DefaultRegisterView(Context context) {
this(context, null);
}
public DefaultRegisterView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DefaultRegisterView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DefaultRegisterView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.register_layout, this, true);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.DefaultRegisterView,
0, 0);
TextView registerTitle = (TextView) findViewById(R.id.register_title);
final TextInputLayout registerUser = (TextInputLayout) findViewById(R.id.register_user);
final TextInputLayout registerPass = (TextInputLayout) findViewById(R.id.register_pass);
final TextInputLayout registerPassRep = (TextInputLayout) findViewById(R.id.register_pass_rep);
TextView registerBtn = (TextView) findViewById(R.id.register_btn);
registerCancel = findViewById(R.id.register_cancel);
registerBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onRegister(registerUser,
registerPass,
registerPassRep);
}
});
try {
String string = a.getString(R.styleable.DefaultRegisterView_registerTitle);
if (string != null) {
registerTitle.setText(string);
}
string = a.getString(R.styleable.DefaultRegisterView_registerHint);
if (string != null) {
registerUser.setHint(string);
}
string = a.getString(R.styleable.DefaultRegisterView_registerPasswordHint);
if (string != null) {
registerPass.setHint(string);
}
string = a.getString(R.styleable.DefaultRegisterView_registerRepeatPasswordHint);
if (string != null) {
registerPassRep.setHint(string);
}
string = a.getString(R.styleable.DefaultRegisterView_registerActionText);
if (string != null) {
registerBtn.setText(string);
}
int color = a.getColor(R.styleable.DefaultRegisterView_registerTextColor, ContextCompat.getColor(getContext(), R.color.material_login_register_text_color));
registerUser.getEditText().setTextColor(color);
registerPass.getEditText().setTextColor(color);
registerPassRep.getEditText().setTextColor(color);
} finally {
a.recycle();
}
}
public void setListener(DefaultRegisterViewListener listener) {
this.listener = listener;
}
@Override
public View getCancelRegisterView() {
return registerCancel;
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/materialLogin/MaterialLoginView.java
================================================
package com.lxm.module_library.materialLogin;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.FrameLayout;
import com.lxm.module_library.R;
import io.codetail.animation.ViewAnimationUtils;
/**
* Created by shem on 1/15/16.
*/
@SuppressLint("RestrictedApi")
public class MaterialLoginView extends FrameLayout {
private static final String TAG = MaterialLoginView.class.getSimpleName();
private FloatingActionButton registerFab;
private View registerCancel;
private ViewGroup loginCard;
private ViewGroup registerCard;
private View registerView;
private View loginView;
public MaterialLoginView(Context context) {
this(context, null);
}
public MaterialLoginView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MaterialLoginView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MaterialLoginView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.login_view, this, true);
loginCard = (ViewGroup) findViewById(R.id.login_card);
registerCard = (ViewGroup) findViewById(R.id.register_card);
registerFab = (FloatingActionButton) findViewById(R.id.register_fab);
registerFab.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
animateRegister();
} else {
//There's a bug in support implementation of FAB, so we firing animation with little delay so it won't be override by Android
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
animateRegister();
}
}, 100);
}
}
});
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.MaterialLoginView,
0, 0);
try {
int loginViewId = a.getResourceId(R.styleable.MaterialLoginView_loginView, R.layout.default_login_view);
inflate(getContext(), loginViewId, loginCard);
loginView = loginCard.getChildAt(0);
int registerViewId = a.getResourceId(R.styleable.MaterialLoginView_registerView, R.layout.default_register_view);
inflate(getContext(), registerViewId, registerCard);
registerView = registerCard.getChildAt(0);
if (registerView instanceof RegisterView) {
registerCancel = ((RegisterView) registerView).getCancelRegisterView();
}else if (registerView.findViewById(R.id.register_cancel) != null) {
registerCancel = registerView.findViewById(R.id.register_cancel);
}
if (registerCancel != null) {
registerCancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
animateLogin();
}
});
} else {
Log.d(TAG, "The register view should implement RegisterView interface or set a view with register_cancel id");
}
registerFab.setImageResource(
a.getResourceId(R.styleable.MaterialLoginView_registerIcon, R.drawable.ic_add));
boolean enabled = a.getBoolean(R.styleable.MaterialLoginView_registerEnabled, true);
registerFab.setVisibility(enabled ? View.VISIBLE : View.GONE);
} finally {
a.recycle();
}
}
private void animateRegister() {
Path path = new Path();
if (isRTL()) {
RectF rect = new RectF(-41F, -40F, 241F, 242F);
path.addArc(rect, -135F, -180F);
path.lineTo(200F, -50F);
} else {
RectF rect = new RectF(-241F, -40F, 41F, 242F);
path.addArc(rect, -45F, 180F);
path.lineTo(-0F, -50F);
}
FabAnimation fabAnimation = new FabAnimation(path);
fabAnimation.setDuration(400);
fabAnimation.setInterpolator(new AccelerateInterpolator());
fabAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
Animator animator = getCircularRevealAnimation(registerCard, isRTL() ? 250 : registerCard.getWidth() - 250, 400, 0f, 2F * registerCard.getHeight());
animator.setDuration(700);
animator.setStartDelay(200);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
registerCard.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
loginCard.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
@Override
public void onAnimationEnd(Animation animation) {
registerFab.setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
registerFab.startAnimation(fabAnimation);
}
private boolean isRTL() {
return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
}
private Animator getCircularRevealAnimation(View view, int centerX, int centerY, float startRadius, float endRadius) {
return ViewAnimationUtils.createCircularReveal(
view, centerX, centerY, startRadius, endRadius);
}
public void animateLogin() {
registerCancel.animate().scaleX(0F).scaleY(0F).alpha(0F).rotation(90F).
setDuration(200).setInterpolator(new AccelerateInterpolator()).start();
Animator animator = getCircularRevealAnimation(registerCard, registerCard.getWidth() / 2, registerCard.getHeight() / 2, 1f * registerCard.getHeight(), 0F);
animator.setDuration(500);
animator.setStartDelay(100);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
loginCard.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
registerCard.setVisibility(View.GONE);
registerCancel.setScaleX(1F);
registerCancel.setScaleY(1F);
registerCancel.setAlpha(1F);
registerCancel.setRotation(0F);
registerFab.setVisibility(View.VISIBLE);
ObjectAnimator animX = ObjectAnimator.ofFloat(registerFab, "scaleX", 0F, 1F);
ObjectAnimator animY = ObjectAnimator.ofFloat(registerFab, "scaleY", 0F, 1F);
ObjectAnimator alpha = ObjectAnimator.ofFloat(registerFab, "alpha", 0F, 1F);
ObjectAnimator rotation = ObjectAnimator.ofFloat(registerFab, "rotation", 90F, 0F);
AnimatorSet animator = new AnimatorSet();
animator.playTogether(animX, animY, alpha, rotation);
animator.setInterpolator(new AccelerateInterpolator());
animator.setDuration(200);
animator.start();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
public View getLoginView() {
return loginView;
}
public View getRegisterView() {
return registerView;
}
class FabAnimation extends Animation {
private PathMeasure measure;
private float[] pos;
public FabAnimation(Path path) {
measure = new PathMeasure(path, false);
pos = new float[]{0, 0};
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
measure.getPosTan(measure.getLength() * interpolatedTime, pos, null);
Matrix matrix = t.getMatrix();
matrix.setTranslate(pos[0], pos[1]);
matrix.preRotate(interpolatedTime * 45);
t.setAlpha(1 - interpolatedTime);
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/materialLogin/RegisterView.java
================================================
package com.lxm.module_library.materialLogin;
import android.view.View;
/**
* Created by shem on 16/09/2016.
*/
public interface RegisterView {
View getCancelRegisterView();
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/statusbar/StatusBarUtil.java
================================================
package com.lxm.module_library.statusbar;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.TabActivity;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.v4.widget.DrawerLayout;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
/**
* Created by Jaeger on 16/2/14.
*
* Email: chjie.jaeger@gmail.com
* GitHub: https://github.com/laobie
*/
public class StatusBarUtil {
public static final int DEFAULT_STATUS_BAR_ALPHA = 112;
/**
* 设置状态栏颜色
*
* @param activity 需要设置的 activity
* @param color 状态栏颜色值
*/
public static void setColor(Activity activity, @ColorInt int color) {
setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA);
}
/**
* 设置状态栏颜色
*
* @param activity 需要设置的activity
* @param color 状态栏颜色值
* @param statusBarAlpha 状态栏透明度
*/
public static void setColor(Activity activity, @ColorInt int color, int statusBarAlpha) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
int count = decorView.getChildCount();
if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
} else {
StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha);
decorView.addView(statusView);
}
setRootView(activity);
}
}
/**
* 设置状态栏纯色 不加半透明效果
*
* @param activity 需要设置的 activity
* @param color 状态栏颜色值
*/
public static void setColorNoTranslucent(Activity activity, @ColorInt int color) {
setColor(activity, color, 0);
}
/**
* 设置状态栏颜色(5.0以下无半透明效果,不建议使用)
*
* @param activity 需要设置的 activity
* @param color 状态栏颜色值
*/
@Deprecated
public static void setColorDiff(Activity activity, @ColorInt int color) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 生成一个状态栏大小的矩形
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
int count = decorView.getChildCount();
if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
decorView.getChildAt(count - 1).setBackgroundColor(color);
} else {
StatusBarView statusView = createStatusBarView(activity, color);
decorView.addView(statusView);
}
setRootView(activity);
}
/**
* 使状态栏半透明
*
* 适用于图片作为背景的界面,此时需要图片填充到状态栏
*
* @param activity 需要设置的activity
*/
public static void setTranslucent(Activity activity) {
setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA);
}
/**
* 使状态栏半透明
*
* 适用于图片作为背景的界面,此时需要图片填充到状态栏
*
* @param activity 需要设置的activity
* @param statusBarAlpha 状态栏透明度
*/
public static void setTranslucent(Activity activity, int statusBarAlpha) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
setTransparent(activity);
addTranslucentView(activity, statusBarAlpha);
}
/**
* 针对根布局是 CoordinatorLayout, 使状态栏半透明
*
* 适用于图片作为背景的界面,此时需要图片填充到状态栏
*
* @param activity 需要设置的activity
* @param statusBarAlpha 状态栏透明度
*/
public static void setTranslucentForCoordinatorLayout(Activity activity, int statusBarAlpha) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
transparentStatusBar(activity);
addTranslucentView(activity, statusBarAlpha);
}
/**
* 设置状态栏全透明
*
* @param activity 需要设置的activity
*/
public static void setTransparent(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
transparentStatusBar(activity);
setRootView(activity);
}
/**
* 使状态栏透明(5.0以上半透明效果,不建议使用)
*
* 适用于图片作为背景的界面,此时需要图片填充到状态栏
*
* @param activity 需要设置的activity
*/
@Deprecated
public static void setTranslucentDiff(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 设置状态栏透明
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
setRootView(activity);
}
}
/**
* 为DrawerLayout 布局设置状态栏变色
*
* @param activity 需要设置的activity
* @param drawerLayout DrawerLayout
* @param color 状态栏颜色值
*/
public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {
setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA);
}
/**
* 为DrawerLayout 布局设置状态栏颜色,纯色
*
* @param activity 需要设置的activity
* @param drawerLayout DrawerLayout
* @param color 状态栏颜色值
*/
public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {
setColorForDrawerLayout(activity, drawerLayout, color, 0);
}
/**
* 为DrawerLayout 布局设置状态栏变色
*
* @param activity 需要设置的activity
* @param drawerLayout DrawerLayout
* @param color 状态栏颜色值
* @param statusBarAlpha 状态栏透明度
*/
public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color,
int statusBarAlpha) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
} else {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
// 生成一个状态栏大小的矩形
// 添加 statusBarView 到布局中
ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
if (contentLayout.getChildCount() > 0 && contentLayout.getChildAt(0) instanceof StatusBarView) {
contentLayout.getChildAt(0).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
} else {
StatusBarView statusBarView = createStatusBarView(activity, color);
contentLayout.addView(statusBarView, 0);
}
// 内容布局不是 LinearLayout 时,设置padding top
if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {
contentLayout.getChildAt(1)
.setPadding(contentLayout.getPaddingLeft(), getStatusBarHeight(activity) + contentLayout.getPaddingTop(),
contentLayout.getPaddingRight(), contentLayout.getPaddingBottom());
}
// 设置属性
ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1);
drawerLayout.setFitsSystemWindows(false);
contentLayout.setFitsSystemWindows(false);
contentLayout.setClipToPadding(true);
drawer.setFitsSystemWindows(false);
addTranslucentView(activity, statusBarAlpha);
}
/**
* 为DrawerLayout 布局设置状态栏变色(5.0以下无半透明效果,不建议使用)
*
* @param activity 需要设置的activity
* @param drawerLayout DrawerLayout
* @param color 状态栏颜色值
*/
@Deprecated
public static void setColorForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 生成一个状态栏大小的矩形
ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
if (contentLayout.getChildCount() > 0 && contentLayout.getChildAt(0) instanceof StatusBarView) {
contentLayout.getChildAt(0).setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA));
} else {
// 添加 statusBarView 到布局中
StatusBarView statusBarView = createStatusBarView(activity, color);
contentLayout.addView(statusBarView, 0);
}
// 内容布局不是 LinearLayout 时,设置padding top
if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {
contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0);
}
// 设置属性
ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1);
drawerLayout.setFitsSystemWindows(false);
contentLayout.setFitsSystemWindows(false);
contentLayout.setClipToPadding(true);
drawer.setFitsSystemWindows(false);
}
}
/**
* 为 DrawerLayout 布局设置状态栏透明
*
* @param activity 需要设置的activity
* @param drawerLayout DrawerLayout
*/
public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) {
setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA);
}
/**
* 为 DrawerLayout 布局设置状态栏透明
*
* @param activity 需要设置的activity
* @param drawerLayout DrawerLayout
*/
public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int statusBarAlpha) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
setTransparentForDrawerLayout(activity, drawerLayout);
addTranslucentView(activity, statusBarAlpha);
}
/**
* 为 DrawerLayout 布局设置状态栏透明
*
* @param activity 需要设置的activity
* @param drawerLayout DrawerLayout
*/
public static void setTransparentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
} else {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
// 内容布局不是 LinearLayout 时,设置padding top
if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {
contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0);
}
// 设置属性
ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1);
drawerLayout.setFitsSystemWindows(false);
contentLayout.setFitsSystemWindows(false);
contentLayout.setClipToPadding(true);
drawer.setFitsSystemWindows(false);
}
/**
* 为 DrawerLayout 布局设置状态栏透明(5.0以上半透明效果,不建议使用)
*
* @param activity 需要设置的activity
* @param drawerLayout DrawerLayout
*/
@Deprecated
public static void setTranslucentForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 设置状态栏透明
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 设置内容布局属性
ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
contentLayout.setFitsSystemWindows(true);
contentLayout.setClipToPadding(true);
// 设置抽屉布局属性
ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1);
vg.setFitsSystemWindows(false);
// 设置 DrawerLayout 属性
drawerLayout.setFitsSystemWindows(false);
}
}
/**
* 为头部是 ImageView 的界面设置状态栏全透明
*
* @param activity 需要设置的activity
* @param needOffsetView 需要向下偏移的 View
*/
public static void setTransparentForImageView(Activity activity, View needOffsetView) {
setTranslucentForImageView(activity, 0, needOffsetView);
}
/**
* 为头部是 ImageView 的界面设置状态栏透明(使用默认透明度)
*
* @param activity 需要设置的activity
* @param needOffsetView 需要向下偏移的 View
*/
public static void setTranslucentForImageView(Activity activity, View needOffsetView) {
setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView);
}
/**
* 为头部是 ImageView 的界面设置状态栏透明
*
* @param activity 需要设置的activity
* @param statusBarAlpha 状态栏透明度
* @param needOffsetView 需要向下偏移的 View
*/
public static void setTranslucentForImageView(Activity activity, int statusBarAlpha, View needOffsetView) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
activity.getWindow()
.getDecorView()
.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
if (activity instanceof TabActivity){
activity.getWindow()//兼容TabActivity
.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
} else {
activity.getWindow()
.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
addTranslucentView(activity, statusBarAlpha);
if (needOffsetView != null) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams();
if (layoutParams != null) {
layoutParams.setMargins(0, getStatusBarHeight(activity), 0, 0);
}
}
}
public static void setMargin(Activity activity, View needOffsetView) {
if (needOffsetView != null) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams();
if (layoutParams != null) {
layoutParams.setMargins(0, getStatusBarHeight(activity), 0, 0);
}
}
}
/**
* 为 fragment 头部是 ImageView 的设置状态栏透明
*
* @param activity fragment 对应的 activity
* @param needOffsetView 需要向下偏移的 View
*/
public static void setTranslucentForImageViewInFragment(Activity activity, View needOffsetView) {
setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView);
}
/**
* 为 fragment 头部是 ImageView 的设置状态栏透明
*
* @param activity fragment 对应的 activity
* @param needOffsetView 需要向下偏移的 View
*/
public static void setTransparentForImageViewInFragment(Activity activity, View needOffsetView) {
setTranslucentForImageViewInFragment(activity, 0, needOffsetView);
}
/**
* 为 fragment 头部是 ImageView 的设置状态栏透明
*
* @param activity fragment 对应的 activity
* @param statusBarAlpha 状态栏透明度
* @param needOffsetView 需要向下偏移的 View
*/
public static void setTranslucentForImageViewInFragment(Activity activity, int statusBarAlpha, View needOffsetView) {
setTranslucentForImageView(activity, statusBarAlpha, needOffsetView);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
clearPreviousSetting(activity);
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private static void clearPreviousSetting(Activity activity) {
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
int count = decorView.getChildCount();
if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
decorView.removeViewAt(count - 1);
ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
rootView.setPadding(0, 0, 0, 0);
}
}
/**
* 添加半透明矩形条
*
* @param activity 需要设置的 activity
* @param statusBarAlpha 透明值
*/
private static void addTranslucentView(Activity activity, int statusBarAlpha) {
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
if (contentView.getChildCount() > 1) {
contentView.getChildAt(1).setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0));
} else {
contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha));
}
}
/**
* 生成一个和状态栏大小相同的彩色矩形条
*
* @param activity 需要设置的 activity
* @param color 状态栏颜色值
* @return 状态栏矩形条
*/
private static StatusBarView createStatusBarView(Activity activity, @ColorInt int color) {
// 绘制一个和状态栏一样高的矩形
StatusBarView statusBarView = new StatusBarView(activity);
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
statusBarView.setLayoutParams(params);
statusBarView.setBackgroundColor(color);
return statusBarView;
}
/**
* 生成一个和状态栏大小相同的半透明矩形条
*
* @param activity 需要设置的activity
* @param color 状态栏颜色值
* @param alpha 透明值
* @return 状态栏矩形条
*/
private static StatusBarView createStatusBarView(Activity activity, @ColorInt int color, int alpha) {
// 绘制一个和状态栏一样高的矩形
StatusBarView statusBarView = new StatusBarView(activity);
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
statusBarView.setLayoutParams(params);
statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));
return statusBarView;
}
/**
* 设置根布局参数
*/
private static void setRootView(Activity activity) {
ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
rootView.setFitsSystemWindows(true);
rootView.setClipToPadding(true);
}
/**
* 使状态栏透明
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
private static void transparentStatusBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
} else {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
/**
* 创建半透明矩形 View
*
* @param alpha 透明值
* @return 半透明 View
*/
private static StatusBarView createTranslucentStatusBarView(Activity activity, int alpha) {
// 绘制一个和状态栏一样高的矩形
StatusBarView statusBarView = new StatusBarView(activity);
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
statusBarView.setLayoutParams(params);
statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0));
return statusBarView;
}
/**
* 获取状态栏高度
*
* @param context context
* @return 状态栏高度
*/
public static int getStatusBarHeight(Context context) {
// 获得状态栏高度
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
return context.getResources().getDimensionPixelSize(resourceId);
}
/**
* 计算状态栏颜色
*
* @param color color值
* @param alpha alpha值
* @return 最终的状态栏颜色
*/
private static int calculateStatusColor(@ColorInt int color, int alpha) {
float a = 1 - alpha / 255f;
int red = color >> 16 & 0xff;
int green = color >> 8 & 0xff;
int blue = color & 0xff;
red = (int) (red * a + 0.5);
green = (int) (green * a + 0.5);
blue = (int) (blue * a + 0.5);
return 0xff << 24 | red << 16 | green << 8 | blue;
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/statusbar/StatusBarView.java
================================================
package com.lxm.module_library.statusbar;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Jaeger on 16/6/8.
*
* Email: chjie.jaeger@gmail.com
* GitHub: https://github.com/laobie
*/
public class StatusBarView extends View {
public StatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StatusBarView(Context context) {
super(context);
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/AppUtils.kt
================================================
package com.lxm.module_library.utils
import android.Manifest
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Environment
import android.os.Handler
import android.support.v4.app.ActivityCompat
import android.telephony.TelephonyManager
import android.util.Log
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import com.lxm.module_library.global.GlobalApplication
import java.io.File
/**
*
* App工具类
*/
object AppUtils {
/**
* 获取上下文对象
*
* @return 上下文对象
*/
val context: Context
get() = GlobalApplication.getContext()
/**
* 获取全局handler
*
* @return 全局handler
*/
private val handler: Handler
get() = GlobalApplication.getHandler()
/**
* 获取主线程id
*
* @return 主线程id
*/
private val mainThreadId: Int
get() = GlobalApplication.getMainThreadId()
/**
* 获取SD卡路径
*
* @return 如果sd卡不存在则返回null
*/
private//判断sd卡是否存在
val sdPath: File?
get() {
var sdDir: File? = null
val sdCardExist = Environment.getExternalStorageState() == Environment
.MEDIA_MOUNTED
if (sdCardExist) {
sdDir = Environment.getExternalStorageDirectory()
}
return sdDir
}
/**
* 判断是否运行在主线程
*
* @return true:当前线程运行在主线程
* fasle:当前线程没有运行在主线程
*/
private// 获取当前线程id, 如果当前线程id和主线程id相同, 那么当前就是主线程
val isRunOnUIThread: Boolean
get() {
val myTid = android.os.Process.myTid()
return if (myTid == mainThreadId) {
true
} else false
}
/**
* 获取版本名称
*/
private fun getAppVersionName(context: Context): String? {
var versionName: String? = null
try {
// ---get the package info---
val pm = context.packageManager
val pi = pm.getPackageInfo(context.packageName, 0)
versionName = pi.versionName
if (versionName == null || versionName.length <= 0) {
return ""
}
} catch (e: Exception) {
Log.e("VersionInfo", "Exception", e)
}
return versionName
}
/**
* 获取版本号
*/
private fun getAppVersionCode(context: Context): Int {
var versioncode = -1
try {
// ---get the package info---
val pm = context.packageManager
val pi = pm.getPackageInfo(context.packageName, 0)
versioncode = pi.versionCode
} catch (e: Exception) {
Log.e("VersionInfo", "Exception", e)
}
return versioncode
}
@SuppressLint("MissingPermission")
private fun getIMEI(context: Context): String? {
val tm = context.getSystemService(Context
.TELEPHONY_SERVICE) as TelephonyManager
return if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
tm.deviceId
} else null
}
/**
* 显示软键盘
*/
private fun openSoftInput(et: EditText) {
val inputMethodManager = et.context
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(et, InputMethodManager.HIDE_NOT_ALWAYS)
}
/**
* 隐藏软键盘
*/
private fun hideSoftInput(et: EditText) {
val inputMethodManager = et.context
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(et.windowToken, InputMethodManager
.HIDE_NOT_ALWAYS)
}
/**
* 安装文件
*
* @param data
*/
private fun promptInstall(context: Context, data: Uri) {
val promptInstall = Intent(Intent.ACTION_VIEW)
.setDataAndType(data, "application/vnd.android.package-archive")
// FLAG_ACTIVITY_NEW_TASK 可以保证安装成功时可以正常打开 app
promptInstall.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(promptInstall)
}
fun copy2clipboard(context: Context, text: String) {
val cm = context.getSystemService(Context
.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("clip", text)
cm.primaryClip = clip
}
/**
* 运行在主线程
*
* @param r 运行的Runnable对象
*/
private fun runOnUIThread(r: Runnable) {
if (isRunOnUIThread) {
// 已经是主线程, 直接运行
r.run()
} else {
// 如果是子线程, 借助handler让其运行在主线程
handler.post(r)
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/BaseTools.java
================================================
package com.lxm.module_library.utils;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import com.lxm.module_library.global.GlobalApplication;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;
/**
* Created by jingbin on 2017/2/13.
*/
public class BaseTools {
//获取图片所在文件夹名称
public static String getDir(String path) {
String subString = path.substring(0, path.lastIndexOf('/'));
return subString.substring(subString.lastIndexOf('/') + 1, subString.length());
}
public static int getWindowWidth(Context context) {
// 获取屏幕分辨率
WindowManager wm = (WindowManager) (context
.getSystemService(Context.WINDOW_SERVICE));
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
int mScreenWidth = dm.widthPixels;
return mScreenWidth;
}
public static int getWindowHeigh(Context context) {
// 获取屏幕分辨率
WindowManager wm = (WindowManager) (context
.getSystemService(Context.WINDOW_SERVICE));
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
int mScreenHeigh = dm.heightPixels;
return mScreenHeigh;
}
//获得状态栏/通知栏的高度
public static int getStatusBarHeight(Context context) {
Class> c = null;
Object obj = null;
Field field = null;
int x = 0, statusBarHeight = 0;
try {
c = Class.forName("com.android.internal.R$dimen");
obj = c.newInstance();
field = c.getField("status_bar_height");
x = Integer.parseInt(field.get(obj).toString());
statusBarHeight = context.getResources().getDimensionPixelSize(x);
} catch (Exception e1) {
e1.printStackTrace();
}
return statusBarHeight;
}
/**
* 使用默认方式显示货币:
* 例如:¥12,345.46 默认保留2位小数,四舍五入
*
* @param d double
* @return String
*/
public static String formatCurrency(double d) {
String s = "";
try {
DecimalFormat nf = (DecimalFormat) NumberFormat.getCurrencyInstance(Locale.CHINA);
s = nf.format(d);
} catch (Exception e) {
e.printStackTrace();
return "" + d;
}
return s;
}
/**
* 去掉无效小数点 ".00"
*/
public static String formatMoney(double d) {
String tmp = formatCurrency(d);
if (tmp.endsWith(".00")) {
return tmp.substring(0, tmp.length() - 3);
} else {
return tmp;
}
}
/**
* 处于栈顶的Activity名
*/
public String getTopActivityName(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List var2 = am.getRunningTasks(1);
return ((ActivityManager.RunningTaskInfo) var2.get(0)).topActivity.getClassName();
}
public static void setText(String text, TextView textView) {
if (textView != null) {
if (TextUtils.isEmpty(text)) {
textView.setText("");
} else {
textView.setText(text);
}
}
}
/**
* 获取当前应用的版本号
*/
public static String getVersionName() {
// 获取packagemanager的实例
PackageManager packageManager = GlobalApplication.getInstance().getPackageManager();
// getPackageName()是你当前类的包名,0代表是获取版本信息
PackageInfo packInfo = null;
try {
packInfo = packageManager.getPackageInfo(GlobalApplication.getInstance().getPackageName(), 0);
return packInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return "1.0";
}
}
/**
* 实现文本复制功能
*
* @param content 复制的文本
*/
public static void copy(String content) {
if (!TextUtils.isEmpty(content)) {
// 得到剪贴板管理器
ClipboardManager cmb = (ClipboardManager) GlobalApplication.getInstance().getSystemService(Context.CLIPBOARD_SERVICE);
cmb.setText(content.trim());
// 创建一个剪贴数据集,包含一个普通文本数据条目(需要复制的数据)
ClipData clipData = ClipData.newPlainText(null, content);
// 把数据集设置(复制)到剪贴板
cmb.setPrimaryClip(clipData);
}
}
/**
* 清空剪切板内容
* 加上 manager.setText(null); 不然小米3Android6.0 清空无效
* 因为api过期使用最新注意使用 manager.getPrimaryClip(),不然小米3Android6.0 清空无效
*/
public static void clearClipboard() {
ClipboardManager manager = (ClipboardManager) GlobalApplication.getInstance().getSystemService(Context.CLIPBOARD_SERVICE);
if (manager != null) {
try {
manager.setPrimaryClip(manager.getPrimaryClip());
manager.setText(null);
} catch (Exception e) {
}
}
}
/**
* 使用浏览器打开链接
*/
public static void openLink(Context context, String content) {
Uri issuesUrl = Uri.parse(content);
Intent intent = new Intent(Intent.ACTION_VIEW, issuesUrl);
context.startActivity(intent);
}
/**
* 判断手机是否安装某个应用
*
* @param context
* @param appPackageName 应用包名
* @return true:安装,false:未安装
*/
public static boolean isApplicationAvilible(Context context, String appPackageName) {
try {
// 获取packagemanager
PackageManager packageManager = context.getPackageManager();
// 获取所有已安装程序的包信息
List pinfo = packageManager.getInstalledPackages(0);
if (pinfo != null) {
for (int i = 0; i < pinfo.size(); i++) {
String pn = pinfo.get(i).packageName;
if (appPackageName.equals(pn)) {
return true;
}
}
}
return false;
} catch (Exception ignored) {
return false;
}
}
/**
* 隐藏软键盘
*
* @param activity 要隐藏软键盘的activity
*/
public static void hideSoftKeyBoard(Activity activity) {
final View v = activity.getWindow().peekDecorView();
if (v != null && v.getWindowToken() != null) {
try {
((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(activity.getCurrentFocus()
.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
} catch (Exception e) {
Log.w("TAG", e.toString());
}
}
}
/**
* 显示软键盘
*/
public static void showSoftKeyBoard(Activity activity, View view) {
((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(view, 0);
}
/**
* 发起添加群流程。群号:Android 云阅交流群(727379132) 的 key 为: jSdY9xxzZ7xXG55_V8OUb8ds_YT6JjAn
* 调用 joinQQGroup(jSdY9xxzZ7xXG55_V8OUb8ds_YT6JjAn) 即可发起手Q客户端申请加群 Android 云阅交流群(727379132)
*
* @param key 由官网生成的key
*/
public static void joinQQGroup(Context context, String key) {
Intent intent = new Intent();
intent.setData(Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26k%3D" + key));
// 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面 //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
try {
context.startActivity(intent);
} catch (Exception e) {
// 未安装手Q或安装的版本不支持
ToastUtil.showToastLong("未安装手Q或安装的版本不支持~");
}
}
public static void joinQQChat(Context context, String qqNumber) {
Intent intent = new Intent();
intent.setData(Uri.parse("mqqwpa://im/chat?chat_type=wpa&uin=" + qqNumber));
try {
context.startActivity(intent);
} catch (Exception e) {
// 未安装手Q或安装的版本不支持
ToastUtil.showToastLong("未安装手Q或安装的版本不支持~");
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/BitmapUtils.kt
================================================
package com.lxm.module_library.utils
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import android.view.View
import java.io.*
/**
*
* 图片工具类
*/
object BitmapUtils {
/**
* 将一个view转换成bitmap位图
*
* @param view 要转换的View
* @return view转换的bitmap
*/
fun viewToBitmap(view: View): Bitmap {
val bitmap = Bitmap.createBitmap(view.measuredWidth, view.measuredHeight,
Bitmap.Config.ARGB_8888)
view.draw(Canvas(bitmap))
return bitmap
}
/**
* 获取模糊虚化的bitmap
*
* @param context
* @param bitmap 要模糊的图片
* @param radius 模糊等级 >=0 && <=25
* @return
*/
fun getBlurBitmap(context: Context, bitmap: Bitmap, radius: Int): Bitmap {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
blurBitmap(context, bitmap, radius)
} else bitmap
}
/**
* android系统的模糊方法
*
* @param bitmap 要模糊的图片
* @param radius 模糊等级 >=0 && <=25
*/
private fun blurBitmap(context: Context, bitmap: Bitmap, radius: Int): Bitmap {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val outBitmap = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap
.Config.ARGB_8888)
val rs = RenderScript.create(context)
val blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs))
val allIn = Allocation.createFromBitmap(rs, bitmap)
val allOut = Allocation.createFromBitmap(rs, outBitmap)
blurScript.setRadius(radius.toFloat())
blurScript.setInput(allIn)
blurScript.forEach(allOut)
allOut.copyTo(outBitmap)
bitmap.recycle()
rs.destroy()
return outBitmap
} else {
return bitmap
}
}
/**
* 根据资源获取Bitmap
*/
open fun getFitSampleBitmap(resources: Resources, id:Int, width: Int, height: Int):Bitmap{
var options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeResource(resources,id,options)
options.inSampleSize = getFitInSampleSize(height,width,options)
options.inJustDecodeBounds = false
return BitmapFactory.decodeResource(resources,id)
}
/**
* 按图片尺寸压缩 参数是bitmap
* @param bitmap
* @param pixelW
* @param pixelH
* @return
*/
fun compressImageFromBitmap(bitmap: Bitmap, pixelW: Int, pixelH: Int): Bitmap {
val os = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os)
if (os.toByteArray().size / 1024 > 512) {//判断如果图片大于0.5M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
os.reset()
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os)//这里压缩50%,把压缩后的数据存放到baos中
}
var `is` = ByteArrayInputStream(os.toByteArray())
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
options.inPreferredConfig = Bitmap.Config.RGB_565
BitmapFactory.decodeStream(`is`, null, options)
options.inJustDecodeBounds = false
options.inSampleSize = getFitInSampleSize(if (pixelH > pixelW) pixelW else pixelH, pixelW * pixelH,options)
`is` = ByteArrayInputStream(os.toByteArray())
return BitmapFactory.decodeStream(`is`, null, options)
}
/**
* 根据文件路径获取Bitmap
*/
private fun getFitSampleBitmap(path: String, width: Int, height: Int):Bitmap{
var options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(path,options)
options.inSampleSize = getFitInSampleSize(height,width,options)
options.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path,options)
}
/**
* 根据字节流获取Bitmap
*/
fun getFitSimpleBitmap(inputStream: InputStream, filePath:String, width: Int, height: Int):Bitmap{
return getFitSampleBitmap(createStreamToFile(filePath,inputStream),width,height)
}
fun createStreamToFile(path: String,inputStream:InputStream):String{
var file = File(path)
if (file.exists()){
file.delete()
}
file.createNewFile()
var outputStream = FileOutputStream(file)
var byte = ByteArray(1024)
var len = 0
while ((inputStream.read(byte))!= -1){
len = inputStream.read(byte)
outputStream.write(byte,0,len)
}
inputStream.close()
outputStream.close()
return path
}
/**
* 获取压缩比例
*/
private fun getFitInSampleSize( height:Int,width:Int,options: BitmapFactory.Options):Int{
var inSampleSize = 1
if (options.outWidth > width || options.outHeight > height){
var widthRadio : Int = Math.round(options.outWidth.toFloat() / width.toFloat())
var heightRadio : Int = Math.round(options.outHeight.toFloat() / height.toFloat())
inSampleSize = Math.min(widthRadio,heightRadio)
}
return inSampleSize
}
/**
* Drawable To Bitmap
*/
private fun drawableToBitamp(drawable: Drawable): Bitmap {
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val w = drawable.intrinsicWidth
val h = drawable.intrinsicHeight
val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, w, h)
drawable.draw(canvas)
return bitmap
}
/**
* 压缩图片质量
*/
fun compressImage(bitmap: Bitmap): Bitmap {
val baos = ByteArrayOutputStream()
//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
var options = 100
//循环判断如果压缩后图片是否大于50kb,大于继续压缩
while (baos.toByteArray().size / 1024 > 50) {
baos.reset()
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos)
options -= 10//每次都减少10
}
//把压缩后的数据baos存放到ByteArrayInputStream中
val isBm = ByteArrayInputStream(baos.toByteArray())
//把ByteArrayInputStream数据生成图片
return BitmapFactory.decodeStream(isBm, null, null)
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/CheckNetwork.java
================================================
package com.lxm.module_library.utils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
/**
* 用于判断是不是联网状态
*
* @author Dzy
*/
public class CheckNetwork {
/**
* 判断网络是否连通
*/
public static boolean isNetworkConnected(Context context) {
try {
if(context!=null){
@SuppressWarnings("static-access")
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
return info != null && info.isConnected();
}else{
/**如果context为空,就返回false,表示网络未连接*/
return false;
}
}catch (Exception e){
e.printStackTrace();
return false;
}
}
public static boolean isWifiConnected(Context context) {
if (context != null) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
return info != null && (info.getType() == ConnectivityManager.TYPE_WIFI);
} else {
/**如果context为null就表示为未连接*/
return false;
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/ClassUtil.kt
================================================
package com.lxm.module_library.utils
import android.arch.lifecycle.AndroidViewModel
import android.arch.lifecycle.ViewModel
import com.lxm.module_library.base.NoViewModel
import java.lang.reflect.ParameterizedType
object ClassUtil {
/**
* 获取泛型ViewModel的class对象
*/
fun getViewModel(obj: Any): Class? {
val currentClass = obj.javaClass
val tClass = getGenericClass(currentClass, ViewModel::class.java)
return if (tClass == null || tClass == AndroidViewModel::class.java || tClass == NoViewModel::class.java) {
null
} else tClass
}
private fun getGenericClass(klass: Class<*>, filterClass: Class<*>): Class? {
val type = klass.genericSuperclass
if (type == null || type !is ParameterizedType) return null
val types = type.actualTypeArguments
for (t in types) {
val tClass = t as Class
if (filterClass.isAssignableFrom(tClass)) {
return tClass
}
}
return null
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/DateUtils.kt
================================================
package com.lxm.module_library.utils
import android.annotation.SuppressLint
import android.text.TextUtils
import android.text.format.DateFormat
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
/**
* Created by Horrarndoo on 2017/8/31.
*
* 日期时间工具类
*/
object DateUtils {
private const val ONE_SECOND_MILLIONS: Long = 1000
private const val ONE_MINUTE_MILLIONS = 60 * ONE_SECOND_MILLIONS
private const val ONE_HOUR_MILLIONS = 60 * ONE_MINUTE_MILLIONS
private const val ONE_DAY_MILLIONS = 24 * ONE_HOUR_MILLIONS
private const val DAY_OF_YEAR = 365
/**
* 日期格式为 2016-02-03 17:04:58
*/
private const val PATTERN_DATE = "yyyy年MM月dd日"
private const val PATTERN_TIME = "HH:mm:ss"
private const val PATTERN_SPLIT = " "
private const val PATTERN = PATTERN_DATE + PATTERN_SPLIT + PATTERN_TIME
fun getShortTime(dateStr: String): String {
val str: String
val date = str2date(dateStr)
val curDate = Date()
val durTime = curDate.time - date!!.time
val dayDiff = calculateDayDiff(date, curDate)
return if (durTime <= 10 * ONE_MINUTE_MILLIONS) {
"刚刚"
} else if (durTime < ONE_HOUR_MILLIONS) {
(durTime / ONE_MINUTE_MILLIONS).toString() + "分钟前"
} else if (dayDiff == 0) {
(durTime / ONE_HOUR_MILLIONS).toString() + "小时前"
} else if (dayDiff == -1) {
"昨天" + DateFormat.format("HH:mm", date)
} else if (isSameYear(date, curDate) && dayDiff < -1) {
DateFormat.format("MM-dd", date).toString()
} else {
DateFormat.format("yyyy-MM", date).toString()
}
}
/**
* 获取日期 PATTERN_DATE 部分
*/
fun getDate(date: String): String {
return if (TextUtils.isEmpty(date) || !date.contains(PATTERN_SPLIT)) {
""
} else date.split(PATTERN_SPLIT.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
}
/**
* 原有日期上累加月
*
* @return 累加后的日期 PATTERN_DATE 部分
*/
fun addMonth(date: String, moonCount: Int): String {
var date = date
if (TextUtils.isEmpty(date)) {
// val df = SimpleDateFormat(PATTERN_DATE + PATTERN_SPLIT + PATTERN_TIME)
val df = SimpleDateFormat.getDateTimeInstance()
date = df.format(Date())
}
val calendar = str2calendar(date)
calendar!!.add(Calendar.MONTH, moonCount)
return getDate(calendar2str(calendar))
}
/**
* 计算天数差
*/
fun calculateDayDiff(targetTime: Date, compareTime: Date): Int {
val sameYear = isSameYear(targetTime, compareTime)
if (sameYear) {
return calculateDayDiffOfSameYear(targetTime, compareTime)
} else {
var dayDiff = 0
// 累计年数差的整年天数
val yearDiff = calculateYearDiff(targetTime, compareTime)
dayDiff += yearDiff * DAY_OF_YEAR
// 累计同一年内的天数
dayDiff += calculateDayDiffOfSameYear(targetTime, compareTime)
return dayDiff
}
}
/**
* 计算同一年内的天数差
*/
fun calculateDayDiffOfSameYear(targetTime: Date?, compareTime: Date?): Int {
if (targetTime == null || compareTime == null) {
return 0
}
val tarCalendar = Calendar.getInstance()
tarCalendar.time = targetTime
val tarDayOfYear = tarCalendar.get(Calendar.DAY_OF_YEAR)
val compareCalendar = Calendar.getInstance()
compareCalendar.time = compareTime
val comDayOfYear = compareCalendar.get(Calendar.DAY_OF_YEAR)
return tarDayOfYear - comDayOfYear
}
/**
* 计算年数差
*/
private fun calculateYearDiff(targetTime: Date?, compareTime: Date?): Int {
if (targetTime == null || compareTime == null) {
return 0
}
val tarCalendar = Calendar.getInstance()
tarCalendar.time = targetTime
val tarYear = tarCalendar.get(Calendar.YEAR)
val compareCalendar = Calendar.getInstance()
compareCalendar.time = compareTime
val comYear = compareCalendar.get(Calendar.YEAR)
return tarYear - comYear
}
/**
* 计算月数差
*
* @param targetTime
* @param compareTime
* @return
*/
fun calculateMonthDiff(targetTime: String, compareTime: String): Int {
return calculateMonthDiff(str2date(targetTime, PATTERN_DATE),
str2date(compareTime, PATTERN_DATE))
}
/**
* 计算月数差
*
* @param targetTime
* @param compareTime
* @return
*/
fun calculateMonthDiff(targetTime: Date?, compareTime: Date?): Int {
val tarCalendar = Calendar.getInstance()
tarCalendar.time = targetTime
val tarYear = tarCalendar.get(Calendar.YEAR)
val tarMonth = tarCalendar.get(Calendar.MONTH)
val compareCalendar = Calendar.getInstance()
compareCalendar.time = compareTime
val comYear = compareCalendar.get(Calendar.YEAR)
val comMonth = compareCalendar.get(Calendar.MONTH)
return (tarYear - comYear) * 12 + tarMonth - comMonth
}
/**
* 是否为同一年
*/
private fun isSameYear(targetTime: Date?, compareTime: Date?): Boolean {
if (targetTime == null || compareTime == null) {
return false
}
val tarCalendar = Calendar.getInstance()
tarCalendar.time = targetTime
val tarYear = tarCalendar.get(Calendar.YEAR)
val compareCalendar = Calendar.getInstance()
compareCalendar.time = compareTime
val comYear = compareCalendar.get(Calendar.YEAR)
return tarYear == comYear
}
@SuppressLint("SimpleDateFormat")
@JvmOverloads
private fun str2date(str: String?, format: String = PATTERN): Date? {
var date: Date? = null
try {
if (str != null) {
val sdf = SimpleDateFormat(format)
date = sdf.parse(str)
}
} catch (e: ParseException) {
e.printStackTrace()
}
return date
}
@JvmOverloads
private fun date2str(date: Date, format: String = PATTERN): String {
val sdf = SimpleDateFormat(format, Locale.CHINA)
return sdf.format(date)
}
private fun str2calendar(str: String): Calendar? {
var calendar: Calendar? = null
val date = str2date(str)
if (date != null) {
calendar = Calendar.getInstance()
calendar!!.time = date
}
return calendar
}
fun str2calendar(str: String, format: String): Calendar? {
var calendar: Calendar? = null
val date = str2date(str, format)
if (date != null) {
calendar = Calendar.getInstance()
calendar!!.time = date
}
return calendar
}
private fun calendar2str(calendar: Calendar): String {
return date2str(calendar.time)
}
fun calendar2str(calendar: Calendar, format: String): String {
return date2str(calendar.time, format)
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/DialogUtils.kt
================================================
package com.lxm.module_library.utils
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.support.v7.app.AlertDialog
/**
* Created by Horrarndoo on 2017/8/31.
*
* 对话框工具类, 提供常用对话框显示, 使用support.v7包内的AlertDialog样式
*/
object DialogUtils {
fun showCommonDialog(context: Context, message: String, positiveText: String,
negativeText: String, listener: DialogInterface.OnClickListener): Dialog {
return AlertDialog.Builder(context)
.setMessage(message)
.setPositiveButton(positiveText, listener)
.setNegativeButton(negativeText, null)
.show()
}
fun showConfirmDialog(context: Context, message: String, positiveText: String,
listener: DialogInterface.OnClickListener): Dialog {
return AlertDialog.Builder(context)
.setMessage(message)
.setPositiveButton(positiveText, listener)
.show()
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/DisplayUtils.kt
================================================
package com.lxm.module_library.utils
import android.app.Activity
import android.content.Context
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import com.lxm.module_library.global.GlobalApplication
/**
* Created by Horrarndoo on 2017/8/31.
*
*
* 显示相关工具类
*/
object DisplayUtils {
/**
* 将px值转换为dp值
*/
fun px2dp(pxValue: Float, context: Context): Int {
val scale = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, pxValue, context.resources.displayMetrics)
return scale.toInt()
}
/**
* 将dp值转换为px值
*/
fun dp2px(dpValue: Float): Int {
val scale = GlobalApplication.getInstance().getResources().getDisplayMetrics().density
return (dpValue * scale + 0.5f).toInt()
}
/**
* 将px值转换为sp值
*/
fun px2sp(pxValue: Float, context: Context): Int {
val scale = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, pxValue, context.resources.displayMetrics)
return scale.toInt()
}
/**
* 将sp值转换为px值
*/
fun sp2px(spValue: Float, context: Context): Int {
val scale = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.resources.displayMetrics)
return scale.toInt()
}
/**
* 获取屏幕宽度
*/
fun getScreenWidthPixels(context: Activity): Int {
val metric = DisplayMetrics()
context.windowManager.defaultDisplay.getMetrics(metric)
return metric.widthPixels
}
/**
* 获取屏幕高度
*/
fun getScreenHeightPixels(context: Activity): Int {
val metric = DisplayMetrics()
context.windowManager.defaultDisplay.getMetrics(metric)
return metric.heightPixels
}
/**
* 设置某个View的margin
*
* @param view 需要设置的view
* @param isDp 需要设置的数值是否为DP
* @param left 左边距
* @param right 右边距
* @param top 上边距
* @param bottom 下边距
* @return
*/
fun setViewMargin(
view: View?,
isDp: Boolean,
left: Int,
right: Int,
top: Int,
bottom: Int
): ViewGroup.LayoutParams? {
if (view == null) {
return null
}
var leftPx = left
var rightPx = right
var topPx = top
var bottomPx = bottom
val params = view.layoutParams
var marginParams: ViewGroup.MarginLayoutParams? = null
//获取view的margin设置参数
if (params is ViewGroup.MarginLayoutParams) {
marginParams = params
} else {
//不存在时创建一个新的参数
marginParams = ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 250)
}
//根据DP与PX转换计算值
if (isDp) {
leftPx = dp2px(left.toFloat())
rightPx = dp2px(right.toFloat())
topPx = dp2px(top.toFloat())
bottomPx = dp2px(bottom.toFloat())
}
//设置margin
marginParams.setMargins(leftPx, topPx, rightPx, bottomPx)
view.layoutParams = marginParams
view.requestLayout()
return marginParams
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/FileUtils.kt
================================================
package com.lxm.module_library.utils
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import android.text.TextUtils
import com.lxm.module_library.R
import com.lxm.module_library.helper.RxHelper
import io.reactivex.Observable
import io.reactivex.ObservableOnSubscribe
import java.io.*
import kotlin.experimental.and
/**
*
*
* 读取文件工具类
*/
object FileUtils {
/**
* Convert byte[] to hex string.将byte转换成int,
* 然后利用Integer.toHexString(int)来转换成16进制字符串。
*
* @param src byte[] data
* @return hex string
*/
fun bytesToHexString(src: ByteArray?): String? {
val stringBuilder = StringBuilder("")
if (src == null || src.size <= 0) {
return null
}
for (i in src.indices) {
val v = src[i] and 0xFF.toByte()
val hv = Integer.toHexString(v.toInt())
if (hv.length < 2) {
stringBuilder.append(0)
}
stringBuilder.append(hv)
}
return stringBuilder.toString()
}
/**
* 根据文件名称和路径,获取sd卡中的文件,以File形式返回byte
*/
fun getFile(fileName: String, folder: String): File? {
val state = Environment.getExternalStorageState()
if (state == Environment.MEDIA_MOUNTED) {
val pathFile = File(Environment.getExternalStorageDirectory().toString() + folder)
// && !pathFile .isDirectory()
if (!pathFile.exists()) {
pathFile.mkdirs()
}
return File(pathFile, fileName)
}
return null
}
/**
* 根据文件名称和路径,获取sd卡中的文件,判断文件是否存在,存在返回true
*/
fun checkFile(fileName: String, folder: String): Boolean {
val targetFile = getFile(fileName, folder)
return targetFile!!.exists()
}
/**
* 根据Uri返回文件绝对路径
* 兼容了file:///开头的 和 content://开头的情况
*/
fun getRealFilePathFromUri(context: Context, uri: Uri?): String? {
if (null == uri) {
return null
}
val scheme = uri.scheme
var data: String? = null
if (scheme == null) {
data = uri.path
} else if (ContentResolver.SCHEME_FILE.equals(scheme, ignoreCase = true)) {
data = uri.path
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme, ignoreCase = true)) {
val cursor = context.contentResolver.query(uri, arrayOf(MediaStore
.Images.ImageColumns.DATA), null, null, null)
if (null != cursor) {
if (cursor.moveToFirst()) {
val index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
if (index > -1) {
data = cursor.getString(index)
}
}
cursor.close()
}
}
return data
}
/**
* 检查文件是否存在
*/
fun checkDirPath(dirPath: String): String {
if (TextUtils.isEmpty(dirPath)) {
return ""
}
val dir = File(dirPath)
if (!dir.exists()) {
dir.mkdirs()
}
return dirPath
}
fun copyFile(sourcefile: File, targetFile: File) {
var input: FileInputStream? = null
var inbuff: BufferedInputStream? = null
var out: FileOutputStream? = null
var outbuff: BufferedOutputStream? = null
try {
input = FileInputStream(sourcefile)
inbuff = BufferedInputStream(input)
out = FileOutputStream(targetFile)
outbuff = BufferedOutputStream(out)
val b = ByteArray(1024 * 5)
var len: Int
len = inbuff.read(b)
while (len != -1) {
outbuff.write(b, 0, len)
len = inbuff.read(b)
}
outbuff.flush()
} catch (ex: Exception) {
} finally {
try {
inbuff?.close()
outbuff?.close()
out?.close()
input?.close()
} catch (ex: Exception) {
}
}
}
/**
* 保存图片到本机
*
* @param context context
* @param fileName 文件名
* @param file file
* @param saveResultCallback 保存结果callback
*/
fun saveImage(context: Context, fileName: String, file: File,
saveResultCallback: SaveResultCallback) {
val disposable = Observable.create(ObservableOnSubscribe { e ->
val appDir = File(Environment.getExternalStorageDirectory(), context.getString(R.string.app_name))
if (!appDir.exists()) {
appDir.mkdir()
}
var saveFileName: String
if (fileName.contains(".png") || fileName.contains(".gif")) {
val fileFormat = fileName.substring(fileName.lastIndexOf("."))
saveFileName = MD5Utils.getMD5(fileName) + fileFormat
} else {
saveFileName = MD5Utils.getMD5(fileName) + ".png"
}
saveFileName = saveFileName.substring(20)
val saveFile = File(appDir, saveFileName)
try {
val `is` = FileInputStream(file)
val fos = FileOutputStream(saveFile)
val buffer = ByteArray(1024 * 1024)
var count: Int
count = `is`.read(buffer)
while (count > 0) {
fos.write(buffer, 0, count)
count = `is`.read(buffer)
}
fos.close()
`is`.close()
e.onNext(saveFile)
} catch (exception: FileNotFoundException) {
e.onError(exception)
} catch (exception: IOException) {
e.onError(exception)
}
}).compose(RxHelper.rxSchedulerHelper())
.subscribe({ saveFile ->
saveResultCallback.onSavedSuccess()
val uri = Uri.fromFile(saveFile)
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri))
}, { throwable ->
if (throwable is FileNotFoundException) {
saveResultCallback.onSavedFailed()
throwable.printStackTrace()
} else if (throwable is IOException) {
saveResultCallback.onSavedFailed()
throwable.printStackTrace()
}
})
}
/**
* 保存Bitmap到本机
*
* @param context context
* @param fileName bitmap文件名
* @param bmp bitmap
* @param saveResultCallback 保存结果callback
*/
fun saveBitmap(context: Context, fileName: String, bmp: Bitmap,
saveResultCallback: SaveResultCallback) {
val disposable = Observable.create(ObservableOnSubscribe { e ->
val appDir = File(Environment.getExternalStorageDirectory(), context.getString(R.string.app_name))
if (!appDir.exists()) {
appDir.mkdir()
}
// 设置以当前时间格式为图片名称
var saveFileName = MD5Utils.getMD5(fileName) + ".png"
saveFileName = saveFileName.substring(20)
val file = File(appDir, saveFileName)
try {
val fos = FileOutputStream(file)
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos)
fos.flush()
fos.close()
e.onNext(file)
} catch (exception: FileNotFoundException) {
e.onError(exception)
} catch (exception: IOException) {
e.onError(exception)
}
}).compose(RxHelper.rxSchedulerHelper())
.subscribe({ saveFile ->
saveResultCallback.onSavedSuccess()
val uri = Uri.fromFile(saveFile)
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri))
}, { throwable ->
if (throwable is FileNotFoundException) {
saveResultCallback.onSavedFailed()
throwable.printStackTrace()
} else if (throwable is IOException) {
saveResultCallback.onSavedFailed()
throwable.printStackTrace()
}
})
}
interface SaveResultCallback {
/**
* 保存成功
*/
fun onSavedSuccess()
/**
* 保存失败
*/
fun onSavedFailed()
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/HtmlUtils.kt
================================================
package com.lxm.module_library.utils
/**
* Created by Horrarndoo on 2017/8/31.
*
* Html工具类
*/
object HtmlUtils {
/**
* css样式,隐藏header
*/
private const val HIDE_HEADER_STYLE = ""
/**
* css style tag,需要格式化
*/
private const val NEEDED_FORMAT_CSS_TAG = ""
/**
* js script tag,需要格式化
*/
private const val NEEDED_FORMAT_JS_TAG = ""
val MIME_TYPE = "text/html; charset=utf-8"
val ENCODING = "utf-8"
/**
* 根据css链接生成Link标签
*
* @param url String
* @return String
*/
fun createCssTag(url: String): String {
return String.format(NEEDED_FORMAT_CSS_TAG, url)
}
/**
* 根据多个css链接生成Link标签
*
* @param urls List
* @return String
*/
fun createCssTag(urls: List): String {
val sb = StringBuilder()
for (url in urls) {
sb.append(createCssTag(url))
}
return sb.toString()
}
/**
* 根据js链接生成Script标签
*
* @param url String
* @return String
*/
fun createJsTag(url: String): String {
return String.format(NEEDED_FORMAT_JS_TAG, url)
}
/**
* 根据多个js链接生成Script标签
*
* @param urls List
* @return String
*/
fun createJsTag(urls: List): String {
val sb = StringBuilder()
for (url in urls) {
sb.append(createJsTag(url))
}
return sb.toString()
}
/**
* 根据样式标签,html字符串,js标签
* 生成完整的HTML文档
*/
fun createHtmlData(html: String, cssList: List, jsList: List): String {
val css = HtmlUtils.createCssTag(cssList)
val js = HtmlUtils.createJsTag(jsList)
return css + HIDE_HEADER_STYLE + html + js
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/HttpUtils.kt
================================================
package com.lxm.module_library.utils
import android.os.Build
import android.webkit.WebSettings
import java.util.*
import java.util.regex.Pattern
/**
*
* HttpUtils 主要用于获取UserAgent
*/
object HttpUtils {
/**
* 获取UserAgent
*
* @return UserAgent
*/
val userAgent: String
get() {
var userAgent: String
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
try {
userAgent = WebSettings.getDefaultUserAgent(AppUtils.context)
} catch (e: Exception) {
userAgent = System.getProperty("http.agent")
}
} else {
userAgent = System.getProperty("http.agent")
}
val sb = StringBuffer()
var i = 0
val length = userAgent.length
while (i < length) {
val c = userAgent[i]
if (c <= '\u001f' || c >= '\u007f') {
sb.append(String.format("\\u%04x", c.toInt()))
} else {
sb.append(c)
}
i++
}
return sb.toString()
}
fun makeUA(): String {
return Build.BRAND + "/" + Build.MODEL + "/" + Build.VERSION.RELEASE
}
fun returnImageUrlsFromHtml(html: String): Array? {
val imageSrcList = ArrayList()
val p = Pattern.compile("
]*\\bsrc\\b\\s*=\\s*('|\")?([^'\"\n\rf>]+(\\" +
".jpg|\\.bmp|\\.eps|\\.gif|\\.mif|\\.miff|\\.png|\\.tif|\\.tiff|\\.svg|\\.wmf|\\" +
".jpe|\\.jpeg|\\.dib|\\.ico|\\.tga|\\.cut|\\.pic|\\b)\\b)[^>]*>", Pattern
.CASE_INSENSITIVE)
val m = p.matcher(html)
var quote: String
var src: String
while (m.find()) {
quote = m.group(1)
src = if (quote == null || quote.trim { it <= ' ' }.isEmpty())
m.group(2).split("//s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
else
m
.group(2)
imageSrcList.add(src)
}
return if (imageSrcList.size == 0) {
null
} else imageSrcList.toTypedArray()
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/ImageUtils.kt
================================================
package com.lxm.module_library.utils
import android.annotation.TargetApi
import android.app.Activity
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.support.v7.app.AlertDialog
/**
* 图片工具类
*/
object ImageUtils {
/**
* 拍照
*/
private const val REQUEST_CODE_FROM_CAMERA = 1 shl 10
/**
* 相册
*/
private const val REQUEST_CODE_FROM_ALBUM = 1 shl 12
/**
* 裁剪
*/
private const val REQUEST_CODE_CROP_IMAGE = 1 shl 14
/**
* 存放拍照图片的uri地址
*/
var imageUriFromCamera: Uri? = null
/**
* 存放裁剪图片的uri地址
*/
var cropImageUri: Uri? = null
/**
* 显示获取照片不同方式对话框
*/
@JvmOverloads
fun showImagePickDialog(activity: Activity, addRequest: Int = 0) {
val title = "选择获取图片方式"
val items = arrayOf("拍照", "相册")
AlertDialog.Builder(activity)
.setTitle(title)
.setItems(items) { dialog, which ->
dialog.dismiss()
when (which) {
0 -> pickImageFromCamera(activity, addRequest)
1 -> pickImageFromAlbum(activity, addRequest)
else -> {
}
}
}
.setNegativeButton("取消", null)
.show()
}
/**
* 打开相机拍照获取图片
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
fun pickImageFromCamera(activity: Activity, addRequest: Int) {
// 先生成一个uri地址用于存放拍照获取的图片
imageUriFromCamera = createImageUri(activity)
val intent = Intent()
intent.action = MediaStore.ACTION_IMAGE_CAPTURE
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUriFromCamera)
activity.startActivityForResult(intent, REQUEST_CODE_FROM_CAMERA + addRequest)
}
/**
* 打开相机拍照获取图片
*/
fun pickImageFromCamera(activity: Activity) {
pickImageFromCamera(activity, 0)
}
/**
* 打开本地相册选取图片
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
fun pickImageFromAlbum(activity: Activity, addRequest: Int) {
val intent = Intent()
intent.action = Intent.ACTION_GET_CONTENT
intent.type = "image/*"
activity.startActivityForResult(intent, REQUEST_CODE_FROM_ALBUM + addRequest)
}
/**
* 打开本地相册选取图片
*/
fun pickImageFromAlbum(activity: Activity) {
pickImageFromAlbum(activity, 0)
}
/**
* 图片裁剪
*/
fun cropImage(activity: Activity, srcUri: Uri) {
cropImageUri = createImageUri(activity)
val intent = Intent("com.android.camera.action.CROP")
intent.setDataAndType(srcUri, "image/*")
intent.putExtra("crop", "true")
////////////////////////////////////////////////////////////////
// 1.宽高和比例都不设置时,裁剪框可以自行调整(比例和大小都可以随意调整)
////////////////////////////////////////////////////////////////
// 2.只设置裁剪框宽高比(aspect)后,裁剪框比例固定不可调整,只能调整大小
/////////////////////////////////
// 3.裁剪后生成图片宽高(output)的设置和裁剪框无关,只决定最终生成图片大小
////////////////////////////////////////////////////////////////
// 4.裁剪框宽高比例(aspect)可以和裁剪后生成图片比例(output)不同,此时,
// 会以裁剪框的宽为准,按照裁剪宽高比例生成一个图片,该图和框选部分可能不同,
// 不同的情况可能是截取框选的一部分,也可能超出框选部分,向下延伸补足
////////////////////////////////////////////////////////////////
// aspectX aspectY 是裁剪框宽高的比例
intent.putExtra("aspectX", 1)
intent.putExtra("aspectY", 1)
// outputX outputY 是裁剪后生成图片的宽高
// intent.putExtra("outputX", 300);
// intent.putExtra("outputY", 100);
// return-data为true时,会直接返回bitmap数据,但是大图裁剪时会出现OOM,推荐下面为false时的方式
// return-data为false时,不会返回bitmap,但需要指定一个MediaStore.EXTRA_OUTPUT保存图片uri
intent.putExtra("return-data", false)
intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri)
activity.startActivityForResult(intent, REQUEST_CODE_CROP_IMAGE)
}
/**
* 创建一条图片uri,用于保存拍照后的照片
*/
private fun createImageUri(context: Context): Uri? {
val name = "boreImg" + System.currentTimeMillis()
val values = ContentValues()
values.put(MediaStore.Images.Media.TITLE, name)
values.put(MediaStore.Images.Media.DISPLAY_NAME, "$name.jpeg")
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
return context.contentResolver.insert(MediaStore.Images.Media
.EXTERNAL_CONTENT_URI, values)
}
/**
* 删除一条图片
*/
fun deleteImageUri(context: Context, uri: Uri) {
context.contentResolver.delete(uri, null, null)
}
/**
* 用第三方应用app打开图片
*/
fun openImageByOtherApp(context: Context, imageUri: Uri) {
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.setDataAndType(imageUri, "image/*")
context.startActivity(intent)
}
/**
* 根据Uri获取图片绝对路径,解决Android4.4以上版本Uri转换
*/
fun getImageAbsolutePath19(context: Context?, imageUri: Uri?): String? {
if (context == null || imageUri == null)
return null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, imageUri)) {
if (isExternalStorageDocument(imageUri)) {
val docId = DocumentsContract.getDocumentId(imageUri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true)) {
return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
}
} else if (isDownloadsDocument(imageUri)) {
val id = DocumentsContract.getDocumentId(imageUri)
val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
return getDataColumn(context, contentUri, null, null)
} else if (isMediaDocument(imageUri)) {
val docId = DocumentsContract.getDocumentId(imageUri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
var contentUri: Uri? = null
if ("image" == type) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
} else if ("video" == type) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
} else if ("audio" == type) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
val selection = MediaStore.Images.Media._ID + "=?"
val selectionArgs = arrayOf(split[1])
return getDataColumn(context, contentUri, selection, selectionArgs)
}
}
// MediaStore (and general)
if ("content".equals(imageUri.scheme, ignoreCase = true)) {
// Return the remote address
return if (isGooglePhotosUri(imageUri)) imageUri.lastPathSegment else getDataColumn(context, imageUri, null, null)
} else if ("file".equals(imageUri.scheme, ignoreCase = true)) {
return imageUri.path
}// File
return null
}
private fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array?): String? {
var cursor: Cursor? = null
val column = MediaStore.Images.Media.DATA
val projection = arrayOf(column)
try {
cursor = context.contentResolver.query(uri!!, projection, selection,
selectionArgs, null)
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
}
} finally {
cursor?.close()
}
return null
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
private fun isExternalStorageDocument(uri: Uri): Boolean {
return "com.android.externalstorage.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
private fun isDownloadsDocument(uri: Uri): Boolean {
return "com.android.providers.downloads.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
private fun isMediaDocument(uri: Uri): Boolean {
return "com.android.providers.media.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
private fun isGooglePhotosUri(uri: Uri): Boolean {
return "com.google.android.apps.photos.content" == uri.authority
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/JsonUtils.kt
================================================
package com.lxm.module_library.utils
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import java.lang.reflect.Type
/**
*
* Json转换工具类
*/
object JsonUtils {
private val mGson = Gson()
/**
* 将对象准换为json字符串
*
* @param object
* @param
* @return
*/
fun serialize(`object`: T): String {
return mGson.toJson(`object`)
}
/**
* 将json字符串转换为对象
*
* @param json
* @param clz
* @param
* @return
*/
@Throws(JsonSyntaxException::class)
fun deserialize(json: String, clz: Class): T {
return mGson.fromJson(json, clz)
}
/**
* 将json对象转换为实体对象
*
* @param json
* @param clz
* @param
* @return
* @throws JsonSyntaxException
*/
@Throws(JsonSyntaxException::class)
fun deserialize(json: JsonObject, clz: Class): T {
return mGson.fromJson(json, clz)
}
/**
* 将json字符串转换为对象
*
* @param json
* @param type
* @param
* @return
*/
@Throws(JsonSyntaxException::class)
fun deserialize(json: String, type: Type): T {
return mGson.fromJson(json, type)
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/MD5Utils.kt
================================================
package com.lxm.module_library.utils
import java.security.MessageDigest
/**
*
* MD5加密工具类
*/
object MD5Utils {
/**
* MD5加密,32位
*/
fun getMD5(str: String): String {
val md5: MessageDigest
try {
md5 = MessageDigest.getInstance("MD5")
} catch (e: Exception) {
e.printStackTrace()
return ""
}
val charArray = str.toCharArray()
val byteArray = ByteArray(charArray.size)
for (i in charArray.indices) {
byteArray[i] = charArray[i].toByte()
}
val md5Bytes = md5.digest(byteArray)
val hexValue = StringBuffer()
for (i in md5Bytes.indices) {
val `val` = md5Bytes[i].toInt() and 0xff
if (`val` < 16) {
hexValue.append("0")
}
hexValue.append(Integer.toHexString(`val`))
}
return hexValue.toString()
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/MyDividerItemDecoration.java
================================================
package com.lxm.module_library.utils;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
public class MyDividerItemDecoration extends RecyclerView.ItemDecoration {
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
private static final String TAG = "DividerItem";
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
private boolean mIsShowBottomDivider;
private boolean mIsShowFirstDivider;
private boolean mIsShowSecondDivider;
/**
* Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
*/
private int mOrientation;
private final Rect mBounds = new Rect();
/**
* Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
* {@link android.support.v7.widget.LinearLayoutManager}.
*
* @param context Current context, it will be used to access resources.
* @param orientation Divider orientation. Should be {@link #HORIZONTAL} or
* {@link #VERTICAL}.
* @param isShowBottomDivider true show bottom divider false not show bottom divider
*/
public MyDividerItemDecoration(Context context, int orientation, boolean isShowBottomDivider) {
this(context, orientation, isShowBottomDivider, true, true);
}
public MyDividerItemDecoration(Context context, int orientation,
boolean isShowBottomDivider, boolean isShowFirstDivider, boolean isShowSecondDivider) {
mIsShowBottomDivider = isShowBottomDivider;
mIsShowFirstDivider = isShowFirstDivider;
mIsShowSecondDivider = isShowSecondDivider;
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
if (mDivider == null) {
Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this "
+ "DividerItemDecoration. Please set that attribute all call setDrawable()");
}
a.recycle();
setOrientation(orientation);
}
/**
* Sets the orientation for this divider. This should be called if
* {@link RecyclerView.LayoutManager} changes orientation.
*
* @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
*/
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL && orientation != VERTICAL) {
throw new IllegalArgumentException(
"Invalid orientation. It should be either HORIZONTAL or VERTICAL");
}
mOrientation = orientation;
}
/**
* Sets the {@link Drawable} for this divider.
*
* @param drawable Drawable that should be used as a divider.
*/
public void setDrawable(@NonNull Drawable drawable) {
if (drawable == null) {
throw new IllegalArgumentException("Drawable cannot be null.");
}
mDivider = drawable;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (parent.getLayoutManager() == null || mDivider == null) {
return;
}
if (mOrientation == VERTICAL) {
drawVertical(c, parent, state);
} else {
drawHorizontal(c, parent, state);
}
}
private void drawVertical(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
canvas.save();
final int left;
final int right;
//noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
if (parent.getClipToPadding()) {
left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight();
canvas.clipRect(left, parent.getPaddingTop(), right,
parent.getHeight() - parent.getPaddingBottom());
} else {
left = 0;
right = parent.getWidth();
}
final int childCount = parent.getChildCount();
final int lastPosition = state.getItemCount() - 1;
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final int childRealPosition = parent.getChildAdapterPosition(child);
// mIsShowFirstDivider false绘制第一个view的divider
if (childRealPosition == 0 && !mIsShowFirstDivider) {
continue;
}
// mIsShowSecondDivider false绘制第二个view的divider
if (childRealPosition == 1 && !mIsShowSecondDivider) {
continue;
}
// mIsShowBottomDivider false的时候不绘制最后一个view的divider
if (mIsShowBottomDivider || childRealPosition < lastPosition) {
parent.getDecoratedBoundsWithMargins(child, mBounds);
final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
final int top = bottom - mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
}
canvas.restore();
}
private void drawHorizontal(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
canvas.save();
final int top;
final int bottom;
//noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
if (parent.getClipToPadding()) {
top = parent.getPaddingTop();
bottom = parent.getHeight() - parent.getPaddingBottom();
canvas.clipRect(parent.getPaddingLeft(), top,
parent.getWidth() - parent.getPaddingRight(), bottom);
} else {
top = 0;
bottom = parent.getHeight();
}
final int childCount = parent.getChildCount();
final int lastPosition = state.getItemCount() - 1;
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final int childRealPosition = parent.getChildAdapterPosition(child);
// mIsShowFirstDivider false绘制第一个view的divider
if (childRealPosition == 0 && !mIsShowFirstDivider) {
continue;
}
// mIsShowSecondDivider false绘制第二个view的divider
if (childRealPosition == 1 && !mIsShowSecondDivider) {
continue;
}
//mIsShowBottomDivider false的时候不绘制最后一个view的divider
if (mIsShowBottomDivider || childRealPosition < lastPosition) {
parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
final int right = mBounds.right + Math.round(child.getTranslationX());
final int left = right - mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
}
canvas.restore();
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
if (mDivider == null) {
outRect.set(0, 0, 0, 0);
return;
}
if (mOrientation == VERTICAL) {
//parent.getChildCount() 不能拿到item的总数
//https://stackoverflow.com/questions/29666598/android-recyclerview-finding-out-first
// -and-last-view-on-itemdecoration
int lastPosition = state.getItemCount() - 1;
int position = parent.getChildAdapterPosition(view);
if (mIsShowBottomDivider || position < lastPosition) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, 0, 0);
}
} else {
int lastPosition = state.getItemCount() - 1;
int position = parent.getChildAdapterPosition(view);
if (mIsShowBottomDivider || position < lastPosition) {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else {
outRect.set(0, 0, 0, 0);
}
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/NetworkConnectionUtils.kt
================================================
package com.lxm.module_library.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
import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiConfiguration.KeyMgmt
import android.net.wifi.WifiManager
import android.os.Build
import java.io.IOException
/**
*
* Wifi连接工具类
*/
object NetworkConnectionUtils {
/**
* 连接指定
*
* @param manager
* @param wifiSSID
* @return
*/
fun connectToSocketWifi(manager: WifiManager, wifiSSID: String): Boolean {
val wifiConfiguration = WifiConfiguration()
wifiConfiguration.SSID = "\"" + wifiSSID + "\""
wifiConfiguration.allowedKeyManagement.set(KeyMgmt.NONE)
//小米手机MIUI7/华为EMUI4.1 需要webKey
wifiConfiguration.wepKeys[0] = "\"" + "\""
var networkId = manager.addNetwork(wifiConfiguration)
if (networkId != -1) {
manager.enableNetwork(networkId, true)
return true
} else {
val wifiConfiguration2 = WifiConfiguration()
wifiConfiguration2.SSID = "\"" + wifiSSID + "\""
//wifiConfiguration.wepKeys[0] = "\"" + "\"";//去掉webKey //小米手机MIUI8不能有webKey
wifiConfiguration2.allowedKeyManagement.set(KeyMgmt.NONE)
networkId = manager.addNetwork(wifiConfiguration2)
if (networkId != -1) {
manager.enableNetwork(networkId, true)
return true
}
}
return false
}
/**
* 获取要连接的wifi节点各个配置选项的加密类型
*
* @param ssid
* @return wifiConfiguration
*/
fun getWifiConfiguration(manager: WifiManager, ssid: String, password: String): WifiConfiguration {
val wifiConfiguration = WifiConfiguration()
wifiConfiguration.SSID = "\"" + ssid + "\""
val list = manager.scanResults
for (scResult in list) {
if (ssid == scResult.SSID) {
val capabilities = scResult.capabilities
if (capabilities.contains("WEP") || capabilities.contains("wep")) {
wifiConfiguration.allowedKeyManagement.set(KeyMgmt.WPA_EAP)
wifiConfiguration.preSharedKey = "\"" + password + "\""
} else if (capabilities.contains("WPA") || capabilities.contains("wpa")) {
wifiConfiguration.allowedKeyManagement.set(KeyMgmt.WPA_PSK)
wifiConfiguration.preSharedKey = "\"" + password + "\""
} else {
wifiConfiguration.allowedKeyManagement.set(KeyMgmt.NONE)
}
}
}
return wifiConfiguration
}
/**
* 给温控器成功发送联网命令后,连接温控器连接的wifi节点
*
* @param context 上下文对象
* @param ssid ssid
* @param password 密码
*/
fun connectWifiSSID(context: Context, manager: WifiManager, ssid: String, password: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
WifiAutoConnectManager(manager).connect(ssid, password, WifiAutoConnectManager
.getCipherType(context, ssid))
} else {
val networkId = manager.addNetwork(getWifiConfiguration(manager, ssid, password))
if (networkId != -1) {
manager.enableNetwork(networkId, true)
}
}
}
/**
* 格式化RouterSSID
*
* @param strRouterSSID 要格式化的当前连接的路由ssid
* @return 去除"\"后的RouterSSID字符串
*/
fun formatRouterSSID(strRouterSSID: String): String {
var strRouterSSID = strRouterSSID
if (strRouterSSID.contains("\"")) {
strRouterSSID = strRouterSSID.replace("\"".toRegex(), "")
}
return strRouterSSID
}
/**
* Ping
* 用于确定手机是否已经连接上指定设备ip地址
*/
fun pingTest(IPOrDomainName: String): Boolean {
var isSuccess = false
val status: Int
var result = "failed"
val p: Process
try {
p = Runtime.getRuntime().exec("ping -c 1 $IPOrDomainName")
// m_strForNetAddress是输入的网址或者Ip地址
status = p.waitFor()
if (status == 0) {
result = "success"
isSuccess = true
}
} catch (e: IOException) {
} catch (e: InterruptedException) {
}
return isSuccess
}
/**
* 判断网络是否连接
*/
fun isConnected(context: Context): Boolean {
val cm = context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val info = cm.activeNetworkInfo
if (null != info && info.isConnected) {
if (info.state == NetworkInfo.State.CONNECTED) {
return true
}
}
return false
}
/**
* 判断是否有网络
*
* @return 返回值
*/
fun isNetworkConnected(context: Context?): Boolean {
if (context != null) {
val mConnectivityManager = context
.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val mNetworkInfo = mConnectivityManager.activeNetworkInfo
if (mNetworkInfo != null) {
return mNetworkInfo.isAvailable
}
}
return false
}
/**
* 判断是否是wifi连接
*/
fun isWifi(context: Context): Boolean {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
?: return false
val info = cm.activeNetworkInfo
if (null != info) {
if (info.type == ConnectivityManager.TYPE_WIFI) {
return true
}
}
return false
}
/**
* 打开网络设置界面
*/
fun openSetting(activity: Activity, requestCode: Int) {
val intent = Intent("/")
val cm = ComponentName("com.android.settings",
"com.android.settings.WirelessSettings")
intent.component = cm
intent.action = Intent.ACTION_VIEW
activity.startActivityForResult(intent, requestCode)
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/PreferencesUtil.kt
================================================
package com.lxm.module_library.utils
import android.content.Context
import android.content.SharedPreferences
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* SharedPreferences工具类封装 委托调用
*/
class PreferencesUtil(val key: String, val default: T) : ReadWriteProperty {
companion object {
lateinit var preferences: SharedPreferences
private var mPreferencesName = "share_preference_default"
public fun get(context: Context) {
preferences = context.getSharedPreferences(context.packageName + mPreferencesName, Context.MODE_PRIVATE)
}
}
override fun getValue(thisRef: Any?, property: KProperty<*>): T = findPreferences()
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = setPreferences(value)
private fun findPreferences(): T {
return with(preferences) {
val res: Any = when (default) {
is Int -> getInt(key, default)
is Boolean -> getBoolean(key, default)
is Float -> getFloat(key, default)
is String -> getString(key, default)
is Long -> getLong(key, default)
else -> throw IllegalArgumentException("This type can be saved into Preferences")
}
res as T
}
}
private fun setPreferences(value: T) = with(preferences.edit()) {
when (value) {
is Int -> putInt(key, value)
is Boolean -> putBoolean(key, value)
is Float -> putFloat(key, value)
is String -> putString(key, value)
is Long -> putLong(key, value)
else -> throw IllegalArgumentException("This type can be saved into Preferences")
}.apply()
}
/**
* 设置preferencesName
*
* @param preferencesName preferencesName
*/
private fun setPreferencesName(preferencesName: String) {
mPreferencesName = preferencesName
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/RefreshHelper.kt
================================================
package com.lxm.module_library.utils
import android.support.v4.content.ContextCompat
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import com.lxm.module_library.R
import com.lxm.module_library.xrecycleview.XRecyclerView
object RefreshHelper {
/**
* 默认不显示最后一个item的分割线
*
* @param isShowFirstDivider 第一个item是否显示分割线
* @param isShowSecondDivider 第二个item是否显示分割线
*/
@JvmOverloads
fun init(recyclerView: XRecyclerView, isShowFirstDivider: Boolean = true, isShowSecondDivider: Boolean = true) {
recyclerView.layoutManager = LinearLayoutManager(recyclerView.context)
recyclerView.setPullRefreshEnabled(false)
recyclerView.clearHeader()
// val itemDecoration = MyDividerItemDecoration(
// recyclerView.context,
// DividerItemDecoration.VERTICAL,
// false,
// isShowFirstDivider,
// isShowSecondDivider
// )
// itemDecoration.setDrawable(ContextCompat.getDrawable(recyclerView.context, R.drawable.shape_line)!!)
// recyclerView.addItemDecoration(itemDecoration)
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/ResourcesUtils.kt
================================================
package com.lxm.module_library.utils
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.view.View
/**
*
* 资源工具类-加载资源文件
*/
object ResourcesUtils {
/**
* 获取strings.xml资源文件字符串
*
* @param id 资源文件id
* @return 资源文件对应字符串
*/
fun getString(id: Int): String {
return AppUtils.context.resources.getString(id)
}
/**
* 获取strings.xml资源文件字符串数组
*
* @param id 资源文件id
* @return 资源文件对应字符串数组
*/
fun getStringArray(id: Int): Array {
return AppUtils.context.resources.getStringArray(id)
}
/**
* 获取drawable资源文件图片
*
* @param id 资源文件id
* @return 资源文件对应图片
*/
fun getDrawable(id: Int): Drawable {
return AppUtils.context.resources.getDrawable(id)
}
/**
* 获取colors.xml资源文件颜色
*
* @param id 资源文件id
* @return 资源文件对应颜色值
*/
fun getColor(id: Int): Int {
return AppUtils.context.resources.getColor(id)
}
/**
* 获取颜色的状态选择器
*
* @param id 资源文件id
* @return 资源文件对应颜色状态
*/
fun getColorStateList(id: Int): ColorStateList? {
return AppUtils.context.resources.getColorStateList(id)
}
/**
* 获取dimens资源文件中具体像素值
*
* @param id 资源文件id
* @return 资源文件对应像素值
*/
fun getDimen(id: Int): Int {
return AppUtils.context.resources.getDimensionPixelSize(id)
}
/**
* 加载布局文件
*
* @param id 布局文件id
* @return 布局view
*/
fun inflate(id: Int): View {
return View.inflate(AppUtils.context, id, null)
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/ScreenAdapterUtils.kt
================================================
package com.lxm.module_library.utils
import android.app.Activity
import android.content.res.Resources
/**
*屏幕适配相关
*
*/
object ScreenAdapterUtils {
/**
* Return whether adapt screen.
*
* @return `true`: yes
`false`: no
*/
val isAdaptScreen: Boolean
get() {
val systemDm = Resources.getSystem().displayMetrics
val appDm = AppUtils.context.resources.displayMetrics
return systemDm.density != appDm.density
}
/**
* Adapt the screen for vertical slide.
*
* @param activity The activity.
* @param designWidthInPx The size of design diagram's width, in pixel.
*/
fun adaptScreen4VerticalSlide(activity: Activity,
designWidthInPx: Int) {
adaptScreen(activity, designWidthInPx, true)
}
/**
* Adapt the screen for horizontal slide.
*
* @param activity The activity.
* @param designHeightInPx The size of design diagram's height, in pixel.
*/
fun adaptScreen4HorizontalSlide(activity: Activity,
designHeightInPx: Int) {
adaptScreen(activity, designHeightInPx, false)
}
/**
* Reference from: https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA
*/
private fun adaptScreen(activity: Activity,
sizeInPx: Int,
isVerticalSlide: Boolean) {
val systemDm = Resources.getSystem().displayMetrics
val appDm = AppUtils.context.resources.displayMetrics
val activityDm = activity.resources.displayMetrics
if (isVerticalSlide) {
activityDm.density = activityDm.widthPixels / sizeInPx.toFloat()
} else {
activityDm.density = activityDm.heightPixels / sizeInPx.toFloat()
}
activityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.density)
activityDm.densityDpi = (160 * activityDm.density).toInt()
appDm.density = activityDm.density
appDm.scaledDensity = activityDm.scaledDensity
appDm.densityDpi = activityDm.densityDpi
}
/**
* Cancel adapt the screen.
*
* @param activity The activity.
*/
fun cancelAdaptScreen(activity: Activity) {
val systemDm = Resources.getSystem().displayMetrics
val appDm = AppUtils.context.resources.displayMetrics
val activityDm = activity.resources.displayMetrics
activityDm.density = systemDm.density
activityDm.scaledDensity = systemDm.scaledDensity
activityDm.densityDpi = systemDm.densityDpi
appDm.density = systemDm.density
appDm.scaledDensity = systemDm.scaledDensity
appDm.densityDpi = systemDm.densityDpi
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/ScreenUtils.kt
================================================
package com.lxm.module_library.utils
import android.app.Activity
import android.app.ActivityGroup
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Rect
import android.os.Build
import android.support.v7.app.AppCompatActivity
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import java.lang.reflect.Field
/**
*
* 屏幕相关工具类
*/
class ScreenUtils private constructor() {
init {
/* cannot be instantiated */
throw UnsupportedOperationException("cannot be instantiated")
}
companion object {
private val mStatusHeight = -1
/**
* 获取屏幕的宽度
*
* @param context
* @return
*/
fun getScreenWidth(context: Context): Int {
val manager = context
.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val displayMetrics = DisplayMetrics()
manager.defaultDisplay.getMetrics(displayMetrics)
return displayMetrics.widthPixels
}
/**
* 获取屏幕的高度
*
* @param context
* @return
*/
fun getScreenHeight(context: Context): Int {
val manager = context
.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val displayMetrics = DisplayMetrics()
manager.defaultDisplay.getMetrics(displayMetrics)
return displayMetrics.heightPixels
}
/**
* 获取当前屏幕截图,不包含状态栏
*
* @param activity
* @return bp
*/
fun snapShotWithoutStatusBar(activity: Activity): Bitmap? {
val view = activity.window.decorView
view.isDrawingCacheEnabled = true
view.buildDrawingCache()
val bmp = view.drawingCache ?: return null
val frame = Rect()
activity.window.decorView.getWindowVisibleDisplayFrame(frame)
val statusBarHeight = frame.top
val bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, bmp.width, bmp.height - statusBarHeight)
view.destroyDrawingCache()
view.isDrawingCacheEnabled = false
return bp
}
/**
* 获取actionbar的像素高度,默认使用android官方兼容包做actionbar兼容
*
* @return
*/
fun getActionBarHeight(context: Context): Int {
var actionBarHeight = 0
if (context is AppCompatActivity && context
.supportActionBar != null) {
actionBarHeight = context.supportActionBar!!.height
} else if (context is Activity && context.actionBar != null) {
actionBarHeight = context.actionBar!!.height
} else if (context is ActivityGroup) {
if (context.currentActivity is AppCompatActivity && (context.currentActivity as AppCompatActivity)
.supportActionBar != null) {
actionBarHeight = (context
.currentActivity as AppCompatActivity).supportActionBar!!.height
} else if (context.currentActivity is Activity && (context.currentActivity as Activity).actionBar != null) {
actionBarHeight = (context.currentActivity as Activity)
.actionBar!!.height
}
}
if (actionBarHeight != 0) {
return actionBarHeight
}
val tv = TypedValue()
if (context.theme.resolveAttribute(android.support.v7.appcompat.R.attr
.actionBarSize, tv, true)) {
if (context.theme.resolveAttribute(android.support.v7.appcompat.R.attr
.actionBarSize, tv, true)) {
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, context
.resources.displayMetrics)
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (context.theme.resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, context
.resources.displayMetrics)
}
} else {
if (context.theme.resolveAttribute(android.support.v7.appcompat.R.attr
.actionBarSize, tv, true)) {
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, context
.resources.displayMetrics)
}
}
return actionBarHeight
}
/**
* 反射获取
*/
fun getStatusBarHeight(context: Context): Int {
val c: Class<*>
val obj: Any
val field: Field
val x: Int
return try {
c = Class.forName("com.android.internal.R\$dimen")
obj = c.newInstance()
field = c.getField("status_bar_height")
x = Integer.parseInt(field.get(obj).toString())
context.resources.getDimensionPixelSize(x)
} catch (e1: Exception) {
e1.printStackTrace()
75
}
}
/**
* 获取系统状态栏高度(会随着修改displayMetrics而改变)
*/
fun getStatusBarHeight(): Int {
val resources = AppUtils.context.resources
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
return resources.getDimensionPixelSize(resourceId)
}
/**
* 获取系统状态栏高度(系统本身处于的状态)
*/
fun getStatusBarHeightSystem(): Int {
val resources = Resources.getSystem()
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
return resources.getDimensionPixelSize(resourceId)
}
/**
* 设置view margin
*
* @param v
* @param l
* @param t
* @param r
* @param b
*/
fun setMargins(v: View, l: Int, t: Int, r: Int, b: Int) {
if (v.layoutParams is ViewGroup.MarginLayoutParams) {
val p = v.layoutParams as ViewGroup.MarginLayoutParams
p.setMargins(l, t, r, b)
v.requestLayout()
}
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/SnackbarUtils.kt
================================================
package com.lxm.module_library.utils
import android.graphics.Color
import android.support.design.widget.Snackbar
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import com.lxm.module_library.R
/**
* Snackbar工具类
*/
object SnackbarUtils {
private const val Info = 1
private const val Confirm = 2
private const val Warning = 3
private const val Alert = 4
private var red = -0xbbcca
private var green = -0xb350b0
private var blue = -0xde6a0d
private var orange = -0x3ef9
/**
* 短显示Snackbar,自定义颜色
*
* @param view
* @param message
* @param messageColor
* @param backgroundColor
* @return
*/
fun getShort(view: View, message: String, messageColor: Int, backgroundColor: Int): Snackbar {
val snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT)
setSnackbarColor(snackbar, messageColor, backgroundColor)
return snackbar
}
/**
* 长显示Snackbar,自定义颜色
*
* @param view
* @param message
* @param messageColor
* @param backgroundColor
* @return
*/
fun getLong(view: View, message: String, messageColor: Int, backgroundColor: Int): Snackbar {
val snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG)
setSnackbarColor(snackbar, messageColor, backgroundColor)
return snackbar
}
/**
* 自定义时常显示Snackbar,自定义颜色
*
* @param view
* @param message
* @param messageColor
* @param backgroundColor
* @return
*/
fun getIndefinite(view: View, message: String, duration: Int, messageColor: Int, backgroundColor: Int): Snackbar {
val snackbar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE).setDuration(duration)
setSnackbarColor(snackbar, messageColor, backgroundColor)
return snackbar
}
/**
* 短显示Snackbar,可选预设类型
*
* @param view
* @param message
* @param type
* @return
*/
fun getShort(view: View, message: String, type: Int): Snackbar {
val snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT)
switchType(snackbar, type)
return snackbar
}
/**
* 长显示Snackbar,可选预设类型
*
* @param view
* @param message
* @param type
* @return
*/
fun getLong(view: View, message: String, type: Int): Snackbar {
val snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG)
switchType(snackbar, type)
return snackbar
}
/**
* 自定义时常显示Snackbar,可选预设类型
*
* @param view
* @param message
* @param type
* @return
*/
fun getIndefinite(view: View, message: String, duration: Int, type: Int): Snackbar {
val snackbar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE).setDuration(duration)
switchType(snackbar, type)
return snackbar
}
//选择预设类型
private fun switchType(snackbar: Snackbar, type: Int) {
when (type) {
Info -> setSnackbarColor(snackbar, blue)
Confirm -> setSnackbarColor(snackbar, green)
Warning -> setSnackbarColor(snackbar, orange)
Alert -> setSnackbarColor(snackbar, Color.YELLOW, red)
}
}
/**
* 设置Snackbar背景颜色
*
* @param snackbar
* @param backgroundColor
*/
fun setSnackbarColor(snackbar: Snackbar, backgroundColor: Int) {
val view = snackbar.view
view?.setBackgroundColor(backgroundColor)
}
/**
* 设置Snackbar文字和背景颜色
*
* @param snackbar
* @param messageColor
* @param backgroundColor
*/
fun setSnackbarColor(snackbar: Snackbar, messageColor: Int, backgroundColor: Int) {
val view = snackbar.view
if (view != null) {
view.setBackgroundColor(backgroundColor)
(view.findViewById(R.id.snackbar_text) as TextView).setTextColor(messageColor)
}
}
/**
* 向Snackbar中添加view
*
* @param snackbar
* @param layoutId
* @param index 新加布局在Snackbar中的位置
*/
fun addView(snackbar: Snackbar, layoutId: Int, index: Int) {
val snackbarview = snackbar.view
val snackbarLayout = snackbarview as Snackbar.SnackbarLayout
val add_view = LayoutInflater.from(snackbarview.getContext()).inflate(layoutId, null)
val p = LinearLayout.LayoutParams(LinearLayout.LayoutParams
.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
p.gravity = Gravity.CENTER_VERTICAL
snackbarLayout.addView(add_view, index, p)
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/StatusBarUtils.kt
================================================
package com.lxm.module_library.utils
import android.app.Activity
import android.graphics.Color
import android.os.Build
import android.support.annotation.ColorInt
import android.support.v7.widget.Toolbar
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
/**
*
* StatusBar工具类
*
*/
object StatusBarUtils {
/**
* 设置状态栏颜色
*
* @param activity 需要设置的 activity
* @param color 状态栏颜色值
*/
private fun setColor(activity: Activity, @ColorInt color: Int) {
setBarColor(activity, color)
}
/**
* 设置状态栏背景色
* 4.4以下不处理
* 4.4使用默认沉浸式状态栏
*
* @param color 要为状态栏设置的颜色值
*/
private fun setBarColor(activity: Activity, color: Int) {
val win = activity.window
val decorView = win.decorView
//沉浸式状态栏(4.4-5.0透明,5.0以上半透明)
win.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
//android5.0以上设置透明效果
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//让应用的主体内容占用系统状态栏的空间
val option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
decorView.systemUiVisibility = decorView.systemUiVisibility or option
win.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
//设置状态栏背景色
win.statusBarColor = color
}
}
/**
* 设置状态栏全透明
*
* @param activity 需要设置的activity
*/
fun setTransparent(activity: Activity) {
setColor(activity, Color.LTGRAY)
}
/**
* 修正 Toolbar 的位置
* 在 Android 4.4 版本下无法显示内容在 StatusBar 下,所以无需修正 Toolbar 的位置
*
* @param toolbar Toolbar
*/
fun fixToolbar(toolbar: Toolbar, activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
val statusHeight = ScreenUtils.getStatusBarHeight(activity)
val layoutParams = toolbar.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.setMargins(0, statusHeight, 0, 0)
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/StringUtils.kt
================================================
package com.lxm.module_library.utils
import android.text.TextUtils
import java.util.regex.Pattern
/**
* 字符串工具类
*/
object StringUtils {
/**
* 判断字符串是否有值,如果为null或者是空字符串或者只有空格或者为"null"字符串,则返回true,否则则返回false
*/
fun isEmpty(value: String?): Boolean {
return !(value != null && !"".equals(value.trim { it <= ' ' }, ignoreCase = true)
&& !"null".equals(value.trim { it <= ' ' }, ignoreCase = true))
}
/**
* 判断字符串是否是邮箱
*
* @param email email
* @return 字符串是否是邮箱
*/
fun isEmail(email: String): Boolean {
val str = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(" + "([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$"
val p = Pattern.compile(str)
val m = p.matcher(email)
return m.matches()
}
/**
* 判断手机号字符串是否合法
*
* @param phoneNumber 手机号字符串
* @return 手机号字符串是否合法
*/
fun isPhoneNumberValid(phoneNumber: String): Boolean {
var isValid = false
val expression = "^1[3|4|5|7|8]\\d{9}$"
val pattern = Pattern.compile(expression)
val matcher = pattern.matcher(phoneNumber)
if (matcher.matches()) {
isValid = true
}
return isValid
}
/**
* 判断手机号字符串是否合法
*
* @param areaCode 区号
* @param phoneNumber 手机号字符串
* @return 手机号字符串是否合法
*/
fun isPhoneNumberValid(areaCode: String, phoneNumber: String): Boolean {
if (TextUtils.isEmpty(phoneNumber)) {
return false
}
if (phoneNumber.length < 5) {
return false
}
if (TextUtils.equals(areaCode, "+86") || TextUtils.equals(areaCode, "86")) {
return isPhoneNumberValid(phoneNumber)
}
var isValid = false
val expression = "^[0-9]*$"
val pattern = Pattern.compile(expression)
val matcher = pattern.matcher(phoneNumber)
if (matcher.matches()) {
isValid = true
}
return isValid
}
/**
* 判断字符串是否是手机号格式
*
* @param areaCode 区号
* @param phoneNumber 手机号字符串
* @return 字符串是否是手机号格式
*/
fun isPhoneFormat(areaCode: String, phoneNumber: String): Boolean {
if (TextUtils.isEmpty(phoneNumber)) {
return false
}
if (phoneNumber.length < 7) {
return false
}
var isValid = false
val expression = "^[0-9]*$"
val pattern = Pattern.compile(expression)
val matcher = pattern.matcher(phoneNumber)
if (matcher.matches()) {
isValid = true
}
return isValid
}
/**
* 判断字符串是否为纯数字
*
* @param str 字符串
* @return 是否纯数字
*/
fun isNumber(str: String): Boolean {
for (i in 0 until str.length) {
if (!Character.isDigit(str[i])) {
return false
}
}
return true
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/TimestampUtils.kt
================================================
package com.lxm.module_library.utils
import java.text.SimpleDateFormat
import java.util.*
object TimestampUtils {
/**
* 获取当前的时间戳,时区为北京
*
* @return
*/
//时间戳的格式必须为 yyyy-MM-dd HH:mm:ss
val currentTimestamp: String?
get() {
var timestamp: String? = null
val format = SimpleDateFormat.getDateTimeInstance()
timestamp = format.format(Date())
return timestamp
}
/**
* 获取当前的时间戳,时区为北京
*
* @return
*/
fun getCurrentTime(times: Long): String {
//时间戳的格式必须为 yyyy-MM-dd HH:mm:ss
val date = Date(java.lang.Long.valueOf(times))
val format = SimpleDateFormat.getDateTimeInstance()
val time = format.format(date)
SimpleDateFormat.getDateTimeInstance()
.format(Date())
return time
}
//法国时间:东一区
fun getDateTimeByGMT(timeZone: Int): String {
val dff = SimpleDateFormat.getDateTimeInstance()
when (timeZone) {
1 -> dff.timeZone = TimeZone.getTimeZone("GMT+1")
8 -> dff.timeZone = TimeZone.getTimeZone("GMT+8")
}
return dff.format(Date())
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/ToastUtil.java
================================================
package com.lxm.module_library.utils;
import android.annotation.SuppressLint;
import android.text.TextUtils;
import android.widget.Toast;
import com.lxm.module_library.global.GlobalApplication;
import me.drakeet.support.toast.ToastCompat;
/**
* Created by jingbin on 2016/12/14.
* 单例Toast,兼容索尼部分手机不弹提示的问题,和vivo7.1.1部分手机崩溃问题
* An Android library to hook and fix Toast BadTokenException
* https://github.com/PureWriter/ToastCompat
*/
public class ToastUtil {
private static ToastCompat mToast;
@SuppressLint("ShowToast")
public static void showToast(String text) {
if (!TextUtils.isEmpty(text)) {
if (mToast == null) {
mToast = ToastCompat.makeText(GlobalApplication.getInstance(), text, Toast.LENGTH_SHORT);
} else {
mToast.cancel();
mToast = ToastCompat.makeText(GlobalApplication.getInstance(), text, Toast.LENGTH_SHORT);
}
mToast.setDuration(Toast.LENGTH_SHORT);
mToast.setText(text);
mToast.show();
}
}
@SuppressLint("ShowToast")
public static void showToastLong(String text) {
if (!TextUtils.isEmpty(text)) {
if (mToast == null) {
mToast = ToastCompat.makeText(GlobalApplication.getInstance(), text, Toast.LENGTH_LONG);
} else {
mToast.cancel();
mToast = ToastCompat.makeText(GlobalApplication.getInstance(), text, Toast.LENGTH_LONG);
}
mToast.setDuration(Toast.LENGTH_LONG);
mToast.setText(text);
mToast.show();
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/ToastUtils.kt
================================================
package com.lxm.module_library.utils
import android.content.Context
import android.widget.Toast
import com.lxm.module_library.helper.RxHelper
import io.reactivex.Observable
/**
*
* toast工具类封装
*/
object ToastUtils {
private var mToast: Toast? = null
/**
* 显示一个toast提示
*
* @param resourceId toast字符串资源id
*/
fun showToast(resourceId: Int) {
showToast(ResourcesUtils.getString(resourceId))
}
/**
* 显示一个toast提示
*
* @param text toast字符串
* @param duration toast显示时间
*/
@JvmOverloads
fun showToast(text: String, duration: Int = Toast.LENGTH_SHORT) {
showToast(AppUtils.context, text, duration)
}
/**
* 显示一个toast提示
*
* @param context context 上下文对象
* @param text toast字符串
* @param duration toast显示时间
*/
fun showToast(context: Context, text: String, duration: Int) {
/**
* 保证运行在主线程
*/
val disposable = Observable.just(0)
.compose(RxHelper.rxSchedulerHelper())
.subscribe {
if (mToast == null) {
mToast = Toast.makeText(context, text, duration)
} else {
mToast!!.setText(text)
mToast!!.duration = duration
}
mToast!!.show()
}
}
}
/**
* 显示一个toast提示
*
* @param text toast字符串
*/
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/UnicodeUtils.kt
================================================
package com.lxm.module_library.utils
/**
* Created by Horrarndoo on 2017/10/11.
*
*/
object UnicodeUtils {
/**
* utf-8 转换成 unicode
*
* @param inStr
* @return
*/
fun utf8ToUnicode(inStr: String): String {
val myBuffer = inStr.toCharArray()
val sb = StringBuffer()
for (i in 0 until inStr.length) {
val ub = Character.UnicodeBlock.of(myBuffer[i])
if (ub === Character.UnicodeBlock.BASIC_LATIN) {
//英文及数字等
sb.append(myBuffer[i])
} else if (ub === Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
//全角半角字符
val j = myBuffer[i].toInt() - 65248
sb.append(j.toChar())
} else {
//汉字
val s = myBuffer[i].toShort()
val hexS = Integer.toHexString(s.toInt())
val unicode = "\\u$hexS"
sb.append(unicode.toLowerCase())
}
}
return sb.toString()
}
/**
* unicode 转换成 utf-8
*
* @param theString
* @return
*/
fun unicodeToUtf8(theString: String): String {
var aChar: Char
val len = theString.length
val outBuffer = StringBuffer(len)
var x = 0
while (x < len) {
aChar = theString[x++]
if (aChar == '\\') {
aChar = theString[x++]
if (aChar == 'u') {
var value = 0
for (i in 0..3) {
aChar = theString[x++]
value = when (aChar) {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> (value shl 4) + aChar.toInt() - '0'.toInt()
'a', 'b', 'c', 'd', 'e', 'f' -> (value shl 4) + 10 + aChar.toInt() - 'a'.toInt()
'A', 'B', 'C', 'D', 'E', 'F' -> (value shl 4) + 10 + aChar.toInt() - 'A'.toInt()
else -> throw IllegalArgumentException(
"Malformed \\uxxxx encoding.")
}
}
outBuffer.append(value.toChar())
} else {
if (aChar == 't') {
aChar = '\t'
} else if (aChar == 'r')
aChar = '\r'
else if (aChar == 'n')
aChar = '\n'
else if (aChar == 'f')
aChar = 'f'
outBuffer.append(aChar)
}
} else
outBuffer.append(aChar)
}
return outBuffer.toString()
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/utils/WifiAutoConnectManager.kt
================================================
package com.lxm.module_library.utils
import android.content.Context
import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiManager
/**
*
* 兼容Android 6.0以上手机连接wifi
*/
class WifiAutoConnectManager(internal var wifiManager: WifiManager) {
/**
* 定义几种加密方式,一种是WEP,一种是WPA,还有没有密码的情况
*/
enum class WifiCipherType {
WIFICIPHER_WEP, WIFICIPHER_WPA, WIFICIPHER_NOPASS, WIFICIPHER_INVALID
}
fun connect(ssid: String, password: String, type: WifiCipherType) {
val thread = Thread(ConnectRunnable(ssid, password, type))
thread.start()
}
/**
* 查看以前是否也配置过这个网络
*
* @param SSID
* @return
*/
private fun isExsits(SSID: String): WifiConfiguration? {
val existingConfigs = wifiManager
.configuredNetworks
for (existingConfig in existingConfigs) {
if (existingConfig.SSID == "\"" + SSID + "\"") {
return existingConfig
}
}
return null
}
private fun createWifiInfo(SSID: String, Password: String,
Type: WifiCipherType): WifiConfiguration {
val config = WifiConfiguration()
config.allowedAuthAlgorithms.clear()
config.allowedGroupCiphers.clear()
config.allowedKeyManagement.clear()
config.allowedPairwiseCiphers.clear()
config.allowedProtocols.clear()
config.SSID = "\"" + SSID + "\""
if (Type == WifiCipherType.WIFICIPHER_NOPASS) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
}
if (Type == WifiCipherType.WIFICIPHER_WEP) {
if (!StringUtils.isEmpty(Password)) {
if (isHexWepKey(Password)) {
config.wepKeys[0] = Password
} else {
config.wepKeys[0] = "\"" + Password + "\""
}
}
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN)
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED)
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
config.wepTxKeyIndex = 0
}
// wpa
if (Type == WifiCipherType.WIFICIPHER_WPA) {
config.preSharedKey = "\"" + Password + "\""
config.hiddenSSID = true
config.allowedAuthAlgorithms
.set(WifiConfiguration.AuthAlgorithm.OPEN)
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP)
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK)
config.allowedPairwiseCiphers
.set(WifiConfiguration.PairwiseCipher.TKIP)
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP)
config.allowedPairwiseCiphers
.set(WifiConfiguration.PairwiseCipher.CCMP)
config.status = WifiConfiguration.Status.ENABLED
}
return config
}
/**
* 打开wifi功能
* @return
*/
private fun openWifi(): Boolean {
var bRet = true
if (!wifiManager.isWifiEnabled) {
bRet = wifiManager.setWifiEnabled(true)
}
return bRet
}
/**
* 关闭WIFI
*/
private fun closeWifi() {
if (wifiManager.isWifiEnabled) {
wifiManager.isWifiEnabled = false
}
}
internal inner class ConnectRunnable(private val ssid: String, private val password: String, private val type: WifiCipherType) : Runnable {
override fun run() {
openWifi()
// 开启wifi功能需要一段时间(我在手机上测试一般需要1-3秒左右),所以要等到wifi
// 状态变成WIFI_STATE_ENABLED的时候才能执行下面的语句
while (wifiManager.wifiState == WifiManager.WIFI_STATE_ENABLING) {
try {
// 为了避免程序一直while循环,让它睡个100毫秒检测……
Thread.sleep(100)
} catch (ie: InterruptedException) {
}
}
val tempConfig = isExsits(ssid)
if (tempConfig != null) {
val b = wifiManager.enableNetwork(tempConfig.networkId,
true)
} else {
val wifiConfig = createWifiInfo(ssid, password,
type) ?: return
val netID = wifiManager.addNetwork(wifiConfig)
val enabled = wifiManager.enableNetwork(netID, true)
val connected = wifiManager.reconnect()
}
}
}
companion object {
private val TAG = WifiAutoConnectManager::class.java
.simpleName
private fun isHexWepKey(wepKey: String): Boolean {
val len = wepKey.length
// WEP-40, WEP-104, and some vendors using 256-bit WEP (WEP-232?)
return if (len != 10 && len != 26 && len != 58) {
false
} else isHex(wepKey)
}
private fun isHex(key: String): Boolean {
for (i in key.length - 1 downTo 0) {
val c = key[i]
val b = !(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')
if (b) {
return false
}
}
return true
}
/**
* 获取ssid的加密方式
*
* @param context Context
* @param ssid String
* @return
*/
fun getCipherType(context: Context, ssid: String): WifiCipherType {
val wifiManager = context
.getSystemService(Context.WIFI_SERVICE) as WifiManager
val list = wifiManager.scanResults
for (scResult in list) {
if (!StringUtils.isEmpty(scResult.SSID) && scResult.SSID == ssid) {
val capabilities = scResult.capabilities
if (!StringUtils.isEmpty(capabilities)) {
return if (capabilities.contains("WPA") || capabilities.contains("wpa")) {
WifiCipherType.WIFICIPHER_WPA
} else if (capabilities.contains("WEP") || capabilities.contains("wep")) {
WifiCipherType.WIFICIPHER_WEP
} else {
WifiCipherType.WIFICIPHER_NOPASS
}
}
}
}
return WifiCipherType.WIFICIPHER_INVALID
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/xrecycleview/BaseRefreshHeader.java
================================================
package com.lxm.module_library.xrecycleview;
/**
* Created by jianghejie on 15/11/22.
*/
interface BaseRefreshHeader {
int STATE_NORMAL = 0;
int STATE_RELEASE_TO_REFRESH = 1;
int STATE_REFRESHING = 2;
int STATE_DONE = 3;
void onMove(float delta);
boolean releaseAction();
void refreshComplate();
int getVisiableHeight();
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/xrecycleview/LoadingMoreFooter.java
================================================
package com.lxm.module_library.xrecycleview;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.lxm.module_library.R;
public class LoadingMoreFooter extends LinearLayout {
public final static int STATE_LOADING = 0;
public final static int STATE_COMPLETE = 1;
public final static int STATE_NOMORE = 2;
private TextView mText;
private AnimationDrawable mAnimationDrawable;
private ImageView mIvProgress;
public LoadingMoreFooter(Context context) {
super(context);
initView(context);
}
/**
* @param context
* @param attrs
*/
public LoadingMoreFooter(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.refresh_footer, this);
mText = (TextView) findViewById(R.id.msg);
mIvProgress = (ImageView) findViewById(R.id.iv_progress);
mAnimationDrawable = (AnimationDrawable) mIvProgress.getDrawable();
if (!mAnimationDrawable.isRunning()) {
mAnimationDrawable.start();
}
setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
public void setState(int state) {
switch (state) {
case STATE_LOADING:
if (!mAnimationDrawable.isRunning()) {
mAnimationDrawable.start();
}
mIvProgress.setVisibility(View.VISIBLE);
mText.setText(getContext().getText(R.string.listview_loading));
this.setVisibility(View.VISIBLE);
break;
case STATE_COMPLETE:
if (mAnimationDrawable.isRunning()) {
mAnimationDrawable.stop();
}
mText.setText(getContext().getText(R.string.listview_loading));
this.setVisibility(View.GONE);
break;
case STATE_NOMORE:
if (mAnimationDrawable.isRunning()) {
mAnimationDrawable.stop();
}
mText.setText(getContext().getText(R.string.nomore_loading));
mIvProgress.setVisibility(View.GONE);
this.setVisibility(View.VISIBLE);
break;
}
}
public void reSet() {
this.setVisibility(GONE);
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/xrecycleview/WrapAdapter.java
================================================
package com.lxm.module_library.xrecycleview;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by yangcai on 2016/1/28.
*/
public class WrapAdapter extends RecyclerView.Adapter {
private static final int TYPE_REFRESH_HEADER = -5;
private static final int TYPE_HEADER = -4;
private static final int TYPE_NORMAL = 0;
private static final int TYPE_FOOTER = -3;
private RecyclerView.Adapter adapter;
private SparseArray mHeaderViews;
private SparseArray mFootViews;
private int headerPosition = 1;
public WrapAdapter(SparseArray headerViews, SparseArray footViews, RecyclerView.Adapter adapter) {
this.adapter = adapter;
this.mHeaderViews = headerViews;
this.mFootViews = footViews;
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return (isHeader(position) || isFooter(position))
? gridManager.getSpanCount() : 1;
}
});
}
}
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow(holder);
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
if (lp != null
&& lp instanceof StaggeredGridLayoutManager.LayoutParams
&& (isHeader(holder.getLayoutPosition()) || isFooter(holder.getLayoutPosition()))) {
StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
p.setFullSpan(true);
}
}
public boolean isHeader(int position) {
return position >= 0 && position < mHeaderViews.size();
}
public boolean isFooter(int position) {
return position < getItemCount() && position >= getItemCount() - mFootViews.size();
}
public boolean isRefreshHeader(int position) {
return position == 0;
}
public int getHeadersCount() {
return mHeaderViews.size();
}
public int getFootersCount() {
return mFootViews.size();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_REFRESH_HEADER) {
return new SimpleViewHolder(mHeaderViews.get(0));
} else if (viewType == TYPE_HEADER) {
return new SimpleViewHolder(mHeaderViews.get(headerPosition++));
} else if (viewType == TYPE_FOOTER) {
return new SimpleViewHolder(mFootViews.get(0));
}
return adapter.onCreateViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeader(position)) {
return;
}
int adjPosition = position - getHeadersCount();
int adapterCount;
if (adapter != null) {
adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
adapter.onBindViewHolder(holder, adjPosition);
}
}
}
@Override
public int getItemCount() {
if (adapter != null) {
return getHeadersCount() + getFootersCount() + adapter.getItemCount();
} else {
return getHeadersCount() + getFootersCount();
}
}
@Override
public int getItemViewType(int position) {
if (isRefreshHeader(position)) {
return TYPE_REFRESH_HEADER;
}
if (isHeader(position)) {
return TYPE_HEADER;
}
if (isFooter(position)) {
return TYPE_FOOTER;
}
int adjPosition = position - getHeadersCount();
int adapterCount;
if (adapter != null) {
adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
return adapter.getItemViewType(adjPosition);
}
}
return TYPE_NORMAL;
}
@Override
public long getItemId(int position) {
if (adapter != null && position >= getHeadersCount()) {
int adjPosition = position - getHeadersCount();
int adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
return adapter.getItemId(adjPosition);
}
}
return -1;
}
@Override
public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {
if (adapter != null) {
adapter.unregisterAdapterDataObserver(observer);
}
}
@Override
public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {
if (adapter != null) {
adapter.registerAdapterDataObserver(observer);
}
}
private class SimpleViewHolder extends RecyclerView.ViewHolder {
public SimpleViewHolder(View itemView) {
super(itemView);
}
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/xrecycleview/XRecyclerView.java
================================================
package com.lxm.module_library.xrecycleview;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by jingbin on 2016/1/28.
*/
public class XRecyclerView extends RecyclerView {
private LoadingListener mLoadingListener;
private WrapAdapter mWrapAdapter;
private SparseArray mHeaderViews = new SparseArray();
private SparseArray mFootViews = new SparseArray();
private boolean pullRefreshEnabled = true;
private boolean loadingMoreEnabled = true;
private YunRefreshHeader mRefreshHeader;
private boolean isLoadingData;
public int previousTotal;
public boolean isnomore;
private float mLastY = -1;
private static final float DRAG_RATE = 1.75f;
// 是否是额外添加FooterView
private boolean isOther = false;
public XRecyclerView(Context context) {
this(context, null);
}
public XRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public XRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
if (pullRefreshEnabled) {
YunRefreshHeader refreshHeader = new YunRefreshHeader(context);
mHeaderViews.put(0, refreshHeader);
mRefreshHeader = refreshHeader;
}
LoadingMoreFooter footView = new LoadingMoreFooter(context);
addFootView(footView, false);
mFootViews.get(0).setVisibility(GONE);
}
/**
* 改为公有。供外添加view使用,使用标识
* 注意:使用后不能使用 上拉加载,否则添加无效
* 使用时 isOther 传入 true,然后调用 noMoreLoading即可。
*/
public void addFootView(final View view, boolean isOther) {
mFootViews.clear();
mFootViews.put(0, view);
this.isOther = isOther;
}
/**
* 相当于加一个空白头布局:
* 只有一个目的:为了滚动条显示在最顶端
* 因为默认加了刷新头布局,不处理滚动条会下移。
* 和 setPullRefreshEnabled(false) 一块儿使用
* 使用下拉头时,此方法不应被使用!
*/
public void clearHeader() {
mHeaderViews.clear();
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1);
View view = new View(getContext());
view.setLayoutParams(params);
mHeaderViews.put(0, view);
}
public void addHeaderView(View view) {
if (pullRefreshEnabled && !(mHeaderViews.get(0) instanceof YunRefreshHeader)) {
YunRefreshHeader refreshHeader = new YunRefreshHeader(getContext());
mHeaderViews.put(0, refreshHeader);
mRefreshHeader = refreshHeader;
}
mHeaderViews.put(mHeaderViews.size(), view);
}
private void loadMoreComplete() {
isLoadingData = false;
View footView = mFootViews.get(0);
if (previousTotal <= getLayoutManager().getItemCount()) {
if (footView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) footView).setState(LoadingMoreFooter.STATE_COMPLETE);
} else {
footView.setVisibility(View.GONE);
}
} else {
if (footView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) footView).setState(LoadingMoreFooter.STATE_NOMORE);
} else {
footView.setVisibility(View.GONE);
}
isnomore = true;
}
previousTotal = getLayoutManager().getItemCount();
}
public void noMoreLoading() {
isLoadingData = false;
final View footView = mFootViews.get(0);
isnomore = true;
if (footView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) footView).setState(LoadingMoreFooter.STATE_NOMORE);
} else {
footView.setVisibility(View.GONE);
}
// 额外添加的footView
if (isOther) {
footView.setVisibility(View.VISIBLE);
}
}
public void refreshComplete() {
// mRefreshHeader.refreshComplate();
if (isLoadingData) {
loadMoreComplete();
} else {
mRefreshHeader.refreshComplate();
}
}
@Override
public void setAdapter(Adapter adapter) {
mWrapAdapter = new WrapAdapter(mHeaderViews, mFootViews, adapter);
super.setAdapter(mWrapAdapter);
adapter.registerAdapterDataObserver(mDataObserver);
}
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
if (state == RecyclerView.SCROLL_STATE_IDLE && mLoadingListener != null && !isLoadingData && loadingMoreEnabled) {
LayoutManager layoutManager = getLayoutManager();
int lastVisibleItemPosition;
if (layoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);
lastVisibleItemPosition = findMax(into);
} else {
lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
}
if (layoutManager.getChildCount() > 0
&& lastVisibleItemPosition >= layoutManager.getItemCount() - 1
&& layoutManager.getItemCount() > layoutManager.getChildCount()
&& !isnomore
&& mRefreshHeader.getState() < YunRefreshHeader.STATE_REFRESHING) {
View footView = mFootViews.get(0);
isLoadingData = true;
if (footView != null) {
if (footView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) footView).setState(LoadingMoreFooter.STATE_LOADING);
} else {
footView.setVisibility(View.VISIBLE);
}
}
if (isNetWorkConnected(getContext())) {
mLoadingListener.onLoadMore();
} else {
postDelayed(new Runnable() {
@Override
public void run() {
mLoadingListener.onLoadMore();
}
}, 1000);
}
}
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLastY == -1) {
mLastY = ev.getRawY();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = ev.getRawY() - mLastY;
mLastY = ev.getRawY();
if (isOnTop() && pullRefreshEnabled) {
mRefreshHeader.onMove(deltaY / DRAG_RATE);
if (mRefreshHeader.getVisiableHeight() > 0 && mRefreshHeader.getState() < YunRefreshHeader.STATE_REFRESHING) {
return false;
}
}
break;
default:
mLastY = -1; // reset
if (isOnTop() && pullRefreshEnabled) {
if (mRefreshHeader.releaseAction()) {
if (mLoadingListener != null) {
mLoadingListener.onRefresh();
isnomore = false;
previousTotal = 0;
final View footView = mFootViews.get(0);
if (footView instanceof LoadingMoreFooter) {
if (footView.getVisibility() != View.GONE) {
footView.setVisibility(View.GONE);
}
}
}
}
}
break;
}
return super.onTouchEvent(ev);
}
private int findMax(int[] lastPositions) {
int max = lastPositions[0];
for (int value : lastPositions) {
if (value > max) {
max = value;
}
}
return max;
}
private int findMin(int[] firstPositions) {
int min = firstPositions[0];
for (int value : firstPositions) {
if (value < min) {
min = value;
}
}
return min;
}
public boolean isOnTop() {
if (mHeaderViews == null || mHeaderViews.size() == 0) {
return false;
}
View view = mHeaderViews.get(0);
if (view.getParent() != null) {
return true;
} else {
return false;
}
}
private final RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
mWrapAdapter.notifyDataSetChanged();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
mWrapAdapter.notifyItemRangeInserted(positionStart, itemCount);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
mWrapAdapter.notifyItemRangeRemoved(positionStart, itemCount);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
mWrapAdapter.notifyItemMoved(fromPosition, toPosition);
}
};
public void setLoadingListener(LoadingListener listener) {
mLoadingListener = listener;
}
public void setPullRefreshEnabled(boolean pullRefreshEnabled) {
this.pullRefreshEnabled = pullRefreshEnabled;
}
public void setLoadingMoreEnabled(boolean loadingMoreEnabled) {
this.loadingMoreEnabled = loadingMoreEnabled;
if (!loadingMoreEnabled) {
if (mFootViews != null) {
mFootViews.remove(0);
}
} else {
if (mFootViews != null) {
LoadingMoreFooter footView = new LoadingMoreFooter(getContext());
addFootView(footView, false);
}
}
}
public void setLoadMoreGone() {
if (mFootViews == null) {
return;
}
View footView = mFootViews.get(0);
if (footView != null && footView instanceof LoadingMoreFooter) {
mFootViews.remove(0);
}
}
public interface LoadingListener {
void onRefresh();
void onLoadMore();
}
/**
* 检测网络是否可用
*
* @param context
* @return
*/
public static boolean isNetWorkConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null) {
return mNetworkInfo.isConnected();
}
}
return false;
}
public void reset() {
isnomore = false;
previousTotal = 0;
final View footView = mFootViews.get(0);
if (footView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) footView).reSet();
}
}
/**
* 是否在刷新数据
*/
public boolean isLoadingData() {
return isLoadingData;
}
}
================================================
FILE: module_library/src/main/java/com/lxm/module_library/xrecycleview/YunRefreshHeader.java
================================================
package com.lxm.module_library.xrecycleview;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.lxm.module_library.R;
/**
* Created by yangcai on 2016/1/27.
*/
public class YunRefreshHeader extends LinearLayout implements BaseRefreshHeader {
private Context mContext;
private AnimationDrawable animationDrawable;
private TextView msg;
private int mState = STATE_NORMAL;
private int mMeasuredHeight;
private LinearLayout mContainer;
public YunRefreshHeader(Context context) {
this(context, null);
}
public YunRefreshHeader(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public YunRefreshHeader(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initView();
}
private void initView() {
LayoutInflater.from(mContext).inflate(R.layout.refresh_header, this);
ImageView img = (ImageView) findViewById(R.id.img);
animationDrawable = (AnimationDrawable) img.getDrawable();
if (animationDrawable.isRunning()) {
animationDrawable.stop();
}
msg = (TextView) findViewById(R.id.msg);
measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mMeasuredHeight = getMeasuredHeight();
setGravity(Gravity.CENTER_HORIZONTAL);
mContainer = (LinearLayout) findViewById(R.id.container);
mContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 0));
this.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
@Override
public void onMove(float delta) {
if (getVisiableHeight() > 0 || delta > 0) {
setVisiableHeight((int) delta + getVisiableHeight());
if (mState <= STATE_RELEASE_TO_REFRESH) { // 未处于刷新状态,更新箭头
if (getVisiableHeight() > mMeasuredHeight) {
setState(STATE_RELEASE_TO_REFRESH);
} else {
setState(STATE_NORMAL);
}
}
}
}
private void setState(int state) {
if (state == mState) return;
switch (state) {
case STATE_NORMAL:
if (animationDrawable.isRunning()) {
animationDrawable.stop();
}
msg.setText(R.string.listview_header_hint_normal);
break;
case STATE_RELEASE_TO_REFRESH:
if (mState != STATE_RELEASE_TO_REFRESH) {
if (!animationDrawable.isRunning()) {
animationDrawable.start();
}
msg.setText(R.string.listview_header_hint_release);
}
break;
case STATE_REFRESHING:
msg.setText(R.string.refreshing);
break;
case STATE_DONE:
msg.setText(R.string.refresh_done);
break;
default:
}
mState = state;
}
@Override
public boolean releaseAction() {
boolean isOnRefresh = false;
int height = getVisiableHeight();
if (height == 0) // not visible.
isOnRefresh = false;
if (getVisiableHeight() > mMeasuredHeight && mState < STATE_REFRESHING) {
setState(STATE_REFRESHING);
isOnRefresh = true;
}
// refreshing and header isn't shown fully. do nothing.
if (mState == STATE_REFRESHING && height <= mMeasuredHeight) {
//return;
}
int destHeight = 0; // default: scroll back to dismiss header.
// is refreshing, just scroll back to show all the header.
if (mState == STATE_REFRESHING) {
destHeight = mMeasuredHeight;
}
smoothScrollTo(destHeight);
return isOnRefresh;
}
@Override
public void refreshComplate() {
setState(STATE_DONE);
new Handler().postDelayed(new Runnable() {
public void run() {
reset();
}
}, 500);
}
public void reset() {
smoothScrollTo(0);
setState(STATE_NORMAL);
}
private void smoothScrollTo(int destHeight) {
ValueAnimator animator = ValueAnimator.ofInt(getVisiableHeight(), destHeight);
animator.setDuration(300).start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setVisiableHeight((int) animation.getAnimatedValue());
}
});
animator.start();
}
private void setVisiableHeight(int height) {
if (height < 0)
height = 0;
// `
LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
lp.height = height;
mContainer.setLayoutParams(lp);
}
@Override
public int getVisiableHeight() {
return mContainer.getHeight();
}
public int getState() {
return mState;
}
}
================================================
FILE: module_library/src/main/res/drawable/app_loading_anim.xml
================================================
================================================
FILE: module_library/src/main/res/drawable/common_progress_cirle.xml
================================================
================================================
FILE: module_library/src/main/res/drawable/header_loading_anim.xml
================================================
================================================
FILE: module_library/src/main/res/drawable/ic_add.xml
================================================
================================================
FILE: module_library/src/main/res/drawable/ic_clear.xml
================================================
================================================
FILE: module_library/src/main/res/drawable/login_back.xml
================================================
================================================
FILE: module_library/src/main/res/drawable/shape_line.xml
================================================
================================================
FILE: module_library/src/main/res/layout/activity_base.xml
================================================
================================================
FILE: module_library/src/main/res/layout/custom_login_view.xml
================================================
================================================
FILE: module_library/src/main/res/layout/custom_register_view.xml
================================================
================================================
FILE: module_library/src/main/res/layout/default_login_view.xml
================================================
================================================
FILE: module_library/src/main/res/layout/default_register_view.xml
================================================
================================================
FILE: module_library/src/main/res/layout/fragment_base.xml
================================================
================================================
FILE: module_library/src/main/res/layout/layout_loading_error.xml
================================================
================================================
FILE: module_library/src/main/res/layout/layout_loading_view.xml
================================================
================================================
FILE: module_library/src/main/res/layout/login_layout.xml
================================================
================================================
FILE: module_library/src/main/res/layout/login_view.xml
================================================
================================================
FILE: module_library/src/main/res/layout/refresh_footer.xml
================================================
================================================
FILE: module_library/src/main/res/layout/refresh_header.xml
================================================
================================================
FILE: module_library/src/main/res/layout/register_layout.xml
================================================
================================================
FILE: module_library/src/main/res/values/attrs.xml
================================================
================================================
FILE: module_library/src/main/res/values/colors.xml
================================================
#343434
#EBEBEB
#D4D4D4
#BBBBBB
#FFDDDDDD
#ff333333
#585858
#999
#666
#ffffffff
#777D7D7D
#343434
#11EE69
#ff5151
#ffcdce
#000
#000
================================================
FILE: module_library/src/main/res/values/dimen.xml
================================================
250dp
================================================
FILE: module_library/src/main/res/values/drawables.xml
================================================
- @drawable/abc_item_background_holo_light
================================================
FILE: module_library/src/main/res/values/strings.xml
================================================
module_library
下拉刷新...
释放刷新...
努力加载中...
没有更多内容了
正在刷新...
刷新完成...
上次更新时间:
分享
复制链接
浏览器打开
刷新
添加到收藏
LOGIN
Name
Password
GO
REGISTER
Repeat Password
NEXT
================================================
FILE: module_library/src/main/res/values/styles.xml
================================================
================================================
FILE: module_library/src/main/res/values-w600dp/dimen.xml
================================================
400dp
================================================
FILE: module_library/src/test/java/com/lxm/module_library/ExampleUnitTest.java
================================================
package com.lxm.module_library;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: settings.gradle
================================================
include ':app', ':module_library'