В 2016 году добавлено следующее:
@Annotation
конфигурационный сэмплерAndroid-библиотека для быстрой разработки, набор инструментов для Android. Задачи управления, загрузки данных и загрузки библиотек см. в других проектах.
Весь код этой библиотеки был тщательно протестирован автором, структурирован и понятен. Он охватывает все аспекты базовой разработки Android и рекомендуется в качестве основной структуры проекта для удобства интеграции различных функций. Если у вас есть какие-либо вопросы или предложения, пожалуйста, свяжитесь с автором: weichou2010@gmail.com, WeChat или добавление в группу @群主.
Несколько констант позволяют легко получить доступ к встроенным или внешним картам памяти 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);
}
}
Достаточно ли только инструмента для работы с картами памяти? Ему нужно определить, существует ли карта памяти, можно ли создать каталог и файл, а затем решить, следует ли записывать файл. А [FStoreLoc] может сделать всё за один шаг:
public static File getImagesCacheDir() throws SdCardNotMountedException,
SdCardNotValidException, FileCreateFailureException {
return FStoreLoc.BIGFILE.getImagesCacheDir(get(), DirLevel.CUSTOM);
}
В приведённом выше коде DirLevel.CUSTOM
означает выбор пользовательского корневого каталога, который действует только тогда, когда карта памяти поддерживает создание пользовательских каталогов, в противном случае будет выдано исключение. Конечно, вы также можете выбрать частный каталог DirLevel.PRIVATE
или адаптивный каталог DirLevel.DEFAULT
, подробности см. в документации кода.
У [FStoreLoc] есть три предустановленных режима хранения:
FStoreLoc.DEFAULT
;FStoreLoc.BIGFILE
;FStoreLoc.SURVIVE
.А режимы большого файла
и выживания
, в зависимости от потребностей бизнеса, могут потребовать настройки корневого каталога карты памяти. В этом случае он будет установлен в приложении:
public class App extends AbsApp {
@Override
public void onCreate() {
Debug.DEBUG = isDebugMode();
if (Debug.DEBUG) {
//...
}
super.onCreate();
//...
/* Установить корневой каталог для режима большого файла. Если корневой каталог карты памяти позволяет записывать файлы, то он создаст этот каталог, иначе он будет использовать частный каталог, выделенный системой для приложения на карте памяти: Android/appname/files/
* Режим большого файла, т.е. хранить только на встроенной или внешней карте памяти, внешняя карта памяти имеет приоритет.
*/
FStoreLoc.BIGFILE.setBaseDirName(this, Const.APP_DIR_NAME);
// Переключиться на внешнюю карту памяти (по умолчанию автоматически выбирается оставшаяся карта с наибольшим пространством). Однако при чтении и записи файлов, если она не существует, она автоматически переключится на встроенную карту.
FStoreLoc.BIGFILE.switchTo(this, Storage.CARD_EXT);
}
//...
}
Keeper обладает следующими возможностями:
Базовый способ использования:
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();
Пример:
@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, которое указывает, является ли точка монтирования доступной только для чтения.
*/
protected void onMediaMounted(SdCard sdcard, boolean readOnly) {}
/**
* Идёт проверка диска.
*/
protected void onMediaChecking(SdCard sdcard) {}
/**
* Запрос на сканирование носителя данных с помощью сканера, чтобы поместить информацию о медиафайлах в базу данных.
*/
}
``` 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);
}
} Вот перевод текста на русский язык:
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<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();
}
//...
}
Пример:
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
``` **13. Абстракция ListView и аналогичных компонентов для обновления данных**
```Java
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);
}
Абстрактный класс AbsListViewActivity, абстрактный фрагмент AbsListViewFragment и абстрактный адаптер AbsAdapter используются для упрощения обновления данных в компонентах типа ListView.
14. Инструменты для работы с изображениями BitmapUtils
В этом разделе не представлен код на Java.
15. Инструмент для чтения свойств из AndroidManifest.xml Manifest
В этом разделе не представлен код на Java.
16. Диалоговые окна, всплывающие окна и индикаторы выполнения DialogHelper, Prompt и PromptProgress
Инструменты DialogHelper, Prompt и PromptProgress позволяют настраивать диалоги, всплывающие окна и индикаторы прогресса. Они предоставляют возможность настройки пользовательского интерфейса и анимации. DialogHelper также позволяет отображать диалоговые окна на рабочем столе.
17. Текст с гиперссылками LinkMovementMethod и LinkSpan
Этот раздел не содержит кода на Java. Он описывает инструменты LinkMovementMethod и LinkSpan, которые обеспечивают эффект клика по гиперссылке в тексте.
18. Приложение с возможностью отслеживания выхода AbsApp
Класс App расширяет класс AbsApp и переопределяет метод onExit(). В этом методе выполняется необходимая логика перед выходом из приложения. Метод super.onExit() вызывает реализацию метода onExit() родительского класса. Если mExitForRestart имеет значение true, то после выхода из приложения будет запущен WelcomeActy.startMe().
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
RsaUtils — это инструмент для генерации асимметричных пар ключей. Он может создавать пары ключей, которые могут быть распознаны iOS-проектами.
20. Временные инструменты TimeUtils
TimeUtils предоставляет различные функции для работы со временем. Он может анализировать нестандартные форматы времени, вычислять возраст по дате рождения, генерировать время без часового пояса и предоставлять информацию о времени суток.
21. Упрощённый логгер L
L — это упрощённый логгер, который позволяет оптимизировать логирование в релизных сборках путём удаления низкоуровневых сообщений. Это достигается за счёт использования аннотаций Burden или настройки Proguard.
22. Глобальный обработчик исключений CrashHandler
CrashHandler — это глобальный обработчик исключений, который может выполнять дамп памяти в файл .hprof для анализа памяти. Можно перехватить определённое или все исключения для некоторого потока;
@Annotation
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )