# Wei.Lib2A **Repository Path**: mrsillythanx/Wei.Lib2A ## Basic Information - **Project Name**: Wei.Lib2A - **Description**: Android快速开发框架,Android常用工具集。(由于同时维护两份比较麻烦,本项目已改为从github强制同步,但是导致了本仓库的提交历史细节全部被覆盖:由于github上的项目是从本项目整理后一次push上去的,没有太多历史提交细节。)/(ㄒoㄒ)/~~不过可以从别人fork我的项目里面看到提交历史细节。 - **Primary Language**: Android - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 42 - **Created**: 2021-09-16 - **Last Updated**: 2021-09-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Wei.Lib2A ------------------------------------------------------------------------------------------------ --- 2016年新增内容: ### 全新基于`@Annotation`的混淆配置库 * 已独立迁移至github并上传jcenter: [Annoguard](http://github.com/WeiChou/Annoguard) ------------------------------------------------------------------------------------------------ --- Android快速开发库,Android常用工具集。任务管理、数据加载、下载库请查阅其他项目。
对于初学者欢迎加 [QQ群:215621863](http://shang.qq.com/wpa/qunwpa?idkey=c39a32d6e9036209430732ff38f23729675bf1fac1d3e9faac09ed2165ae6e17 "Android编程&移动互联") 相互学习探讨! ### 这个库都有什么?能帮我们做什么? #### 我写代码遵循两个基本原则: * 减少代码量。希望达到的效果是,让使用者尽可能减少代码,能一句话搞定的,绝不两句,能静态方法搞定的,绝不new对象;
* 增强适应性和稳定性。由于Android平台厂商定制的碎片化和众多版本兼容性问题,本库的开发会着重考虑这些因素,而且基本都是经上线的项目考验过的。 本库的所有代码都经过本人严格测试,字斟句酌,了如指掌,注释也很清晰。覆盖Android基础开发的方方面面,推荐作为项目的基础框架来使用,以便于各功能的正常便利集成。有任何疑问或建议请联系作者: [weichou2010@gmail.com](mailto:weichou2010@gmail.com)、[微信](http://github.com/WeiChou/Wei.Lib2A/#联系作者) 或 [加群@群主](http://shang.qq.com/wpa/qunwpa?idkey=c39a32d6e9036209430732ff38f23729675bf1fac1d3e9faac09ed2165ae6e17 "Android编程&移动互联")。 ### 基础常用组件介绍如下: ### 1、存储卡工具类 [Storage](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/phone/Storage.java) 仅通过几个常量即可便捷的取得内置或外置存储卡 [Storage.SdCard](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/phone/Storage.java#L149) 对象。例如: ```Java //输出外置存储卡的路径到logcat if (Storage.CARD_EXT != null) L.i(this, Storage.CARD_EXT.path); //是否可以在默认的卡上创建任意目录 if (Storage.CARD_DEF.isCustomDirCreatable(context)) { //... try { File dir = FileUtils.makeDir(dirPath, true); L.i(this, dir.getPath()); //... } catch (FileCreateFailureException e) { L.e(this, e); } } ``` ### 2、存储卡自动选择与存储 [FStoreLoc](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/file/FStoreLoc.java) 仅有`存储卡工具`就够了吗?它需要去判断存储卡是否存在、能否创建目录和文件,再决定是否写文件。而`FStoreLoc`可以一步完成: ```Java public static File getImagesCacheDir() throws SdCardNotMountedException, SdCardNotValidException, FileCreateFailureException { return FStoreLoc.BIGFILE.getImagesCacheDir(get(), DirLevel.CUSTOM); } ``` * 上面代码中的 [`DirLevel.CUSTOM`](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/file/FStoreLoc.java#L65) 表示选择自定义根目录,只有在存储卡支持创建自定义目录时才有效,否则抛异常。当然也可选择私有目录 [`DirLevel.PRIVATE`](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/file/FStoreLoc.java#L67) 或自适应目录 [`DirLevel.DEFAULT`](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/file/FStoreLoc.java#L69),详见代码中的文档。 `FStoreLoc`有三种预置存储模式: * 小文件模式:[`FStoreLoc.DEFAULT`](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/file/FStoreLoc.java#L50); * 大文件模式:[`FStoreLoc.BIGFILE`](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/file/FStoreLoc.java#L52); * 生存模式:[`FStoreLoc.SURVIVE`](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/file/FStoreLoc.java#L60). 而`大文件模式`和`生存模式`根据业务需要可能需要设置存储卡根目录,那就放在App初始化里: ```Java public class App extends AbsApp { @Override public void onCreate() { Debug.DEBUG = isDebugMode(); if (Debug.DEBUG) { //... } super.onCreate(); //... /* 设置大文件模式的文件根目录。若存储卡根目录允许写文件,则创建该目录,否则会使用存储卡上的 * 系统为App分配的私有目录:Android/appname/files/ * 大文件模式,即只存放在内置或外置存储卡上,外置优先。 */ FStoreLoc.BIGFILE.setBaseDirName(this, Const.APP_DIR_NAME); //切换到外置卡(默认会自动选择剩余空间最大的那张卡)。但是读写文件过程中如果不存在会自动切换到内置卡。 FStoreLoc.BIGFILE.switchTo(this, Storage.CARD_EXT); } //... } ``` ### 3、文件工具 [FileUtils](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/file/FileUtils.java) 和基于版本控制的多进程文件并发读写工具 [FileVersioned](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/file/FileVersioned.java) ### 4、增强的SharedPreferences工具:[Keeper](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/persist/Keeper.java) 具有多进程读写安全、基于Locale的文件隔离能力等。 基本用法: ```Java public final class XxxKeeper extends Keeper.Wrapper { private static final String SPREF_NAME = "spref_name"; // 以下为固定写法 public static WrapperImpl get() { return get(AbsApp.get().getApplicationContext(), SPREF_NAME); } public static final String KEY_VIEW_PEGER_INDEX = "view_peger_index"; private static final String KEY_XXX_JSON = "xxx_json"; // ... public static void saveXxxObj(XxxObj entity) { get() .withLocale() // 根据需求可选 .multiProcess() // 根据需求可选 .edit() // 根据需求可选 .keepString(KEY_XXX_JSON, AbsJson.toJsonWithAllFields(entity)); // 如果上面的Api无法满足需求,可用原生的 .getSharedPreferences() .putString(KEY_XXX_JSON, AbsJson.toJsonWithAllFields(entity)) .xxx(); } public static XxxObj getXxxObj() { try { return AbsJson.fromJsonWithAllFields( get() .withLocale() // 根据需求可选 .multiProcess() // 根据需求可选 .edit() // 根据需求可选 .readString(KEY_XXX_JSON) // 如果上面的Api无法满足需求,可用原生的 .getSharedPreferences() .getString(KEY_XXX_JSON, null) , XxxObj.clazz); } catch (Exception e) { return null; } } } // 或者这样使用 XxxKeeper.get(). .withLocale() // 根据需求可选 .multiProcess() // 根据需求可选 .edit() // 根据需求可选 .keepInt(XxxKeeper.KEY_VIEW_PEGER_INDEX, 1); // 如果上面的Api无法满足需求,可用原生的 .getSharedPreferences() .putInt(XxxKeeper.KEY_VIEW_PEGER_INDEX, 1) .xxx(); ``` ### 5、网络连接状况判断 [Network](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/phone/Network.java) ### 6、网络连接状况监听 [NetConnectionReceiver](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/receiver/net) ### 7、存储卡挂载状况监听 [StorageReceiver](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/receiver/storage) 示例: ```Java @Override protected void onResume() { super.onResume(); NetConnectionReceiver.registerObserver(mNetObserver); StorageReceiver.registerObserver(mToastStorageObserver); //StorageReceiver.registerObserver(mStorageObserver); } @Override protected void onPause() { StorageReceiver.unregisterObserver(mToastStorageObserver); //StorageReceiver.unregisterObserver(mStorageObserver); NetConnectionReceiver.unregisterObserver(mNetObserver); super.onPause(); } private final NetObserver mNetObserver = new NetObserver() { @Override public void onChanged(Type type, State state) { ensureViewState(true, false); } }; private final ToastStorageObserver mToastStorageObserver = new ToastStorageObserver(this); private final StorageObserver mStorageObserver = new StorageObserver() { /**按下"MediaButton"按键时发出的广播,假如有"MediaButton"按键的话(硬件按键), * Intent.EXTRA_KEY_EVENT携带了这个KeyEvent对象**/ protected void onMediaButton(KeyEvent ev) {} /**已经插入,但是不能挂载**/ protected void onMediaUnMountable(SdCard sdcard) {} /**对象为空白或正在使用不受支持的文件系统,未格式化**/ protected void onMediaNoFS(SdCard sdcard) {} /**扩展介质被插入,而且已经被挂载。intent包含一个名为"read-only"的boolean extra表明挂载点是否只读**/ protected void onMediaMounted(SdCard sdcard, boolean readOnly) {} /**正在磁盘检查**/ protected void onMediaChecking(SdCard sdcard) {} /**请求媒体扫描器扫描存储介质,以将媒体文件信息放入数据库**/ protected void onMediaScannerScanFile(String filePath) {} /**媒体扫描器开始扫描文件目录**/ protected void onMediaScannerStarted(String dirPath) {} /**媒体扫描器完成扫描文件目录**/ protected void onMediaScannerFinished(String dirPath) {} /**由于通过USB存储共享导致无法挂载,挂载点路径可参见参数Intent.mData**/ protected void onMediaShared(SdCard sdcard) {} /**用户希望弹出外部存储介质,收到此广播之后应该立即关闭正在读写的文件; * 要弹出的卡的路径在intent.getData()里面包含了,可以先判断是不是正在读写的文件的那个卡**/ protected void onMediaEject(SdCard sdcard) {} /**已经卸载但未拔出**/ protected void onMediaUnmounted(SdCard sdcard) {} /**已经卸载并拔出**/ protected void onMediaRemoved(SdCard sdcard) {} /**未卸载直接拔出**/ protected void onMediaBadRemoval(SdCard sdcard) {} } ``` ### 8、托管广播事件监听器 [EventDelegater](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/framework/EventDelegater.java) 基于但简化了`Broadcast`和`LocalBroadcast`的发送/接收操作,在页面初始化的时候可以调用[AbsActivity/AbsFragment.hostingLocalEventReceiver(...)](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/framework/AbsActivity.java#L233)将事件监听器进行托管,不再需要在`onPause()`、`onResume()`以及`onDestroy()`等事件的时候进行`unregisterXxx()`和`registerXxx()`编码,托管组件将自动完成。受惠于`Broadcast`的松散耦合机制和简化了的发送/接收操作,可将传统的`Callack`模式用本事件模式重构,轻松解决由于某些原因导致的`资源无法释放`和`内存泄露`等问题。示例: ```Java //在Activity或Fragment中发送事件 @ViewLayoutId(R.layout.m_f_tabs_left_btn_page) public class MTabsLeftFrgmt extends AbsFragment { public void scroll2TopAndRefresh() { //发送事件 sendLocalEvent(mRbtnRecommend.isChecked() ? Const.EventName.SCROLL_2_TOP_AND_REFRESH_frgmt_recommend : Const.EventName.SCROLL_2_TOP_AND_REFRESH_frgmt_favorite, null); } //... } //在子Fragment中接收事件 @ViewLayoutId(R.layout.f_m_f_tabs_left_btn_page_recommend) public class RecommendFrgmt extends AbsFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //只需要提前托管就好了,不用考虑onPause()、onResume()以及onDestroy()的时候还要取消注册或重新注册事件监听 hostingLocalEventReceiver(Const.EventName.SCROLL_2_TOP_AND_REFRESH_frgmt_recommend, PeriodMode.PAUSE_RESUME, new EventReceiver() { @Override public void onEvent(Bundle data) { scroll2TopAndRefresh(); } }); } public void scroll2TopAndRefresh() { L.i(this, "scroll2TopAndRefresh"); //TODO 这里需要处理多次连续调用的情况 } } ``` ### 9、ResourcesId注解 [anno.inject](http://github.com/WeiChou/Wei.Lib2A/tree/master/Wei.Lib2A/src/hobby/wei/c/anno/inject) * 注意`@ViewOnClick`的用法非常灵活。示例: ```Java @ViewLayoutId(R.layout.m_edit) public class EditActy extends AbsActivity implements OnClickListener { @ViewOnClick // 当本类 implements OnClickListener, 那么直接加上本注解即可,不用参数 @ViewId(R.id.m_edit_title_left_btn_back) private ImageButton mBtnBack; @ViewId(value = R.id.m_edit_magic_board, visibility = View.GONE) // 多参数 private MagicBoardView mMagicBoard; @ViewId(name = "m_edit_text", visibility = View.INVISIBLE) // 对于库项目,R.id.xxx非final的情况下,可写name字符串 private TextView mText; //... @Override @ViewOnClick(@Ids(R.id.m_edit_magic_board)) // 若前面没有加@ViewOnClick, 也可以写在onClick()上面,参数也可以像下面这样 @ViewOnClick(@Ids({R.id.m_edit_title_left_btn_back, R.id.m_edit_magic_board})) public void onClick(View v) { switch (v.getId()) { case R.id.m_acty_btn_download: //... break; } @ViewOnClick(@Ids({R.id.m_edit_title_left_btn_back, R.id.m_edit_magic_board})) // 或者可以写在任意自定义方法上面 private void myOnClick(View v) { // 这里也可以不用参数,像这样: private void myOnClick() {} //... } @ViewOnClick(@Ids({R.id.m_edit_title_left_btn_back, R.id.m_edit_magic_board})) //甚至还可以这样,有没有觉得很cool private OnClickListener mOnClickListener = new OnClickListener() { @Override public void onClick(View v) { //... } }; } ``` ### 10、Json的抽象 [IJson](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/data/abs) 示例: ```Java /**由于typeKey可能全局都一样,做一个抽象**/ public abstract class AbsData> extends AbsJsonTyped { public static final String KEY_RESULT = "result"; @Expose public String result; protected String typeKey() { return KEY_RESULT; } } /**构建与Json字符串对应的类结构**/ public class EditBean extends AbsData { @Expose public long id; @Expose public long topicId; @Expose public boolean favorite; @Expose public Info info; public boolean selected = false; public static class Info { public Temp[] temps; public int total_number; } public static class Temp { public long mid; public MagicBoardBody body; public int forwarding_count; public int classify; public long start_time; public long end_time; public int collect; } public EditBean() {} @Override public int hashCode() { return (int)id; } @Override public boolean equals(Object o) { EditBean fb = ((EditBean)o); return fb.id == id && fb.topicId == topicId; } @Override public EditBean fromJson(String json) { return fromJsonWithExposeAnnoFields(json, getTypeToken()); } @Override public String toJson() { return toJsonWithExposeAnnoFields(this); } @Override protected String[] typeValues() { return null; } @Override protected TypeToken getTypeToken() { return new TypeToken(){}; } } /*以下为操作数据*/ EditBean edit = new EditBean(); //edit.xxx = xxx; //... //序列化 edit.toJson(); //判断是否属于本类型,一般情况用不到。 if (new EditBean().isBelongToMe(new JSONObject(jsonString))) { //... } //反序列化 new EditBean().fromJson(jsonString); ``` ### 11、用途广泛的 [ViewHolder](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/framework/ViewHolder.java) 既可用于`ListView`的`Adapter`: ```Java @Override public View getView(int position, View convertView, ViewGroup parent) { return EditGridViewHolder.getAndBindView(position, convertView, parent, getInflater(), EditGridViewHolder.class, getItem(position), mOnFavoriteClick); } //... @ViewLayoutId(R.layout.i_m_edit) public static class EditGridViewHolder extends ViewHolder { @ViewId(R.id.i_m_edit_magic_board) private MagicBoardView mMagicBoard; @ViewId(R.id.i_m_edit_cb_favorite) private CheckBox mCBFav; private Animation mAnimYes, mAnimNo; private boolean mAnimYesStarted = false, mAnimNoStarted = false; public EditGridViewHolder(View view) { super(view); } @Override protected void init(OnClickListener... args) { mCBFav.setOnClickListener(args[0]); } @Override public void bind(int position, EditListBean data) { mCBFav.setTag(this); MagicBoardUtils.display(getView().getContext(), mMagicBoard, data.magicBoard); mPanelTranslucence.setVisibility(data.selected ? View.VISIBLE : View.GONE); updateFavorite(false); } //... } ``` 也可用于`ViewPager`的`PagerAdapter`: ```Java mViewPager.setAdapter(new PagerAdapter() { @Override public int getCount() { return 5; } @Override public boolean isViewFromObject(View view, Object obj) { return view == ((ViewHolder)obj).getView(); } @Override public int getItemPosition(Object obj) { int position = super.getItemPosition(obj); if (obj instanceof ViewHolder4) { position = 4; } else if (obj instanceof ViewHolder3) { position = 3; } else if (obj instanceof ViewHolder2) { position = 2; } else if (obj instanceof ViewHolder1) { position = 1; } else if (obj instanceof ViewHolder0) { position = 0; } return position; } @Override public Object instantiateItem(ViewGroup container, int position) { ViewHolder vHolder = null; switch (position) { case 0: vHolder = ViewHolder.bindView(0, ViewHolder.makeView(ViewHolder0.class, getLayoutInflater(), container), ViewHolder0.class, null, mOnNextClick); break; case 1: vHolder = ViewHolder.bindView(0, ViewHolder.makeView(ViewHolder1.class, getLayoutInflater(), container), ViewHolder1.class, null, mOnNextClick); break; case 2: vHolder = ViewHolder.bindView(0, ViewHolder.makeView(ViewHolder2.class, getLayoutInflater(), container), ViewHolder2.class, null, mOnNextClick); break; case 3: vHolder = ViewHolder.bindView(0, ViewHolder.makeView(ViewHolder3.class, getLayoutInflater(), container), ViewHolder3.class, null, mOnNextClick); break; case 4: vHolder = ViewHolder.bindView(0, ViewHolder.makeView(ViewHolder4.class, getLayoutInflater(), container), ViewHolder4.class, null, mOnCompleteClick); break; } container.addView(vHolder.getView()); return vHolder; } @Override public void destroyItem(ViewGroup container, int position, Object obj) { container.removeView(((ViewHolder)obj).getView()); } }); } //... @ViewLayoutId(R.layout.i_m_guide_next) private static class ViewHolder0 extends ViewHolder { @ViewId(R.id.i_m_guide_bg) protected View mBg; @ViewId(R.id.i_m_guide_btn_next) protected ImageButton mBtnNext; protected static final int WIDTH = 720; protected static final int HEIGHT = 1280; protected static final int WIDTH_BTN = 224; protected static final int HEIGHT_BTN = 88; protected static final int RIGHT_BTN = 16; protected static final int BOTTOM_BTN = 29; public ViewHolder0(View view) { super(view); } @Override protected void init(OnClickListener... args) { mBg.setBackgroundResource(R.drawable.img_i_m_guide_0); mBtnNext.setTag(0); mBtnNext.setOnClickListener(args[0]); initBtnNextPosition(); } @Override public void bind(int position, Void data) {} protected void initBtnNextPosition() { int screenWidth = Device.getInstance(getView().getContext()).width; int screenHeight = Device.getInstance(getView().getContext()).height; RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mBtnNext.getLayoutParams(); float widthScale = screenWidth * 1.0f / WIDTH; float heightScale = screenHeight * 1.0f / HEIGHT; lp.width = (int) (widthScale * WIDTH_BTN); lp.height = (int) (heightScale * HEIGHT_BTN); lp.rightMargin = (int) (widthScale * RIGHT_BTN); lp.bottomMargin = (int) (heightScale * BOTTOM_BTN); mBtnNext.setLayoutParams(lp); } } private static class ViewHolder1 extends ViewHolder0 { public ViewHolder1(View view) { super(view); } @Override protected void init(OnClickListener... args) { mBg.setBackgroundResource(R.drawable.img_i_m_guide_1); mBtnNext.setTag(1); mBtnNext.setOnClickListener(args[0]); initBtnNextPosition(); } } //... ``` 还可用于其他任何场景: ```Java public class Xxx { @Override public void onClick(View v) { //弹出删除对话框 FavoriteDeleteViewHolder.showDeleteDialog(FavoriteActy.this, (ViewGroup)getWindow().getDecorView(), mOnDeleteClick); } //... } @ViewLayoutId(R.layout.m_favorite_delete_panel) public class FavoriteDeleteViewHolder extends ViewHolder { @ViewId(R.id.m_favorite_delete_panel_content) private ViewGroup mContentView; @ViewId(R.id.m_favorite_delete_panel_btn_delete) private Button mBtnDelete; @ViewId(R.id.m_favorite_delete_panel_btn_cancel) private Button mBtnCancel; private Context mContext; private OnClickListener mOnDeleteClickCallback; private boolean mDelete; public FavoriteDeleteViewHolder(View view) { super(view); mContext = view.getContext(); } public static FavoriteDeleteViewHolder showDeleteDialog(Activity context, ViewGroup parent, OnClickListener onDeleteClickCallback) { View view = FavoriteDeleteViewHolder.makeView(FavoriteDeleteViewHolder.class, context.getLayoutInflater(), parent); FavoriteDeleteViewHolder vHolder = FavoriteDeleteViewHolder.bindView(0, view, FavoriteDeleteViewHolder.class, null, onDeleteClickCallback); parent.addView(view); vHolder.startAnimIn(); return vHolder; } public void destroy() { startAnimOut(); } //... } ``` * 简直神乎其技呀,有木有?!!! ### 12、相册选择图片、剪裁,保存图片到相册并广播刷新 [PhotoUtils](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/utils/PhotoUtils.java) 示例: ```Java private static final String EXTRA_SESSION = ContributeActy.class.getName() + ".SESSION"; private static final String EXTRA_LIST_DATA = ContributeActy.class.getName() + ".LIST_DATA"; private static final int REQUEST_CODE_PICK_PHOTO = 100; private static final int REQUEST_CODE_CROP = 101; private PhotoUtils.Session session; //... @Override public void onClick(View v) { try { session = PhotoUtils.openSysGallery2ChoosePhoto(ContributeActy.this, REQUEST_CODE_PICK_PHOTO, new CropArgs(REQUEST_CODE_CROP, new File(App.getImagesCacheDirPrivate(), "croptemp-" + System.currentTimeMillis() + ".png").getPath(), Bitmap.CompressFormat.PNG, false, true, 0, 0, 0, 0, 640, 640)); L.i(ContributeActy.class, session.toString()); } catch (Exception e) { L.e(ContributeActy.class, e); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Uri uri = PhotoUtils.onActivityResult(this, session, requestCode, resultCode, data); if (uri != null) { getAdapter().getData().add(uri); getAdapter().notifyDataSetChanged(); session = null; } } @Override protected void onSaveInstanceState(Bundle outState) { List data = getAdapter().getData(); if (data.size() > 0) { ArrayList value = new ArrayList(); for (Uri uri : data) { value.add(uri.toString()); } outState.putStringArrayList(EXTRA_LIST_DATA, value); } if (session != null) outState.putString(EXTRA_SESSION, session.toJson()); super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); ArrayList value = savedInstanceState.getStringArrayList(EXTRA_LIST_DATA); if (value != null && value.size() > 0) { List data = new ArrayList(); for (String s : value) { data.add(Uri.parse(s)); } getAdapter().setDataSource(data); } String json = savedInstanceState.getString(EXTRA_SESSION); if (json != null) session = Session.fromJsonWithAllFields(json, Session.class); } ``` ### 13、简化ListView及同类组件数据更新的套件 [AbsListViewActivity](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/framework/AbsListViewActivity.java)、[AbsListViewFragment](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/framework/AbsListViewFragment.java) 和 [AbsAdapter](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/adapter/AbsAdapter.java) 示例: ```Java @ViewLayoutId(R.layout.m_edit) @ViewListId(R.id.m_edit_grid_view) public class EditActy extends AbsListViewActivity { private static final String EXTRA_CATEGORY_ID = EditActy.class.getName() + ".EXTRA_CATEGORY_ID"; public static void startMe(Context context, long categoryId) { Intent intent = new Intent(context, EditActy.class); intent.putExtra(EXTRA_CATEGORY_ID, categoryId); startMe(context, intent); } @ViewId(R.id.m_edit_title_left_btn_back) private ImageButton mBtnBack; @ViewId(R.id.m_edit_magic_board) private MagicBoardView mMagicBoard; @Override protected EditGridAdapter newAdapter() { return new EditGridAdapter(this, mOnFavoriteClick); } //... } ``` ### 14、图像工具 [BitmapUtils](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/utils/BitmapUtils.java) ### 15、AndroidManifest.xml属性读取工具 [Manifest](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/utils/Manifest.java) ### 16、对话框、弹窗和进度条 [DialogHelper、Prompt 和 PromptProgress](http://github.com/WeiChou/Wei.Lib2A/tree/master/Wei.Lib2A/src/hobby/wei/c/widget/) * UI和动画都可全局定制; * `DialogHelper`可弹出在桌面上。 ### 17、文本超链接点击效果 [LinkMovementMethod、LinkSpan](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/widget/text) ### 18、具有退出监听能力的 [AbsApp](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/framework/AbsApp.java) ```Java public class App extends AbsApp { @Override protected boolean onExit() { L.i(this, "程序正常退出------App.onExit()"); //... super.onExit(); if (mExitForRestart) { WelcomeActy.startMe(this); mExitForRestart = false; } return false; } //... } ``` ### 19、非对称密钥对生成工具 [RsaUtils](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/utils/RsaUtils.java) * 可生成iOS项目能识别的公钥密钥文件(iOS要求比较苛刻,而java的库对密钥文件的识别能力较强)。 ###20、时间工具 [TimeUtils](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/utils/TimeUtils.java) * 可解析C#生成的Java标准API`SimpleDateFormat`无法识别的格式化字符串如`2013-05-22T09:16:44.871589GMT+0800`和`2013-05-22T09:16:44.871589+08:00`; * 可根据生日计算年龄; * 可生成无时区的时间长度表示,并定制单位,如:3天前,1周前,2年6个月前等; * 可根据系统时间返回凌晨、上午、中午、下午、晚上、晚休等信息。 ### 21、Log的简化版[ L ](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/L.java) * 可在release打包混淆时,将低级别日志自动删除,同时优化掉作为参数的字符串常量(对参数有规范化要求,见代码文档:[@Burden](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/anno/proguard/Burden.java) 或 [proguard.README](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/anno/proguard/README.md))。 ### 22、全局异常拦截器 [CrashHandler](http://github.com/WeiChou/Wei.Lib2A/blob/master/Wei.Lib2A/src/hobby/wei/c/utils/CrashHandler.java) * 可dump运行时内存到存储卡(.hprof文件)用于内存分析; * 可拦截某线程的某异常或所有异常; * 可拦截所有线程的某异常或所有异常; * 拦截后的处理方式应该根据业务需要自定义处理,默认是作闪退处理。 ### 23、全新基于`@Annotation`的混淆配置库 * 已独立迁移至github并上传jcenter: [Annoguard](http://github.com/WeiChou/Annoguard) #### 联系作者 ![微信](weichat_qr_code.jpg) ![支付宝](zhifubao_qr_code.jpg)