上一篇
简易集成的MVP模块化App框架(1/3)
前言
一直想整理一个自己app框架,现在刚好不是很忙就整理一下,尚不成熟还有待改进
大纲
1.整体结构:MVP模式+模块化
2.网络框架:Retrofit+Rxjava
3.屏幕适配方案:头条的AndroidAutoSize
4.分享框架:Mob的ShareSDK
5.其他:base、常用工具类以及简易的自定义控件等
6.常见问题
7.使用说明
项目链接
https://github.com/UncleQing/QingFrame
5.其他
utils
glide,对glide进行一次二次封装以及配置
GlideApp对glide一些api进行二次封装,便于调用
public class GlideApp {
private GlideApp() {
}
@Nullable
public static File getPhotoCacheDir(Context context) {
return Glide.getPhotoCacheDir(context);
}
@Nullable
public static File getPhotoCacheDir(Context context, String cacheName) {
return Glide.getPhotoCacheDir(context, cacheName);
}
public static Glide get(Context context) {
return Glide.get(context);
}
@VisibleForTesting
@SuppressLint({"VisibleForTests"})
public static void init(Glide glide) {
Glide.init(glide);
}
@VisibleForTesting
@SuppressLint({"VisibleForTests"})
public static void tearDown() {
Glide.tearDown();
}
public static GlideRequests with(Context context) {
return (GlideRequests)Glide.with(context);
}
public static GlideRequests with(Activity activity) {
return (GlideRequests)Glide.with(activity);
}
public static GlideRequests with(FragmentActivity activity) {
return (GlideRequests)Glide.with(activity);
}
public static GlideRequests with(Fragment fragment) {
return (GlideRequests)Glide.with(fragment);
}
public static GlideRequests with(android.support.v4.app.Fragment fragment) {
return (GlideRequests)Glide.with(fragment);
}
public static GlideRequests with(View view) {
return (GlideRequests)Glide.with(view);
}
}
其他都是一些glide配置,就不贴了
permission,因为6.0之后为了安全需要动态请求一些权限,因此对于权限请求封装了下
IPermissionRequestProxy请求权限的接口
public interface IPermissionRequestProxy {
boolean checkSelfPermission(String[] permissions);
void permissionsCheck(String[] permissions, int requestCode, int error_msg);
void setResultListener(IRequestResult listener);
}
IRequestResult请求结果的接口
public interface IRequestResult {
void onRequestResultSuccess();
void onRequestResultFailed();
}
PermissionWrapper权限申请管理类
/**
* android 6.0 以后的权限申请,使用一个fragment代理的方式请求权限
*/
public class PermissionWrapper {
public static final String PACKAGE = "package:";
private static final String FRAGMENT_PERMISSION = "fragment_permission_request";
private IPermissionRequestProxy iPermissionRequestProxy;
private IRequestResult iRequestResult;
private String[] permissions;
private int requestCode;
private int error_msg = R.string.permission_tip;
private PermissionWrapper(Activity mActivity) {
iPermissionRequestProxy = getPermissionsFragment((AppCompatActivity) mActivity);
}
private PermissionFragmentRequestProxy getPermissionsFragment(AppCompatActivity activity) {
PermissionFragmentRequestProxy permissionFragmentRequestProxy = findPermissionsFragment(activity);
if (null == permissionFragmentRequestProxy) {
permissionFragmentRequestProxy = new PermissionFragmentRequestProxy();
FragmentManager fragmentManager = activity.getSupportFragmentManager();
fragmentManager
.beginTransaction()
.add(permissionFragmentRequestProxy, FRAGMENT_PERMISSION)
.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
return permissionFragmentRequestProxy;
}
private PermissionFragmentRequestProxy findPermissionsFragment(AppCompatActivity activity) {
return (PermissionFragmentRequestProxy) activity.getSupportFragmentManager().findFragmentByTag(FRAGMENT_PERMISSION);
}
/**
* 检查权限是否已申请
*
* @return true 有权限,false 无权限
*/
private boolean checkSelfPermission() {
return iPermissionRequestProxy.checkSelfPermission(permissions);
}
/**
* 获取权限dialog
*
* @return
*/
private void permissionsCheck() {
// 注意这里要使用shouldShowRequestPermissionRationale而不要使用requestPermission方法
// 因为requestPermissions方法会显示不在询问按钮
iPermissionRequestProxy.permissionsCheck(permissions, requestCode, error_msg);
}
/**
* 请求权限
*/
public void requestPermission() {
if (checkSelfPermission()) {
iRequestResult.onRequestResultSuccess();
} else {
iPermissionRequestProxy.setResultListener(iRequestResult);
permissionsCheck();
}
}
public void clear() {
permissions = null;
requestCode = -1;
}
public static class Builder {
PermissionWrapper permissionWrapper;
public Builder with(Activity mActivity) {
permissionWrapper = new PermissionWrapper(mActivity);
return this;
}
public Builder with(Fragment fragment) {
permissionWrapper = new PermissionWrapper(fragment.getActivity());
return this;
}
public Builder requePermissionString(String... permissions) {
permissionWrapper.permissions = permissions;
return this;
}
public Builder requestCode(int requestCode) {
if (requestCode <= 0) {
throw new RuntimeException("The request code cannot be less than or equal to 0");
}
permissionWrapper.requestCode = requestCode;
return this;
}
public Builder setErrorMsg(int errorMsg) {
permissionWrapper.error_msg = errorMsg;
return this;
}
public Builder onResultListener(IRequestResult iRequestResult) {
permissionWrapper.iRequestResult = iRequestResult;
return this;
}
public PermissionWrapper create() {
return permissionWrapper;
}
}
}
PermissionRequestCode权限请求码管理
/**
* 权限请求码,避免不同的地方重复
*/
public class PermissionRequestCode {
//联系人
public static final int WRITE_CONTACTS = 10000;
public static final int GET_ACCOUNTS = 10001;
public static final int READ_CONTACTS = 10002;
//拨号、读取手机状态等
public static final int READ_CALL_LOG = 10003;
public static final int READ_PHONE_STATE = 10004;
public static final int CALL_PHONE = 10005;
public static final int USE_SIP = 10006;
public static final int PROCESS_OUTGOING_CALLS = 10007;
public static final int ADD_VOICEMAIL = 10008;
//相机
public static final int CAMERA = 10009;
//日历
public static final int READ_CALENDAR = 10010;
public static final int WRITE_CALENDAR = 10011;
//传感器
public static final int BODY_SENSORS = 10012;
//定位
public static final int ACCESS_FINE_LOCATION = 10013;
public static final int ACCESS_COARSE_LOCATION = 10014;
//sd卡存储
public static final int READ_EXTERNAL_STORAGE = 10015;
public static final int WRITE_EXTERNAL_STORAGE = 10016;
//麦克风
public static final int RECORD_AUDIO = 10017;
//短信
public static final int READ_SMS = 10018;
public static final int RECEIVE_WAP_PUSH = 10019;
public static final int RECEIVE_MMS = 10020;
public static final int RECEIVE_SMS = 10021;
public static final int SEND_SMS = 10022;
}
ActivityStackUtils,做为activity管理栈
/**
* activity stack管理类
*/
public class ActivityStackUtils {
private static Stack<Activity> sActivityStack = new Stack<Activity>();
/**
* 入栈
* oncreate之后调用
*
* @param activity
*/
public static void addActivity(Activity activity) {
sActivityStack.add(activity);
}
/**
* 出栈
* onDestroy之后调用
*
* @param activity
*/
public static void removeActivity(Activity activity) {
if (activity != null) {
sActivityStack.remove(activity);
}
}
/**
* 返回栈顶activity
*
* @return
*/
public static Activity getTopActivity() {
return sActivityStack.isEmpty() ? null : sActivityStack.lastElement();
}
/**
* 结束当前activity
* 代替context.finish
*/
public static void finishActivity() {
Activity activity = sActivityStack.lastElement();
finishActivity(activity);
}
/**
* 结束指定activity
*
* @param activity
*/
public static void finishActivity(Activity activity) {
if (activity != null) {
sActivityStack.remove(activity);
finishAnimtor(activity);
activity = null;
}
}
/**
* 结束动画,5.0以后区分
*
* @param activity
*/
private static void finishAnimtor(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.finishAfterTransition();
} else {
activity.finish();
}
}
/**
* 结束所有activity
* exit时调用
*/
public static void removeAllActivity() {
if (sActivityStack != null && sActivityStack.size() > 0) {
Iterator var0 = sActivityStack.iterator();
while (var0.hasNext()) {
Activity activity = (Activity) var0.next();
if (null != activity) {
finishAnimtor(activity);
}
}
}
}
/**
* 结束除目标以外的所有activity
* 一般用于返回到登录页面
* @param clazz
*/
public void removeAllActivityWithoutThis(Class<?> clazz) {
Stack<Activity> tmp = new Stack<>();
while (!sActivityStack.empty()) {
Activity current = sActivityStack.pop();//获取到并移除栈顶的对象。
if (current != null
&& !current.getClass().getSimpleName()
.equals(clazz.getSimpleName())) {
finishAnimtor(current);
} else if (null != current &&
(current.getClass().getSimpleName().equals(clazz.getSimpleName()))) {
tmp.push(current);
}
}
if (null != tmp && tmp.size() > 0) {
sActivityStack.addAll(tmp);
}
}
}
AppUtils,使用反射获取app对象
public class AppUtils {
/**
* 反射获取app对象
*/
public static Application getApp() {
Application application = null;
try {
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field appField = activityThreadClass.getDeclaredField("mInitialApplication");
Method method = activityThreadClass.getMethod("currentActivityThread");
Object localObject = method.invoke(null, (Object[]) null);
appField.setAccessible(true);
application = (Application) appField.get(localObject);
} catch (ClassNotFoundException var5) {
var5.printStackTrace();
} catch (NoSuchFieldException var6) {
var6.printStackTrace();
} catch (IllegalAccessException var7) {
var7.printStackTrace();
} catch (IllegalArgumentException var8) {
var8.printStackTrace();
} catch (NoSuchMethodException var9) {
var9.printStackTrace();
} catch (InvocationTargetException var10) {
var10.printStackTrace();
}
return application;
}
}
CacheCleanUtils,有时候会有清理缓存的需求
/**
* 缓存获取清理管理类
*/
public class CacheCleanUtils {
/**
* 获取缓存大小
*
* @param context
* @return
* @throws Exception
*/
public static String getTotalCacheSize(Context context) throws Exception {
long cacheSize = getFolderSize(context.getCacheDir());
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
cacheSize += getFolderSize(context.getExternalCacheDir());
}
return getFormatSize(cacheSize);
}
/**
* 清除缓存
*
* @param context
*/
public static void clearAllCache(Context context) {
deleteDir(context.getCacheDir());
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
deleteDir(context.getExternalCacheDir());
}
}
private static boolean deleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
return dir.delete();
}
// 获取文件大小
//Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据
//Context.getExternalCacheDir() --> SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据
public static long getFolderSize(File file) {
long size = 0;
try {
File[] fileList = file.listFiles();
for (int i = 0; i < fileList.length; i++) {
// 如果下面还有文件
if (fileList[i].isDirectory()) {
size = size + getFolderSize(fileList[i]);
} else {
size = size + fileList[i].length();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return size;
}
/**
* 格式化单位
*
* @param size
* @return
*/
public static String getFormatSize(double size) {
double kiloByte = size / 1024;
if (kiloByte < 1) {
return "0K";
}
double megaByte = kiloByte / 1024;
if (megaByte < 1) {
BigDecimal result1 = new BigDecimal(Double.toString(kiloByte));
return result1.setScale(2, BigDecimal.ROUND_HALF_UP)
.toPlainString() + "K";
}
double gigaByte = megaByte / 1024;
if (gigaByte < 1) {
BigDecimal result2 = new BigDecimal(Double.toString(megaByte));
return result2.setScale(2, BigDecimal.ROUND_HALF_UP)
.toPlainString() + "M";
}
double teraBytes = gigaByte / 1024;
if (teraBytes < 1) {
BigDecimal result3 = new BigDecimal(Double.toString(gigaByte));
return result3.setScale(2, BigDecimal.ROUND_HALF_UP)
.toPlainString() + "GB";
}
BigDecimal result4 = new BigDecimal(teraBytes);
return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString()
+ "TB";
}
}
其他一些util就不一一列举了,详细请见项目内
自定义控件
picker,一个图片选择器,就是弹出拍照或图库选择的popwindow,结合PhotoUtils对图片进行一些处理
ClipImageView,用于裁剪页面的自定义控件
ClipView,用于裁剪页面的自定义边框
CutPictureActivity,裁剪页面
TakePhotoPopWindow,选择器的popwindow
public class TakePhotoPopWindow extends PopupWindow implements OnClickListener {
private View mView;
public Button btnSaveProject, btnAbandonProject, btnCancelProject;
Context mcontext;
private PickPhotoListener mListener;
public TakePhotoPopWindow(Activity context) {
super(context);
mcontext = context;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mView = inflater.inflate(R.layout.populwindow_takephoto, null);
btnSaveProject = mView.findViewById(R.id.popupwindow_Button_saveProject);
btnAbandonProject = mView.findViewById(R.id.popupwindow_Button_abandonProject);
btnCancelProject = mView.findViewById(R.id.popupwindow_cancelButton);
// 设置按钮监听
btnCancelProject.setOnClickListener(this);
btnSaveProject.setOnClickListener(this);
btnAbandonProject.setOnClickListener(this);
//设置PopupWindow的View
this.setContentView(mView);
//设置PopupWindow弹出窗体的宽
this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
//设置PopupWindow弹出窗体的高
this.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
//设置PopupWindow弹出窗体可点击
this.setFocusable(true);
//设置SelectPicPopupWindow弹出窗体动画效果
this.setAnimationStyle(R.style.AnimationBottomFade);
//实例化一个ColorDrawable颜色为半透明
ColorDrawable dw = new ColorDrawable(0x60000000);
//设置SelectPicPopupWindow弹出窗体的背景
this.setBackgroundDrawable(dw);
//全屏遮罩
this.setClippingEnabled(false);
//底部状态栏计算
if (UIUtils.isNavigationBarShow(context)) {
int heigth = UIUtils.getNavigationBarHeight(context);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) btnCancelProject.getLayoutParams();
lp.bottomMargin = heigth;
btnCancelProject.setLayoutParams(lp);
}
}
public void setTitle(String... t1) {
if (t1.length > 0) {
btnSaveProject.setText(t1[0]);
}
if (t1.length > 1) {
btnAbandonProject.setText(t1[1]);
} else {
btnAbandonProject.setVisibility(View.GONE);
}
}
public void setCancel(String cancel) {
btnCancelProject.setText(cancel);
}
public View getmView() {
return mView;
}
@Override
public void onClick(View v) {
if (null != mListener) {
int id = v.getId();
if (id == R.id.popupwindow_Button_saveProject) {
mListener.takePhoto();
} else if (id == R.id.popupwindow_Button_abandonProject) {
mListener.pickPhoto();
} else if (id == R.id.popupwindow_cancelButton) {
}
}
dismiss();
}
/**
* 设置拍照回调监听
*
* @param listener
*/
public void setPickPhotoListener(PickPhotoListener listener) {
mListener = listener;
}
public interface PickPhotoListener {
void takePhoto();
void pickPhoto();
}
public static class Builder {
private TakePhotoPopWindow photoPopWindow;
public Builder(Activity mActivity) {
photoPopWindow = new TakePhotoPopWindow(mActivity);
}
public Builder setTitle(String... t1) {
photoPopWindow.setTitle(t1);
return this;
}
public Builder setCancel(String t1) {
photoPopWindow.setCancel(t1);
return this;
}
public Builder setListener(PickPhotoListener listener) {
photoPopWindow.setPickPhotoListener(listener);
return this;
}
public void show(int resId) {
View view = LayoutInflater.from(photoPopWindow.mcontext).inflate(resId, null, false);
photoPopWindow.showAtLocation(view, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0);
}
}
}
share,分享的popwindow以及bean,分享那块说过了
MyToolBar,自定义toolbar,系统自带的个人感觉真是非常难用,显示效果也不理想,所以自定义一套,可以根据实际需求加工下,使用也很简单
详细:
未完待续