From 0b74ed62580c5d4a2687000322ebfdfe67fc53a3 Mon Sep 17 00:00:00 2001 From: linglongxin24 Date: Tue, 26 Jul 2016 19:31:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=86=E6=80=A7=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=B8=80=E4=BA=9Bbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.name | 1 - .idea/misc.xml | 17 +- app/src/main/res/layout/ac_main.xml | 2 +- libray_lxndroid/build.gradle | 2 +- .../yuandl/adapter/FragmentTabAdapter.java | 4 +- .../common/abslistview/CommonAdapter.java | 76 ++- .../common/recyclerview/CommonAdapter.java | 7 +- .../com/kejiang/yuandl/base/BaseActivity.java | 591 ++++++++++------ .../com/kejiang/yuandl/base/BaseFragment.java | 100 ++- .../yuandl/base/BaseNetErrorActivity.java | 49 ++ .../kejiang/yuandl/utils/ImageDispose.java | 2 +- .../kejiang/yuandl/utils/MyHttpCallback.java | 22 + .../kejiang/yuandl/utils/MyHttpUtills.java | 304 +++++++++ .../com/kejiang/yuandl/utils/Reflector.java | 61 ++ .../kejiang/yuandl/utils/SeralizableMap.java | 20 + .../yuandl/utils/SharedPreferencesUtils.java | 2 +- .../com/kejiang/yuandl/view/MyGallery.java | 1 + .../com/kejiang/yuandl/view/RatingBar.java | 4 +- .../yuandl/view/SlideDetailsLayout.java | 632 ++++++++++++++++++ .../src/main/res/drawable/net_error.png | Bin 0 -> 42056 bytes .../src/main/res/drawable/reload.png | Bin 0 -> 5060 bytes .../src/main/res/layout/ac_title.xml | 6 +- .../src/main/res/layout/pub_net_error.xml | 15 + libray_lxndroid/src/main/res/values/attrs.xml | 13 + 24 files changed, 1628 insertions(+), 303 deletions(-) delete mode 100644 .idea/.name create mode 100644 libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseNetErrorActivity.java create mode 100644 libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/MyHttpCallback.java create mode 100644 libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/MyHttpUtills.java create mode 100644 libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/Reflector.java create mode 100644 libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/SeralizableMap.java create mode 100644 libray_lxndroid/src/main/java/com/kejiang/yuandl/view/SlideDetailsLayout.java create mode 100644 libray_lxndroid/src/main/res/drawable/net_error.png create mode 100644 libray_lxndroid/src/main/res/drawable/reload.png create mode 100644 libray_lxndroid/src/main/res/layout/pub_net_error.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index bb17c9a..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -AndroidFrame \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 1a3eaff..f0ee888 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -27,20 +27,5 @@ - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/ac_main.xml b/app/src/main/res/layout/ac_main.xml index 890c422..74a2518 100644 --- a/app/src/main/res/layout/ac_main.xml +++ b/app/src/main/res/layout/ac_main.xml @@ -19,7 +19,7 @@ app:starEmpty="@mipmap/star_grey" app:starFill="@mipmap/star_yellow" app:starHalf="@mipmap/star_half_yellow" - app:starImageSize="20dp" + app:starImageSize="30dp" app:starStep="4.5"> extends BaseAdapter -{ - protected Context mContext; - protected List mDatas; - protected LayoutInflater mInflater; - private int layoutId; +public abstract class CommonAdapter extends BaseAdapter { + protected Context mContext; + protected List mDatas; + protected LayoutInflater mInflater; + private int layoutId; - public CommonAdapter(Context context,int layoutId, List datas ) - { - this.mContext = context; - mInflater = LayoutInflater.from(context); - this.mDatas = datas; - this.layoutId = layoutId; - } + public CommonAdapter(Context context, int layoutId, List datas) { + this.mContext = context; + mInflater = LayoutInflater.from(context); + this.mDatas = datas; + this.layoutId = layoutId; + } - @Override - public int getCount() - { - return mDatas.size(); - } + public void notifyDataSetChanged(List datas) { + this.mDatas = datas; + this.notifyDataSetChanged(); + } - @Override - public T getItem(int position) - { - return mDatas.get(position); - } + @Override + public int getCount() { + return mDatas==null?0:mDatas.size(); + } - @Override - public long getItemId(int position) - { - return position; - } + public List getDatas() { + return mDatas; + } + @Override + public T getItem(int position) { + return mDatas.get(position); + } - @Override - public View getView(int position, View convertView, ViewGroup parent) - { - ViewHolder holder = ViewHolder.get(mContext, convertView, parent, - layoutId, position); - convert(holder, getItem(position)); - return holder.getConvertView(); - } + @Override + public long getItemId(int position) { + return position; + } - public abstract void convert(ViewHolder holder, T t); + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder = ViewHolder.get(mContext, convertView, parent, + layoutId, position); + convert(holder, getItem(position)); + return holder.getConvertView(); + } + + public abstract void convert(ViewHolder holder, T t); } diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/adapter/common/recyclerview/CommonAdapter.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/adapter/common/recyclerview/CommonAdapter.java index dada422..420eac3 100644 --- a/libray_lxndroid/src/main/java/com/kejiang/yuandl/adapter/common/recyclerview/CommonAdapter.java +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/adapter/common/recyclerview/CommonAdapter.java @@ -53,7 +53,10 @@ public abstract class CommonAdapter extends RecyclerView.Adapter { return true; } - + public void notifyDataSetChanged(List datas) { + this.mDatas = datas; + this.notifyDataSetChanged(); + } protected void setListener(final ViewGroup parent, final ViewHolder viewHolder, int viewType) { @@ -99,7 +102,7 @@ public abstract class CommonAdapter extends RecyclerView.Adapter @Override public int getItemCount() { - return mDatas.size(); + return mDatas==null?0:mDatas.size(); } diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseActivity.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseActivity.java index 79834d6..ec4459b 100644 --- a/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseActivity.java +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseActivity.java @@ -1,21 +1,30 @@ package com.kejiang.yuandl.base; -import android.app.Dialog; +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.Application; import android.content.Context; +import android.content.ContextWrapper; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.os.IBinder; +import android.os.Looper; +import android.os.MessageQueue; import android.support.v4.util.ArrayMap; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.Window; +import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; import android.widget.FrameLayout; +import android.widget.GridView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; @@ -30,6 +39,8 @@ import com.kejiang.yuandl.R; import com.kejiang.yuandl.bean.JsonBean; import com.kejiang.yuandl.utils.AppManager; import com.kejiang.yuandl.utils.CheckNetwork; +import com.kejiang.yuandl.utils.Reflector; +import com.kejiang.yuandl.utils.SharedPreferencesUtils; import com.kejiang.yuandl.utils.Tools; import com.kejiang.yuandl.view.LoadingDialog; import com.orhanobut.logger.Logger; @@ -38,13 +49,17 @@ import com.zhy.autolayout.utils.AutoUtils; import org.xutils.common.Callback; import org.xutils.common.util.KeyValue; -import org.xutils.common.util.MD5; import org.xutils.ex.HttpException; import org.xutils.http.RequestParams; import org.xutils.x; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.SocketTimeoutException; import java.util.List; import java.util.Map; +import java.util.Set; /** * com.bm.falvzixun.activities.BaseActivity; @@ -60,7 +75,6 @@ public abstract class BaseActivity extends AutoLayoutActivity implements View.On private LinearLayout llRoot; private LinearLayout layout_titlebar; protected Context context; - Dialog dialog; /** * 加载数据对话框 */ @@ -120,6 +134,10 @@ public abstract class BaseActivity extends AutoLayoutActivity implements View.On } } + protected FrameLayout getContentLayout() { + return mContentLayout; + } + /** * 加载 activity_title 布局 ,并获取标题及两侧按钮 */ @@ -373,216 +391,251 @@ public abstract class BaseActivity extends AutoLayoutActivity implements View.On protected void startActivity(Class c) { startActivity(new Intent(context, c)); } -// -// /** -// * 异步网络请求类 -// * -// * @param requestParams -// */ -// protected void ajax(RequestParams requestParams) { -// if (!CheckNetwork.isNetworkAvailable(context)) { -// showToast("网络不可用,请检查网络连接!"); -// return; -// } -// if (loadingDialog == null) { -// loadingDialog = new LoadingDialog(context); -// } -// if (requestParams != null) { -// String uri = requestParams.getUri(); -// if (!uri.isEmpty()) { -// String[] split = uri.split("/"); -//// Logger.d("split=" + Arrays.toString(split)); -// -// String method = null; -// try { -// method = split[split.length - 1].substring(0, split[split.length - 1].indexOf(".")); -// } catch (Exception e) { -// e.printStackTrace(); -// Logger.d("请检查服务器地址后面是否含有.html"); -// } -// String mode = split[split.length - 2]; -// String sign = MD5.md5(mode + method); -// requestParams.addBodyParameter("sign", sign); -// } -// -// } -// Logger.d("url=" + requestParams.getUri() + "\nrequestParams=" + requestParams.getStringParams().toString()); -// List params = requestParams.getStringParams(); -// for (KeyValue keyValue : params) { -// if (keyValue.key.contains(":")) { -// throw new RuntimeException("参数异常!"); -// } -// } -// // jsonBean = JSON.parseObject(result, JsonBean.class); -// cancelable = x.http().post(requestParams, new Callback.ProgressCallback() { -// -// @Override -// public void onWaiting() { -// } -// -// @Override -// -// public void onStarted() { -// netOnStart(); -// } -// -// @Override -// public void onLoading(long total, long current, boolean isDownloading) { -// netOnLoading(total, current, isDownloading); -// } -// -// @Override -// public void onSuccess(String result) { -// -// Logger.json(result); -// JsonBean jsonBean = null; -// try { -//// jsonBean = JSON.parseObject(result, JsonBean.class); -// jsonBean = jsonParse(result); -// if (jsonBean.getMsg() != null && !jsonBean.getMsg().isEmpty()) { -// showToast(jsonBean.getMsg()); -// } -// if (jsonBean.getCode() == 200) { -// Map data = new ArrayMap(); -// if (null != jsonBean.getData() && jsonBean.getData().size() > 0) { -// data = jsonBean.getData(); -// } -// netOnSuccess(data); -// } else { -// -// netOnOtherStates(jsonBean.getCode(), jsonBean.getMsg()); -// } -// } catch (Exception e) { -// if (loadingDialog != null && loadingDialog.isShowing()) { -// loadingDialog.dismiss(); -// } -// Logger.d(result); -// showToast("服务器异常!"); -// e.printStackTrace(); -// } finally { -// } -// } -// -// @Override -// public void onError(Throwable ex, boolean isOnCallback) { -// netOnFailure(ex); -// -// } -// -// @Override -// public void onCancelled(CancelledException cex) { -// Logger.d("用户取消了访问网络...."); -// netOnCancelled(); -// } -// -// @Override -// public void onFinished() { -// netOnFinish(); -// } -// -// }); -// } -// -// -// private JsonBean jsonParse(String json) throws JSONException { -// ArrayMap arrayMap= JSON.parseObject(json,new TypeReference>(){ -// }.getType()); -// JsonBean jsonBean = new JsonBean(); -// if (arrayMap.containsKey("data")) { -// Object data = arrayMap.get("data"); + + /** + * 异步网络请求类 + * + * @param requestParams + */ + protected void ajax(RequestParams requestParams) { + ajax(requestParams, 0); + } + + /** + * 异步网络请求类 + * + * @param requestParams + * @param requestCode 区分不同的网络请求 + */ + protected void ajax(RequestParams requestParams, int requestCode) { + if (!CheckNetwork.isNetworkAvailable(context)) { + showToast("网络不可用,请检查网络连接!"); + netOnFailure(new Exception("网络不可用"), requestCode); + return; + } + if (loadingDialog == null) { + loadingDialog = new LoadingDialog(context); + } + SharedPreferencesUtils sp = new SharedPreferencesUtils(x.app()); + boolean isLogin = (boolean) sp.getParam("login", false); + if (isLogin) { + requestParams.addBodyParameter("mId", (String) sp.getParam("mId", "")); + } + boolean hasLocation = (boolean) sp.getParam("hasLocation", false); + if (hasLocation) { + requestParams.addBodyParameter("lng", (String) sp.getParam("lng", "")); + requestParams.addBodyParameter("lat", (String) sp.getParam("lat", "")); + } + Logger.d("url=" + requestParams.getUri() + "\nrequestParams=" + requestParams.getStringParams().toString()); + List params = requestParams.getStringParams(); + for (KeyValue keyValue : params) { + if (keyValue.key.contains(":")) { + throw new RuntimeException("参数异常!"); + } + } + cancelable = x.http().post(requestParams, new MyCallback(requestCode)); + } + + private class MyCallback implements Callback.ProgressCallback { + private int requestCode = 0; + + public MyCallback(int requestCode) { + this.requestCode = requestCode; + } + + @Override + public void onWaiting() { + } + + @Override + + public void onStarted() { + netOnStart(); + } + + @Override + public void onLoading(long total, long current, boolean isDownloading) { + netOnLoading(total, current, isDownloading); + } + + @Override + public void onSuccess(String result) { + Logger.json(result); + JsonBean jsonBean = null; + try { + jsonBean = jsonParse(result); + if (jsonBean.getMsg() != null && !jsonBean.getMsg().isEmpty()) { + showToast(jsonBean.getMsg()); + } + if (jsonBean.getStatus() == 1) { + Map data = new ArrayMap(); + if (null != jsonBean.getData() && jsonBean.getData().size() > 0) { + data = jsonBean.getData(); + } + netOnSuccess(data, requestCode); + } else { + netOnOtherStates(jsonBean.getStatus(), jsonBean.getMsg(), requestCode); + } + } catch (Exception e) { + if (loadingDialog != null && loadingDialog.isShowing()) { + loadingDialog.dismiss(); + } + Logger.d(result); + showToast("服务器异常!"); + e.printStackTrace(); + } finally { + } + } + + @Override + public void onError(Throwable ex, boolean isOnCallback) { + netOnFailure(ex, requestCode); + + } + + @Override + public void onCancelled(Callback.CancelledException cex) { + Logger.d("用户取消了访问网络...."); + netOnCancelled(); + } + + @Override + public void onFinished() { + netOnFinish(requestCode); + } + + + } + + private JsonBean jsonParse(String json) throws JSONException { + ArrayMap arrayMap = JSON.parseObject(json, new TypeReference>() { + }.getType()); + JsonBean jsonBean = new JsonBean(); + if (arrayMap.containsKey("data")) { + Object data = arrayMap.get("data"); // System.out.println("data.getClass().getName()=" + data.getClass().getName()); -// ArrayMap rrData = null; -// if (data instanceof String) { + ArrayMap rrData = null; + if (data instanceof String) { // System.out.println("data instanceof String"); -// rrData = new ArrayMap(); -// rrData.put("data", data.toString()); -// } else if (data instanceof JSONArray) { + rrData = new ArrayMap(); + rrData.put("data", data.toString()); + } else if (data instanceof JSONArray) { // System.out.println("data instanceof JSONArray"); -// rrData = new ArrayMap(); -// rrData.put("data", data); -// } else if (data instanceof com.alibaba.fastjson.JSONObject) { + rrData = new ArrayMap(); + rrData.put("data", data); + } else if (data instanceof com.alibaba.fastjson.JSONObject) { // System.out.println("data instanceof JSONObject"); -// rrData = JSON.parseObject(data.toString(),new TypeReference>(){ -// }.getType()); -// } -// jsonBean.setData(rrData); -// } -// jsonBean.setCode(Integer.valueOf(arrayMap.get("code").toString())); -// jsonBean.setMsg(Tools.getValue(arrayMap, "msg")); -// -// return jsonBean; -// } -// -// -// /** -// * 开始访问网络 -// */ -// public void netOnStart() { -// loadingDialog.show("正在获取数据..."); -// } -// -// /** -// * 访问网络的进程 -// */ -// public void netOnLoading(long total, long current, boolean isUploading) { -// } -// -// /** -// * 访问网络成功 -// */ -// public void netOnSuccess(Map data) { -// if (loadingDialog != null) { -// loadingDialog.dismiss(); -// } -// } -// -// /** -// * 访问网络成功的其他状态 -// */ -// public void netOnOtherStates(int code, String msg) { -// if (loadingDialog != null) { -// loadingDialog.dismiss(); -// } -// } -// -// /** -// * 访问网络结束 -// */ -// public void netOnFinish() { -// loadingDialog.dismiss(); -// } -// -// /** -// * 访问网络失败 -// */ -// public void netOnFailure(Throwable ex) { -// if (loadingDialog != null) { -// loadingDialog.dismiss(); -// } -// Logger.d(ex.getMessage()); -// if (ex instanceof HttpException) { // 网络错误 -// HttpException httpEx = (HttpException) ex; -// int responseCode = httpEx.getCode(); -// String responseMsg = httpEx.getMessage(); -// String errorResult = httpEx.getResult(); -// Toast.makeText(x.app(), "网络错误:" + ex.getMessage(), Toast.LENGTH_LONG).show(); -// // ... -// } else { // 其他错误 -// Toast.makeText(context, "连接服务器失败,请稍后再试!ex=" + ex.getMessage(), Toast.LENGTH_SHORT).show(); -// // ... -// } -// -// -// } -// -// /** -// * 取消访问网络 -// */ -// public void netOnCancelled() { -// if (loadingDialog != null) { -// loadingDialog.dismiss(); -// } -// } + rrData = JSON.parseObject(data.toString(), new TypeReference>() { + }.getType()); + } + jsonBean.setData(rrData); + } else { + Set keys = arrayMap.keySet(); + ArrayMap rrData = new ArrayMap<>(); + for (String s : keys) { + if (!s.equals("status")) { + rrData.put(s, arrayMap.get(s)); + } + } + jsonBean.setData(rrData); + } + jsonBean.setStatus(Integer.valueOf(arrayMap.get("status").toString())); + jsonBean.setMsg(Tools.getValue(arrayMap, "msg")); + + return jsonBean; + } + + /** + * 开始访问网络 + */ + protected void netOnStart() { + loadingDialog.show("Loading..."); + } + + /** + * 访问网络的进程 + */ + protected void netOnLoading(long total, long current, boolean isUploading) { + } + + /** + * 访问网络成功 + */ + protected void netOnSuccess(Map data, int requestCode) { + netOnSuccess(data); + } + + /** + * 访问网络成功 + */ + protected void netOnSuccess(Map data) { + + } + + /** + * 访问网络成功的其他状态 + */ + protected void netOnOtherStates(int status, String msg) { + if (loadingDialog != null && loadingDialog.isShowing()) { + loadingDialog.dismiss(); + } + } + + /** + * 访问网络成功的其他状态 + */ + protected void netOnOtherStates(int status, String msg, int requestCode) { + netOnOtherStates(status, msg); + } + + /** + * 访问网络结束 + */ + protected void netOnFinish() { + loadingDialog.dismiss(); + } + + /** + * 访问网络结束 + */ + protected void netOnFinish(int requestCode) { + loadingDialog.dismiss(); + netOnFinish(); + } + + /** + * 访问网络失败 + */ + protected void netOnFailure(Throwable ex, int requestCode) { + netOnFailure(ex); + } + + /** + * 访问网络失败 + */ + protected void netOnFailure(Throwable ex) { + Logger.d(ex.getMessage()); + if (ex instanceof HttpException) { // 网络错误 + HttpException httpEx = (HttpException) ex; + int responseCode = httpEx.getCode(); + String responseMsg = httpEx.getMessage(); + String errorResult = httpEx.getResult(); + Toast.makeText(x.app(), "网络错误:" + ex.getMessage(), Toast.LENGTH_LONG).show(); + // ... + } else if (ex instanceof SocketTimeoutException) { + Toast.makeText(x.app(), "连接服务器超时", Toast.LENGTH_LONG).show(); + } else if (ex != null && "网络不可用".equals(ex.getMessage())) { + } else { // 其他错误 + Toast.makeText(x.app(), "连接服务器失败,请稍后再试!", Toast.LENGTH_SHORT).show(); + // ... + } + } + + /** + * 取消访问网络 + */ + protected void netOnCancelled() { + } @Override public void onBackPressed() { @@ -599,7 +652,7 @@ public abstract class BaseActivity extends AutoLayoutActivity implements View.On } - protected void setEmptyView(ListView listView) { + protected T setEmptyView(ListView listView) { TextView emptyView = new TextView(context); emptyView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); emptyView.setText("暂无数据!"); @@ -608,9 +661,10 @@ public abstract class BaseActivity extends AutoLayoutActivity implements View.On emptyView.setVisibility(View.GONE); ((ViewGroup) listView.getParent()).addView(emptyView); listView.setEmptyView(emptyView); + return (T) emptyView; } - protected void setEmptyView(ListView listView, String text) { + protected T setEmptyView(ListView listView, String text) { TextView emptyView = new TextView(context); emptyView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); emptyView.setText(text); @@ -619,26 +673,121 @@ public abstract class BaseActivity extends AutoLayoutActivity implements View.On emptyView.setVisibility(View.GONE); ((ViewGroup) listView.getParent()).addView(emptyView); listView.setEmptyView(emptyView); + return (T) emptyView; + } + + protected T setEmptyView(GridView gridView) { + TextView emptyView = new TextView(context); + emptyView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + emptyView.setText("暂无数据!"); + emptyView.setGravity(Gravity.CENTER); + emptyView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); + emptyView.setVisibility(View.GONE); + ((ViewGroup) gridView.getParent()).addView(emptyView); + gridView.setEmptyView(emptyView); + return (T) emptyView; + } + + protected T setEmptyView(GridView gridView, String text) { + TextView emptyView = new TextView(context); + emptyView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + emptyView.setText(text); + emptyView.setGravity(Gravity.CENTER); + emptyView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); + emptyView.setVisibility(View.GONE); + ((ViewGroup) gridView.getParent()).addView(emptyView); + gridView.setEmptyView(emptyView); + return (T) emptyView; } @Override protected void onDestroy() { - super.onDestroy(); +// fixFocusedViewLeak(x.app()); +// fixInputMethodManager(); mContentLayout.removeAllViews(); mContentLayout = null; + if (cancelable != null && !cancelable.isCancelled()) { + cancelable.cancel(); + } AppManager.getAppManager().finishActivity(this); + super.onDestroy(); } @Override - public boolean onTouchEvent(MotionEvent event) { - if (null != this.getCurrentFocus()) { - /** - * 点击空白位置 隐藏软键盘 - */ - InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); - return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0); + public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + View v = getCurrentFocus(); + if (isShouldHideKeyboard(v, ev)) { + hideKeyboard(v.getWindowToken()); + } } - return super.onTouchEvent(event); + return super.dispatchTouchEvent(ev); } + + /** + * 根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘,因为当用户点击EditText时则不能隐藏 + * + * @param v + * @param event + * @return + */ + private boolean isShouldHideKeyboard(View v, MotionEvent event) { + if (v != null && (v instanceof EditText)) { + int[] l = {0, 0}; + v.getLocationInWindow(l); + int left = l[0], + top = l[1], + bottom = top + v.getHeight(), + right = left + v.getWidth(); + if (event.getX() > left && event.getX() < right + && event.getY() > top && event.getY() < bottom) { + // 点击EditText的事件,忽略它。 + return false; + } else { + return true; + } + } + // 如果焦点不是EditText则忽略,这个发生在视图刚绘制完,第一个焦点不在EditText上,和用户用轨迹球选择其他的焦点 + return false; + } + + /** + * 获取InputMethodManager,隐藏软键盘 + * + * @param token + */ + private void hideKeyboard(IBinder token) { + if (token != null) { + InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + im.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + // @Override +// public boolean onTouchEvent(MotionEvent event) { +// if (null != this.getCurrentFocus()) { +// /** +// * 点击空白位置 隐藏软键盘 +// */ +// InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); +// return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0); +// } +// return super.onTouchEvent(event); +// } + private void fixInputMethodManager() { + final Object imm = getSystemService(Context.INPUT_METHOD_SERVICE); + + final Reflector.TypedObject windowToken + = new Reflector.TypedObject(getWindow().getDecorView().getWindowToken(), IBinder.class); + + Reflector.invokeMethodExceptionSafe(imm, "windowDismissed", windowToken); + + final Reflector.TypedObject view + = new Reflector.TypedObject(null, View.class); + + Reflector.invokeMethodExceptionSafe(imm, "startGettingWindowFocus", view); + } + + } diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseFragment.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseFragment.java index 3b16f29..ca10fa8 100644 --- a/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseFragment.java +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseFragment.java @@ -9,6 +9,7 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.GridView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; @@ -20,6 +21,7 @@ import com.alibaba.fastjson.TypeReference; import com.kejiang.yuandl.app.MyApplication; import com.kejiang.yuandl.bean.JsonBean; import com.kejiang.yuandl.utils.CheckNetwork; +import com.kejiang.yuandl.utils.SharedPreferencesUtils; import com.kejiang.yuandl.utils.Tools; import com.kejiang.yuandl.view.LoadingDialog; import com.orhanobut.logger.Logger; @@ -35,6 +37,7 @@ import org.xutils.x; import java.net.SocketTimeoutException; import java.util.List; import java.util.Map; +import java.util.Set; /** * com.bm.falvzixun.fragment.BaseFragment @@ -50,7 +53,6 @@ public class BaseFragment extends Fragment { } - private Callback.Cancelable cancelable; /** * 加载数据对话框 @@ -63,7 +65,7 @@ public class BaseFragment extends Fragment { * * @param requestParams */ - private void ajax(RequestParams requestParams) { + protected void ajax(RequestParams requestParams) { ajax(requestParams, 0); } @@ -76,11 +78,24 @@ public class BaseFragment extends Fragment { protected void ajax(RequestParams requestParams, int requestCode) { if (!CheckNetwork.isNetworkAvailable(getContext())) { showToast("网络不可用,请检查网络连接!"); + netOnFailure(new Exception("网络不可用")); return; } if (loadingDialog == null) { loadingDialog = new LoadingDialog(getContext()); } + + SharedPreferencesUtils sp = new SharedPreferencesUtils(x.app()); + boolean isLogin = (boolean) sp.getParam("login", false); + if (isLogin) { + requestParams.addBodyParameter("mId", (String) sp.getParam("mId", "")); + } + boolean hasLocation = (boolean) sp.getParam("hasLocation", false); + if (hasLocation) { + requestParams.addBodyParameter("lng", (String) sp.getParam("lng", "")); + requestParams.addBodyParameter("lat", (String) sp.getParam("lat", "")); + } + Logger.d("url=" + requestParams.getUri() + "\nrequestParams=" + requestParams.getStringParams().toString()); List params = requestParams.getStringParams(); for (KeyValue keyValue : params) { @@ -129,8 +144,7 @@ public class BaseFragment extends Fragment { } netOnSuccess(data, requestCode); } else { - - netOnOtherStates(jsonBean.getStatus(), jsonBean.getMsg()); + netOnOtherStates(jsonBean.getStatus(), jsonBean.getMsg(), requestCode); } } catch (Exception e) { if (loadingDialog != null && loadingDialog.isShowing()) { @@ -157,7 +171,7 @@ public class BaseFragment extends Fragment { @Override public void onFinished() { - netOnFinish(); + netOnFinish(requestCode); } } @@ -168,22 +182,31 @@ public class BaseFragment extends Fragment { JsonBean jsonBean = new JsonBean(); if (arrayMap.containsKey("data")) { Object data = arrayMap.get("data"); - System.out.println("data.getClass().getName()=" + data.getClass().getName()); +// System.out.println("data.getClass().getName()=" + data.getClass().getName()); ArrayMap rrData = null; if (data instanceof String) { - System.out.println("data instanceof String"); +// System.out.println("data instanceof String"); rrData = new ArrayMap(); rrData.put("data", data.toString()); } else if (data instanceof JSONArray) { - System.out.println("data instanceof JSONArray"); +// System.out.println("data instanceof JSONArray"); rrData = new ArrayMap(); rrData.put("data", data); } else if (data instanceof com.alibaba.fastjson.JSONObject) { - System.out.println("data instanceof JSONObject"); +// System.out.println("data instanceof JSONObject"); rrData = JSON.parseObject(data.toString(), new TypeReference>() { }.getType()); } jsonBean.setData(rrData); + } else { + Set keys = arrayMap.keySet(); + ArrayMap rrData = new ArrayMap<>(); + for (String s : keys) { + if (!s.equals("status")) { + rrData.put(s, arrayMap.get(s)); + } + } + jsonBean.setData(rrData); } jsonBean.setStatus(Integer.valueOf(arrayMap.get("status").toString())); jsonBean.setMsg(Tools.getValue(arrayMap, "msg")); @@ -194,50 +217,65 @@ public class BaseFragment extends Fragment { /** * 开始访问网络 */ - public void netOnStart() { + protected void netOnStart() { loadingDialog.show("Loading..."); } /** * 访问网络的进程 */ - public void netOnLoading(long total, long current, boolean isUploading) { + protected void netOnLoading(long total, long current, boolean isUploading) { } /** * 访问网络成功 */ - public void netOnSuccess(Map data, int requestCode) { + protected void netOnSuccess(Map data, int requestCode) { netOnSuccess(data); } /** * 访问网络成功 */ - public void netOnSuccess(Map data) { + protected void netOnSuccess(Map data) { } /** * 访问网络成功的其他状态 */ - public void netOnOtherStates(int status, String msg) { + protected void netOnOtherStates(int status, String msg) { if (loadingDialog != null && loadingDialog.isShowing()) { loadingDialog.dismiss(); } } + /** + * 访问网络成功的其他状态 + */ + protected void netOnOtherStates(int status, String msg, int requestCode) { + netOnOtherStates(status, msg); + } + /** * 访问网络结束 */ - public void netOnFinish() { + protected void netOnFinish() { loadingDialog.dismiss(); } + /** + * 访问网络结束 + */ + protected void netOnFinish(int requestCode) { + loadingDialog.dismiss(); + netOnFinish(); + } + /** * 访问网络失败 */ - public void netOnFailure(Throwable ex) { + protected void netOnFailure(Throwable ex) { Logger.d(ex.getMessage()); if (ex instanceof HttpException) { // 网络错误 HttpException httpEx = (HttpException) ex; @@ -248,8 +286,10 @@ public class BaseFragment extends Fragment { // ... } else if (ex instanceof SocketTimeoutException) { Toast.makeText(x.app(), "连接服务器超时", Toast.LENGTH_LONG).show(); + } else if (ex!= null && "网络不可用".equals(ex.getMessage())) { + } else { // 其他错误 - Toast.makeText(x.app(), "连接服务器失败,请稍后再试!ex=" + ex.getMessage(), Toast.LENGTH_SHORT).show(); + Toast.makeText(x.app(), "连接服务器失败,请稍后再试!", Toast.LENGTH_SHORT).show(); // ... } @@ -358,10 +398,36 @@ public class BaseFragment extends Fragment { listView.setEmptyView(emptyView); } + protected void setEmptyView(GridView gridView) { + TextView emptyView = new TextView(getContext()); + emptyView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + emptyView.setText("暂无数据!"); + emptyView.setGravity(Gravity.CENTER); + emptyView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); + emptyView.setVisibility(View.GONE); + ((ViewGroup) gridView.getParent()).addView(emptyView); + gridView.setEmptyView(emptyView); + } + + protected void setEmptyView(GridView gridView, String text) { + TextView emptyView = new TextView(getContext()); + emptyView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + emptyView.setText(text); + emptyView.setGravity(Gravity.CENTER); + emptyView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); + emptyView.setVisibility(View.GONE); + ((ViewGroup) gridView.getParent()).addView(emptyView); + gridView.setEmptyView(emptyView); + } + @Override public void onDestroy() { super.onDestroy(); + if (cancelable != null && !cancelable.isCancelled()) { + cancelable.cancel(); + } RefWatcher refWatcher = MyApplication.getRefWatcher(getActivity()); refWatcher.watch(this); } + } diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseNetErrorActivity.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseNetErrorActivity.java new file mode 100644 index 0000000..d41df92 --- /dev/null +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/base/BaseNetErrorActivity.java @@ -0,0 +1,49 @@ +package com.kejiang.yuandl.base; + +import android.view.View; +import android.view.ViewStub; + +import com.kejiang.yuandl.R; + +import java.util.Map; + +/** + * com.bm.falvzixun.activities.BaseActivity; + * + * @author yuandl on 2015/12/17. + * 所有页面的基类 + */ +public abstract class BaseNetErrorActivity extends BaseActivity { + private boolean isInflate = false; + + @Override + protected void netOnSuccess(Map data, int requestCode) { + super.netOnSuccess(data, requestCode); + (findViewById(R.id.vs)).setVisibility(View.GONE); + getContentLayout().setVisibility(View.VISIBLE); + } + + @Override + protected void netOnFailure(Throwable ex, int requestCode) { + super.netOnFailure(ex, requestCode); + getContentLayout().setVisibility(View.GONE); + if (!isInflate) { + ((ViewStub) findViewById(R.id.vs)).inflate(); + isInflate = true; + } else { + (findViewById(R.id.vs)).setVisibility(View.VISIBLE); + } + findViewById(R.id.bt_reload).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + reLoad(); + } + }); + } + + /** + * 重新加载数据 + */ + protected abstract void reLoad(); +} + diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/ImageDispose.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/ImageDispose.java index e87b99f..5ead3f5 100644 --- a/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/ImageDispose.java +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/ImageDispose.java @@ -90,7 +90,7 @@ public class ImageDispose { System.gc(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true; - options.inSampleSize = 2; + options.inSampleSize = inSampleSize; return options; } diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/MyHttpCallback.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/MyHttpCallback.java new file mode 100644 index 0000000..755dd45 --- /dev/null +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/MyHttpCallback.java @@ -0,0 +1,22 @@ +package com.kejiang.yuandl.utils; + +import java.util.Map; + +/** + * Created by yuandl on 2016/6/27 0027. + */ +public interface MyHttpCallback { + void netOnStart(); + + void netOnSuccess(Map data, int requestCode); + + void netOnSuccess(Map data); + + void netOnOtherStatus(int status, String msg, int requestCode); + void netOnOtherStatus(int status, String msg); + + void netOnFinish(); + + void netOnFailure(Throwable ex); + +} diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/MyHttpUtills.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/MyHttpUtills.java new file mode 100644 index 0000000..17c43e6 --- /dev/null +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/MyHttpUtills.java @@ -0,0 +1,304 @@ +package com.kejiang.yuandl.utils; + +import android.content.Context; +import android.support.v4.util.ArrayMap; +import android.widget.Toast; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.TypeReference; +import com.kejiang.yuandl.bean.JsonBean; +import com.kejiang.yuandl.view.LoadingDialog; +import com.orhanobut.logger.Logger; + +import org.xutils.common.Callback; +import org.xutils.common.util.KeyValue; +import org.xutils.ex.HttpException; +import org.xutils.http.RequestParams; +import org.xutils.x; + +import java.net.SocketTimeoutException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by yuandl on 2016/6/27 0027. + */ +public class MyHttpUtills { + + private Callback.Cancelable cancelable; + + private LoadingDialog loadingDialog; + + private Toast toast; + + private Context context; + + private MyHttpCallback myHttpCallback; + + public MyHttpUtills(Context context,MyHttpCallback myHttpCallback) { + this.context = context; + this.myHttpCallback = myHttpCallback; + } + + /** + * 异步网络请求类 + * + * @param requestParams + */ + public void ajax(RequestParams requestParams) { + ajax(requestParams, 0); + } + + /** + * 异步网络请求类 + * + * @param requestParams + * @param requestCode 区分不同的网络请求 + */ + public void ajax(RequestParams requestParams, int requestCode) { + if (!CheckNetwork.isNetworkAvailable(context)) { + showToast("网络不可用,请检查网络连接!"); + return; + } + if (loadingDialog == null) { + loadingDialog = new LoadingDialog(context); + } + SharedPreferencesUtils sp = new SharedPreferencesUtils(x.app()); + boolean isLogin = (boolean) sp.getParam("login", false); + if (isLogin) { + requestParams.addBodyParameter("mId", (String) sp.getParam("mId", "")); + } + boolean hasLocation = (boolean) sp.getParam("hasLocation", false); + if (hasLocation) { + requestParams.addBodyParameter("lng", (String) sp.getParam("lng", "")); + requestParams.addBodyParameter("lat", (String) sp.getParam("lat", "")); + } + Logger.d("url=" + requestParams.getUri() + "\nrequestParams=" + requestParams.getStringParams().toString()); + List params = requestParams.getStringParams(); + for (KeyValue keyValue : params) { + if (keyValue.key.contains(":")) { + throw new RuntimeException("参数异常!"); + } + } + cancelable = x.http().post(requestParams, new MyCallback(requestCode)); + } + + private class MyCallback implements Callback.ProgressCallback { + private int requestCode = 0; + + public MyCallback(int requestCode) { + this.requestCode = requestCode; + } + + @Override + public void onWaiting() { + } + + @Override + + public void onStarted() { + netOnStart(); + } + + @Override + public void onLoading(long total, long current, boolean isDownloading) { + netOnLoading(total, current, isDownloading); + } + + @Override + public void onSuccess(String result) { + Logger.json(result); + JsonBean jsonBean = null; + try { + jsonBean = jsonParse(result); + if (jsonBean.getMsg() != null && !jsonBean.getMsg().isEmpty()) { + showToast(jsonBean.getMsg()); + } + if (jsonBean.getStatus() == 1) { + Map data = new ArrayMap(); + if (null != jsonBean.getData() && jsonBean.getData().size() > 0) { + data = jsonBean.getData(); + } + netOnSuccess(data, requestCode); + } else { + netOnOtherStates(jsonBean.getStatus(), jsonBean.getMsg(), requestCode); + } + } catch (Exception e) { + if (loadingDialog != null && loadingDialog.isShowing()) { + loadingDialog.dismiss(); + } + Logger.d(result); + showToast("服务器异常!"); + e.printStackTrace(); + } finally { + } + } + + @Override + public void onError(Throwable ex, boolean isOnCallback) { + netOnFailure(ex); + + } + + @Override + public void onCancelled(Callback.CancelledException cex) { + Logger.d("用户取消了访问网络...."); + netOnCancelled(); + } + + @Override + public void onFinished() { + netOnFinish(requestCode); + } + + + } + + private JsonBean jsonParse(String json) throws JSONException { + ArrayMap arrayMap = JSON.parseObject(json, new TypeReference>() { + }.getType()); + JsonBean jsonBean = new JsonBean(); + if (arrayMap.containsKey("data")) { + Object data = arrayMap.get("data"); + System.out.println("data.getClass().getName()=" + data.getClass().getName()); + ArrayMap rrData = null; + if (data instanceof String) { + System.out.println("data instanceof String"); + rrData = new ArrayMap(); + rrData.put("data", data.toString()); + } else if (data instanceof JSONArray) { + System.out.println("data instanceof JSONArray"); + rrData = new ArrayMap(); + rrData.put("data", data); + } else if (data instanceof com.alibaba.fastjson.JSONObject) { + System.out.println("data instanceof JSONObject"); + rrData = JSON.parseObject(data.toString(), new TypeReference>() { + }.getType()); + } + jsonBean.setData(rrData); + } else { + Set keys = arrayMap.keySet(); + ArrayMap rrData = new ArrayMap<>(); + for (String s : keys) { + if (!s.equals("status") && !s.equals("msg")) { + rrData.put(s, arrayMap.get(s)); + } + } + jsonBean.setData(rrData); + } + jsonBean.setStatus(Integer.valueOf(arrayMap.get("status").toString())); + jsonBean.setMsg(Tools.getValue(arrayMap, "msg")); + + return jsonBean; + } + + /** + * 开始访问网络 + */ + protected void netOnStart() { + loadingDialog.show("Loading..."); + myHttpCallback.netOnStart(); + } + + /** + * 访问网络的进程 + */ + protected void netOnLoading(long total, long current, boolean isUploading) { + } + + /** + * 访问网络成功 + */ + protected void netOnSuccess(Map data, int requestCode) { + netOnSuccess(data); + myHttpCallback.netOnSuccess(data,requestCode); + } + + /** + * 访问网络成功 + */ + protected void netOnSuccess(Map data) { + myHttpCallback.netOnSuccess(data); + } + + /** + * 访问网络成功的其他状态 + */ + protected void netOnOtherStates(int status, String msg) { + myHttpCallback.netOnOtherStatus(status, msg); + if (loadingDialog != null && loadingDialog.isShowing()) { + loadingDialog.dismiss(); + } + } + + /** + * 访问网络成功的其他状态 + */ + protected void netOnOtherStates(int status, String msg, int requestCode) { + netOnOtherStates(status, msg); + myHttpCallback.netOnOtherStatus(status, msg, requestCode); + } + + /** + * 访问网络结束 + */ + protected void netOnFinish() { + loadingDialog.dismiss(); + myHttpCallback.netOnFinish(); + } + + /** + * 访问网络结束 + */ + protected void netOnFinish(int requestCode) { + loadingDialog.dismiss(); + netOnFinish(); + } + + /** + * 访问网络失败 + */ + protected void netOnFailure(Throwable ex) { + Logger.d(ex.getMessage()); + if (ex instanceof HttpException) { // 网络错误 + HttpException httpEx = (HttpException) ex; + int responseCode = httpEx.getCode(); + String responseMsg = httpEx.getMessage(); + String errorResult = httpEx.getResult(); + Toast.makeText(x.app(), "网络错误:" + ex.getMessage(), Toast.LENGTH_LONG).show(); + // ... + } else if (ex instanceof SocketTimeoutException) { + Toast.makeText(x.app(), "连接服务器超时", Toast.LENGTH_LONG).show(); + } else { // 其他错误 + Toast.makeText(x.app(), "连接服务器失败,请稍后再试!ex=" + ex.getMessage(), Toast.LENGTH_SHORT).show(); + // ... + } + myHttpCallback.netOnFailure(ex); + myHttpCallback.netOnFinish(); + + } + + /** + * 取消访问网络 + */ + protected void netOnCancelled() { + } + + /** + * 弹出Toast便捷方法 + * + * @param charSequence + */ + public void showToast(CharSequence charSequence) { + if (null == toast) { + toast = Toast.makeText(x.app(), charSequence, Toast.LENGTH_SHORT); + } else { + toast.setText(charSequence); + } + toast.show(); + + } +} diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/Reflector.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/Reflector.java new file mode 100644 index 0000000..554547c --- /dev/null +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/Reflector.java @@ -0,0 +1,61 @@ +package com.kejiang.yuandl.utils; + +import java.lang.reflect.Method; + +/** + * Created by yuandl on 2016/7/19 0019. + */ +public class Reflector { + public static final class TypedObject + { + private final Object object; + private final Class type; + + public TypedObject(final Object object, final Class type) + { + this.object = object; + this.type = type; + } + + Object getObject() + { + return object; + } + + Class getType() + { + return type; + } + } + + public static void invokeMethodExceptionSafe(final Object methodOwner, final String method, final TypedObject... arguments) + { + if (null == methodOwner) + { + return; + } + + try + { + final Class[] types = null == arguments ? new Class[0] : new Class[arguments.length]; + final Object[] objects = null == arguments ? new Object[0] : new Object[arguments.length]; + + if (null != arguments) + { + for (int i = 0, limit = types.length; i < limit; i++) + { + types[i] = arguments[i].getType(); + objects[i] = arguments[i].getObject(); + } + } + + final Method declaredMethod = methodOwner.getClass().getDeclaredMethod(method, types); + + declaredMethod.setAccessible(true); + declaredMethod.invoke(methodOwner, objects); + } + catch (final Throwable ignored) + { + } + } +} diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/SeralizableMap.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/SeralizableMap.java new file mode 100644 index 0000000..59f6b33 --- /dev/null +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/SeralizableMap.java @@ -0,0 +1,20 @@ +package com.kejiang.yuandl.utils; + +import java.io.Serializable; +import java.util.Map; + +public class SeralizableMap implements Serializable { + /** + * + */ + private static final long serialVersionUID = 1L; + private Map map; + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } +} diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/SharedPreferencesUtils.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/SharedPreferencesUtils.java index 3184510..70a52c1 100644 --- a/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/SharedPreferencesUtils.java +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/utils/SharedPreferencesUtils.java @@ -49,7 +49,7 @@ public class SharedPreferencesUtils { String type = object.getClass().getSimpleName(); SharedPreferences sp = context.getSharedPreferences(FILE_NAME, - Context.MODE_PRIVATE); + Context.MODE_MULTI_PROCESS); SharedPreferences.Editor editor = sp.edit(); if ("String".equals(type)) { editor.putString(key, object.toString()); diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/MyGallery.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/MyGallery.java index 6e0a75a..a99ffe8 100644 --- a/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/MyGallery.java +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/MyGallery.java @@ -140,6 +140,7 @@ public class MyGallery extends Gallery implements OnItemSelectedListener { public void destroy() { timer.cancel(); + mHandler.removeCallbacks(task); } @Override diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/RatingBar.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/RatingBar.java index 79036fa..4fdb9ca 100644 --- a/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/RatingBar.java +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/RatingBar.java @@ -93,7 +93,7 @@ public class RatingBar extends LinearLayout { } if (indexOfChild(v) > fint) { - setStar(indexOfChild(v) + 0.5f); + setStar(indexOfChild(v) + 1); } else if (indexOfChild(v) == fint) { if (imageView.getDrawable().getCurrent().getConstantState().equals(starHalfDrawable.getConstantState())) { setStar(indexOfChild(v) + 1); @@ -101,7 +101,7 @@ public class RatingBar extends LinearLayout { setStar(indexOfChild(v) + 0.5f); } } else { - setStar(indexOfChild(v) + 1); + setStar(indexOfChild(v) + 1f); } } diff --git a/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/SlideDetailsLayout.java b/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/SlideDetailsLayout.java new file mode 100644 index 0000000..7ec4c56 --- /dev/null +++ b/libray_lxndroid/src/main/java/com/kejiang/yuandl/view/SlideDetailsLayout.java @@ -0,0 +1,632 @@ +package com.kejiang.yuandl.view; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.AbsListView; + +import com.kejiang.yuandl.R; + + +/** + * Project: SlideDetailsLayout
+ * Create Date: 16/1/22
+ * Author: Gordon
+ * Description: + * Pull up to open panel, pull down to close panel. + *
+ */ +@SuppressWarnings("unused") +public class SlideDetailsLayout extends ViewGroup { + + /** + * Callback for panel OPEN-CLOSE status changed. + */ + public interface OnSlideDetailsListener { + /** + * Called after status changed. + * + * @param status {@link Status} + */ + void onStatucChanged(Status status); + } + + public enum Status { + /** Panel is closed */ + CLOSE, + /** Panel is opened */ + OPEN; + + public static Status valueOf(int stats) { + if (0 == stats) { + return CLOSE; + } else if (1 == stats) { + return OPEN; + } else { + return CLOSE; + } + } + } + + private static final float DEFAULT_PERCENT = 0.2f; + private static final int DEFAULT_DURATION = 300; + private static final float DEFAULT_MAX_VELOCITY = 2500f; + + private View mFrontView; + private View mBehindView; + + private float mTouchSlop; + private float mInitMotionY; + private float mInitMotionX; + + + private View mTarget; + private float mSlideOffset; + private Status mStatus = Status.CLOSE; + private boolean isFirstShowBehindView = true; + private float mPercent = DEFAULT_PERCENT; + private long mDuration = DEFAULT_DURATION; + private int mDefaultPanel = 0; + private VelocityTracker mVelocityTracker; + + private OnSlideDetailsListener mOnSlideDetailsListener; + + public SlideDetailsLayout(Context context) { + this(context, null); + } + + public SlideDetailsLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlideDetailsLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlideDetailsLayout, defStyleAttr, 0); + mPercent = a.getFloat(R.styleable.SlideDetailsLayout_percent, DEFAULT_PERCENT); + mDuration = a.getInt(R.styleable.SlideDetailsLayout_duration, DEFAULT_DURATION); + mDefaultPanel = a.getInt(R.styleable.SlideDetailsLayout_default_panel, 0); + a.recycle(); + + mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + } + + /** + * Set the callback of panel OPEN-CLOSE status. + * + * @param listener {@link OnSlideDetailsListener} + */ + public void setOnSlideDetailsListener(OnSlideDetailsListener listener) { + this.mOnSlideDetailsListener = listener; + } + + /** + * Open pannel smoothly. + * + * @param smooth true, smoothly. false otherwise. + */ + public void smoothOpen(boolean smooth) { + if (mStatus != Status.OPEN) { + mStatus = Status.OPEN; + final float height = -getMeasuredHeight(); + animatorSwitch(0, height, true, smooth ? mDuration : 0); + } + } + + /** + * Close pannel smoothly. + * + * @param smooth true, smoothly. false otherwise. + */ + public void smoothClose(boolean smooth) { + if (mStatus != Status.CLOSE) { + mStatus = Status.OPEN; + final float height = -getMeasuredHeight(); + animatorSwitch(height, 0, true, smooth ? mDuration : 0); + } + } + + /** + * Set the float value for indicate the moment of switch panel + * + * @param percent (0.0, 1.0) + */ + public void setPercent(float percent) { + this.mPercent = percent; + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new MarginLayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateLayoutParams(LayoutParams p) { + return new MarginLayoutParams(p); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + final int childCount = getChildCount(); + if (1 >= childCount) { + throw new RuntimeException("SlideDetailsLayout only accept childs more than 1!!"); + } + + mFrontView = getChildAt(0); + mBehindView = getChildAt(1); + + // set behindview's visibility to GONE before show. + mBehindView.setVisibility(GONE); + if (mDefaultPanel == 1) { + post(new Runnable() { + @Override + public void run() { + smoothOpen(false); + } + }); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int pWidth = MeasureSpec.getSize(widthMeasureSpec); + final int pHeight = MeasureSpec.getSize(heightMeasureSpec); + + final int childWidthMeasureSpec = + MeasureSpec.makeMeasureSpec(pWidth, MeasureSpec.EXACTLY); + final int childHeightMeasureSpec = + MeasureSpec.makeMeasureSpec(pHeight, MeasureSpec.EXACTLY); + + View child; + for (int i = 0; i < getChildCount(); i++) { + child = getChildAt(i); + // skip measure if gone + if (child.getVisibility() == GONE) { + continue; + } + + measureChild(child, childWidthMeasureSpec, childHeightMeasureSpec); + } + + setMeasuredDimension(pWidth, pHeight); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int left = l; + final int right = r; + int top; + int bottom; + + final int offset = (int) mSlideOffset; + + View child; + for (int i = 0; i < getChildCount(); i++) { + child = getChildAt(i); + + // skip layout + if (child.getVisibility() == GONE) { + continue; + } + + if (child == mBehindView) { + top = b + offset; + bottom = top + b - t; + } else { + top = 0; + bottom = b + offset; + } + + child.layout(left, top, right, bottom); + } + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + ensureTarget(); + if (null == mTarget) { + return false; + } + + if (!isEnabled()) { + return false; + } + + final int aciton = MotionEventCompat.getActionMasked(ev); + + boolean shouldIntercept = false; + switch (aciton) { + case MotionEvent.ACTION_DOWN: { + if (null == mVelocityTracker) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + mVelocityTracker.addMovement(ev); + + mInitMotionX = ev.getX(); + mInitMotionY = ev.getY(); + shouldIntercept = false; + break; + } + case MotionEvent.ACTION_MOVE: { + final float x = ev.getX(); + final float y = ev.getY(); + + final float xDiff = x - mInitMotionX; + final float yDiff = y - mInitMotionY; + + if (canChildScrollVertically((int) yDiff)) { + shouldIntercept = false; +// if (DEBUG) { +// Log.d(TAG, "intercept, child can scroll vertically, do not intercept"); +// } + } else { + final float xDiffabs = Math.abs(xDiff); + final float yDiffabs = Math.abs(yDiff); + + // intercept rules: + // 1. The vertical displacement is larger than the horizontal displacement; + // 2. Panel stauts is CLOSE:slide up + // 3. Panel status is OPEN:slide down + if (yDiffabs > mTouchSlop && yDiffabs >= xDiffabs + && !(mStatus == Status.CLOSE && yDiff > 0 + || mStatus == Status.OPEN && yDiff < 0)) { + shouldIntercept = true; +// if (DEBUG) { +// Log.d(TAG, "intercept, intercept events"); +// } + } + } + break; + } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + shouldIntercept = false; + break; + } + + } + + return shouldIntercept; + } + + private void recycleVelocityTracker(){ + if(null != mVelocityTracker){ + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + ensureTarget(); + if (null == mTarget) { + return false; + } + + if (!isEnabled()) { + return false; + } + + + boolean wantTouch = true; + final int action = MotionEventCompat.getActionMasked(ev); + + switch (action) { + case MotionEvent.ACTION_DOWN: { + // if target is a view, we want the DOWN action. + if (mTarget instanceof View) { + wantTouch = true; + } + break; + } + + case MotionEvent.ACTION_MOVE: { + mVelocityTracker.addMovement(ev); + mVelocityTracker.computeCurrentVelocity(1000); + final float y = ev.getY(); + final float yDiff = y - mInitMotionY; + if (canChildScrollVertically(((int) yDiff))) { + wantTouch = false; + } else { + processTouchEvent(yDiff); + wantTouch = true; + } + break; + } + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + finishTouchEvent(); + recycleVelocityTracker(); + wantTouch = false; + break; + } + } + return wantTouch; + } + + /** + * @param offset Displacement in vertically. + */ + private void processTouchEvent(final float offset) { + if (Math.abs(offset) < mTouchSlop) { + return; + } + + final float oldOffset = mSlideOffset; + // pull up to open + if (mStatus == Status.CLOSE) { + // reset if pull down + if (offset >= 0) { + mSlideOffset = 0; + } else { + mSlideOffset = offset; + } + + if (mSlideOffset == oldOffset) { + return; + } + + // pull down to close + } else if (mStatus == Status.OPEN) { + final float pHeight = -getMeasuredHeight(); + // reset if pull up + if (offset <= 0) { + mSlideOffset = pHeight; + } else { + final float newOffset = pHeight + offset; + mSlideOffset = newOffset; + } + + if (mSlideOffset == oldOffset) { + return; + } + } + +// if (SlideDebug.DEBUG) { +// Log.v("slide", "process, offset: " + mSlideOffset); +// } + // relayout + requestLayout(); + } + + /** + * Called after gesture is ending. + */ + private void finishTouchEvent() { + final int pHeight = getMeasuredHeight(); + final int percent = (int) (pHeight * mPercent); + final float offset = mSlideOffset; + + boolean changed = false; + final float yVelocity = mVelocityTracker.getYVelocity(); + +// if (DEBUG) { +// Log.v(TAG, "finish, offset: " + offset + ", percent: " + percent + ", yVelocity: " + yVelocity); +// } + + if (Status.CLOSE == mStatus) { + if (offset <= -percent || yVelocity <= -DEFAULT_MAX_VELOCITY) { + mSlideOffset = -pHeight; + mStatus = Status.OPEN; + changed = true; + } else { + // keep panel closed + mSlideOffset = 0; + } + } else if (Status.OPEN == mStatus) { + if ((offset + pHeight) >= percent || yVelocity >= DEFAULT_MAX_VELOCITY) { + mSlideOffset = 0; + mStatus = Status.CLOSE; + changed = true; + } else { + // keep panel opened + mSlideOffset = -pHeight; + } + } + + animatorSwitch(offset, mSlideOffset, changed); + } + + private void animatorSwitch(final float start, final float end) { + animatorSwitch(start, end, true, mDuration); + } + + private void animatorSwitch(final float start, final float end, final long duration) { + animatorSwitch(start, end, true, duration); + } + + private void animatorSwitch(final float start, final float end, final boolean changed) { + animatorSwitch(start, end, changed, mDuration); + } + + private void animatorSwitch(final float start, + final float end, + final boolean changed, + final long duration) { + ValueAnimator animator = ValueAnimator.ofFloat(start, end); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mSlideOffset = (float) animation.getAnimatedValue(); + requestLayout(); + } + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (changed) { + if (mStatus == Status.OPEN) { + checkAndFirstOpenPanel(); + } + + if (null != mOnSlideDetailsListener) { + mOnSlideDetailsListener.onStatucChanged(mStatus); + } + } + } + }); + animator.setDuration(duration); + animator.start(); + } + + /** + * Whether the closed pannel is opened at first time. + * If open first, we should set the behind view's visibility as VISIBLE. + */ + private void checkAndFirstOpenPanel() { + if (isFirstShowBehindView) { + isFirstShowBehindView = false; + mBehindView.setVisibility(VISIBLE); + } + } + + /** + * When pulling, target view changed by the panel status. If panel opened, the target is behind view. + * Front view is for otherwise. + */ + private void ensureTarget() { + if (mStatus == Status.CLOSE) { + mTarget = mFrontView; + } else { + mTarget = mBehindView; + } + } + + /** + * Check child view can srcollable in vertical direction. + * + * @param direction Negative to check scrolling up, positive to check scrolling down. + * + * @return true if this view can be scrolled in the specified direction, false otherwise. + */ + protected boolean canChildScrollVertically(int direction) { + return innerCanChildScrollVertically(mTarget, -direction); + } + + private boolean innerCanChildScrollVertically(View view, int direction) { + if (view instanceof ViewGroup) { + final ViewGroup vGroup = (ViewGroup) view; + View child; + boolean result; + for (int i = 0; i < vGroup.getChildCount(); i++) { + child = vGroup.getChildAt(i); + if (child instanceof View) { + result = ViewCompat.canScrollVertically(child, direction); + } else { + result = innerCanChildScrollVertically(child, direction); + } + + if (result) { + return true; + } + } + } + + return ViewCompat.canScrollVertically(view, direction); + } + + protected boolean canListViewSroll(AbsListView absListView) { + if (mStatus == Status.OPEN) { + return absListView.getChildCount() > 0 + && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) + .getTop() < + absListView.getPaddingTop()); + } else { + final int count = absListView.getChildCount(); + return count > 0 + && (absListView.getLastVisiblePosition() < count - 1 + || absListView.getChildAt(count - 1) + .getBottom() > absListView.getMeasuredHeight()); + } + } + + @Override + protected Parcelable onSaveInstanceState() { + SavedState ss = new SavedState(super.onSaveInstanceState()); + ss.offset = mSlideOffset; + ss.status = mStatus.ordinal(); + return ss; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + mSlideOffset = ss.offset; + mStatus = Status.valueOf(ss.status); + + if (mStatus == Status.OPEN) { + mBehindView.setVisibility(VISIBLE); + } + + requestLayout(); + } + + static class SavedState extends BaseSavedState { + + private float offset; + private int status; + + /** + * Constructor used when reading from a parcel. Reads the state of the superclass. + * + * @param source + */ + public SavedState(Parcel source) { + super(source); + offset = source.readFloat(); + status = source.readInt(); + } + + /** + * Constructor called by derived classes when creating their SavedState objects + * + * @param superState The state of the superclass of this view + */ + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeFloat(offset); + out.writeInt(status); + } + + public static final Creator CREATOR = + new Creator() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/libray_lxndroid/src/main/res/drawable/net_error.png b/libray_lxndroid/src/main/res/drawable/net_error.png new file mode 100644 index 0000000000000000000000000000000000000000..a30a416f6eeeeb7d39a7793dd430898cc4b5fe0c GIT binary patch literal 42056 zcmeFZXH-*N*Df4DKor3TZVMt{A@n8)NJp_CO{9d-gAjTX2uKYG%56pI9g!v^^d>bR zDm8#$=n#}9p(z+52}!=i{k-KnKhGHF=ksHajJ?+0Yp*)XHLsZ`rY5(J9XWFZ0)ZU6 zt$*DN0%4PdK-h~8bAS@Ii&{^>4@;ogt!t2q!LzI2=8)S}FU*RNVUa9E$^j6m92tIlgdGqf9HqJLjFr++syLM2oG)GMBm zL&-1oS4P59=86jRx6Zw}AO+XeEj)F!ZAMXR6h|kntPc!kBquG3T4!h`;GFxoLlVt* zt(o<6w{ zK;F1tI|_lAUd{)7mdO0iw}iZR&`TI zBN#h{L!(i@e^fIvK2>wVV7Ji8uetl+mp8U}xbK;Rm2C!@_L zz7*WTbj9;haAjo+bLH4A3jLM(cCoVIm#63+EF+cw0jx@7nI zRpLk`|5*7{$B4lyT=f~&wlPZaoeO)(F$HJmSp~KZPhekRFU$s1UL>z<2q#~>_40D# zX{6-1XVOUZ3%XhL3qci=sP=pwbwO3%ACVQ=+z7(IY#>L*Dw2(?HdC}D`1v`rhB9XV z{DIepjutak`g$QS!F={Nrg%mYos;w`z<(;Ba-o|vM00vPi8Aa7d5ZN|u@=#-0~=g}F4xR*y|q-Ba++NPiI)!?&Km7TqlU{Qv`U`mB6g;S;PMp=?a{#y{p2ZU~}zg$E)O+ z>f?Q%At&#+vz$&;R5)ocdAj5KVOdD6DaU6CNkKOF>3ntshguWkm7+qbextYd+L&zL zCy49$I*8H5n5F%l5M`)SUk6-DQlOuUX4~lN%WBllG4BFq+fijgL_6ZWx&_hfNP{1- zT^;+|q&mK6B>C?b36=}a3;5i$ zd4cKo_X|=^MQelAZ3rQ@dr~&r2-VKrVApTJh3+AmlrN75QRS6*(=by(oj zaVHUYQZR=X49;Wpn|TQ4Fm=H#ww=cra_wl<%~kHto(c~Xkg$6}0&HU!^4Z~mK^)G? z9tNLDwlJH)D+|9py$ep9LTvoYCR+Kbn*yxmP@0czqe>PTb((GLOg{USC1{n4GLp$) zQ;)4CDGPlj{j8}cIvF*ct~&Xn=y3LS&4rgapW71*IxWX{C^HxvC0ke?LJRAe zlZ!Tw0hOLy5}Z802OZTO{CL&zAv5hwVoEBV7LJjF3B~;LfQ@9lhsumJSF9w5;U~W3 zlxHlMj2C}8%95{6?yYQja=564e>!qBTgLulK!5SR(%_Z0Gt=&Y+Ewb-9xSk&L&>q= z*moonPQFQmmP~i~^EvFU&)RP2`0JLQu9A}E zWrH1qI#pd!(?Ul?7=a=Z9JKcci(Q|rZJe~jigKSUmMmXSdusf(QZkxw&7I}xc+j;n zPMemHtx`_=!JOz%?uMUga5uoKHiu<(zf2`oVQU}-wO~(^vIB>_9GoFV{5H||l#68O za!!4nh`JijQp~>}ejUUWTNLuvVm(^%(t-bIn%y9}zHi@xq0^hg5vym_0_qN=KmR?5 zfoL7%#%gNZDj!Gx!q0DADEAEazK`3@Y!WYqS^v7YBpJ^m1Os*0)nU~yM`be+AhY4|}*j7LQ^Rupax%J{fI*>_F3R2aRr-X+L@TVk0U~`H)O~EH(=dU9T9pFaEK3G(wXN12OdD;N zf@3?7fCy`{%A80UkBIurS$DT2`DEG3FJ1VuA6MNZSfs&dQjIxuCR-8%lJrmr7FYuI zsma*^M(ss0!Dys-9i(4OT6*zC3P?@8+xyBIWE08` zJM86nPL`|kU-GkAKTA=P1zo@Z=qcFd4&QUeR@#M zA)gV6myz)&dD)yF|Mn?AslyDM?blwsuQj9W++*s`*5W^-1Z zOXhq&`)PrA{SQ;i!?zKoU<>HG1QX6DRy8cuZ-ElYgOV%P`3}dMuo_-{#gO9f{yXQqxD;M?D(^L^B>m3<&?d zKl$xqeAcFUD34grS*@_bz4ZAmQWBxLnz|P zipSR`X^#gL<7U_RKk_5}zi4 zB?Ig4t0g%wpQ*Z19yvi8kv%OP3tzHRVx=-l&)Xhm{7K zhrRa-7iFa7xMVna&c5RjDbw+TAzo5oo>Xb|zTUpX`PKv5xjz+;KZcM4-4xF+ItxtB zU%!5NjxPTy{#tAzPM}i9k|W5meWk+iW~l4NQzPx=)cJrP!{1yVnW|Zbf|K74E)y;p zpb9*8PhQDG?uiB!J{P+xDlOjDQB%z&DezkvBk4?DArmzSY<&?(WKkn)V4J;iznzuIH;8WV* zJvpM;&Eg8qn^GSmWhZ3A)3q3N%=V1HyZWpjwYQ*ID?9hq3~`w$kHb20VWtUP}+h2aEnAcCrCw|x77}9 z_nT@hRrgsLq^kL`|LdQ%m0)Ymqwi{rSE&`a6Dl;N^0LTs=d

co$lbf)Mg5VZ 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 literal 0 HcmV?d00001 diff --git a/libray_lxndroid/src/main/res/layout/ac_title.xml b/libray_lxndroid/src/main/res/layout/ac_title.xml index 1455f20..61ab346 100644 --- a/libray_lxndroid/src/main/res/layout/ac_title.xml +++ b/libray_lxndroid/src/main/res/layout/ac_title.xml @@ -17,5 +17,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - + \ No newline at end of file diff --git a/libray_lxndroid/src/main/res/layout/pub_net_error.xml b/libray_lxndroid/src/main/res/layout/pub_net_error.xml new file mode 100644 index 0000000..4b9980b --- /dev/null +++ b/libray_lxndroid/src/main/res/layout/pub_net_error.xml @@ -0,0 +1,15 @@ + + + +