Skip to content

weichou/Wei.Lib2A

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Wei.Lib2A



2016年新增内容:

全新基于@Annotation的混淆配置库

  • 已独立迁移至github并上传jcenter: Annoguard


Android快速开发库,Android常用工具集。任务管理、数据加载、下载库请查阅其他项目。
对于初学者欢迎加 QQ群:215621863 相互学习探讨!

这个库都有什么?能帮我们做什么?

我写代码遵循两个基本原则:

  • 减少代码量。希望达到的效果是,让使用者尽可能减少代码,能一句话搞定的,绝不两句,能静态方法搞定的,绝不new对象;
  • 增强适应性和稳定性。由于Android平台厂商定制的碎片化和众多版本兼容性问题,本库的开发会着重考虑这些因素,而且基本都是经上线的项目考验过的。

本库的所有代码都经过本人严格测试,字斟句酌,了如指掌,注释也很清晰。覆盖Android基础开发的方方面面,推荐作为项目的基础框架来使用,以便于各功能的正常便利集成。有任何疑问或建议请联系作者: weichou2010@gmail.com微信加群@群主

基础常用组件介绍如下:

1、存储卡工具类 Storage

仅通过几个常量即可便捷的取得内置或外置存储卡 Storage.SdCard 对象。例如:

    //输出外置存储卡的路径到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

仅有存储卡工具就够了吗?它需要去判断存储卡是否存在、能否创建目录和文件,再决定是否写文件。而FStoreLoc可以一步完成:

    public static File getImagesCacheDir() throws SdCardNotMountedException,
                                SdCardNotValidException, FileCreateFailureException {
		return FStoreLoc.BIGFILE.getImagesCacheDir(get(), DirLevel.CUSTOM);
	}
  • 上面代码中的 DirLevel.CUSTOM 表示选择自定义根目录,只有在存储卡支持创建自定义目录时才有效,否则抛异常。当然也可选择私有目录 DirLevel.PRIVATE 或自适应目录 DirLevel.DEFAULT,详见代码中的文档。

FStoreLoc有三种预置存储模式:

大文件模式生存模式根据业务需要可能需要设置存储卡根目录,那就放在App初始化里:

    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 和基于版本控制的多进程文件并发读写工具 FileVersioned

4、增强的SharedPreferences工具:Keeper

具有多进程读写安全、基于Locale的文件隔离能力等。

基本用法:

    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

6、网络连接状况监听 NetConnectionReceiver

7、存储卡挂载状况监听 StorageReceiver

示例:

    @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

基于但简化了BroadcastLocalBroadcast的发送/接收操作,在页面初始化的时候可以调用AbsActivity/AbsFragment.hostingLocalEventReceiver(...)将事件监听器进行托管,不再需要在onPause()onResume()以及onDestroy()等事件的时候进行unregisterXxx()registerXxx()编码,托管组件将自动完成。受惠于Broadcast的松散耦合机制和简化了的发送/接收操作,可将传统的Callack模式用本事件模式重构,轻松解决由于某些原因导致的资源无法释放内存泄露等问题。示例:

    //在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

  • 注意@ViewOnClick的用法非常灵活。示例:
    @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

示例:

    /**由于typeKey可能全局都一样,做一个抽象**/
    public abstract class AbsData<T extends AbsData<T>> extends AbsJsonTyped<T> {
        public static final String KEY_RESULT		= "result";
    
        @Expose
        public String result;
    
        protected String typeKey() {
            return KEY_RESULT;
        }
    }
    
    /**构建与Json字符串对应的类结构**/
    public class EditBean extends AbsData<EditBean> {
        @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<EditBean> getTypeToken() {
            return new TypeToken<EditBean>(){};
        }
    }
    
    /*以下为操作数据*/
    
    EditBean edit = new EditBean();
    //edit.xxx = xxx;
    //...
    //序列化
    edit.toJson();
    
    //判断是否属于本类型,一般情况用不到。
    if (new EditBean().isBelongToMe(new JSONObject(jsonString))) {
        //...
    }
    
    //反序列化
    new EditBean().fromJson(jsonString);

11、用途广泛的 ViewHolder

既可用于ListViewAdapter

    @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<EditBean, OnClickListener> {
		@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);
		}
        //...
    }

也可用于ViewPagerPagerAdapter:

    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<Void, OnClickListener> 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<Void, OnClickListener> {
		@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();
		}
	}
    //...

还可用于其他任何场景:

    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<Void, OnClickListener> {
        @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

示例:

    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<Uri> data = getAdapter().getData();
		if (data.size() > 0) {
			ArrayList<String> value = new ArrayList<String>();
			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<String> value = savedInstanceState.getStringArrayList(EXTRA_LIST_DATA);
		if (value != null && value.size() > 0) {
			List<Uri> data = new ArrayList<Uri>();
			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及同类组件数据更新的套件 AbsListViewActivityAbsListViewFragmentAbsAdapter

示例:

    @ViewLayoutId(R.layout.m_edit)
    @ViewListId(R.id.m_edit_grid_view)
    public class EditActy extends AbsListViewActivity<GridView, EditBean, EditGridAdapter> {
        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

15、AndroidManifest.xml属性读取工具 Manifest

16、对话框、弹窗和进度条 DialogHelper、Prompt 和 PromptProgress

  • UI和动画都可全局定制;
  • DialogHelper可弹出在桌面上。

17、文本超链接点击效果 LinkMovementMethod、LinkSpan

18、具有退出监听能力的 AbsApp

    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

  • 可生成iOS项目能识别的公钥密钥文件(iOS要求比较苛刻,而java的库对密钥文件的识别能力较强)。

###20、时间工具 TimeUtils

  • 可解析C#生成的Java标准APISimpleDateFormat无法识别的格式化字符串如2013-05-22T09:16:44.871589GMT+08002013-05-22T09:16:44.871589+08:00
  • 可根据生日计算年龄;
  • 可生成无时区的时间长度表示,并定制单位,如:3天前,1周前,2年6个月前等;
  • 可根据系统时间返回凌晨、上午、中午、下午、晚上、晚休等信息。

21、Log的简化版 L

  • 可在release打包混淆时,将低级别日志自动删除,同时优化掉作为参数的字符串常量(对参数有规范化要求,见代码文档:@Burdenproguard.README)。

22、全局异常拦截器 CrashHandler

  • 可dump运行时内存到存储卡(.hprof文件)用于内存分析;
  • 可拦截某线程的某异常或所有异常;
  • 可拦截所有线程的某异常或所有异常;
  • 拦截后的处理方式应该根据业务需要自定义处理,默认是作闪退处理。

23、全新基于@Annotation的混淆配置库

  • 已独立迁移至github并上传jcenter: Annoguard

联系作者

微信 支付宝

About

Android快速开发框架,Android常用工具集。

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published