Initial commit

androidx
Zhanty 5 years ago
commit 60d4aa0b20
  1. 22
      lib_base/.gitignore
  2. 20
      lib_base/README.md
  3. 130
      lib_base/build.gradle
  4. 17
      lib_base/proguard-rules.pro
  5. 7
      lib_base/src/main/AndroidManifest.xml
  6. 81
      lib_base/src/main/java/com/android/base/adapter/DataManager.java
  7. 72
      lib_base/src/main/java/com/android/base/adapter/ItemHelper.java
  8. 178
      lib_base/src/main/java/com/android/base/adapter/list/BaseListAdapter.java
  9. 183
      lib_base/src/main/java/com/android/base/adapter/list/ListDataManagerImpl.java
  10. 21
      lib_base/src/main/java/com/android/base/adapter/list/SmartViewHolder.java
  11. 35
      lib_base/src/main/java/com/android/base/adapter/list/ViewHolder.java
  12. 33
      lib_base/src/main/java/com/android/base/adapter/pager/SimpleViewPagerAdapter.kt
  13. 47
      lib_base/src/main/java/com/android/base/adapter/pager/ViewPageFragmentAdapter.java
  14. 17
      lib_base/src/main/java/com/android/base/adapter/pager/ViewPageInfo.java
  15. 48
      lib_base/src/main/java/com/android/base/adapter/pager/ViewPageStateFragmentAdapter.java
  16. 69
      lib_base/src/main/java/com/android/base/adapter/pager/ViewPagerAdapter.java
  17. 152
      lib_base/src/main/java/com/android/base/adapter/pager/recycler/RecycleBin.java
  18. 114
      lib_base/src/main/java/com/android/base/adapter/pager/recycler/RecyclingPagerAdapter.java
  19. 49
      lib_base/src/main/java/com/android/base/adapter/recycler/AdapterDataObserverProxy.java
  20. 255
      lib_base/src/main/java/com/android/base/adapter/recycler/DiffRecyclerAdapter.java
  21. 5
      lib_base/src/main/java/com/android/base/adapter/recycler/HeaderSize.java
  22. 35
      lib_base/src/main/java/com/android/base/adapter/recycler/ItemViewBinder.kt
  23. 166
      lib_base/src/main/java/com/android/base/adapter/recycler/MultiTypeAdapter.java
  24. 164
      lib_base/src/main/java/com/android/base/adapter/recycler/RecyclerAdapter.java
  25. 248
      lib_base/src/main/java/com/android/base/adapter/recycler/RecyclerDataManagerImpl.java
  26. 59
      lib_base/src/main/java/com/android/base/adapter/recycler/SimpleRecyclerAdapter.kt
  27. 22
      lib_base/src/main/java/com/android/base/adapter/recycler/SmartViewHolder.java
  28. 28
      lib_base/src/main/java/com/android/base/adapter/recycler/ViewHolder.java
  29. 116
      lib_base/src/main/java/com/android/base/app/ApplicationDelegate.java
  30. 46
      lib_base/src/main/java/com/android/base/app/BaseAppContext.java
  31. 208
      lib_base/src/main/java/com/android/base/app/BaseKit.java
  32. 113
      lib_base/src/main/java/com/android/base/app/CrashHandler.java
  33. 79
      lib_base/src/main/java/com/android/base/app/aac/AutoClearedValue.kt
  34. 225
      lib_base/src/main/java/com/android/base/app/aac/AutoDisposeLiveData.kt
  35. 227
      lib_base/src/main/java/com/android/base/app/aac/ResourceLiveData.kt
  36. 36
      lib_base/src/main/java/com/android/base/app/aac/SingleLiveData.kt
  37. 61
      lib_base/src/main/java/com/android/base/app/aac/SingleLiveEvent.java
  38. 68
      lib_base/src/main/java/com/android/base/app/activity/ActivityDelegate.java
  39. 19
      lib_base/src/main/java/com/android/base/app/activity/ActivityDelegateOwner.java
  40. 137
      lib_base/src/main/java/com/android/base/app/activity/ActivityDelegates.java
  41. 11
      lib_base/src/main/java/com/android/base/app/activity/ActivityStatus.kt
  42. 71
      lib_base/src/main/java/com/android/base/app/activity/BackHandlerHelper.java
  43. 222
      lib_base/src/main/java/com/android/base/app/activity/BaseActivity.java
  44. 13
      lib_base/src/main/java/com/android/base/app/activity/OnBackPressListener.java
  45. 14
      lib_base/src/main/java/com/android/base/app/dagger/ActivityScope.java
  46. 22
      lib_base/src/main/java/com/android/base/app/dagger/ContextType.java
  47. 14
      lib_base/src/main/java/com/android/base/app/dagger/FragmentScope.java
  48. 18
      lib_base/src/main/java/com/android/base/app/dagger/HasViewInjector.java
  49. 31
      lib_base/src/main/java/com/android/base/app/dagger/Injectable.java
  50. 14
      lib_base/src/main/java/com/android/base/app/dagger/ViewComponent.java
  51. 15
      lib_base/src/main/java/com/android/base/app/dagger/ViewComponentBuilder.java
  52. 60
      lib_base/src/main/java/com/android/base/app/dagger/ViewInjection.java
  53. 18
      lib_base/src/main/java/com/android/base/app/dagger/ViewKey.java
  54. 45
      lib_base/src/main/java/com/android/base/app/dagger/ViewModelFactory.java
  55. 18
      lib_base/src/main/java/com/android/base/app/dagger/ViewModelKey.java
  56. 22
      lib_base/src/main/java/com/android/base/app/dagger/ViewModelModule.java
  57. 19
      lib_base/src/main/java/com/android/base/app/dagger/ViewScope.java
  58. 278
      lib_base/src/main/java/com/android/base/app/fragment/BaseDialogFragment.java
  59. 289
      lib_base/src/main/java/com/android/base/app/fragment/BaseFragment.java
  60. 150
      lib_base/src/main/java/com/android/base/app/fragment/BaseListFragment.java
  61. 102
      lib_base/src/main/java/com/android/base/app/fragment/BaseListV2Fragment.kt
  62. 145
      lib_base/src/main/java/com/android/base/app/fragment/BaseStateDialogFragment.java
  63. 152
      lib_base/src/main/java/com/android/base/app/fragment/BaseStateFragment.java
  64. 26
      lib_base/src/main/java/com/android/base/app/fragment/DelegateEx.kt
  65. 24
      lib_base/src/main/java/com/android/base/app/fragment/FragmentConfig.java
  66. 73
      lib_base/src/main/java/com/android/base/app/fragment/FragmentDelegate.java
  67. 16
      lib_base/src/main/java/com/android/base/app/fragment/FragmentDelegateOwner.java
  68. 166
      lib_base/src/main/java/com/android/base/app/fragment/FragmentDelegates.java
  69. 132
      lib_base/src/main/java/com/android/base/app/fragment/FragmentInfo.java
  70. 463
      lib_base/src/main/java/com/android/base/app/fragment/Fragments.kt
  71. 117
      lib_base/src/main/java/com/android/base/app/fragment/LazyDelegate.java
  72. 11
      lib_base/src/main/java/com/android/base/app/fragment/LoadingViewFactory.java
  73. 68
      lib_base/src/main/java/com/android/base/app/fragment/RefreshLoadMoreStateLayoutImpl.kt
  74. 189
      lib_base/src/main/java/com/android/base/app/fragment/RefreshableStateLayoutImpl.java
  75. 207
      lib_base/src/main/java/com/android/base/app/fragment/TabManager.java
  76. 44
      lib_base/src/main/java/com/android/base/app/mvp/AbstractPresenter.kt
  77. 3
      lib_base/src/main/java/com/android/base/app/mvp/IBaseView.kt
  78. 13
      lib_base/src/main/java/com/android/base/app/mvp/IPresenter.kt
  79. 31
      lib_base/src/main/java/com/android/base/app/mvp/Lifecycle.kt
  80. 66
      lib_base/src/main/java/com/android/base/app/mvp/PresenterBinder.kt
  81. 65
      lib_base/src/main/java/com/android/base/app/mvp/RxPresenter.kt
  82. 60
      lib_base/src/main/java/com/android/base/app/mvvm/ArchViewModel.kt
  83. 32
      lib_base/src/main/java/com/android/base/app/mvvm/ViewModelEx.kt
  84. 39
      lib_base/src/main/java/com/android/base/app/ui/AutoPageNumber.java
  85. 22
      lib_base/src/main/java/com/android/base/app/ui/CommonId.java
  86. 28
      lib_base/src/main/java/com/android/base/app/ui/LoadingView.java
  87. 9
      lib_base/src/main/java/com/android/base/app/ui/OnRetryActionListener.java
  88. 99
      lib_base/src/main/java/com/android/base/app/ui/PageNumber.java
  89. 31
      lib_base/src/main/java/com/android/base/app/ui/RefreshListLayout.java
  90. 40
      lib_base/src/main/java/com/android/base/app/ui/RefreshLoadMoreView.java
  91. 31
      lib_base/src/main/java/com/android/base/app/ui/RefreshLoadViewFactory.java
  92. 11
      lib_base/src/main/java/com/android/base/app/ui/RefreshStateLayout.java
  93. 32
      lib_base/src/main/java/com/android/base/app/ui/RefreshView.java
  94. 35
      lib_base/src/main/java/com/android/base/app/ui/RefreshViewFactory.java
  95. 27
      lib_base/src/main/java/com/android/base/app/ui/StateLayout.java
  96. 63
      lib_base/src/main/java/com/android/base/app/ui/StateLayoutConfig.java
  97. 51
      lib_base/src/main/java/com/android/base/app/ui/SwipeRefreshView.java
  98. 153
      lib_base/src/main/java/com/android/base/app/ui/UIEx.kt
  99. 106
      lib_base/src/main/java/com/android/base/concurrent/GoUtil.java
  100. 37
      lib_base/src/main/java/com/android/base/concurrent/JobExecutor.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,22 @@
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild
*.iml
.idea/
.gradle
/local.properties
.DS_Store
/build
/captures
*.apk
*.ap_
*.dex
*.class
bin/
gen/
local.properties

@ -0,0 +1,20 @@
# Android Development Base Library
## 1 Third party Libraries
- [AndroidX](https://developer.android.com/jetpack/androidx)
- [RxJava(2)](https://github.com/ReactiveX/RxJava)
- [RxAndroid(2)](https://github.com/ReactiveX/RxAndroid)
- [AutoDispose](https://github.com/uber/AutoDispose)
- [Dagger2](https://github.com/google/dagger)
- [Glide](https://github.com/bumptech/glide)
- [OkHttp](https://github.com/square/okhttp)
- [Timber](https://github.com/JakeWharton/timber)
- [WrapperAdapter](https://github.com/Ztiany/WrapperAdapter)
- [MultiTypeAdapter](https://github.com/drakeet/MultiType)
- [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode)
## 2 Environment
- AndroidStudio 3+
- Java8 with desugar

@ -0,0 +1,130 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
androidExtensions {
experimental = true
}
android {
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode 1
versionName "1.0"
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath false
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main {
java.srcDirs += "src/github/java"
res.srcDirs += "src/github/res"
}
}
project.afterEvaluate {
Task assembleReleaseTask = project.tasks.findByPath("assembleRelease")
if (assembleReleaseTask != null) {
assembleReleaseTask.doLast {
File infile = project.file("build/outputs/aar/")
File outfile = project.file("./release")
project.copy {
from infile
into outfile
}
}
}
}
}
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
//
testImplementation testLibraries.junit
//AndroidSupport
api androidLibraries.androidCompatV4
api androidLibraries.androidCompatV7
api androidLibraries.androidRecyclerView
api androidLibraries.androidDesign
api androidLibraries.androidPrecent
api androidLibraries.constraintLayout
//AAC
api androidLibraries.lifecycle
api androidLibraries.lifecycleJava8
api androidLibraries.lifecycleExtensions
api androidLibraries.liveDataReactiveStreams
//Kotlin
api kotlinLibraries.kotlinStdlib
api kotlinLibraries.kotlinReflect
api kotlinLibraries.kotlinCoroutines
api kotlinLibraries.kotlinAndroidCoroutines
//RxJava
api thirdLibraries.rxJava
api thirdLibraries.rxAndroid
api thirdLibraries.rxBinding
api thirdLibraries.autoDispose
api thirdLibraries.autoDisposeLifecycleArchcomponents
/*Dagger2*/
api thirdLibraries.dagger2
api thirdLibraries.jsr305
api thirdLibraries.dagger2Android
api thirdLibraries.dagger2AndroidSupport
kapt thirdLibraries.dagger2Apt
kapt thirdLibraries.dagger2AndroidApt
//LoadMore
api uiLibraries.wrapperAdapter
//Adapter
api uiLibraries.multiType
api uiLibraries.multiTypeKotlin
//Log
api thirdLibraries.timber
//ImageLoader
api thirdLibraries.glide
api thirdLibraries.glideOkHttp
api thirdLibraries.okHttp
//Utils
api thirdLibraries.utilcode
api thirdLibraries.jOOR
api thirdLibraries.andPermission
api thirdLibraries.supportOptional
}

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in E:\DeveloperTools\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

@ -0,0 +1,7 @@
<manifest package="com.android.base">
<application>
</application>
</manifest>

@ -0,0 +1,81 @@
package com.android.base.adapter;
import java.util.List;
/**
* <pre>
* 注意数据源引用的替换只有setDataSource方法会把elements替换掉之前的数据源引用其他方法都是基于现有数据集合做删除与添加操作
* </pre>
*
* @author Ztiany
* Date : 2016-09-12 11:33
* Email: 1169654504@qq.com
*/
public interface DataManager<T> {
//Add op
void add(T element);
void addAt(int location, T element);
void addItems(List<T> elements);
/**
* 添加元素前会使用equals方法进行比较荣旧的数据集合中删除相同的数据在添加
*
* @param elements 元素
*/
void addItemsChecked(List<T> elements);
void addItemsAt(int location, List<T> elements);
//update op
void replace(T oldElement, T newElement);
void replaceAt(int index, T element);
/**
* 清除之前集合中的数据然后把elements添加到之前的集合中不会使用elements作为数据源
*
* @param elements 元素
*/
void replaceAll(List<T> elements);
/**
* 此方法会使用elements替换掉之前的数据源而不对之前的数据源做任何操作
*
* @param newDataSource 新的数据集
* @param notifyDataSetChanged 是否调用adapter的notifyDataSetChanged方法
*/
void setDataSource(List<T> newDataSource, boolean notifyDataSetChanged);
//remove opt
void remove(T element);
void removeAt(int index);
void removeItems(List<T> elements);
//get
T getItem(int position);
List<T> getItems();
int getDataSize();
//contains
boolean contains(T element);
boolean isEmpty();
//clear opt
void clear();
//Utils
/**
* @param t element
* @return -1 if not contains this element
*/
int getItemPosition(T t);
}

@ -0,0 +1,72 @@
package com.android.base.adapter;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.util.SparseArray;
import android.view.View;
import android.widget.TextView;
/**
* @author Ztiany
* Email 1169654504@qq.com
* Date 015-12-29 20:47
*/
public class ItemHelper {
private View mItemView;
private SparseArray<View> views;
public ItemHelper(View itemView) {
mItemView = itemView;
views = new SparseArray<>();
}
@SuppressWarnings("unchecked")
public <T extends View> T getView(int viewId) {
View view = views.get(viewId);
if (view == null) {
view = mItemView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
}
public ItemHelper setText(CharSequence str, @IdRes int viewId) {
((TextView) getView(viewId)).setText(str == null ? "" : str);
return this;
}
public ItemHelper setText(@StringRes int strId, @IdRes int viewId) {
((TextView) getView(viewId)).setText(strId);
return this;
}
public ItemHelper setTag(@NonNull Object object, @IdRes int tagId, @IdRes int viewID) {
View view = getView(viewID);
view.setTag(tagId, object);
return this;
}
public ItemHelper setTag(@NonNull Object object, @IdRes int viewID) {
View view = getView(viewID);
view.setTag(object);
return this;
}
public <T> T getTag(@IdRes int tagId, @IdRes int viewID) {
View view = getView(viewID);
@SuppressWarnings("unchecked")
T tag = (T) view.getTag(tagId);
return tag;
}
public <T> T getTag(@IdRes int viewID) {
View view = getView(viewID);
@SuppressWarnings("unchecked")
T tag = (T) view.getTag();
return tag;
}
}

@ -0,0 +1,178 @@
package com.android.base.adapter.list;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.android.base.R;
import com.android.base.adapter.DataManager;
import java.util.List;
/**
* absListView通用的Adapter,注意只有setDataSource才能替换原有数据源的引用
*
* @param <T> 数据模型
* @author Ztiany
*/
@SuppressWarnings("unused")
public abstract class BaseListAdapter<T, VH extends ViewHolder> extends BaseAdapter implements DataManager<T> {
protected final Context mContext;
private final static int ITEM_ID = R.id.base_item_tag_view_id;
private DataManager<T> mDataManager;
private final LayoutInflater mLayoutInflater;
public BaseListAdapter(@NonNull Context context) {
this(context, null);
}
@SuppressWarnings("all")
public BaseListAdapter(Context context, List<T> data) {
this.mContext = context;
mLayoutInflater = LayoutInflater.from(context);
mDataManager = new ListDataManagerImpl<>(data, this);
}
@Override
public int getCount() {
return getDataSize();
}
@Override
public long getItemId(int position) {
return position;
}
@Override
@SuppressWarnings("unchecked")
public View getView(int position, View convertView, ViewGroup parent) {
VH viewHolder;
int type = getItemViewType(position);
if (convertView == null) {
viewHolder = onCreateViewHolder(mLayoutInflater, parent, type);
viewHolder.mItemView.setTag(ITEM_ID, viewHolder);
} else {
viewHolder = (VH) convertView.getTag(ITEM_ID);
}
viewHolder.setPosition(position);
viewHolder.setType(type);
T item = getItem(position);
onBindData(viewHolder, item);
return viewHolder.mItemView;
}
@SuppressWarnings("all")
protected abstract void onBindData(@NonNull VH viewHolder, T item);
@NonNull
protected abstract VH onCreateViewHolder(@NonNull LayoutInflater layoutInflater, @NonNull ViewGroup parent, int type);
@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
@Override
public int getViewTypeCount() {
return super.getViewTypeCount();
}
///////////////////////////////////////////////////////////////////////////
// DataManager
///////////////////////////////////////////////////////////////////////////
@Override
public void add(T elem) {
mDataManager.add(elem);
}
@Override
public void addAt(int location, T elem) {
mDataManager.addAt(location, elem);
}
@Override
public void addItems(List<T> elements) {
mDataManager.addItems(elements);
}
@Override
public void addItemsChecked(List<T> elements) {
mDataManager.addItemsChecked(elements);
}
@Override
public void addItemsAt(int location, List<T> elements) {
mDataManager.addItemsAt(location, elements);
}
@Override
public void replace(T oldElem, T newElem) {
mDataManager.replace(oldElem, newElem);
}
@Override
public void replaceAt(int index, T elem) {
mDataManager.replaceAt(index, elem);
}
@Override
public void replaceAll(List<T> elements) {
mDataManager.replaceAll(elements);
}
@Override
public void remove(T elem) {
mDataManager.remove(elem);
}
@Override
public void removeItems(List<T> elements) {
mDataManager.removeItems(elements);
}
@Override
public void removeAt(int index) {
mDataManager.removeAt(index);
}
@Override
public T getItem(int position) {
return mDataManager.getItem(position);
}
@Override
public final int getDataSize() {
return mDataManager.getDataSize();
}
@Override
public boolean contains(T elem) {
return mDataManager.contains(elem);
}
@Override
public void setDataSource(List<T> elements, boolean notifyDataSetChanged) {
mDataManager.setDataSource(elements, notifyDataSetChanged);
}
@Override
public int getItemPosition(T t) {
return mDataManager.getItemPosition(t);
}
@Override
public void clear() {
mDataManager.clear();
}
@Override
public List<T> getItems() {
return mDataManager.getItems();
}
}

@ -0,0 +1,183 @@
package com.android.base.adapter.list;
import android.widget.BaseAdapter;
import com.android.base.adapter.DataManager;
import com.android.base.utils.common.Checker;
import java.util.ArrayList;
import java.util.List;
final class ListDataManagerImpl<T> implements DataManager<T> {
private List<T> mData;
private BaseAdapter mBaseAdapter;
ListDataManagerImpl(List<T> tList, BaseAdapter adapter) {
mData = tList;
mBaseAdapter = adapter;
}
private void checkData() {
if (mData == null) {
mData = new ArrayList<>();
}
}
@Override
public void add(T elem) {
checkData();
mData.add(elem);
notifyDataSetChanged();
}
@Override
public void addAt(int location, T elem) {
checkData();
mData.add(location, elem);
notifyDataSetChanged();
}
@Override
public void addItems(List<T> elements) {
checkData();
mData.addAll(elements);
notifyDataSetChanged();
}
@Override
public void addItemsChecked(List<T> elements) {
if (Checker.isEmpty(elements)) {
return;
}
if (mData == null) {
addItems(elements);
return;
}
for (T element : elements) {
if (element == null) {
continue;
}
mData.remove(element);
}
mData.addAll(elements);
notifyDataSetChanged();
}
@Override
public void addItemsAt(int location, List<T> elements) {
checkData();
mData.addAll(location, elements);
notifyDataSetChanged();
}
@Override
public void replace(T oldElem, T newElem) {
if (mData != null && mData.contains(oldElem)) {
replaceAt(mData.indexOf(oldElem), newElem);
}
}
@Override
public void replaceAt(int index, T elem) {
if (mData != null && mData.size() > index) {
mData.set(index, elem);
notifyDataSetChanged();
}
}
@Override
public void replaceAll(List<T> elements) {
if (mData == null) {
mData = new ArrayList<>();
}
mData.clear();
if (elements != null) {
mData.addAll(elements);
}
notifyDataSetChanged();
}
@Override
public void setDataSource(List<T> newDataSource, boolean notifyDataSetChanged) {
mData = newDataSource;
if (notifyDataSetChanged) {
notifyDataSetChanged();
}
}
@Override
public void remove(T elem) {
if (mData != null && mData.contains(elem)) {
mData.remove(elem);
notifyDataSetChanged();
}
}
@Override
public void removeItems(List<T> elements) {
if (mData != null && mData.containsAll(elements)) {
mData.removeAll(elements);
notifyDataSetChanged();
}
}
@Override
public void removeAt(int index) {
if (mData != null && mData.size() > index) {
mData.remove(index);
notifyDataSetChanged();
}
}
@Override
public T getItem(int position) {
if (mData != null && mData.size() > position) {
return mData.get(position);
}
return null;
}
@Override
public final int getDataSize() {
return mData == null ? 0 : mData.size();
}
@Override
public boolean contains(T elem) {
return mData != null && mData.contains(elem);
}
@Override
public boolean isEmpty() {
return mData == null || mData.size() == 0;
}
@Override
public void clear() {
if (mData != null && !mData.isEmpty()) {
mData.clear();
notifyDataSetChanged();
}
}
@Override
public int getItemPosition(T t) {
List<T> items = getItems();
if (items == null) {
return -1;
}
return items.indexOf(t);
}
@Override
public List<T> getItems() {
return mData;
}
private void notifyDataSetChanged() {
mBaseAdapter.notifyDataSetChanged();
}
}

@ -0,0 +1,21 @@
package com.android.base.adapter.list;
import android.view.View;
import com.android.base.adapter.ItemHelper;
@SuppressWarnings("unused")
public class SmartViewHolder extends ViewHolder {
protected final ItemHelper mHelper;
public SmartViewHolder(View itemView) {
super(itemView);
mHelper = new ItemHelper(itemView);
}
public ItemHelper helper() {
return mHelper;
}
}

@ -0,0 +1,35 @@
package com.android.base.adapter.list;
import android.view.View;
public class ViewHolder {
@SuppressWarnings("all")
protected final View mItemView;
private int mPosition;
private int mType;
public ViewHolder(View itemView) {
mItemView = itemView;
}
public int getPosition() {
return mPosition;
}
void setPosition(int position) {
mPosition = position;
}
public int getType() {
return mType;
}
void setType(int type) {
mType = type;
}
public View getItemView() {
return mItemView;
}
}

@ -0,0 +1,33 @@
package com.android.base.adapter.pager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.extensions.LayoutContainer
/**
* 如果使用可缩放的 View 作为 pager可能不适合使用此Adapter
*
* @param <T> 数据
* @param <VH> View Holder类型
</VH></T> */
abstract class SimpleViewPagerAdapter<T>(data: List<T>) : ViewPagerAdapter<T, KtViewHolder>(data) {
override fun createViewHolder(container: ViewGroup): KtViewHolder {
val layout = provideLayout(container)
val itemView = if (layout is Int) {
LayoutInflater.from(container.context).inflate(layout, container, false)
} else {
layout as View
}
return KtViewHolder(itemView)
}
/**provide a layout id or a View*/
abstract fun provideLayout(parent: ViewGroup): Any
}
class KtViewHolder(itemView: View) : ViewPagerAdapter.ViewHolder(itemView), LayoutContainer {
override val containerView: View = itemView
}

@ -0,0 +1,47 @@
package com.android.base.adapter.pager;
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("unused")
public class ViewPageFragmentAdapter extends FragmentPagerAdapter {
private final List<ViewPageInfo> mTabs;
private Context mContext;
public ViewPageFragmentAdapter(FragmentManager fragmentManager, Context context) {
super(fragmentManager);
mContext = context;
mTabs = new ArrayList<>();
}
public void setDataSource(List<ViewPageInfo> viewPageInfoList) {
mTabs.clear();
mTabs.addAll(viewPageInfoList);
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public Fragment getItem(int position) {
ViewPageInfo viewPageInfo = mTabs.get(position);
return Fragment.instantiate(mContext, viewPageInfo.clazz.getName(), viewPageInfo.args);
}
@Override
public CharSequence getPageTitle(int position) {
return mTabs.get(position).title;
}
public List<ViewPageInfo> getTabs() {
return mTabs;
}
}

@ -0,0 +1,17 @@
package com.android.base.adapter.pager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@SuppressWarnings("all")
public class ViewPageInfo {
public final Class<? extends Fragment> clazz;
public final Bundle args;
public final String title;
public ViewPageInfo(String title, Class<? extends Fragment> clazz, Bundle args) {
this.title = title;
this.clazz = clazz;
this.args = args;
}
}

@ -0,0 +1,48 @@
package com.android.base.adapter.pager;
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import java.util.ArrayList;
import java.util.List;
public class ViewPageStateFragmentAdapter extends FragmentStatePagerAdapter {
private final List<ViewPageInfo> mTabs;
private Context mContext;
public ViewPageStateFragmentAdapter(FragmentManager fragmentManager, Context context) {
super(fragmentManager);
mContext = context;
mTabs = new ArrayList<>();
}
public void setDataSource(List<ViewPageInfo> viewPageInfoList) {
mTabs.clear();
mTabs.addAll(viewPageInfoList);
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public Fragment getItem(int position) {
ViewPageInfo viewPageInfo = mTabs.get(position);
return Fragment.instantiate(mContext, viewPageInfo.clazz.getName(), viewPageInfo.args);
}
@Override
public CharSequence getPageTitle(int position) {
return mTabs.get(position).title;
}
protected List<ViewPageInfo> getTabs() {
return mTabs;
}
}

@ -0,0 +1,69 @@
package com.android.base.adapter.pager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import com.android.base.R;
import com.android.base.adapter.pager.recycler.RecyclingPagerAdapter;
import com.android.base.utils.common.Checker;
import java.util.List;
/**
* 如果使用可缩放的 View 作为 pager可能不适合使用此Adapter
*
* @param <T> 数据
* @param <VH> View Holder类型
*/
public abstract class ViewPagerAdapter<T, VH extends ViewPagerAdapter.ViewHolder> extends RecyclingPagerAdapter {
private List<T> mData;
public ViewPagerAdapter(@Nullable List<T> data) {
mData = data;
}
@Override
@SuppressWarnings("unchecked")
public View getView(int position, View convertView, ViewGroup container) {
VH viewHolder;
if (convertView == null) {
viewHolder = createViewHolder(container);
} else {
viewHolder = (VH) convertView.getTag(R.id.base_item_tag_view_id);
}
T item = getItem(position);
onBindData(viewHolder, item);
return viewHolder.itemView;
}
protected abstract VH createViewHolder(@NonNull ViewGroup container);
protected abstract void onBindData(@NonNull VH viewHolder, @NonNull T item);
@Override
public int getCount() {
return Checker.isEmpty(mData) ? 0 : mData.size();
}
public T getItem(int position) {
if (position < 0 || position >= mData.size()) {
return null;
}
return mData.get(position);
}
public static class ViewHolder {
public View itemView;
public ViewHolder(@NonNull View itemView) {
this.itemView = itemView;
itemView.setTag(R.id.base_item_tag_view_id, this);
}
}
}

@ -0,0 +1,152 @@
package com.android.base.adapter.pager.recycler;
import android.os.Build;
import android.util.SparseArray;
import android.view.View;
/**
* The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
* storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
* start of a layout. By construction, they are displaying current information. At the end of
* layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
* could potentially be used by the adapter to avoid allocating views unnecessarily.
* <p>
* This class was taken from Android's implementation of {@link android.widget.AbsListView} which
* is copyrighted 2006 The Android Open Source Project.
*/
public class RecycleBin {
/**
* Views that were on screen at the start of layout. This array is populated at the start of
* layout, and at the end of layout all view in activeViews are moved to scrapViews.
* Views in activeViews represent a contiguous range of Views, with position of the first
* view store in mFirstActivePosition.
*/
private View[] activeViews = new View[0];
private int[] activeViewTypes = new int[0];
/** Unsorted views that can be used by the adapter as a convert view. */
private SparseArray<View>[] scrapViews;
private int viewTypeCount;
private SparseArray<View> currentScrapViews;
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
}
//noinspection unchecked
SparseArray<View>[] scrapViews = new SparseArray[viewTypeCount];
for (int i = 0; i < viewTypeCount; i++) {
scrapViews[i] = new SparseArray<View>();
}
this.viewTypeCount = viewTypeCount;
currentScrapViews = scrapViews[0];
this.scrapViews = scrapViews;
}
protected boolean shouldRecycleViewType(int viewType) {
return viewType >= 0;
}
/** @return A view from the ScrapViews collection. These are unordered. */
View getScrapView(int position, int viewType) {
if (viewTypeCount == 1) {
return retrieveFromScrap(currentScrapViews, position);
} else if (viewType >= 0 && viewType < scrapViews.length) {
return retrieveFromScrap(scrapViews[viewType], position);
}
return null;
}
/**
* Put a view into the ScrapViews list. These views are unordered.
*
* @param scrap The view to add
*/
void addScrapView(View scrap, int position, int viewType) {
if (viewTypeCount == 1) {
currentScrapViews.put(position, scrap);
} else {
scrapViews[viewType].put(position, scrap);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
scrap.setAccessibilityDelegate(null);
}
}
/** Move all views remaining in activeViews to scrapViews. */
void scrapActiveViews() {
final View[] activeViews = this.activeViews;
final int[] activeViewTypes = this.activeViewTypes;
final boolean multipleScraps = viewTypeCount > 1;
SparseArray<View> scrapViews = currentScrapViews;
final int count = activeViews.length;
for (int i = count - 1; i >= 0; i--) {
final View victim = activeViews[i];
if (victim != null) {
int whichScrap = activeViewTypes[i];
activeViews[i] = null;
activeViewTypes[i] = -1;
if (!shouldRecycleViewType(whichScrap)) {
continue;
}
if (multipleScraps) {
scrapViews = this.scrapViews[whichScrap];
}
scrapViews.put(i, victim);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
victim.setAccessibilityDelegate(null);
}
}
}
pruneScrapViews();
}
/**
* Makes sure that the size of scrapViews does not exceed the size of activeViews.
* (This can happen if an adapter does not recycle its views).
*/
private void pruneScrapViews() {
final int maxViews = activeViews.length;
final int viewTypeCount = this.viewTypeCount;
final SparseArray<View>[] scrapViews = this.scrapViews;
for (int i = 0; i < viewTypeCount; ++i) {
final SparseArray<View> scrapPile = scrapViews[i];
int size = scrapPile.size();
final int extras = size - maxViews;
size--;
for (int j = 0; j < extras; j++) {
scrapPile.remove(scrapPile.keyAt(size--));
}
}
}
static View retrieveFromScrap(SparseArray<View> scrapViews, int position) {
int size = scrapViews.size();
if (size > 0) {
// See if we still have a view for this position.
for (int i = 0; i < size; i++) {
int fromPosition = scrapViews.keyAt(i);
View view = scrapViews.get(fromPosition);
if (fromPosition == position) {
scrapViews.remove(fromPosition);
return view;
}
}
int index = size - 1;
View r = scrapViews.valueAt(index);
scrapViews.remove(scrapViews.keyAt(index));
return r;
} else {
return null;
}
}
}

@ -0,0 +1,114 @@
package com.android.base.adapter.pager.recycler;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
/**
* A {@link PagerAdapter} which behaves like an {@link android.widget.Adapter} with view types and view recycling.
*
* @see <a href='https://github.com/JakeWharton/salvage'>JakeWharton/salvage</a>
*/
public abstract class RecyclingPagerAdapter extends PagerAdapter {
static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
private final RecycleBin recycleBin;
public RecyclingPagerAdapter() {
this(new RecycleBin());
}
RecyclingPagerAdapter(RecycleBin recycleBin) {
this.recycleBin = recycleBin;
recycleBin.setViewTypeCount(getViewTypeCount());
}
@Override
public void notifyDataSetChanged() {
recycleBin.scrapActiveViews();
super.notifyDataSetChanged();
}
@Override
public final Object instantiateItem(ViewGroup container, int position) {
int viewType = getItemViewType(position);
View view = null;
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
view = recycleBin.getScrapView(position, viewType);
}
view = getView(position, view, container);
container.addView(view);
return view;
}
@Override
public final void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
int viewType = getItemViewType(position);
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
recycleBin.addScrapView(view, position, viewType);
}
}
@Override
public final boolean isViewFromObject(View view, Object object) {
return view == object;
}
/**
* <p>
* Returns the number of types of Views that will be created by
* {@link #getView}. Each type represents a set of views that can be
* converted in {@link #getView}. If the adapter always returns the same
* type of View for all items, this method should return 1.
* </p>
* <p>
* This method will only be called when when the adapter is set on the
* the {@link AdapterView}.
* </p>
*
* @return The number of types of Views that will be created by this adapter
*/
public int getViewTypeCount() {
return 1;
}
/**
* Get the type of View that will be created by {@link #getView} for the specified item.
*
* @param position The position of the item within the adapter's data set whose view type we
* want.
* @return An integer representing the type of View. Two views should share the same type if one
* can be converted to the other in {@link #getView}. Note: Integers must be in the
* range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can
* also be returned.
* @see #IGNORE_ITEM_VIEW_TYPE
*/
@SuppressWarnings("UnusedParameters") // Argument potentially used by subclasses.
public int getItemViewType(int position) {
return 0;
}
/**
* Get a View that displays the data at the specified position in the data set. You can either
* create a View manually or inflate it from an XML layout file. When the View is inflated, the
* parent View (GridView, ListView...) will apply default layout parameters unless you use
* {@link android.view.LayoutInflater#inflate(int, ViewGroup, boolean)}
* to specify a root view and to prevent attachment to the root.
*
* @param position The position of the item within the adapter's data set of the item whose view
* we want.
* @param convertView The old view to reuse, if possible. Note: You should check that this view
* is non-null and of an appropriate type before using. If it is not possible to convert
* this view to display the correct data, this method can create a new view.
* Heterogeneous lists can specify their number of view types, so that this View is
* always of the right type (see {@link #getViewTypeCount()} and
* {@link #getItemViewType(int)}).
* @param parent The parent that this view will eventually be attached to
* @return A View corresponding to the data at the specified position.
*/
public abstract View getView(int position, View convertView, ViewGroup container);
}

@ -0,0 +1,49 @@
package com.android.base.adapter.recycler;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
/**
* @see <a href='http://www.cezcb.com/2018/08/24/PagingWithHeader/'>PagingWithHeader</a>
*/
class AdapterDataObserverProxy extends RecyclerView.AdapterDataObserver {
private RecyclerView.AdapterDataObserver adapterDataObserver;
private int headerCount;
AdapterDataObserverProxy(RecyclerView.AdapterDataObserver adapterDataObserver, int headerCount) {
this.adapterDataObserver = adapterDataObserver;
this.headerCount = headerCount;
}
@Override
public void onChanged() {
adapterDataObserver.onChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
adapterDataObserver.onItemRangeChanged(positionStart + headerCount, itemCount);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
adapterDataObserver.onItemRangeChanged(positionStart + headerCount, itemCount, payload);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
adapterDataObserver.onItemRangeInserted(positionStart + headerCount, itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
adapterDataObserver.onItemRangeRemoved(positionStart + headerCount, itemCount);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
super.onItemRangeMoved(fromPosition + headerCount, toPosition + headerCount, itemCount);
}
}

@ -0,0 +1,255 @@
package com.android.base.adapter.recycler;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.recyclerview.extensions.AsyncDifferConfig;
import android.support.v7.recyclerview.extensions.AsyncListDiffer;
import android.support.v7.util.AdapterListUpdateCallback;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import com.android.base.adapter.DataManager;
import com.android.base.utils.common.Checker;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* RecyclerView 的适配器注意 只有{@link #setDataSource(List, boolean)}才能替换原有数据源的引用
*
* @param <T> 当前列表使用的数据类型
* @author Ztiany
* date : 2015-05-11 22:38
* email: 1169654504@qq.com
*/
@SuppressWarnings("unused")
public abstract class DiffRecyclerAdapter<T, VH extends ViewHolder> extends RecyclerView.Adapter<VH> implements DataManager<T> {
@NonNull
protected Context mContext;
private AsyncListDiffer<T> mAsyncListDiffer;
private final int mHeaderCount;
@SuppressWarnings("WeakerAccess")
public DiffRecyclerAdapter(@NonNull Context context, @NonNull DiffUtil.ItemCallback<T> itemCallback, @Nullable Executor executor, int headerCount) {
mContext = context;
mHeaderCount = headerCount;
AsyncDifferConfig.Builder<T> tBuilder = new AsyncDifferConfig.Builder<>(itemCallback);
if (executor != null) {
tBuilder.setBackgroundThreadExecutor(executor);
}
AsyncDifferConfig<T> differConfig = tBuilder.build();
mAsyncListDiffer = new AsyncListDiffer<>(new AdapterListUpdateCallback(this), differConfig);
}
public DiffRecyclerAdapter(@NonNull Context context, @NonNull DiffUtil.ItemCallback<T> itemCallback, int headerCount) {
this(context, itemCallback, null, headerCount);
}
public DiffRecyclerAdapter(@NonNull Context context, @NonNull DiffUtil.ItemCallback<T> itemCallback) {
this(context, itemCallback, null, 0);
}
@Override
public void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {
if (mHeaderCount != 0) {
super.registerAdapterDataObserver(new AdapterDataObserverProxy(observer, mHeaderCount));
} else {
super.registerAdapterDataObserver(observer);
}
}
@Override
public int getItemCount() {
return getDataSize();
}
@NonNull
@Override
public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
@Override
public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) {
super.onBindViewHolder(holder, position, payloads);
}
@Override
public abstract void onBindViewHolder(@NonNull VH viewHolder, int position);
public void notifyEntryChanged(T t) {
int itemPosition = getItemPosition(t);
if (itemPosition != -1) {
notifyItemChanged(itemPosition);
}
}
@Override
public void add(T element) {
List<T> newList = newList();
newList.add(element);
mAsyncListDiffer.submitList(newList);
}
@Override
public void addAt(int location, T element) {
if (location > getDataSize()) {
location = getDataSize();
}
List<T> newList = newList();
newList.add(location, element);
mAsyncListDiffer.submitList(newList);
}
@Override
public void addItems(List<T> elements) {
if (Checker.isEmpty(elements)) {
return;
}
List<T> newList = newList();
newList.addAll(elements);
mAsyncListDiffer.submitList(newList);
}
@Override
public void addItemsChecked(List<T> elements) {
if (Checker.isEmpty(elements)) {
return;
}
List<T> newList = newList();
for (T element : elements) {
if (element == null) {
continue;
}
newList.remove(element);
}
newList.addAll(elements);
mAsyncListDiffer.submitList(newList);
}
@Override
public void addItemsAt(int location, List<T> elements) {
if (Checker.isEmpty(elements)) {
return;
}
List<T> newList = newList();
if (location > newList.size()) {
location = newList.size();
}
newList.addAll(location, elements);
mAsyncListDiffer.submitList(newList);
}
@Override
public void replace(T oldElement, T newElement) {
if (!contains(oldElement)) {
return;
}
List<T> newList = newList();
newList.set(newList.indexOf(oldElement), newElement);
mAsyncListDiffer.submitList(newList);
}
@Override
public void replaceAt(int index, T element) {
if (getDataSize() > index) {
List<T> newList = newList();
newList.set(index, element);
mAsyncListDiffer.submitList(newList);
}
}
@Override
public void replaceAll(List<T> elements) {
List<T> newList = new ArrayList<>(elements);
mAsyncListDiffer.submitList(newList);
}
@Override
public void remove(T element) {
if (contains(element)) {
List<T> newList = newList();
newList.remove(element);
mAsyncListDiffer.submitList(newList);
}
}
@Override
public void removeItems(List<T> elements) {
if (Checker.isEmpty(elements) || isEmpty() || !getItems().containsAll(elements)) {
return;
}
List<T> newList = newList();
newList.removeAll(elements);
mAsyncListDiffer.submitList(newList);
}
@Override
public void removeAt(int index) {
if (getDataSize() > index) {
List<T> newList = newList();
newList.remove(index);
mAsyncListDiffer.submitList(newList);
}
}
@Override
public T getItem(int position) {
return getDataSize() > position ? getItems().get(position) : null;
}
@Override
public final int getDataSize() {
return getItems() == null ? 0 : getItems().size();
}
@Override
public boolean contains(T element) {
return !isEmpty() && getItems().contains(element);
}
@Override
public void clear() {
mAsyncListDiffer.submitList(null);
}
@Override
public void setDataSource(List<T> elements, boolean notifyDataSetChanged) {
mAsyncListDiffer.submitList(elements);
}
@Override
public List<T> getItems() {
return mAsyncListDiffer.getCurrentList();
}
@Override
public boolean isEmpty() {
return getDataSize() == 0;
}
@Override
public int getItemPosition(T t) {
return isEmpty() ? -1 : getItems().indexOf(t);
}
private List<T> newList() {
if (getItems() == null) {
return new ArrayList<>();
} else {
return new ArrayList<>(getItems());
}
}
}

@ -0,0 +1,5 @@
package com.android.base.adapter.recycler;
public interface HeaderSize {
int getHeaderSize();
}

@ -0,0 +1,35 @@
package com.android.base.adapter.recycler
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.android.base.kotlin.KtViewHolder
/**
* @author Ztiany
* Email: 1169654504@qq.com
* Date : 2017-09-13 15:33
*/
abstract class ItemViewBinder<T, VH : RecyclerView.ViewHolder> : me.drakeet.multitype.ItemViewBinder<T, VH>() {
protected val dataManager: MultiTypeAdapter
get() = adapter as MultiTypeAdapter
}
abstract class SimpleItemViewBinder<T> : ItemViewBinder<T, KtViewHolder>() {
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): KtViewHolder {
val layout = provideLayout(inflater, parent)
val itemView = if (layout is Int) {
inflater.inflate(layout, parent, false)
} else
layout as View
return KtViewHolder(itemView)
}
/**provide a layout id or a View*/
abstract fun provideLayout(inflater: LayoutInflater, parent: ViewGroup): Any
}

@ -0,0 +1,166 @@
package com.android.base.adapter.recycler;
import android.content.Context;
import android.support.annotation.NonNull;
import com.android.base.adapter.DataManager;
import java.util.ArrayList;
import java.util.List;
import me.drakeet.multitype.TypePool;
/**
* @see <a href='https://github.com/drakeet/MultiType'>drakeet/MultiTypeAdapter</a>
*/
public class MultiTypeAdapter extends me.drakeet.multitype.MultiTypeAdapter implements DataManager<Object> {
protected final Context mContext;
private RecyclerDataManagerImpl<Object> mRecyclerDataManager;
public MultiTypeAdapter(Context context) {
super();
mContext = context;
ArrayList<Object> objects = new ArrayList<>();
mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this);
super.setItems(objects);
}
public MultiTypeAdapter(Context context, @NonNull List<?> items) {
super();
mContext = context;
ArrayList<Object> objects = new ArrayList<>(items);
mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this);
super.setItems(objects);
}
public MultiTypeAdapter(Context context, @NonNull List<?> items, int initialCapacity) {
super(items, initialCapacity);
mContext = context;
ArrayList<Object> objects = new ArrayList<>(items);
mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this);
super.setItems(objects);
}
public MultiTypeAdapter(Context context, @NonNull List<?> items, @NonNull TypePool pool) {
super(items, pool);
mContext = context;
ArrayList<Object> objects = new ArrayList<>(items);
mRecyclerDataManager = new RecyclerDataManagerImpl<>(objects, this);
super.setItems(objects);
}
@SuppressWarnings("unused")
public void notifyEntryChanged(Object entry) {
int itemPosition = getItemPosition(entry);
if (itemPosition != -1) {
notifyItemChanged(itemPosition);
}
}
@Override
public void add(Object elem) {
mRecyclerDataManager.add(elem);
}
@Override
public void addAt(int location, Object elem) {
mRecyclerDataManager.addAt(location, elem);
}
@Override
public void addItems(List<Object> elements) {
mRecyclerDataManager.addItems(elements);
}
@Override
public void addItemsChecked(List<Object> elements) {
mRecyclerDataManager.addItemsChecked(elements);
}
@Override
public void addItemsAt(int location, List<Object> elements) {
mRecyclerDataManager.addItemsAt(location, elements);
}
@Override
public void replace(Object oldElem, Object newElem) {
mRecyclerDataManager.replace(oldElem, newElem);
}
@Override
public void replaceAt(int index, Object elem) {
mRecyclerDataManager.replaceAt(index, elem);
}
@Override
public void replaceAll(List<Object> elements) {
mRecyclerDataManager.replaceAll(elements);
}
@Override
public void setDataSource(List<Object> newDataSource, boolean notifyDataSetChanged) {
super.setItems(newDataSource);
mRecyclerDataManager.setDataSource(newDataSource, notifyDataSetChanged);
}
@Override
public void remove(Object elem) {
mRecyclerDataManager.remove(elem);
}
@Override
public void removeAt(int index) {
mRecyclerDataManager.removeAt(index);
}
@Override
public void removeItems(List<Object> elements) {
mRecyclerDataManager.removeItems(elements);
}
@Override
public Object getItem(int position) {
return mRecyclerDataManager.getItem(position);
}
@Override
public int getDataSize() {
return mRecyclerDataManager.getDataSize();
}
@Override
public boolean contains(Object elem) {
return mRecyclerDataManager.contains(elem);
}
@Override
public boolean isEmpty() {
return mRecyclerDataManager.isEmpty();
}
@Override
public void clear() {
mRecyclerDataManager.clear();
}
@NonNull
@Override
public List<Object> getItems() {
return mRecyclerDataManager.getItems();
}
@Override
public int getItemPosition(Object o) {
return mRecyclerDataManager.getItemPosition(o);
}
@Override
public void setItems(@NonNull List<?> items) {
ArrayList<Object> objects = new ArrayList<>(items);
super.setItems(objects);
mRecyclerDataManager.setDataSource(objects, true);
}
}

@ -0,0 +1,164 @@
package com.android.base.adapter.recycler;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import com.android.base.adapter.DataManager;
import java.util.List;
/**
* RecyclerView 的适配器
* 注意 只有setDataSource才能替换原有数据源的引用
*
* @param <T> 当前列表使用的数据类型
* @author Ztiany
* date : 2015-05-11 22:38
* email: 1169654504@qq.com
*/
public abstract class RecyclerAdapter<T, VH extends ViewHolder> extends RecyclerView.Adapter<VH> implements DataManager<T> {
private RecyclerDataManagerImpl<T> mDataManager;
@NonNull
protected Context mContext;
public RecyclerAdapter(@NonNull Context context, List<T> data) {
mDataManager = new RecyclerDataManagerImpl<>(data, this);
this.mContext = context;
}
public RecyclerAdapter(@NonNull Context context) {
this(context, null);
}
@Override
public int getItemCount() {
return getDataSize();
}
@NonNull
@Override
public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
@Override
public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) {
super.onBindViewHolder(holder, position, payloads);
}
@Override
public abstract void onBindViewHolder(@NonNull VH viewHolder, int position);
public void notifyEntryChanged(T t) {
int itemPosition = getItemPosition(t);
if (itemPosition != -1) {
notifyItemChanged(itemPosition);
}
}
///////////////////////////////////////////////////////////////////////////
// DataManager
///////////////////////////////////////////////////////////////////////////
@Override
public void add(T elem) {
mDataManager.add(elem);
}
@Override
public void addAt(int location, T elem) {
mDataManager.addAt(location, elem);
}
@Override
public void addItems(List<T> elements) {
mDataManager.addItems(elements);
}
@Override
public void addItemsChecked(List<T> elements) {
mDataManager.addItemsChecked(elements);
}
@Override
public void addItemsAt(int location, List<T> elements) {
mDataManager.addItemsAt(location, elements);
}
@Override
public void replace(T oldElem, T newElem) {
mDataManager.replace(oldElem, newElem);
}
@Override
public void replaceAt(int index, T elem) {
mDataManager.replaceAt(index, elem);
}
@Override
public void replaceAll(List<T> elements) {
mDataManager.replaceAll(elements);
}
@Override
public void remove(T elem) {
mDataManager.remove(elem);
}
@Override
public void removeItems(List<T> elements) {
mDataManager.removeItems(elements);
}
@Override
public void removeAt(int index) {
mDataManager.removeAt(index);
}
@Override
public T getItem(int position) {
return mDataManager.getItem(position);
}
@Override
public final int getDataSize() {
return mDataManager.getDataSize();
}
@Override
public boolean contains(T elem) {
return mDataManager.contains(elem);
}
@Override
public void clear() {
mDataManager.clear();
}
@Override
public void setDataSource(List<T> elements, boolean notifyDataSetChanged) {
mDataManager.setDataSource(elements, notifyDataSetChanged);
}
@Override
public List<T> getItems() {
return mDataManager.getItems();
}
protected final void setHeaderSize(HeaderSize headerSize) {
mDataManager.setHeaderSize(headerSize);
}
@Override
public boolean isEmpty() {
return mDataManager.isEmpty();
}
@Override
public int getItemPosition(T t) {
return mDataManager.getItemPosition(t);
}
}

@ -0,0 +1,248 @@
package com.android.base.adapter.recycler;
import android.support.v7.widget.RecyclerView;
import com.android.base.adapter.DataManager;
import com.android.base.utils.common.Checker;
import java.util.ArrayList;
import java.util.List;
final class RecyclerDataManagerImpl<T> implements DataManager<T> {
private List<T> mData;
private RecyclerView.Adapter mAdapter;
private HeaderSize mHeaderSize;
RecyclerDataManagerImpl(List<T> tList, RecyclerView.Adapter adapter) {
mData = tList;
mAdapter = adapter;
}
@Override
public boolean isEmpty() {
return getDataSize() == 0;
}
@Override
public void add(T element) {
addAt(getDataSize(), element);
}
@Override
public void addAt(int location, T element) {
if (mData != null) {
if (mData.isEmpty()) {
mData.add(element);
notifyItemInserted(getHeaderSize());
} else {
int size = mData.size();
int lastIndex = (location >= size ? size : location) + getHeaderSize();
mData.add(location, element);
notifyItemInserted(lastIndex);
}
} else {
mData = new ArrayList<>();
mData.add(element);
notifyDataSetChanged();
}
}
@Override
public void addItems(List<T> elements) {
addItemsAt(getDataSize(), elements);
}
@Override
public void addItemsChecked(List<T> elements) {
if (Checker.isEmpty(elements)) {
return;
}
if (mData == null) {
addItems(elements);
return;
}
boolean hasRemovedElements = false;
for (T element : elements) {
if (element == null) {
continue;
}
if (mData.contains(element)) {
mData.remove(element);
if (!hasRemovedElements) {
hasRemovedElements = true;
}
}
}
if (hasRemovedElements) {
mData.addAll(elements);
notifyDataSetChanged();
} else {
addItems(elements);
}
}
@Override
public void addItemsAt(int location, List<T> elements) {
if (Checker.isEmpty(elements)) {
return;
}
if (this.mData != null) {
if (mData.isEmpty()) {
mData.addAll(elements);
notifyItemRangeInserted(getHeaderSize(), elements.size());
} else {
int size = mData.size();
int lastIndex = (location >= size ? size : location) + getHeaderSize();
this.mData.addAll(location, elements);
notifyItemRangeInserted(lastIndex, elements.size());
}
} else {
this.mData = new ArrayList<>(elements.size());
mData.addAll(elements);
notifyDataSetChanged();
}
}
@Override
public void replace(T oldElem, T newElem) {
if (mData != null && mData.contains(newElem)) {
replaceAt(mData.indexOf(oldElem), newElem);
}
}
@Override
public void replaceAt(int index, T element) {
if (getDataSize() > index) {
mData.set(index, element);
notifyItemChanged(index + getHeaderSize());
}
}
@Override
public void replaceAll(List<T> elements) {
if (mData == null) {
mData = new ArrayList<>();
}
mData.clear();
if (elements != null) {
mData.addAll(elements);
}
notifyDataSetChanged();
}
@Override
public void setDataSource(List<T> newDataSource, boolean notifyDataSetChanged) {
mData = newDataSource;
if (notifyDataSetChanged) {
notifyDataSetChanged();
}
}
@Override
public void remove(T element) {
if (mData == null || mData.isEmpty()) {
return;
}
if (mData.contains(element)) {
int index = mData.indexOf(element) + getHeaderSize();
mData.remove(element);
notifyItemRemoved(index);
}
}
@Override
public void removeAt(int index) {
if (getDataSize() > index) {
mData.remove(index);
notifyItemRemoved(index + getHeaderSize());
}
}
@Override
public void removeItems(List<T> elements) {
if (!Checker.isEmpty(elements) && mData != null && mData.containsAll(elements)) {
mData.removeAll(elements);
notifyDataSetChanged();
}
}
@Override
public T getItem(int position) {
position = position - getHeaderSize();
if (position >= 0 && getDataSize() > position) {
return mData.get(position);
}
return null;
}
@Override
public final int getDataSize() {
return mData == null ? 0 : mData.size();
}
@Override
public boolean contains(T element) {
return mData != null && mData.contains(element);
}
@Override
public void clear() {
if (mData != null) {
mData.clear();
notifyDataSetChanged();
}
}
@Override
public int getItemPosition(T t) {
List<T> items = getItems();
if (items == null) {
return -1;
}
return items.indexOf(t);
}
@Override
public List<T> getItems() {
return mData;
}
///////////////////////////////////////////////////////////////////////////
// Size
///////////////////////////////////////////////////////////////////////////
private int getHeaderSize() {
return mHeaderSize == null ? 0 : mHeaderSize.getHeaderSize();
}
///////////////////////////////////////////////////////////////////////////
// Adapter Call
///////////////////////////////////////////////////////////////////////////
private void notifyItemChanged(int position) {
mAdapter.notifyItemChanged(position);
}
private void notifyDataSetChanged() {
mAdapter.notifyDataSetChanged();
}
private void notifyItemInserted(int position) {
mAdapter.notifyItemInserted(position);
}
private void notifyItemRangeInserted(int position, int size) {
mAdapter.notifyItemRangeInserted(position, size);
}
private void notifyItemRemoved(int index) {
mAdapter.notifyItemRemoved(index);
}
void setHeaderSize(HeaderSize headerSize) {
mHeaderSize = headerSize;
}
}

@ -0,0 +1,59 @@
package com.android.base.adapter.recycler
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlin.reflect.KClass
import kotlin.reflect.full.primaryConstructor
/**
* A simple way to build a simple list. If you want to build a multi type list, perhaps you need to use [MultiTypeAdapter].
*
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2019-01-15 11:41
*/
abstract class SimpleRecyclerAdapter<T, VH : ViewHolder>(context: Context, data: List<T>? = null) : RecyclerAdapter<T, VH>(context, data) {
private var mLayoutInflater: LayoutInflater = LayoutInflater.from(mContext)
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
val layout = provideLayout(parent, viewType)
val itemView = if (layout is Int) {
mLayoutInflater.inflate(layout, parent, false)
} else
layout as View
return provideViewHolder(itemView)
}
@Suppress("UNCHECKED_CAST")
open fun provideViewHolder(itemView: View): VH {
return (this::class.supertypes[0].arguments[1].type?.classifier as? KClass<VH>)?.primaryConstructor?.call(itemView)
?: throw IllegalArgumentException("need primaryConstructor, and arguments is (View)")
}
/**provide a layout id or a View*/
abstract fun provideLayout(parent: ViewGroup, viewType: Int): Any
override fun getItemViewType(position: Int): Int {
return TYPE_ITEM
}
override fun onBindViewHolder(viewHolder: VH, position: Int) {
if (viewHolder.itemViewType == TYPE_ITEM) {
bind(viewHolder, getItem(position))
} else {
bindOtherTypes(viewHolder, position)
}
}
protected abstract fun bind(viewHolder: VH, item: T)
protected open fun bindOtherTypes(viewHolder: ViewHolder, position: Int) {}
companion object {
protected const val TYPE_ITEM = 0
}
}

@ -0,0 +1,22 @@
package com.android.base.adapter.recycler;
import android.view.View;
import com.android.base.adapter.ItemHelper;
public class SmartViewHolder extends ViewHolder {
@SuppressWarnings("all")
protected final ItemHelper mHelper;
public SmartViewHolder(View itemView) {
super(itemView);
mHelper = new ItemHelper(itemView);
}
public ItemHelper helper() {
return mHelper;
}
}

@ -0,0 +1,28 @@
package com.android.base.adapter.recycler;
import android.content.Context;
import android.support.annotation.IdRes;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
protected Context getContext() {
return itemView.getContext();
}
public <V extends View> V findView(@IdRes int viewId) {
return itemView.findViewById(viewId);
}
public ViewHolder setItemClickListener(View.OnClickListener onClickListener) {
itemView.setOnClickListener(onClickListener);
return this;
}
}

@ -0,0 +1,116 @@
package com.android.base.app;
import android.app.Application;
import android.content.Context;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
import com.android.base.receiver.NetStateReceiver;
import com.android.base.utils.BaseUtils;
import com.blankj.utilcode.util.AppUtils;
import com.blankj.utilcode.util.Utils;
import java.util.concurrent.atomic.AtomicBoolean;
import io.reactivex.Flowable;
import io.reactivex.processors.BehaviorProcessor;
import timber.log.Timber;
/**
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2018-10-12 18:19
*/
@SuppressWarnings("WeakerAccess,unused")
public final class ApplicationDelegate {
private Application mApplication;
private CrashHandler mCrashHandler;
private BehaviorProcessor<Boolean> mAppStatus;
private AtomicBoolean mOnCreateCalled = new AtomicBoolean(false);
private AtomicBoolean mOnAttachBaseCalled = new AtomicBoolean(false);
ApplicationDelegate() {
}
public void attachBaseContext(@SuppressWarnings("unused") Context base) {
if (!mOnAttachBaseCalled.compareAndSet(false, true)) {
throw new IllegalStateException("Can only be called once");
}
//no op
}
public void onCreate(Application application) {
if (!mOnCreateCalled.compareAndSet(false, true)) {
throw new IllegalStateException("Can only be called once");
}
mApplication = application;
//工具类初始化
BaseUtils.init(application);
//异常日志记录
mCrashHandler = CrashHandler.register(application);
//网络状态
listenNetworkState();
//App前台后台
listenActivityLifecycleCallbacks();
}
public void onTerminate() {
//no op
}
public void onConfigurationChanged(Configuration newConfig) {
//no op
}
public void onTrimMemory(int level) {
//no op
}
public void onLowMemory() {
//no op
}
private void listenNetworkState() {
NetStateReceiver netStateReceiver = new NetStateReceiver();
mApplication.registerReceiver(netStateReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
private void listenActivityLifecycleCallbacks() {
mAppStatus = BehaviorProcessor.create();
AppUtils.registerAppStatusChangedListener(this, new Utils.OnAppStatusChangedListener() {
@Override
public void onForeground() {
Timber.d("app进入前台");
mAppStatus.onNext(true);
}
@Override
public void onBackground() {
Timber.d("app进入后台");
mAppStatus.onNext(false);
}
});
}
/**
* 获取可观察的 app 生命周期
*/
Flowable<Boolean> appAppState() {
return mAppStatus;
}
void setCrashProcessor(BaseKit.CrashProcessor crashProcessor) {
if (mCrashHandler != null) {
mCrashHandler.setCrashProcessor(crashProcessor);
}
}
Application getApplication() {
return mApplication;
}
}

@ -0,0 +1,46 @@
package com.android.base.app;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
public class BaseAppContext extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
BaseKit.get().getApplicationDelegate().attachBaseContext(base);
}
@Override
public void onCreate() {
super.onCreate();
BaseKit.get().getApplicationDelegate().onCreate(this);
}
@Override
public void onLowMemory() {
super.onLowMemory();
BaseKit.get().getApplicationDelegate().onLowMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
BaseKit.get().getApplicationDelegate().onTrimMemory(level);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
BaseKit.get().getApplicationDelegate().onConfigurationChanged(newConfig);
}
@Override
public void onTerminate() {
super.onTerminate();
BaseKit.get().getApplicationDelegate().onTerminate();
}
}

@ -0,0 +1,208 @@
package com.android.base.app;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import com.android.base.app.dagger.Injectable;
import com.android.base.app.fragment.FragmentConfig;
import com.android.base.app.fragment.LoadingViewFactory;
import com.android.base.app.ui.PageNumber;
import com.android.base.app.ui.RefreshLoadViewFactory;
import com.android.base.app.ui.RefreshViewFactory;
import com.android.base.interfaces.adapter.ActivityLifecycleCallbacksAdapter;
import com.android.base.receiver.NetworkState;
import com.blankj.utilcode.util.ActivityUtils;
import com.blankj.utilcode.util.AppUtils;
import dagger.android.AndroidInjection;
import dagger.android.support.AndroidSupportInjection;
import io.reactivex.Flowable;
/**
* 基础库工具
*
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2018-04-16 17:12
*/
@UiThread
public final class BaseKit {
private static final BaseKit ONLY_BASE = new BaseKit();
private BaseKit() {
mApplicationDelegate = new ApplicationDelegate();
}
public static BaseKit get() {
return ONLY_BASE;
}
/**
* LoadingView
*/
private LoadingViewFactory mLoadingViewFactory;
/**
* Application lifecycle delegate
*/
private ApplicationDelegate mApplicationDelegate;
/**
* 错误类型检查
*/
private ErrorClassifier mErrorClassifier;
/**
* 获取 Application 代理
*/
@SuppressWarnings("WeakerAccess")
public ApplicationDelegate getApplicationDelegate() {
return mApplicationDelegate;
}
public BaseKit registerLoadingFactory(LoadingViewFactory loadingViewFactory) {
if (mLoadingViewFactory != null) {
throw new UnsupportedOperationException("LoadingViewFactory had already set");
}
mLoadingViewFactory = loadingViewFactory;
return this;
}
public LoadingViewFactory getLoadingViewFactory() {
if (mLoadingViewFactory == null) {
throw new NullPointerException("you have not set the LoadingViewFactory by AndroidBase");
}
return mLoadingViewFactory;
}
public Flowable<NetworkState> networkState() {
return NetworkState.observableState();
}
public interface CrashProcessor {
void uncaughtException(Thread thread, Throwable ex);
}
public BaseKit setCrashProcessor(CrashProcessor crashProcessor) {
mApplicationDelegate.setCrashProcessor(crashProcessor);
return this;
}
public BaseKit setDefaultPageStart(int pageStart) {
PageNumber.setDefaultPageStart(pageStart);
return this;
}
public BaseKit setDefaultPageSize(int defaultPageSize) {
PageNumber.setDefaultPageSize(defaultPageSize);
return this;
}
/**
* {@link com.android.base.app.fragment.Fragments } 设置一个默认的容器 id在使用 其相关方法而没有传入特定的容器 id 则使用默认的容器 id
*
* @param defaultContainerId 容器id
*/
public BaseKit setDefaultFragmentContainerId(int defaultContainerId) {
FragmentConfig.setDefaultContainerId(defaultContainerId);
return this;
}
public BaseKit enableAutoInject() {
Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity instanceof Injectable) {
if (((Injectable) activity).enableInject()) {
AndroidInjection.inject(activity);
if (activity instanceof FragmentActivity) {
handedFragmentInject((FragmentActivity) activity);
}
}
}
}
private void handedFragmentInject(FragmentActivity activity) {
activity.getSupportFragmentManager().registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) {
if (f instanceof Injectable) {
if (((Injectable) f).enableInject()) {
AndroidSupportInjection.inject(f);
}
}
}
}, true);
}
};
mApplicationDelegate.getApplication().registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
return this;
}
/**
* 获取可观察的 app 生命周期
*/
public Flowable<Boolean> appState() {
return mApplicationDelegate.appAppState();
}
/**
* 获取当前resume的Activity
*
* @return activity
*/
@Nullable
public Activity getTopActivity() {
return ActivityUtils.getTopActivity();
}
/**
* App是否在前台运行
*
* @return true 表示App在前台运行
*/
public boolean isForeground() {
return AppUtils.isAppForeground();
}
public interface ErrorClassifier {
boolean isNetworkError(Throwable throwable);
boolean isServerError(Throwable throwable);
}
@SuppressWarnings("all")
public BaseKit setErrorClassifier(ErrorClassifier errorClassifier) {
if (mErrorClassifier != null) {
throw new UnsupportedOperationException("ErrorClassifier had already set");
}
mErrorClassifier = errorClassifier;
return this;
}
public ErrorClassifier errorClassifier() {
return mErrorClassifier;
}
public BaseKit registerRefreshLoadViewFactory(RefreshLoadViewFactory.Factory factory) {
RefreshLoadViewFactory.registerFactory(factory);
return this;
}
public BaseKit registerRefreshViewFactory(RefreshViewFactory.Factory factory) {
RefreshViewFactory.registerFactory(factory);
return this;
}
}

@ -0,0 +1,113 @@
package com.android.base.app;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import android.os.Process;
import com.blankj.utilcode.util.AppUtils;
import java.io.File;
import java.io.PrintStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import timber.log.Timber;
/**
* 全局异常处理
*/
final class CrashHandler implements Thread.UncaughtExceptionHandler {
private Context mContext;
private BaseKit.CrashProcessor mCrashProcessor;
public static CrashHandler register(Application application) {
CrashHandler crashHandler = new CrashHandler(application);
Thread.setDefaultUncaughtExceptionHandler(crashHandler);
return crashHandler;
}
void setCrashProcessor(BaseKit.CrashProcessor crashProcessor) {
mCrashProcessor = crashProcessor;
}
private CrashHandler(Context context) {
this.mContext = context;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (mCrashProcessor != null) {
mCrashProcessor.uncaughtException(thread, ex);
} else {
// 收集异常信息,写入到sd卡
restoreCrash(thread, ex);
//退出
killProcess();
}
}
private void restoreCrash(@SuppressWarnings("unused") Thread thread, Throwable ex) {
ex.printStackTrace(System.err);
// 收集异常信息,写入到sd卡
File dir = new File(mContext.getExternalFilesDir(null) + File.separator + "crash");
if (!dir.exists()) {
boolean mkdirs = dir.mkdirs();
if (!mkdirs) {
Timber.e("CrashHandler create dir fail");
return;
}
}
try {
@SuppressLint("SimpleDateFormat")
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String name = dateFormat.format(new Date(System.currentTimeMillis())) + ".log";
File fileName = new File(dir, name);
if (!fileName.exists()) {
@SuppressWarnings("unused")
boolean newFile = fileName.createNewFile();
}
PrintStream err = new PrintStream(fileName);
err.println("--------------------------------AppInfo--------------------------------");
err.println("AndroidVersion: " + AppUtils.getAppVersionName());
err.println();
err.println();
err.println("--------------------------------SystemInfo:--------------------------------");
err.println("Product: " + android.os.Build.PRODUCT);
err.println("CPU_ABI: " + android.os.Build.CPU_ABI);
err.println("TAGS: " + android.os.Build.TAGS);
err.println("VERSION_CODES.BASE:" + android.os.Build.VERSION_CODES.BASE);
err.println("MODEL: " + android.os.Build.MODEL);
err.println("SDK: " + Build.VERSION.SDK_INT);
err.println("VERSION.RELEASE: " + android.os.Build.VERSION.RELEASE);
err.println("DEVICE: " + android.os.Build.DEVICE);
err.println("DISPLAY: " + android.os.Build.DISPLAY);
err.println("BRAND: " + android.os.Build.BRAND);
err.println("BOARD: " + android.os.Build.BOARD);
err.println("FINGERPRINT: " + android.os.Build.FINGERPRINT);
err.println("ID: " + android.os.Build.ID);
err.println("MANUFACTURER: " + android.os.Build.MANUFACTURER);
err.println("USER: " + android.os.Build.USER);
err.println();
err.println();
err.println("--------------------------------CrashContent--------------------------------");
ex.printStackTrace(err);
err.println();
} catch (Exception e) {
e.printStackTrace();
}
}
private void killProcess() {
Process.killProcess(Process.myPid());
}
}

@ -0,0 +1,79 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.base.app.aac
import android.arch.lifecycle.DefaultLifecycleObserver
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleOwner
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class AutoClearedValue<T : Any>(
lifecycle: Lifecycle,
private val _event: Lifecycle.Event,
private val onCleared: (() -> Unit)?
) : ReadWriteProperty<Fragment, T> {
private var _value: T? = null
init {
lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onPause(owner: LifecycleOwner) {
clearValue(Lifecycle.Event.ON_PAUSE)
}
override fun onStop(owner: LifecycleOwner) {
clearValue(Lifecycle.Event.ON_STOP)
}
override fun onDestroy(owner: LifecycleOwner) {
clearValue(Lifecycle.Event.ON_DESTROY)
}
})
}
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
return _value
?: throw IllegalStateException("should never call auto-cleared-value get when it might not be available")
}
override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) {
_value = value
}
private fun clearValue(event: Lifecycle.Event) {
if (_event == event) {
_value = null
onCleared?.invoke()
}
}
}
fun <T : Any> Fragment.autoCleared(
event: Lifecycle.Event = Lifecycle.Event.ON_DESTROY,
onCleared: (() -> Unit)? = null
) = AutoClearedValue<T>(this.lifecycle, event, onCleared)
fun <T : Any> FragmentActivity.autoCleared(
event: Lifecycle.Event = Lifecycle.Event.ON_DESTROY,
onCleared: (() -> Unit)? = null
) = AutoClearedValue<T>(this.lifecycle, event, onCleared)

@ -0,0 +1,225 @@
package com.android.base.app.aac
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import com.android.base.data.Resource
import com.android.base.rx.subscribeIgnoreError
import com.github.dmstocking.optional.java.util.Optional
import com.uber.autodispose.*
fun <T> ObservableSubscribeProxy<T>.toLiveData(): LiveData<T> {
val liveData = MutableLiveData<T>()
this.subscribeIgnoreError {
liveData.postValue(it)
}
return liveData
}
fun <T> FlowableSubscribeProxy<T>.toLiveData(): LiveData<T> {
val liveData = MutableLiveData<T>()
this.subscribeIgnoreError {
liveData.postValue(it)
}
return liveData
}
fun <T> SingleSubscribeProxy<T>.toLiveData(): LiveData<T> {
val liveData = MutableLiveData<T>()
this.subscribeIgnoreError {
liveData.postValue(it)
}
return liveData
}
fun <T> MaybeSubscribeProxy<T>.toLiveData(): LiveData<T> {
val liveData = MutableLiveData<T>()
this.subscribeIgnoreError {
liveData.postValue(it)
}
return liveData
}
//-----------------------------------------------------------------------------------------
fun <T> ObservableSubscribeProxy<T>.subscribeWithLiveData(liveData: MutableLiveData<Resource<T>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(it))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T, R> ObservableSubscribeProxy<T>.subscribeWithLiveData(liveData: MutableLiveData<Resource<R>>, map: (T) -> R) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(map(it)))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T> ObservableSubscribeProxy<Optional<T>>.subscribeOptionalWithLiveData(liveData: MutableLiveData<Resource<T>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(it.orElse(null)))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T, R> ObservableSubscribeProxy<Optional<T>>.subscribeOptionalWithLiveData(liveData: MutableLiveData<Resource<R>>, map: (T?) -> R?) {
liveData.postValue(Resource.loading())
this.subscribe(
{
val value = map(it.orElse(null))
liveData.postValue(Resource.success(value))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T> FlowableSubscribeProxy<T>.subscribeWithLiveData(liveData: MutableLiveData<Resource<T>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(it))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T, R> FlowableSubscribeProxy<T>.subscribeWithLiveData(liveData: MutableLiveData<Resource<R>>, map: (T) -> R) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(map(it)))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T> FlowableSubscribeProxy<Optional<T>>.subscribeOptionalWithLiveData(liveData: MutableLiveData<Resource<T>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(it.orElse(null)))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T, R> FlowableSubscribeProxy<Optional<T>>.subscribeOptionalWithLiveData(liveData: MutableLiveData<Resource<R>>, map: (T?) -> R?) {
liveData.postValue(Resource.loading())
this.subscribe(
{
val value = map(it.orElse(null))
liveData.postValue(Resource.success(value))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun CompletableSubscribeProxy.subscribeWithLiveData(liveData: MutableLiveData<Resource<Any>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success())
},
{
liveData.postValue(Resource.error(it))
}
)
}
//-----------------------------------------------------------------------------------------
fun <T> ObservableSubscribeProxy<T>.toResourceLiveData(): LiveData<Resource<T>> {
val mutableLiveData = MutableLiveData<Resource<T>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success(it))
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}
fun <T> ObservableSubscribeProxy<Optional<T>>.optionalToResourceLiveData(): LiveData<Resource<T>> {
val mutableLiveData = MutableLiveData<Resource<T>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success(it.orElse(null)))
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}
fun <T> FlowableSubscribeProxy<T>.toResourceLiveData(): LiveData<Resource<T>> {
val mutableLiveData = MutableLiveData<Resource<T>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success(it))
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}
fun <T> FlowableSubscribeProxy<Optional<T>>.optionalToResourceLiveData(): LiveData<Resource<T>> {
val mutableLiveData = MutableLiveData<Resource<T>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success(it.orElse(null)))
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}
fun CompletableSubscribeProxy.toResourceLiveData(): LiveData<Resource<Any>> {
val mutableLiveData = MutableLiveData<Resource<Any>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success())
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}

@ -0,0 +1,227 @@
package com.android.base.app.aac
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import com.android.base.data.Resource
import com.android.base.rx.subscribeIgnoreError
import com.github.dmstocking.optional.java.util.Optional
import io.reactivex.*
//-----------------------------------------------------------------------------------------
fun <T> Observable<T>.subscribeWithLiveData(liveData: MutableLiveData<Resource<T>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(it))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T, R> Observable<T>.subscribeWithLiveData(liveData: MutableLiveData<Resource<R>>, map: (T) -> R) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(map(it)))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T> Observable<Optional<T>>.subscribeOptionalWithLiveData(liveData: MutableLiveData<Resource<T>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(it.orElse(null)))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T, R> Observable<Optional<T>>.subscribeOptionalWithLiveData(liveData: MutableLiveData<Resource<R>>, map: (T?) -> R?) {
liveData.postValue(Resource.loading())
this.subscribe(
{
val value = map(it.orElse(null))
liveData.postValue(Resource.success(value))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T> Flowable<T>.subscribeWithLiveData(liveData: MutableLiveData<Resource<T>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(it))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T, R> Flowable<T>.subscribeWithLiveData(liveData: MutableLiveData<Resource<R>>, map: (T) -> R) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(map(it)))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T> Flowable<Optional<T>>.subscribeOptionalWithLiveData(liveData: MutableLiveData<Resource<T>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success(it.orElse(null)))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun <T, R> Flowable<Optional<T>>.subscribeOptionalWithLiveData(liveData: MutableLiveData<Resource<R>>, map: (T?) -> R?) {
liveData.postValue(Resource.loading())
this.subscribe(
{
val value = map(it.orElse(null))
liveData.postValue(Resource.success(value))
},
{
liveData.postValue(Resource.error(it))
}
)
}
fun Completable.subscribeWithLiveData(liveData: MutableLiveData<Resource<Any>>) {
liveData.postValue(Resource.loading())
this.subscribe(
{
liveData.postValue(Resource.success())
},
{
liveData.postValue(Resource.error(it))
}
)
}
//-----------------------------------------------------------------------------------------
fun <T> Observable<T>.toResourceLiveData(): LiveData<Resource<T>> {
val mutableLiveData = MutableLiveData<Resource<T>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success(it))
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}
fun <T> Observable<Optional<T>>.optionalToResourceLiveData(): LiveData<Resource<T>> {
val mutableLiveData = MutableLiveData<Resource<T>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success(it.orElse(null)))
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}
fun <T> Flowable<T>.toResourceLiveData(): LiveData<Resource<T>> {
val mutableLiveData = MutableLiveData<Resource<T>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success(it))
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}
fun <T> Flowable<Optional<T>>.optionalToResourceLiveData(): LiveData<Resource<T>> {
val mutableLiveData = MutableLiveData<Resource<T>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success(it.orElse(null)))
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}
fun Completable.toResourceLiveData(): LiveData<Resource<Any>> {
val mutableLiveData = MutableLiveData<Resource<Any>>()
mutableLiveData.value = Resource.loading()
subscribe(
{
mutableLiveData.postValue(Resource.success())
},
{
mutableLiveData.postValue(Resource.error(it))
}
)
return mutableLiveData
}
//-----------------------------------------------------------------------------------------
fun <T> Observable<T>.toLiveData(): LiveData<T> {
val liveData = MutableLiveData<T>()
this.subscribeIgnoreError {
liveData.postValue(it)
}
return liveData
}
fun <T> Flowable<T>.toLiveData(): LiveData<T> {
val liveData = MutableLiveData<T>()
this.subscribeIgnoreError {
liveData.postValue(it)
}
return liveData
}
fun <T> Single<T>.toLiveData(): LiveData<T> {
val liveData = MutableLiveData<T>()
this.subscribeIgnoreError {
liveData.postValue(it)
}
return liveData
}
fun <T> Maybe<T>.toLiveData(): LiveData<T> {
val liveData = MutableLiveData<T>()
this.subscribeIgnoreError {
liveData.postValue(it)
}
return liveData
}

@ -0,0 +1,36 @@
package com.android.base.app.aac
import android.arch.lifecycle.LifecycleOwner
import android.arch.lifecycle.MediatorLiveData
import android.arch.lifecycle.Observer
/** https://github.com/Shopify/livedata-ktx */
class SingleLiveData<T> : MediatorLiveData<T>() {
private var _version = 0
private val version: Int get() = _version
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
val observerVersion = version
super.observe(owner, Observer {
if (observerVersion < version) {
observer.onChanged(it)
}
})
}
override fun observeForever(observer: Observer<T>) {
val observeSinceVersion = version
super.observeForever {
if (version > observeSinceVersion) {
observer.onChanged(it)
}
}
}
override fun setValue(value: T?) {
_version++
super.setValue(value)
}
}

@ -0,0 +1,61 @@
package com.android.base.app.aac;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Observer;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
* <p>
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
* <p>
* Note that only one observer is going to be notified of changes.
*
* @see <a href='https://github.com/googlesamples/android-architecture/blob/dev-todo-mvvm-live/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/SingleLiveEvent.java'>SingleLiveEvent</a>
*/
public class SingleLiveEvent<T> extends MutableLiveData<T> {
private static final String TAG = "SingleLiveEvent";
private final AtomicBoolean mPending = new AtomicBoolean(false);
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull final Observer<T> observer) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
}
// Observe the internal MutableLiveData
super.observe(owner, t -> {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
});
}
@MainThread
public void setValue(@Nullable T t) {
mPending.set(true);
super.setValue(t);
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
}

@ -0,0 +1,68 @@
package com.android.base.app.activity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Activity生命周期代理
*
* @author Ztiany
* Date : 2016-05-06 15:04
* Email: 1169654504@qq.com
*/
@SuppressWarnings("unused")
public interface ActivityDelegate<T extends Activity> {
default void onAttachedToActivity(@NonNull T activity) {
}
default void onDetachedFromActivity() {
}
default void onCreateBeforeSetContentView(@Nullable Bundle savedInstanceState) {
}
default void onCreateAfterSetContentView(@Nullable Bundle savedInstanceState) {
}
default void onSaveInstanceState(Bundle savedInstanceState) {
}
default void onStart() {
}
default void onResume() {
}
default void onPause() {
}
default void onStop() {
}
default void onDestroy() {
}
default void onActivityResult(int requestCode, int resultCode, Intent data) {
}
default void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
}
default void onRestoreInstanceState(Bundle savedInstanceState) {
}
default void onRestart() {
}
default void onPostCreate(@Nullable Bundle savedInstanceState) {
}
default void onResumeFragments() {
}
}

@ -0,0 +1,19 @@
package com.android.base.app.activity;
import android.support.annotation.UiThread;
import com.github.dmstocking.optional.java.util.function.Predicate;
@UiThread
@SuppressWarnings("unused")
public interface ActivityDelegateOwner {
void addDelegate(ActivityDelegate fragmentDelegate);
boolean removeDelegate(ActivityDelegate fragmentDelegate);
ActivityDelegate findDelegate(Predicate<ActivityDelegate> predicate);
ActivityStatus getStatus();
}

@ -0,0 +1,137 @@
package com.android.base.app.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity;
import com.github.dmstocking.optional.java.util.function.Predicate;
import java.util.ArrayList;
import java.util.List;
/**
* @author Ztiany
* Email: 1169654504@qq.com
* Date : 2016-12-20 11:43
*/
@UiThread
final class ActivityDelegates {
private final List<ActivityDelegate> mDelegates;
private AppCompatActivity mBaseActivity;
ActivityDelegates(AppCompatActivity baseActivity) {
mDelegates = new ArrayList<>(4);
mBaseActivity = baseActivity;
}
void callOnCreateBeforeSetContentView(@Nullable Bundle savedInstanceState) {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onCreateBeforeSetContentView(savedInstanceState);
}
}
void callOnCreateAfterSetContentView(@Nullable Bundle savedInstanceState) {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onCreateAfterSetContentView(savedInstanceState);
}
}
void callOnRestoreInstanceState(Bundle savedInstanceState) {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onRestoreInstanceState(savedInstanceState);
}
}
void callOnPostCreate(@Nullable Bundle savedInstanceState) {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onPostCreate(savedInstanceState);
}
}
void callOnSaveInstanceState(Bundle outState) {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onSaveInstanceState(outState);
}
}
void callOnDestroy() {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onDestroy();
}
}
void callOnStop() {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onStop();
}
}
void callOnPause() {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onPause();
}
}
void callOnResume() {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onResume();
}
}
void callOnStart() {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onStart();
}
}
void callOnRestart() {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onRestart();
}
}
void callOnActivityResult(int requestCode, int resultCode, Intent data) {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onActivityResult(requestCode, resultCode, data);
}
}
void callOnRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
void callOnResumeFragments() {
for (ActivityDelegate activityDelegate : mDelegates) {
activityDelegate.onResumeFragments();
}
}
@SuppressWarnings("unchecked")
void addActivityDelegate(ActivityDelegate activityDelegate) {
mDelegates.add(activityDelegate);
activityDelegate.onAttachedToActivity(mBaseActivity);
}
boolean removeActivityDelegate(ActivityDelegate activityDelegate) {
boolean remove = mDelegates.remove(activityDelegate);
if (remove) {
activityDelegate.onDetachedFromActivity();
}
return remove;
}
ActivityDelegate findDelegate(Predicate<ActivityDelegate> predicate) {
for (ActivityDelegate delegate : mDelegates) {
if (predicate.test(delegate)) {
return delegate;
}
}
return null;
}
}

@ -0,0 +1,11 @@
package com.android.base.app.activity
enum class ActivityStatus {
INITIALIZED,
CREATE,
START,
RESUME,
PAUSE,
STOP,
DESTROY
}

@ -0,0 +1,71 @@
package com.android.base.app.activity;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import java.util.List;
/**
* <pre>
* 1.Fragment需要自己处理BackPress事件如果不处理就交给子Fragment处理都不处理则由Activity处理
* 2.BackPress的传递由低层往深层传递同一层级的中外层中的 Fragment优先处理
* 3.在Fragment中嵌套使用Fragment时请使用getSupportChildFragmentManager
* </pre>
*/
public class BackHandlerHelper {
/**
* 将back事件分发给 FragmentManager 中管理的子Fragment如果该 FragmentManager 中的所有Fragment都
* 没有处理back事件则尝试 FragmentManager.popBackStack()
*
* @return 如果处理了back键则返回 <b>true</b>
* @see #handleBackPress(Fragment)
* @see #handleBackPress(FragmentActivity)
*/
public static boolean handleBackPress(FragmentManager fragmentManager) {
List<Fragment> fragments = fragmentManager.getFragments();
for (int i = fragments.size() - 1; i >= 0; i--) {
Fragment child = fragments.get(i);
if (isFragmentBackHandled(child)) {
return true;
}
}
return false;
}
/**
* 将back事件分发给Fragment中的子Fragment,
* 该方法调用了 {@link #handleBackPress(FragmentManager)}
*
* @return 如果处理了back键则返回 <b>true</b>
*/
public static boolean handleBackPress(Fragment fragment) {
return handleBackPress(fragment.getChildFragmentManager());
}
/**
* 将back事件分发给Activity中的子Fragment,
* 该方法调用了 {@link #handleBackPress(FragmentManager)}
*
* @return 如果处理了back键则返回 <b>true</b>
*/
public static boolean handleBackPress(FragmentActivity fragmentActivity) {
return handleBackPress(fragmentActivity.getSupportFragmentManager());
}
/**
* 判断Fragment是否处理了Back键
*
* @return 如果处理了back键则返回 <b>true</b>
*/
@SuppressWarnings("WeakerAccess")
public static boolean isFragmentBackHandled(Fragment fragment) {
return fragment != null
&& fragment.isVisible()
&& fragment.getUserVisibleHint() //getUserVisibleHint默认情况下为true,在ViewPager中会被使用到。
&& fragment instanceof OnBackPressListener
&& ((OnBackPressListener) fragment).onBackPressed();
}
}

@ -0,0 +1,222 @@
package com.android.base.app.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import com.android.base.utils.android.compat.AndroidVersion;
import com.github.dmstocking.optional.java.util.function.Predicate;
import timber.log.Timber;
/**
* <pre>
* 1封装通用流程
* 2onBackPressed 事件分发优先交给 Fragment 处理
* 3提供 RxJava 的生命周期绑定
* </pre>
*
* @author Ztiany
* Date : 2016-05-04 15:40
* Email: 1169654504@qq.com
*/
public abstract class BaseActivity extends AppCompatActivity implements ActivityDelegateOwner {
private final ActivityDelegates mActivityDelegates = new ActivityDelegates(this);
private ActivityStatus mActivityStatus = ActivityStatus.INITIALIZED;
private String tag() {
return this.getClass().getSimpleName();
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
Timber.tag(tag()).d("---->onCreate before call super");
initialize(savedInstanceState);
mActivityDelegates.callOnCreateBeforeSetContentView(savedInstanceState);
super.onCreate(savedInstanceState);
Timber.tag(tag()).d("---->onCreate after call super " + "bundle = " + savedInstanceState);
Object layout = layout();
if (layout instanceof View) {
setContentView((View) layout);
} else if (layout instanceof Integer) {
setContentView((Integer) layout);
} else if (layout == null) {
Timber.d("layout() return null layout");
} else {
throw new IllegalArgumentException("layout() return type no support, layout = " + layout);
}
mActivityStatus = ActivityStatus.CREATE;
mActivityDelegates.callOnCreateAfterSetContentView(savedInstanceState);
setupView(savedInstanceState);
}
@Override
protected void onRestart() {
Timber.tag(tag()).d("---->onRestart before call super");
super.onRestart();
Timber.tag(tag()).d("---->onRestart after call super ");
mActivityDelegates.callOnRestart();
}
@Override
protected void onStart() {
Timber.tag(tag()).d("---->onStart before call super");
super.onStart();
Timber.tag(tag()).d("---->onStart after call super");
mActivityStatus = ActivityStatus.START;
mActivityDelegates.callOnStart();
}
@Override
protected void onResume() {
Timber.tag(tag()).d("---->onResume before call super");
super.onResume();
Timber.tag(tag()).d("---->onResume after call super");
mActivityStatus = ActivityStatus.RESUME;
mActivityDelegates.callOnResume();
}
@Override
protected void onPause() {
Timber.tag(tag()).d("---->onPause before call super");
mActivityStatus = ActivityStatus.PAUSE;
mActivityDelegates.callOnPause();
super.onPause();
Timber.tag(tag()).d("---->onPause after call super ");
}
@Override
protected void onStop() {
Timber.tag(tag()).d("---->onStop before call super");
mActivityStatus = ActivityStatus.STOP;
mActivityDelegates.callOnStop();
super.onStop();
Timber.tag(tag()).d("---->onStop after call super");
}
@Override
protected void onDestroy() {
Timber.tag(tag()).d("---->onDestroy before call super");
mActivityStatus = ActivityStatus.DESTROY;
mActivityDelegates.callOnDestroy();
super.onDestroy();
Timber.tag(tag()).d("---->onDestroy after call super");
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mActivityDelegates.callOnPostCreate(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mActivityDelegates.callOnSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mActivityDelegates.callOnRestoreInstanceState(savedInstanceState);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mActivityDelegates.callOnActivityResult(requestCode, resultCode, data);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
mActivityDelegates.callOnRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onResumeFragments() {
super.onResumeFragments();
mActivityDelegates.callOnResumeFragments();
}
///////////////////////////////////////////////////////////////////////////
// interface impl
///////////////////////////////////////////////////////////////////////////
@UiThread
@Override
public final void addDelegate(@NonNull ActivityDelegate activityDelegate) {
mActivityDelegates.addActivityDelegate(activityDelegate);
}
@SuppressWarnings("unused")
@UiThread
@Override
public final boolean removeDelegate(@NonNull ActivityDelegate activityDelegate) {
return mActivityDelegates.removeActivityDelegate(activityDelegate);
}
@Override
public ActivityDelegate findDelegate(Predicate<ActivityDelegate> predicate) {
return mActivityDelegates.findDelegate(predicate);
}
@Override
public ActivityStatus getStatus() {
return mActivityStatus;
}
/**
* Before call super.onCreate and setContentView
*
* @param savedInstanceState state
*/
protected void initialize(@Nullable Bundle savedInstanceState) {
}
/**
* provide a layoutId (int) or layout (View)
*
* @return layoutId
*/
@Nullable
protected abstract Object layout();
/**
* after setContentView
*/
protected abstract void setupView(@Nullable Bundle savedInstanceState);
@Override
public void onBackPressed() {
if (BackHandlerHelper.handleBackPress(this)) {
Timber.d("onBackPressed() called but child fragment handle it");
} else {
superOnBackPressed();
}
}
protected void superOnBackPressed() {
super.onBackPressed();
}
@Override
public boolean isDestroyed() {
if (AndroidVersion.atLeast(17)) {
return super.isDestroyed();
} else {
return getStatus() == ActivityStatus.DESTROY;
}
}
}

@ -0,0 +1,13 @@
package com.android.base.app.activity;
/**
* Activity的返回键监听
*/
public interface OnBackPressListener {
/**
* @return true 表示Fragment处理back pressfalse表示由Activity处理
*/
boolean onBackPressed();
}

@ -0,0 +1,14 @@
package com.android.base.app.dagger;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ActivityScope {
}

@ -0,0 +1,22 @@
package com.android.base.app.dagger;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import javax.inject.Qualifier;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface ContextType {
String ACTIVITY = "Activity";
String CONTEXT = "Context";
String APPLICATION = "Application";
String value() default APPLICATION;
}

@ -0,0 +1,14 @@
package com.android.base.app.dagger;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FragmentScope {
}

@ -0,0 +1,18 @@
package com.android.base.app.dagger;
import android.view.View;
import java.util.Map;
import javax.inject.Provider;
/**
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2017-06-21 14:58
*/
public interface HasViewInjector {
Map<Class<? extends View>, Provider<ViewComponentBuilder>> viewInjectors();
}

@ -0,0 +1,31 @@
package com.android.base.app.dagger;
/**
* Activity 实现此接口时如果需要为内部的 Fragment 提供注入容器需要实现 HasSupportFragmentInjector具体如下面代码
*
* <pre>{@code
*
* class InjectableActivity implements Injectable, HasSupportFragmentInjector{
* @Inject
* DispatchingAndroidInjector<Fragment> fragmentInjector;
*
* public AndroidInjector<Fragment> supportFragmentInjector() {
* return fragmentInjector;
* }
* }
* }
*
* 标记接口用于标记此类需要被注入依赖
*
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2018-12-11 12:38
*/
public interface Injectable {
default boolean enableInject() {
return true;
}
}

@ -0,0 +1,14 @@
package com.android.base.app.dagger;
import android.view.View;
import dagger.MembersInjector;
/**
* how to use it? refer https://github.com/Ztiany/Programming-Notes-Code/blob/master/Android/Dagger2AndroidInjection-v2.19/README.md。
*
* @param <A>
*/
@SuppressWarnings("WeakerAccess")
public interface ViewComponent<A extends View> extends MembersInjector<A> {
}

@ -0,0 +1,15 @@
package com.android.base.app.dagger;
import android.view.View;
import dagger.BindsInstance;
public interface ViewComponentBuilder<T extends View, C extends ViewComponent<T>> {
@BindsInstance
ViewComponentBuilder bindInstance(T t);
C build();
}

@ -0,0 +1,60 @@
package com.android.base.app.dagger;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import android.view.View;
import java.util.Map;
import javax.inject.Provider;
/**
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2017-06-21 14:55
*/
@SuppressWarnings("unused")
public class ViewInjection {
private static final String TAG = ViewInjection.class.getSimpleName();
@SuppressWarnings("unchecked")
public static void inject(View view) {
if (null == view) {
throw new NullPointerException();
}
HasViewInjector hasViewInjector = findHasViewInjectors(view);
Log.d(TAG, String.format("An injector for %s was found in %s", view.getClass().getCanonicalName(), hasViewInjector.getClass().getCanonicalName()));
Map<Class<? extends View>, Provider<ViewComponentBuilder>> viewInjectors = hasViewInjector.viewInjectors();
Provider<ViewComponentBuilder> provider = viewInjectors.get(view.getClass());
if (provider != null) {
ViewComponentBuilder viewComponentBuilder = provider.get();
viewComponentBuilder
.bindInstance(view)
.build()
.injectMembers(view);
} else {
throw new NullPointerException("ViewInjection fail ");
}
}
private static HasViewInjector findHasViewInjectors(View view) {
Context context = view.getContext();
if (context instanceof HasViewInjector) {
return (HasViewInjector) context;
}
Application application = (Application) context.getApplicationContext();
if (application instanceof HasViewInjector) {
return (HasViewInjector) application;
}
throw new IllegalArgumentException(String.format("No injector was found for %s", view.getClass().getCanonicalName()));
}
}

@ -0,0 +1,18 @@
package com.android.base.app.dagger;
import android.view.View;
import java.lang.annotation.Target;
import dagger.MapKey;
import dagger.internal.Beta;
import static java.lang.annotation.ElementType.METHOD;
@Beta
@MapKey
@Target(METHOD)
@SuppressWarnings("unused")
public @interface ViewKey {
Class<? extends View> value();
}

@ -0,0 +1,45 @@
package com.android.base.app.dagger;
import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProvider;
import android.support.annotation.NonNull;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
/**
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2018-11-05 14:31
*/
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> mCreatorMap;
@Inject
ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> classProviderMap) {
mCreatorMap = classProviderMap;
}
@NonNull
@Override
@SuppressWarnings("unchecked")
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
Provider<ViewModel> viewModelProvider = mCreatorMap.get(modelClass);
if (viewModelProvider == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : mCreatorMap.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
viewModelProvider = entry.getValue();
break;
}
}
}
if (viewModelProvider != null) {
return (T) viewModelProvider.get();
}
throw new NullPointerException("can not find provider for " + modelClass);
}
}

@ -0,0 +1,18 @@
package com.android.base.app.dagger;
import android.arch.lifecycle.ViewModel;
import dagger.MapKey;
import kotlin.annotation.AnnotationTarget;
import kotlin.annotation.MustBeDocumented;
import kotlin.annotation.Retention;
import kotlin.annotation.Target;
@MustBeDocumented
@Target(allowedTargets = AnnotationTarget.FUNCTION)
@Retention()
@MapKey
public @interface ViewModelKey {
Class<? extends ViewModel> value();
}

@ -0,0 +1,22 @@
package com.android.base.app.dagger;
import android.arch.lifecycle.ViewModelProvider;
import dagger.Binds;
import dagger.Module;
/**
* 使用 ViewModelModule 情况下所有的 ViewModule 都由 Activity 界别容器提供因此 Fragment 级容器无法为其 ViewModule 提供依赖这是仅有的局限性
*
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2018-11-05 16:20
*/
@Module
public abstract class ViewModelModule {
@Binds
@ActivityScope
abstract ViewModelProvider.Factory provideViewModelFactory(ViewModelFactory viewModelFactory);
}

@ -0,0 +1,19 @@
package com.android.base.app.dagger;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import javax.inject.Scope;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2017-05-23 09:59
*/
@Scope
@Documented
@Retention(RUNTIME)
public @interface ViewScope {
}

@ -0,0 +1,278 @@
package com.android.base.app.fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatDialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.base.app.BaseKit;
import com.android.base.app.activity.BackHandlerHelper;
import com.android.base.app.activity.OnBackPressListener;
import com.android.base.app.ui.LoadingView;
import com.github.dmstocking.optional.java.util.function.Predicate;
import timber.log.Timber;
/**
* 提供
* <pre>
* 1. RxJava 生命周期绑定
* 2. 返回键监听
* 3. 显示 LoadingDialog Message
* 4. 可以添加生命周期代理
* </pre>
*
* @author Ztiany
* date : 2016-03-19 23:09
* email: 1169654504@qq.com
*/
public class BaseDialogFragment extends AppCompatDialogFragment implements LoadingView, OnBackPressListener, FragmentDelegateOwner {
private LoadingView mLoadingViewImpl;
private View mLayoutView;
/* just for cache*/
private View mCachedView;
private final FragmentDelegates mFragmentDelegates = new FragmentDelegates(this);
private String tag() {
return this.getClass().getSimpleName();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Timber.tag(tag()).d("onAttach() called with: context = [" + context + "]");
mFragmentDelegates.onAttach(context);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Timber.tag(tag()).d("-->onCreate savedInstanceState = " + savedInstanceState);
mFragmentDelegates.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mCachedView == null) {
Object layout = provideLayout();
if (layout == null) {
return null;
}
if (layout instanceof Integer) {
return mCachedView = inflater.inflate((Integer) layout, container, false);
}
if (layout instanceof View) {
return mCachedView = (View) layout;
}
throw new IllegalArgumentException("Here you should provide a layout id or a View");
}
return mCachedView;
}
/**
* 使用此方法提供的布局将只会被缓存起来即此方法将只会被调用一次
*
* @return provide a layout id or a View
*/
@Nullable
@SuppressWarnings("unused")
protected Object provideLayout() {
return null;
}
@Override
public final void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Timber.tag(tag()).d("-->onViewCreated savedInstanceState = " + savedInstanceState);
if (mLayoutView != view) {
mLayoutView = view;
internalOnViewPrepared(view, savedInstanceState);
onViewPrepared(view, savedInstanceState);
}
mFragmentDelegates.onViewCreated(view, savedInstanceState);
}
void internalOnViewPrepared(@NonNull View view, @Nullable Bundle savedInstanceState) {
}
/**
* View is prepared, If {@link android.support.v4.app.Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)} return same layout, it will be called once
*
* @param view view of fragment
*/
protected void onViewPrepared(@NonNull View view, @Nullable Bundle savedInstanceState) {
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Timber.tag(tag()).d("-->onActivityCreated savedInstanceState = " + savedInstanceState);
mFragmentDelegates.onActivityCreated(savedInstanceState);
}
@Override
public void onStart() {
super.onStart();
Timber.tag(tag()).d("-->onStart");
mFragmentDelegates.onStart();
}
@Override
public void onResume() {
super.onResume();
Timber.tag(tag()).d("-->onResume");
mFragmentDelegates.onResume();
}
@Override
public void onPause() {
Timber.tag(tag()).d("-->onPause");
mFragmentDelegates.onPause();
super.onPause();
}
@Override
public void onStop() {
Timber.tag(tag()).d("-->onStop");
mFragmentDelegates.onStop();
super.onStop();
}
@Override
public void onDestroyView() {
Timber.tag(tag()).d("-->onDestroyView");
mFragmentDelegates.onDestroyView();
super.onDestroyView();
}
@Override
public void onDestroy() {
Timber.tag(tag()).d("-->onDestroy");
mFragmentDelegates.onDestroy();
super.onDestroy();
dismissLoadingDialog();
}
@Override
public void onDetach() {
Timber.tag(tag()).d("-->onDetach");
mFragmentDelegates.onDetach();
super.onDetach();
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
mFragmentDelegates.onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Timber.tag(tag()).d("-->setUserVisibleHint ==" + isVisibleToUser);
mFragmentDelegates.setUserVisibleHint(isVisibleToUser);
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
Timber.tag(tag()).d("-->onHiddenChanged = " + hidden);
mFragmentDelegates.onHiddenChanged(hidden);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
mFragmentDelegates.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mFragmentDelegates.onActivityResult(requestCode, resultCode, data);
}
@Override
@UiThread
public final void addDelegate(FragmentDelegate fragmentDelegate) {
mFragmentDelegates.addDelegate(fragmentDelegate);
}
@Override
@UiThread
public final boolean removeDelegate(FragmentDelegate fragmentDelegate) {
return mFragmentDelegates.removeDelegate(fragmentDelegate);
}
@Override
public FragmentDelegate findDelegate(Predicate<FragmentDelegate> predicate) {
return mFragmentDelegates.findDelegate(predicate);
}
@Override
public boolean onBackPressed() {
return handleBackPress() || BackHandlerHelper.handleBackPress(this);
}
/**
* Fragment需要自己处理BackPress事件如果不处理就交给子Fragment处理都不处理则由Activity处理
*/
protected boolean handleBackPress() {
return false;
}
private LoadingView getLoadingViewImpl() {
if (mLoadingViewImpl == null) {
mLoadingViewImpl = BaseKit.get().getLoadingViewFactory().createLoadingDelegate(getContext());
}
return mLoadingViewImpl;
}
@Override
public void showLoadingDialog() {
getLoadingViewImpl().showLoadingDialog(true);
}
@Override
public void showLoadingDialog(boolean cancelable) {
getLoadingViewImpl().showLoadingDialog(cancelable);
}
@Override
public void showLoadingDialog(CharSequence message, boolean cancelable) {
getLoadingViewImpl().showLoadingDialog(message, cancelable);
}
@Override
public void showLoadingDialog(@StringRes int messageId, boolean cancelable) {
getLoadingViewImpl().showLoadingDialog(messageId, cancelable);
}
@Override
public void dismissLoadingDialog() {
getLoadingViewImpl().dismissLoadingDialog();
}
@Override
public void showMessage(CharSequence message) {
getLoadingViewImpl().showMessage(message);
}
@Override
public void showMessage(@StringRes int messageId) {
getLoadingViewImpl().showMessage(messageId);
}
}

@ -0,0 +1,289 @@
package com.android.base.app.fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.annotation.UiThread;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import com.android.base.app.BaseKit;
import com.android.base.app.activity.BackHandlerHelper;
import com.android.base.app.activity.OnBackPressListener;
import com.android.base.app.ui.LoadingView;
import com.github.dmstocking.optional.java.util.function.Predicate;
import timber.log.Timber;
/**
* 提供
* <pre>
* 1. RxJava 生命周期绑定
* 2. 返回键监听
* 3. 显示 LoadingDialog Message
* 4. 可以添加生命周期代理
* </pre>
*
* @author Ztiany
* date : 2016-03-19 23:09
* email: 1169654504@qq.com
*/
public class BaseFragment extends Fragment implements LoadingView, OnBackPressListener, FragmentDelegateOwner {
private LoadingView mLoadingViewImpl;
private View mLayoutView;
/* just for cache*/
private View mCachedView;
private final FragmentDelegates mFragmentDelegates = new FragmentDelegates(this);
private String tag() {
return this.getClass().getSimpleName();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Timber.tag(tag()).d("onAttach() called with: context = [" + context + "]");
mFragmentDelegates.onAttach(context);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Timber.tag(tag()).d("-->onCreate savedInstanceState = " + savedInstanceState);
mFragmentDelegates.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mCachedView == null) {
Object layout = provideLayout();
if (layout == null) {
return null;
}
if (layout instanceof Integer) {
return mCachedView = inflater.inflate((Integer) layout, container, false);
}
if (layout instanceof View) {
return mCachedView = (View) layout;
}
throw new IllegalArgumentException("Here you should provide a layout id or a View");
}
Timber.tag(tag()).d("mCachedView.parent: " + mCachedView.getParent());
if (mCachedView.getParent() != null) {
ViewParent parent = mCachedView.getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(mCachedView);
}
}
return mCachedView;
}
/**
* 使用此方法提供的布局将只会被缓存起来即此方法将只会被调用一次
*
* @return provide a layout id or a View
*/
@Nullable
@SuppressWarnings("unused")
protected Object provideLayout() {
return null;
}
@Override
public final void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Timber.tag(tag()).d("-->onViewCreated savedInstanceState = " + savedInstanceState);
if (mLayoutView != view) {
mLayoutView = view;
internalOnViewPrepared(view, savedInstanceState);
onViewPrepared(view, savedInstanceState);
}
mFragmentDelegates.onViewCreated(view, savedInstanceState);
}
void internalOnViewPrepared(@NonNull View view, @Nullable Bundle savedInstanceState) {
}
/**
* View is prepared, If {@link android.support.v4.app.Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)} return same layout, it will be called once
*
* @param view view of fragment
*/
protected void onViewPrepared(@NonNull View view, @Nullable Bundle savedInstanceState) {
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Timber.tag(tag()).d("-->onActivityCreated savedInstanceState = " + savedInstanceState);
mFragmentDelegates.onActivityCreated(savedInstanceState);
}
@Override
public void onStart() {
super.onStart();
Timber.tag(tag()).d("-->onStart");
mFragmentDelegates.onStart();
}
@Override
public void onResume() {
super.onResume();
Timber.tag(tag()).d("-->onResume");
mFragmentDelegates.onResume();
}
@Override
public void onPause() {
Timber.tag(tag()).d("-->onPause");
mFragmentDelegates.onPause();
super.onPause();
}
@Override
public void onStop() {
Timber.tag(tag()).d("-->onStop");
mFragmentDelegates.onStop();
super.onStop();
}
@Override
public void onDestroyView() {
Timber.tag(tag()).d("-->onDestroyView");
mFragmentDelegates.onDestroyView();
super.onDestroyView();
}
@Override
public void onDestroy() {
Timber.tag(tag()).d("-->onDestroy");
mFragmentDelegates.onDestroy();
super.onDestroy();
dismissLoadingDialog();
}
@Override
public void onDetach() {
Timber.tag(tag()).d("-->onDetach");
mFragmentDelegates.onDetach();
super.onDetach();
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
mFragmentDelegates.onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Timber.tag(tag()).d("-->setUserVisibleHint ==" + isVisibleToUser);
mFragmentDelegates.setUserVisibleHint(isVisibleToUser);
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
Timber.tag(tag()).d("-->onHiddenChanged = " + hidden);
mFragmentDelegates.onHiddenChanged(hidden);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
mFragmentDelegates.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mFragmentDelegates.onActivityResult(requestCode, resultCode, data);
}
@Override
@UiThread
public final void addDelegate(FragmentDelegate fragmentDelegate) {
mFragmentDelegates.addDelegate(fragmentDelegate);
}
@Override
@UiThread
public final boolean removeDelegate(FragmentDelegate fragmentDelegate) {
return mFragmentDelegates.removeDelegate(fragmentDelegate);
}
@Override
public FragmentDelegate findDelegate(Predicate<FragmentDelegate> predicate) {
return mFragmentDelegates.findDelegate(predicate);
}
@Override
public boolean onBackPressed() {
return handleBackPress() || BackHandlerHelper.handleBackPress(this);
}
/**
* Fragment需要自己处理BackPress事件如果不处理就交给子Fragment处理都不处理则由Activity处理
*/
protected boolean handleBackPress() {
return false;
}
private LoadingView getLoadingViewImpl() {
if (mLoadingViewImpl == null) {
mLoadingViewImpl = BaseKit.get().getLoadingViewFactory().createLoadingDelegate(getContext());
}
return mLoadingViewImpl;
}
@Override
public void showLoadingDialog() {
getLoadingViewImpl().showLoadingDialog(true);
}
@Override
public void showLoadingDialog(boolean cancelable) {
getLoadingViewImpl().showLoadingDialog(cancelable);
}
@Override
public void showLoadingDialog(CharSequence message, boolean cancelable) {
getLoadingViewImpl().showLoadingDialog(message, cancelable);
}
@Override
public void showLoadingDialog(@StringRes int messageId, boolean cancelable) {
getLoadingViewImpl().showLoadingDialog(messageId, cancelable);
}
@Override
public void dismissLoadingDialog() {
getLoadingViewImpl().dismissLoadingDialog();
}
@Override
public void showMessage(CharSequence message) {
getLoadingViewImpl().showMessage(message);
}
@Override
public void showMessage(@StringRes int messageId) {
getLoadingViewImpl().showMessage(messageId);
}
}

@ -0,0 +1,150 @@
package com.android.base.app.fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import com.android.base.adapter.DataManager;
import com.android.base.app.ui.AutoPageNumber;
import com.android.base.app.ui.PageNumber;
import com.android.base.app.ui.RefreshListLayout;
import com.ztiany.loadmore.adapter.ILoadMore;
import com.ztiany.loadmore.adapter.OnLoadMoreListener;
import com.ztiany.loadmore.adapter.WrapperAdapter;
import java.util.List;
/**
* 通用的RecyclerView列表界面支持下拉刷新和加载更多
*
* @param <T> 当前列表使用的数据类型
* @author Ztiany
* date : 2016-03-19 23:09
* email: 1169654504@qq.com
*/
public abstract class BaseListFragment<T> extends BaseStateFragment implements RefreshListLayout<T> {
/**
* 加载更多
*/
private ILoadMore mLoadMore;
/**
* 列表数据管理
*/
private DataManager<T> mDataManager;
/**
* 分页页码
*/
private PageNumber mPageNumber;
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mDataManager == null) {
throw new NullPointerException("you need set DataManager");
}
}
protected final void setDataManager(@NonNull DataManager<T> dataManager) {
mDataManager = dataManager;
}
/**
* Default PageNumber is {@link AutoPageNumber}
*
* @param recyclerAdapter adapter
* @return recycler adapter wrapper
*/
protected final RecyclerView.Adapter setupLoadMore(@NonNull RecyclerView.Adapter<?> recyclerAdapter) {
if (mDataManager == null) {
throw new IllegalStateException("you should setup a DataManager before call this method");
}
return setupLoadMore(recyclerAdapter, new AutoPageNumber(this, mDataManager));
}
protected final RecyclerView.Adapter setupLoadMore(@NonNull RecyclerView.Adapter<?> recyclerAdapter, @NonNull PageNumber pageNumber) {
mPageNumber = pageNumber;
WrapperAdapter wrap = WrapperAdapter.wrap(recyclerAdapter);
mLoadMore = wrap;
mLoadMore.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore() {
BaseListFragment.this.onLoadMore();
}
@Override
public boolean canLoadMore() {
return !isRefreshing();
}
});
return wrap;
}
@Override
protected void onRefresh() {
onStartLoad();
}
protected void onLoadMore() {
onStartLoad();
}
@Override
final boolean canRefresh() {
return !isLoadingMore();
}
/**
* call by {@link #onRefresh()} or {@link #onLoadMore()}, you can get current loading type from {@link #isRefreshing()} or {@link #isLoadingMore()}.
*/
protected void onStartLoad() {
}
@Override
public void replaceData(List<T> data) {
mDataManager.replaceAll(data);
}
@Override
public void addData(List<T> data) {
mDataManager.addItems(data);
}
protected final ILoadMore getLoadMoreController() {
return mLoadMore;
}
@Override
public PageNumber getPager() {
return mPageNumber;
}
@Override
public boolean isEmpty() {
return mDataManager.isEmpty();
}
@Override
public boolean isLoadingMore() {
return mLoadMore != null && mLoadMore.isLoadingMore();
}
@Override
public void loadMoreCompleted(boolean hasMore) {
if (mLoadMore != null) {
mLoadMore.loadCompleted(hasMore);
}
}
@Override
public void loadMoreFailed() {
if (mLoadMore != null) {
mLoadMore.loadFail();
}
}
}

@ -0,0 +1,102 @@
package com.android.base.app.fragment
import android.os.Bundle
import android.view.View
import com.android.base.adapter.DataManager
import com.android.base.app.ui.AutoPageNumber
import com.android.base.app.ui.RefreshListLayout
import com.android.base.app.ui.StateLayoutConfig
/**
* 区别于 [BaseListFragment] 只能支持 RecyclerView[BaseListFragment] 采用包装 [android.support.v7.widget.RecyclerView.Adapter] 的方式
* 在底部添加 load more view item来实现加载更多BaseListV2Fragment 没有采用此种方式所以你使用的刷新视图应该是支持这下来刷新和加载更多功能的
*
* 在调用BaseListV2Fragment [onActivityCreated] 之前你应该设置好 [dataManager]
*
*@author Ztiany
* Email: ztiany3@gmail.com
* Date : 2019-03-26 15:06
*/
abstract class BaseListV2Fragment<T> : BaseFragment(), RefreshListLayout<T> {
companion object {
protected const val CONTENT = StateLayoutConfig.CONTENT
protected const val LOADING = StateLayoutConfig.LOADING
protected const val ERROR = StateLayoutConfig.ERROR
protected const val EMPTY = StateLayoutConfig.EMPTY
protected const val NET_ERROR = StateLayoutConfig.NET_ERROR
protected const val SERVER_ERROR = StateLayoutConfig.SERVER_ERROR
}
private lateinit var stateLayout: RefreshLoadMoreStateLayoutImpl
protected open lateinit var dataManager: DataManager<T>
internal override fun internalOnViewPrepared(view: View, savedInstanceState: Bundle?) {
stateLayout = RefreshLoadMoreStateLayoutImpl.init(view)
stateLayout.refreshView.setRefreshHandler {
onRefresh()
}
stateLayout.refreshView.setLoadMoreHandler {
onLoadMore()
}
stateLayout.setStateRetryListener(this::onRetry)
}
protected open fun onRetry(state: Int) {
if (!isRefreshing) {
autoRefresh()
}
}
protected open fun onRefresh() = onStartLoad()
protected open fun onLoadMore() = onStartLoad()
/**
* call by [onRefresh] or [onLoadMore], you can get current loading type from [isRefreshing] or [isLoadingMore].
*/
protected open fun onStartLoad() {}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (::dataManager.isInitialized) {
throw NullPointerException("you need set DataManager")
}
}
override fun onDestroyView() {
super.onDestroyView()
refreshCompleted()
}
override fun replaceData(data: MutableList<T>?) {
dataManager.replaceAll(data)
}
override fun addData(data: MutableList<T>?) {
dataManager.addItems(data)
}
fun setRefreshEnable(enable: Boolean) = stateLayout.refreshView.setRefreshEnable(enable)
fun setLoadMoreEnable(enable: Boolean) = stateLayout.refreshView.setLoadMoreEnable(enable)
override fun getPager() = AutoPageNumber(this, dataManager)
override fun isEmpty() = dataManager.isEmpty
override fun loadMoreCompleted(hasMore: Boolean) = stateLayout.refreshView.loadMoreCompleted(hasMore)
override fun loadMoreFailed() = stateLayout.refreshView.loadMoreFailed()
override fun isRefreshing() = stateLayout.refreshView.isRefreshing
override fun showContentLayout() = stateLayout.showContentLayout()
override fun showLoadingLayout() = stateLayout.showLoadingLayout()
override fun refreshCompleted() = stateLayout.refreshView.refreshCompleted()
override fun showEmptyLayout() = stateLayout.showEmptyLayout()
override fun showErrorLayout() = stateLayout.showErrorLayout()
override fun showRequesting() = stateLayout.showRequesting()
override fun showBlank() = stateLayout.showBlank()
override fun getStateLayoutConfig() = stateLayout.stateLayoutConfig
override fun autoRefresh() = stateLayout.refreshView.autoRefresh()
override fun showNetErrorLayout() = stateLayout.showNetErrorLayout()
override fun showServerErrorLayout() = stateLayout.showServerErrorLayout()
override fun isLoadingMore() = stateLayout.refreshView.isLoadingMore
}

@ -0,0 +1,145 @@
package com.android.base.app.fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import com.android.base.app.ui.RefreshStateLayout;
import com.android.base.app.ui.RefreshView;
import com.android.base.app.ui.StateLayoutConfig;
/**
* @author Ztiany
* date : 2016-03-19 23:09
* email: 1169654504@qq.com
* @see BaseStateFragment
*/
@SuppressWarnings("unused")
public abstract class BaseStateDialogFragment extends BaseDialogFragment implements RefreshStateLayout {
private RefreshableStateLayoutImpl mStateLayout;
protected static final int CONTENT = StateLayoutConfig.CONTENT;
protected static final int LOADING = StateLayoutConfig.LOADING;
protected static final int ERROR = StateLayoutConfig.ERROR;
protected static final int EMPTY = StateLayoutConfig.EMPTY;
protected static final int NET_ERROR = StateLayoutConfig.NET_ERROR;
protected static final int SERVER_ERROR = StateLayoutConfig.SERVER_ERROR;
@Override
void internalOnViewPrepared(@NonNull View view, @Nullable Bundle savedInstanceState) {
mStateLayout = RefreshableStateLayoutImpl.init(view);
mStateLayout.setRefreshHandler(new RefreshView.RefreshHandler() {
@Override
public void onRefresh() {
BaseStateDialogFragment.this.onRefresh();
}
@Override
public boolean canRefresh() {
return BaseStateDialogFragment.this.canRefresh();
}
});
mStateLayout.setStateRetryListenerUnchecked(this::onRetry);
}
@Override
public void onDestroyView() {
super.onDestroyView();
refreshCompleted();
}
boolean canRefresh() {
return true;
}
final RefreshView getRefreshView() {
return mStateLayout.getRefreshView();
}
protected void onRetry(int state) {
if (getRefreshView() != null) {
if (!getRefreshView().isRefreshing()) {
autoRefresh();
}
} else {
onRefresh();
}
}
public final void setRefreshEnable(boolean enable) {
if (getRefreshView() != null) {
getRefreshView().setRefreshEnable(enable);
}
}
protected void onRefresh() {
}
@Override
public final StateLayoutConfig getStateLayoutConfig() {
return mStateLayout.getStateLayoutConfig();
}
private RefreshStateLayout getStateLayout() {
return mStateLayout;
}
@Override
public final boolean isRefreshing() {
return mStateLayout.isRefreshing();
}
@Override
public void refreshCompleted() {
getStateLayout().refreshCompleted();
}
@Override
public void autoRefresh() {
getStateLayout().autoRefresh();
}
@Override
public void showContentLayout() {
getStateLayout().showContentLayout();
}
@Override
public void showLoadingLayout() {
getStateLayout().showLoadingLayout();
}
@Override
public void showEmptyLayout() {
getStateLayout().showEmptyLayout();
}
@Override
public void showErrorLayout() {
getStateLayout().showErrorLayout();
}
@Override
public void showRequesting() {
getStateLayout().showRequesting();
}
@Override
public void showBlank() {
getStateLayout().showBlank();
}
@Override
public void showNetErrorLayout() {
getStateLayout().showNetErrorLayout();
}
@Override
public void showServerErrorLayout() {
getStateLayout().showServerErrorLayout();
}
}

@ -0,0 +1,152 @@
package com.android.base.app.fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import com.android.base.app.ui.RefreshStateLayout;
import com.android.base.app.ui.RefreshView;
import com.android.base.app.ui.StateLayoutConfig;
/**
* <pre>
* 1: 支持显示{CONTENTLOADINGERROREMPTY}四种布局支持下拉刷新
* 2: 使用的布局中必须有一个id = R.id.base_status_layout的Layout切改Layout实现了StateLayout
* 3: RefreshView(下拉刷新)的id必须设置为 R.id.refresh_layout没有添加则表示不需要下拉刷新功能
* 4: 默认所有重试和下拉刷新都会调用{@link #onRefresh()}子类可以修改该行为
* </pre>
*
* @author Ztiany
* date : 2016-03-19 23:09
* email: 1169654504@qq.com
*/
@SuppressWarnings("unused")
public abstract class BaseStateFragment extends BaseFragment implements RefreshStateLayout {
private RefreshableStateLayoutImpl mStateLayout;
protected static final int CONTENT = StateLayoutConfig.CONTENT;
protected static final int LOADING = StateLayoutConfig.LOADING;
protected static final int ERROR = StateLayoutConfig.ERROR;
protected static final int EMPTY = StateLayoutConfig.EMPTY;
protected static final int NET_ERROR = StateLayoutConfig.NET_ERROR;
protected static final int SERVER_ERROR = StateLayoutConfig.SERVER_ERROR;
@Override
void internalOnViewPrepared(@NonNull View view, @Nullable Bundle savedInstanceState) {
mStateLayout = RefreshableStateLayoutImpl.init(view);
mStateLayout.setRefreshHandler(new RefreshView.RefreshHandler() {
@Override
public void onRefresh() {
BaseStateFragment.this.onRefresh();
}
@Override
public boolean canRefresh() {
return BaseStateFragment.this.canRefresh();
}
});
mStateLayout.setStateRetryListenerUnchecked(this::onRetry);
}
@Override
public void onDestroyView() {
super.onDestroyView();
refreshCompleted();
}
boolean canRefresh() {
return true;
}
final RefreshView getRefreshView() {
return mStateLayout.getRefreshView();
}
protected void onRetry(int state) {
if (getRefreshView() != null) {
if (!getRefreshView().isRefreshing()) {
autoRefresh();
}
} else {
onRefresh();
}
}
protected void onRefresh() {
}
public final void setRefreshEnable(boolean enable) {
if (getRefreshView() != null) {
getRefreshView().setRefreshEnable(enable);
}
}
@Override
@NonNull
public final StateLayoutConfig getStateLayoutConfig() {
return mStateLayout.getStateLayoutConfig();
}
private RefreshStateLayout getStateLayout() {
return mStateLayout;
}
@Override
public final boolean isRefreshing() {
return mStateLayout.isRefreshing();
}
@Override
public void refreshCompleted() {
getStateLayout().refreshCompleted();
}
@Override
public void autoRefresh() {
getStateLayout().autoRefresh();
}
@Override
public void showContentLayout() {
getStateLayout().showContentLayout();
}
@Override
public void showLoadingLayout() {
getStateLayout().showLoadingLayout();
}
@Override
public void showEmptyLayout() {
getStateLayout().showEmptyLayout();
}
@Override
public void showErrorLayout() {
getStateLayout().showErrorLayout();
}
@Override
public void showRequesting() {
getStateLayout().showRequesting();
}
@Override
public void showBlank() {
getStateLayout().showBlank();
}
@Override
public void showNetErrorLayout() {
getStateLayout().showNetErrorLayout();
}
@Override
public void showServerErrorLayout() {
getStateLayout().showServerErrorLayout();
}
}

@ -0,0 +1,26 @@
package com.android.base.app.fragment
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/**
* 懒加载代理
*
*@author Ztiany
* Email: ztiany3@gmail.com
* Date : 2019-03-08 12:50
*/
class LazyLoad(private val onPrepared: (() -> Unit)) : ReadOnlyProperty<BaseFragment, LazyDelegate> {
private lateinit var lazyDelegate: LazyDelegate
override fun getValue(thisRef: BaseFragment, property: KProperty<*>): LazyDelegate {
if (!::lazyDelegate.isInitialized) {
lazyDelegate = LazyDelegate.attach(thisRef) {
onPrepared.invoke()
}
}
return lazyDelegate
}
}

@ -0,0 +1,24 @@
package com.android.base.app.fragment;
/**
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2019-03-05 15:25
*/
public class FragmentConfig {
private static final int INVALIDATE_ID = -1;
private static int sDefaultContainerId = INVALIDATE_ID;
public static void setDefaultContainerId(int defaultContainerId) {
sDefaultContainerId = defaultContainerId;
}
public static int defaultContainerId() {
if (sDefaultContainerId == INVALIDATE_ID) {
throw new IllegalStateException("sDefaultContainerId has not set");
}
return sDefaultContainerId;
}
}

@ -0,0 +1,73 @@
package com.android.base.app.fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.View;
public interface FragmentDelegate<T extends Fragment> {
/**
* 当该Delegate被添加到Fragment中
*/
default void onAttachToFragment(T fragment) {
}
/**
* 调用此方法时清除Fragment的引用
*/
default void onDetachFromFragment() {
}
default void onAttach(Context context) {
}
default void onCreate(@Nullable Bundle savedInstanceState) {
}
default void onActivityCreated(@Nullable Bundle savedInstanceState) {
}
default void onSaveInstanceState(Bundle savedInstanceState) {
}
default void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
}
default void onStart() {
}
default void onResume() {
}
default void onPause() {
}
default void onStop() {
}
default void onDestroy() {
}
default void onDestroyView() {
}
default void onDetach() {
}
default void setUserVisibleHint(boolean isVisibleToUser) {
}
default void onHiddenChanged(boolean hidden) {
}
default void onActivityResult(int requestCode, int resultCode, Intent data) {
}
default void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
}
}

@ -0,0 +1,16 @@
package com.android.base.app.fragment;
import android.support.annotation.UiThread;
import com.github.dmstocking.optional.java.util.function.Predicate;
@UiThread
public interface FragmentDelegateOwner {
void addDelegate(FragmentDelegate fragmentDelegate);
boolean removeDelegate(FragmentDelegate fragmentDelegate);
FragmentDelegate findDelegate(Predicate<FragmentDelegate> predicate);
}

@ -0,0 +1,166 @@
package com.android.base.app.fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v4.app.Fragment;
import android.view.View;
import com.github.dmstocking.optional.java.util.function.Predicate;
import java.util.ArrayList;
import java.util.List;
@UiThread
final class FragmentDelegates implements FragmentDelegate<Fragment>, FragmentDelegateOwner {
private final Fragment mDelegateOwner;
private List<FragmentDelegate> mDelegates = new ArrayList<>(4);
<T extends Fragment & FragmentDelegateOwner> FragmentDelegates(T delegateOwner) {
mDelegateOwner = delegateOwner;
}
@Override
public void onAttach(Context context) {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onAttach(context);
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onCreate(savedInstanceState);
}
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onViewCreated(view, savedInstanceState);
}
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onActivityCreated(savedInstanceState);
}
}
@Override
public void onStart() {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onStart();
}
}
@Override
public void onResume() {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onResume();
}
}
@Override
public void onPause() {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onPause();
}
}
@Override
public void onStop() {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onStop();
}
}
@Override
public void onDestroy() {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onDestroy();
}
}
@Override
public void onDestroyView() {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onDestroyView();
}
}
@Override
public void onDetach() {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onDetach();
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.setUserVisibleHint(isVisibleToUser);
}
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onSaveInstanceState(savedInstanceState);
}
}
@Override
public void onHiddenChanged(boolean hidden) {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onHiddenChanged(hidden);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
for (FragmentDelegate fragmentDelegate : mDelegates) {
fragmentDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@Override
@SuppressWarnings("unchecked")
public void addDelegate(FragmentDelegate fragmentDelegate) {
mDelegates.add(fragmentDelegate);
fragmentDelegate.onAttachToFragment(mDelegateOwner);
}
@Override
public boolean removeDelegate(FragmentDelegate fragmentDelegate) {
boolean remove = mDelegates.remove(fragmentDelegate);
if (remove) {
fragmentDelegate.onDetachFromFragment();
}
return remove;
}
@Override
public FragmentDelegate findDelegate(Predicate<FragmentDelegate> predicate) {
for (FragmentDelegate delegate : mDelegates) {
if (predicate.test(delegate)) {
return delegate;
}
}
return null;
}
}

@ -0,0 +1,132 @@
package com.android.base.app.fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import java.lang.ref.WeakReference;
@SuppressWarnings("WeakerAccess,unused")
public class FragmentInfo {
private final int mPageId;
private final String mTag;
private final Class<? extends Fragment> mClazz;
private final int mTitleId;
private final Bundle mArguments;
private final boolean mIsToStack;
private final String mStackName;
private WeakReference<Fragment> mFragment;
private FragmentInfo(int pageId, String tag, Class<? extends Fragment> clazz, int titleId, Bundle arguments, boolean toStack, String stackName) {
mPageId = pageId;
mTag = tag;
mClazz = clazz;
mTitleId = titleId;
mArguments = arguments;
mIsToStack = toStack;
mStackName = stackName;
}
public Fragment getInstance() {
return mFragment == null ? null : mFragment.get();
}
public void setInstance(Fragment fragment) {
mFragment = new WeakReference<>(fragment);
}
public Fragment newFragment(Context context) {
return Fragment.instantiate(context, mClazz.getName(), mArguments);
}
public boolean isToStack() {
return mIsToStack;
}
public String getStackName() {
return mStackName;
}
public Bundle getArguments() {
return mArguments;
}
public int getTitleId() {
return mTitleId;
}
public int getPageId() {
return mPageId;
}
public String getTag() {
return mTag;
}
public Class<? extends Fragment> getClazz() {
return mClazz;
}
public static PageBuilder builder() {
return new PageBuilder();
}
public static class PageBuilder {
private int mPagerId;
private String mTag;
private Class<? extends Fragment> mClazz;
private int mTitleId;
private Bundle mArguments;
private boolean mIsToStack;
private String mStackName;
public FragmentInfo build() {
return new FragmentInfo(mPagerId, mTag, mClazz, mTitleId, mArguments, mIsToStack, mStackName);
}
public PageBuilder pagerId(int pagerId) {
mPagerId = pagerId;
return this;
}
public PageBuilder tag(String tag) {
this.mTag = tag;
return this;
}
public PageBuilder clazz(Class<? extends Fragment> clazz) {
mClazz = clazz;
return this;
}
public PageBuilder titleId(int titleId) {
mTitleId = titleId;
return this;
}
public PageBuilder arguments(Bundle arguments) {
mArguments = arguments;
return this;
}
public PageBuilder toStack(boolean toStack) {
this.mIsToStack = toStack;
return this;
}
/**
* 如果需要加入到Stack建议加上StackName
*
* @param stackName StackName
* @return PageBuilder
*/
public PageBuilder stackName(String stackName) {
mStackName = stackName;
return this;
}
}
}

@ -0,0 +1,463 @@
@file:JvmName("Fragments")
package com.android.base.app.fragment
import android.support.annotation.NonNull
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentTransaction
import android.view.View
import com.android.base.app.activity.ActivityDelegate
import com.android.base.app.activity.ActivityDelegateOwner
import com.android.base.app.activity.ActivityStatus
import com.android.base.kotlin.javaClassName
import kotlin.reflect.KClass
@JvmOverloads
fun Fragment.exitFragment(immediate: Boolean = false) {
activity.exitFragment(immediate)
}
@JvmOverloads
fun FragmentActivity?.exitFragment(immediate: Boolean = false) {
if (this == null) {
return
}
val supportFragmentManager = this.supportFragmentManager
val backStackEntryCount = supportFragmentManager.backStackEntryCount
if (backStackEntryCount > 0) {
if (immediate) {
supportFragmentManager.popBackStackImmediate()
} else {
supportFragmentManager.popBackStack()
}
} else {
this.supportFinishAfterTransition()
}
}
/**
* @param clazz the interface container must implemented
* @param <T> Type
* @return the interface context must implemented
*/
fun <T> Fragment.requireContainerImplement(clazz: Class<T>): T? {
if (clazz.isInstance(parentFragment)) {
return clazz.cast(parentFragment)
}
return if (clazz.isInstance(activity)) {
clazz.cast(activity)
} else {
throw RuntimeException("use this Fragment:$this, Activity or Fragment must impl interface :$clazz")
}
}
/**
* @param clazz the interface context must implemented
* @param <T> Type
* @return the interface context must implemented
*/
fun <T> Fragment.requireContextImplement(clazz: Class<T>): T? {
return if (!clazz.isInstance(activity)) {
throw RuntimeException("use this Fragment:$this, Activity must impl interface :$clazz")
} else {
clazz.cast(activity)
}
}
/**
* @param clazz the interface parent must implemented
* @param <T> Type
* @return the interface context must implemented
*/
fun <T> Fragment.requireParentImplement(clazz: Class<T>): T? {
return if (!clazz.isInstance(parentFragment)) {
throw RuntimeException("use this Fragment:$this, ParentFragment must impl interface :$clazz")
} else {
clazz.cast(parentFragment)
}
}
/** 使用 [clazz] 的全限定类名作为 tag 查找 Fragment */
fun <T : Fragment> FragmentManager.findFragmentByTag(clazz: KClass<T>): T? {
@Suppress("UNCHECKED_CAST")
return findFragmentByTag(clazz.java.name) as? T
}
fun FragmentManager.popBackTo(flag: String, immediate: Boolean = false) {
if (immediate) {
popBackStackImmediate(flag, FragmentManager.POP_BACK_STACK_INCLUSIVE)
} else {
popBackStack(flag, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
}
fun FragmentManager.clearBackStack(immediate: Boolean = false) {
if (immediate) {
this.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
} else {
this.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
}
private fun FragmentManager.isFragmentInStack(clazz: Class<out Fragment>): Boolean {
val backStackEntryCount = backStackEntryCount
if (backStackEntryCount == 0) {
return false
}
for (i in 0 until backStackEntryCount) {
if (clazz.name == getBackStackEntryAt(i).name) {
return true
}
}
return false
}
inline fun FragmentManager.inTransaction(func: EnhanceFragmentTransaction.() -> Unit) {
val fragmentTransaction = beginTransaction()
EnhanceFragmentTransaction(this, fragmentTransaction).func()
fragmentTransaction.commit()
}
inline fun FragmentActivity.inFragmentTransaction(func: EnhanceFragmentTransaction.() -> Unit) {
val transaction = supportFragmentManager.beginTransaction()
EnhanceFragmentTransaction(supportFragmentManager, transaction).func()
transaction.commit()
}
fun <T> T.inSafelyFragmentTransaction(
func: EnhanceFragmentTransaction.() -> Unit
): Boolean where T : FragmentActivity, T : ActivityDelegateOwner {
var delegate = findDelegate {
it is SafelyFragmentTransactionActivityDelegate
}as? SafelyFragmentTransactionActivityDelegate
if (delegate == null) {
delegate = SafelyFragmentTransactionActivityDelegate()
addDelegate(delegate)
}
val transaction = supportFragmentManager.beginTransaction()
EnhanceFragmentTransaction(supportFragmentManager, transaction).func()
return delegate.safeCommit(this, transaction)
}
inline fun Fragment.inChildFragmentTransaction(func: EnhanceFragmentTransaction.() -> Unit) {
val transaction = childFragmentManager.beginTransaction()
EnhanceFragmentTransaction(childFragmentManager, transaction).func()
transaction.commit()
}
fun <T> T.inSafelyChildFragmentTransaction(
func: EnhanceFragmentTransaction.() -> Unit
): Boolean where T : Fragment, T : FragmentDelegateOwner {
var delegate: SafelyFragmentTransactionFragmentDelegate? = findDelegate {
it is SafelyFragmentTransactionFragmentDelegate
} as? SafelyFragmentTransactionFragmentDelegate
if (delegate == null) {
delegate = SafelyFragmentTransactionFragmentDelegate()
addDelegate(delegate)
}
val transaction = childFragmentManager.beginTransaction()
EnhanceFragmentTransaction(childFragmentManager, transaction).func()
return delegate.safeCommit(this, transaction)
}
private class SafelyFragmentTransactionActivityDelegate : ActivityDelegate<FragmentActivity> {
private val mPendingTransactions = mutableListOf<FragmentTransaction>()
fun safeCommit(@NonNull activityDelegateOwner: ActivityDelegateOwner, @NonNull transaction: FragmentTransaction): Boolean {
val status = activityDelegateOwner.status
val isCommitterResumed = (status == ActivityStatus.CREATE || status == ActivityStatus.START || status == ActivityStatus.RESUME)
return if (isCommitterResumed) {
transaction.commit()
false
} else {
mPendingTransactions.add(transaction)
true
}
}
override fun onResumeFragments() {
if (mPendingTransactions.isNotEmpty()) {
mPendingTransactions.forEach { it.commit() }
mPendingTransactions.clear()
}
}
}
private class SafelyFragmentTransactionFragmentDelegate : FragmentDelegate<Fragment> {
private val mPendingTransactions = mutableListOf<FragmentTransaction>()
fun safeCommit(@NonNull fragment: Fragment, @NonNull transaction: FragmentTransaction): Boolean {
return if (fragment.isResumed) {
transaction.commit()
false
} else {
mPendingTransactions.add(transaction)
true
}
}
override fun onResume() {
if (!mPendingTransactions.isEmpty()) {
mPendingTransactions.forEach { it.commit() }
mPendingTransactions.clear()
}
}
}
class EnhanceFragmentTransaction constructor(
private val fragmentManager: FragmentManager,
private val fragmentTransaction: FragmentTransaction
) : FragmentTransaction() {
//------------------------------------------------------------------------------------------------
// extra functions
//------------------------------------------------------------------------------------------------
/**
* [fragment] 添加到回退栈中 hide 其他 fragment
* 如果 [containerId]==0则使用 [com.android.base.app.BaseKit.setDefaultFragmentContainerId] 中配置的 id
* 如果 [tag] ==null 则使用 fragment 对应 class 的全限定类名
*/
fun addWithStack(containerId: Int = 0, fragment: Fragment, tag: String? = null, transition: Boolean = true): EnhanceFragmentTransaction {
//hide top
hideTopFragment()
//set add to stack
val nonnullTag = (tag ?: fragment.javaClassName())
addToBackStack(nonnullTag)
//add
fragmentTransaction.add(containerId.confirmId(), fragment, nonnullTag)
if (transition) {
//set a transition
setTransitionOpen()
}
return this
}
/**
* replace 方式把 [fragment] 添加到回退栈中
* 如果 [containerId]==0则使用 [com.android.base.app.BaseKit.setDefaultFragmentContainerId] 中配置的 id
* 如果 [tag] ==null 则使用 fragment 对应 class 的全限定类名
*/
fun replaceWithStack(containerId: Int = 0, fragment: Fragment, tag: String? = null, transition: Boolean = true): EnhanceFragmentTransaction {
//set add to stack
val nonnullTag = (tag ?: fragment.javaClassName())
addToBackStack(nonnullTag)
//add
fragmentTransaction.replace(containerId.confirmId(), fragment, nonnullTag)
//set a transition
if (transition) {
setTransitionOpen()
}
return this
}
private fun Int.confirmId(): Int {
return if (this == 0) {
FragmentConfig.defaultContainerId()
} else {
this
}
}
/**
* 添加 [fragment]
* 默认使用 [com.android.base.app.BaseKit.setDefaultFragmentContainerId] 中配置的 id
* 如果 [tag] 为null则使用 [fragment] 的全限定类名*
*/
fun addWithDefaultContainer(fragment: Fragment, tag: String? = null): FragmentTransaction {
val nonnullTag = (tag ?: fragment.javaClassName())
return fragmentTransaction.add(FragmentConfig.defaultContainerId(), fragment, nonnullTag)
}
/**
* 替换为 [fragment]
* id 使用 [com.android.base.app.BaseKit.setDefaultFragmentContainerId] 中配置的 id
* 如果 [tag] 为null则使用 [fragment] 的全限定类名
*/
fun replaceWithDefaultContainer(fragment: Fragment, tag: String? = null, transition: Boolean = true): FragmentTransaction {
val nonnullTag = (tag ?: fragment.javaClassName())
if (transition) {
setTransitionOpen()
}
return fragmentTransaction.replace(FragmentConfig.defaultContainerId(), fragment, nonnullTag)
}
/**隐藏所有的 fragment */
private fun hideOtherFragments() {
for (fragment in fragmentManager.fragments) {
if (fragment != null && fragment.isVisible) {
fragmentTransaction.hide(fragment)
}
}
}
/**隐藏第一个可见的 fragment */
private fun hideTopFragment() {
fragmentManager.fragments.lastOrNull { it.isVisible }?.let {
fragmentTransaction.hide(it)
}
}
fun setTransitionOpen(): FragmentTransaction {
return fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
}
fun setTransitionClose(): FragmentTransaction {
return fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
}
fun setTransitionFade(): FragmentTransaction {
return fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
}
//------------------------------------------------------------------------------------------------
// original functions
//------------------------------------------------------------------------------------------------
override fun setBreadCrumbShortTitle(res: Int): FragmentTransaction {
return fragmentTransaction.setBreadCrumbShortTitle(res)
}
override fun setBreadCrumbShortTitle(text: CharSequence?): FragmentTransaction {
return fragmentTransaction.setBreadCrumbShortTitle(text)
}
override fun setPrimaryNavigationFragment(fragment: Fragment?): FragmentTransaction {
return fragmentTransaction.setPrimaryNavigationFragment(fragment)
}
override fun runOnCommit(runnable: Runnable): FragmentTransaction {
return fragmentTransaction.runOnCommit(runnable)
}
override fun add(fragment: Fragment, tag: String?): FragmentTransaction {
return fragmentTransaction.add(fragment, tag)
}
override fun add(containerViewId: Int, fragment: Fragment): FragmentTransaction {
return fragmentTransaction.add(containerViewId, fragment)
}
override fun add(containerViewId: Int, fragment: Fragment, tag: String?): FragmentTransaction {
return fragmentTransaction.add(containerViewId, fragment, tag)
}
override fun hide(fragment: Fragment): FragmentTransaction {
return fragmentTransaction.hide(fragment)
}
override fun replace(containerViewId: Int, fragment: Fragment): FragmentTransaction {
return fragmentTransaction.replace(containerViewId, fragment)
}
override fun replace(containerViewId: Int, fragment: Fragment, tag: String?): FragmentTransaction {
return fragmentTransaction.replace(containerViewId, fragment, tag)
}
override fun detach(fragment: Fragment): FragmentTransaction {
return fragmentTransaction.detach(fragment)
}
@Deprecated("")
override fun setAllowOptimization(allowOptimization: Boolean): FragmentTransaction {
return fragmentTransaction.setAllowOptimization(allowOptimization)
}
override fun setCustomAnimations(enter: Int, exit: Int): FragmentTransaction {
return fragmentTransaction.setCustomAnimations(enter, exit)
}
override fun setCustomAnimations(enter: Int, exit: Int, popEnter: Int, popExit: Int): FragmentTransaction {
return fragmentTransaction.setCustomAnimations(enter, exit, popEnter, popExit)
}
override fun addToBackStack(name: String?): FragmentTransaction {
return fragmentTransaction.addToBackStack(name)
}
override fun disallowAddToBackStack(): FragmentTransaction {
return fragmentTransaction.disallowAddToBackStack()
}
override fun setTransitionStyle(styleRes: Int): FragmentTransaction {
return fragmentTransaction.setTransitionStyle(styleRes)
}
override fun setTransition(transit: Int): FragmentTransaction {
return fragmentTransaction.setTransition(transit)
}
override fun attach(fragment: Fragment): FragmentTransaction {
return fragmentTransaction.attach(fragment)
}
override fun show(fragment: Fragment): FragmentTransaction {
return fragmentTransaction.show(fragment)
}
override fun isEmpty(): Boolean {
return fragmentTransaction.isEmpty
}
override fun remove(fragment: Fragment): FragmentTransaction {
return fragmentTransaction.remove(fragment)
}
override fun isAddToBackStackAllowed(): Boolean {
return fragmentTransaction.isAddToBackStackAllowed
}
override fun addSharedElement(sharedElement: View, name: String): FragmentTransaction {
return fragmentTransaction.addSharedElement(sharedElement, name)
}
override fun setBreadCrumbTitle(res: Int): FragmentTransaction {
return fragmentTransaction.setBreadCrumbTitle(res)
}
override fun setBreadCrumbTitle(text: CharSequence?): FragmentTransaction {
return fragmentTransaction.setBreadCrumbTitle(text)
}
override fun setReorderingAllowed(reorderingAllowed: Boolean): FragmentTransaction {
return fragmentTransaction.setReorderingAllowed(reorderingAllowed)
}
@Deprecated("commit will be called automatically")
override fun commit(): Int {
throw UnsupportedOperationException("commit will be called automatically")
}
@Deprecated("commitAllowingStateLoss will be called automatically")
override fun commitAllowingStateLoss(): Int {
throw UnsupportedOperationException("commitAllowingStateLoss will be called automatically")
}
@Deprecated("commitNow will be called automatically", ReplaceWith("throw UnsupportedOperationException(\"commitNow will be called automatically\")"))
override fun commitNow() {
throw UnsupportedOperationException("commitNow will be called automatically")
}
@Deprecated("commitNowAllowingStateLoss will be called automatically", ReplaceWith("throw UnsupportedOperationException(\"commitNowAllowingStateLoss will be called automatically\")"))
override fun commitNowAllowingStateLoss() {
throw UnsupportedOperationException("commitNowAllowingStateLoss will be called automatically")
}
}

@ -0,0 +1,117 @@
package com.android.base.app.fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.View;
/**
* <pre>
* 在ViewPager中实现懒加载的Fragment
* changed--1: Android Support 24 把setUserVisibleHint方法放到了Attach之前调用了,所以请在在构造代码块中设置LazyDelegate
* </pre>
*
* @author Ztiany
* Date : Date : 2016-05-06 15:02
* Email: 1169654504@qq.com
*/
public class LazyDelegate implements FragmentDelegate<Fragment> {
/**
* View是否准备好如果不需要绑定view数据只是加载网络数据那么该字段可以去掉
*/
private boolean mIsViewPrepared;
/**
* 滑动过来后View是否可见
*/
private boolean mIsViewVisible;
private onPreparedListener mOnPreparedListener;
private LazyDelegate() {
}
public static LazyDelegate attach(FragmentDelegateOwner delegateFragment, final onPreparedListener onPreparedListener) {
LazyDelegate delegate = new LazyDelegate();
delegate.mOnPreparedListener = onPreparedListener;
delegateFragment.addDelegate(delegate);
return delegate;
}
/**
* 在这里实现Fragment数据的缓加载.
*
* @param isVisibleToUser true表用户可见false表不可见
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
if (isVisibleToUser) {
mIsViewVisible = true;
onVisible();
} else {
mIsViewVisible = false;
onInvisible();
}
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
mIsViewPrepared = true;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
lazyLoad();
}
/**
* 滑动过来后界面可见时执行
*/
@SuppressWarnings("all")
protected void onVisible() {
lazyLoad();
}
/**
* 滑动过来后界面不可见时执行
*/
@SuppressWarnings("all")
protected void onInvisible() {
}
private void lazyLoad() {
if (mIsViewPrepared && mIsViewVisible) {
notifyLazyLoad();
}
}
/**
* 懒加载数据并在此绑定View数据
*/
private void notifyLazyLoad() {
if (mOnPreparedListener != null) {
mOnPreparedListener.onPrepared();
}
}
public interface onPreparedListener {
void onPrepared();
}
public static abstract class SimpleLazyLoadListener implements onPreparedListener {
private boolean mIsCalled;
@Override
public final void onPrepared() {
if (!mIsCalled) {
onFirstLoad();
mIsCalled = true;
}
}
protected abstract void onFirstLoad();
}
}

@ -0,0 +1,11 @@
package com.android.base.app.fragment;
import android.content.Context;
import com.android.base.app.ui.LoadingView;
public interface LoadingViewFactory {
LoadingView createLoadingDelegate(Context context);
}

@ -0,0 +1,68 @@
package com.android.base.app.fragment
import android.graphics.drawable.Drawable
import android.view.View
import com.android.base.app.ui.CommonId
import com.android.base.app.ui.*
internal class RefreshLoadMoreStateLayoutImpl private constructor(private val mLayout: View) : StateLayout, StateLayoutConfig {
companion object {
fun init(view: View): RefreshLoadMoreStateLayoutImpl {
return RefreshLoadMoreStateLayoutImpl(view)
}
}
private var mMultiStateView: StateLayout = mLayout.findViewById<View>(CommonId.STATE_ID) as StateLayout
private var mRefreshView: RefreshLoadMoreView
val refreshView: RefreshLoadMoreView
get() = mRefreshView
init {
val refreshLayout = mLayout.findViewById<View>(CommonId.REFRESH_ID)
mRefreshView = RefreshLoadViewFactory.createRefreshView(refreshLayout)
}
override fun showLoadingLayout() = mMultiStateView.showLoadingLayout()
override fun showContentLayout() = mMultiStateView.showContentLayout()
override fun showEmptyLayout() = mMultiStateView.showEmptyLayout()
override fun showErrorLayout() = mMultiStateView.showErrorLayout()
override fun showRequesting() = mMultiStateView.showRequesting()
override fun showBlank() = mMultiStateView.showBlank()
override fun showNetErrorLayout() = mMultiStateView.showNetErrorLayout()
override fun showServerErrorLayout() = mMultiStateView.showServerErrorLayout()
override fun getStateLayoutConfig(): StateLayoutConfig = mMultiStateView.stateLayoutConfig
override fun setStateMessage(state: Int, message: CharSequence?): StateLayoutConfig {
mMultiStateView.stateLayoutConfig.setStateMessage(state, message)
return mMultiStateView.stateLayoutConfig
}
override fun setStateIcon(state: Int, drawable: Drawable?): StateLayoutConfig {
mMultiStateView.stateLayoutConfig.setStateIcon(state, drawable)
return mMultiStateView.stateLayoutConfig
}
override fun setStateIcon(state: Int, drawableId: Int): StateLayoutConfig {
mMultiStateView.stateLayoutConfig.setStateIcon(state, drawableId)
return mMultiStateView.stateLayoutConfig
}
override fun setStateAction(state: Int, actionText: CharSequence?): StateLayoutConfig {
mMultiStateView.stateLayoutConfig.setStateAction(state, actionText)
return mMultiStateView.stateLayoutConfig
}
override fun setStateRetryListener(retryActionListener: OnRetryActionListener?): StateLayoutConfig {
mMultiStateView.stateLayoutConfig.setStateRetryListener(retryActionListener)
return mMultiStateView.stateLayoutConfig
}
override fun disableOperationWhenRequesting(disable: Boolean): StateLayoutConfig {
mMultiStateView.stateLayoutConfig.disableOperationWhenRequesting(disable)
return mMultiStateView.stateLayoutConfig
}
}

@ -0,0 +1,189 @@
package com.android.base.app.fragment;
import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes;
import android.view.View;
import com.android.base.app.ui.OnRetryActionListener;
import com.android.base.app.ui.RefreshStateLayout;
import com.android.base.app.ui.RefreshView;
import com.android.base.app.ui.RefreshViewFactory;
import com.android.base.app.ui.StateLayout;
import com.android.base.app.ui.StateLayoutConfig;
import static com.android.base.app.ui.CommonId.REFRESH_ID;
import static com.android.base.app.ui.CommonId.STATE_ID;
/**
* @author Ztiany
*/
final class RefreshableStateLayoutImpl implements RefreshStateLayout, StateLayoutConfig {
private StateLayout mMultiStateView;
private RefreshView mRefreshView;
private RefreshView.RefreshHandler mRefreshHandler;
static RefreshableStateLayoutImpl init(View layoutView) {
return new RefreshableStateLayoutImpl(layoutView);
}
private RefreshableStateLayoutImpl(View layoutView) {
setupBaseUiLogic(layoutView);
setupRefreshLogic(layoutView);
}
RefreshView getRefreshView() {
return mRefreshView;
}
void setRefreshHandler(RefreshView.RefreshHandler refreshHandler) {
mRefreshHandler = refreshHandler;
}
void setStateRetryListenerUnchecked(OnRetryActionListener retryActionListener) {
if (mMultiStateView != null) {
setStateRetryListener(retryActionListener);
}
}
@SuppressWarnings("all")
private void setupBaseUiLogic(View layoutView) {
mMultiStateView = (StateLayout) layoutView.findViewById(STATE_ID);
}
private void setupRefreshLogic(View layoutView) {
View refreshLayout = layoutView.findViewById(REFRESH_ID);
if (refreshLayout == null) {
return;
}
mRefreshView = RefreshViewFactory.createRefreshView(refreshLayout);
mRefreshView.setRefreshHandler(new RefreshView.RefreshHandler() {
@Override
public boolean canRefresh() {
return mRefreshHandler.canRefresh();
}
@Override
public void onRefresh() {
mRefreshHandler.onRefresh();
}
});
}
@Override
public final void autoRefresh() {
if (mRefreshView != null) {
mRefreshView.autoRefresh();
}
}
@Override
public void showLoadingLayout() {
checkMultiStateView().showLoadingLayout();
}
@Override
public void showContentLayout() {
refreshCompleted();
checkMultiStateView().showContentLayout();
}
@Override
public void showEmptyLayout() {
refreshCompleted();
checkMultiStateView().showEmptyLayout();
}
@Override
public void showErrorLayout() {
refreshCompleted();
checkMultiStateView().showErrorLayout();
}
@Override
public void showRequesting() {
checkMultiStateView().showRequesting();
}
@Override
public void showBlank() {
checkMultiStateView().showBlank();
}
@Override
public void showNetErrorLayout() {
refreshCompleted();
checkMultiStateView().showNetErrorLayout();
}
@Override
public void showServerErrorLayout() {
refreshCompleted();
checkMultiStateView().showNetErrorLayout();
}
@Override
public StateLayoutConfig getStateLayoutConfig() {
checkMultiStateView();
return this;
}
@Override
public void refreshCompleted() {
if (mRefreshView != null) {
mRefreshView.refreshCompleted();
}
}
@Override
public boolean isRefreshing() {
if (mRefreshView != null) {
return mRefreshView.isRefreshing();
}
return false;
}
@Override
public StateLayoutConfig setStateMessage(@RetryableState int state, CharSequence message) {
checkMultiStateView().getStateLayoutConfig().setStateMessage(state, message);
return this;
}
@Override
public StateLayoutConfig setStateIcon(@RetryableState int state, Drawable drawable) {
checkMultiStateView().getStateLayoutConfig().setStateIcon(state, drawable);
return this;
}
@Override
public StateLayoutConfig setStateIcon(@RetryableState int state, @DrawableRes int drawableId) {
checkMultiStateView().getStateLayoutConfig().setStateIcon(state, drawableId);
return this;
}
@Override
public StateLayoutConfig setStateAction(@RetryableState int state, CharSequence actionText) {
checkMultiStateView().getStateLayoutConfig().setStateAction(state, actionText);
return this;
}
@Override
public StateLayoutConfig setStateRetryListener(OnRetryActionListener retryActionListener) {
checkMultiStateView().getStateLayoutConfig().setStateRetryListener(retryActionListener);
return this;
}
@Override
public StateLayoutConfig disableOperationWhenRequesting(boolean disable) {
checkMultiStateView().getStateLayoutConfig().disableOperationWhenRequesting(disable);
return this;
}
private StateLayout checkMultiStateView() {
if (mMultiStateView == null) {
throw new IllegalStateException("Calling this function requires defining a view that implements StateLayout in the Layout");
}
return mMultiStateView;
}
}

@ -0,0 +1,207 @@
package com.android.base.app.fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class TabManager {
@SuppressWarnings("WeakerAccess") public static final int ATTACH_DETACH = 1;
@SuppressWarnings("WeakerAccess") public static final int SHOW_HIDE = 2;
private final FragmentManager mFragmentManager;
private final int mContainerId;
private final Tabs mMainTabs;
private final Context mContext;
private FragmentInfo mCurrentFragmentInfo;
private static final String CURRENT_ID_KET = "main_tab_id";
private static final int NONE = -1;
private int mCurrentId = NONE;
private final int mOperationType;
public TabManager(Context context, FragmentManager fragmentManager, Tabs tabs, int containerId) {
this(context, fragmentManager, tabs, containerId, ATTACH_DETACH);
}
/**
* @param operationType {@link #ATTACH_DETACH} or {@link #SHOW_HIDE}
*/
@SuppressWarnings("WeakerAccess")
public TabManager(Context context, FragmentManager fragmentManager, Tabs tabs, int containerId, int operationType) {
if (operationType != ATTACH_DETACH && operationType != SHOW_HIDE) {
throw new IllegalArgumentException("the operationType must be ATTACH_DETACH or SHOW_HIDE");
}
mMainTabs = tabs;
mContainerId = containerId;
mContext = context;
mFragmentManager = fragmentManager;
mOperationType = operationType;
}
public final void setup(Bundle bundle) {
int pageId = mMainTabs.homePage().getPageId();
if (bundle == null) {
switchPage(pageId);
} else {
mCurrentId = bundle.getInt(CURRENT_ID_KET, pageId);
restoreState();
}
}
private void restoreState() {
List<FragmentInfo> pages = mMainTabs.getPages();
for (FragmentInfo page : pages) {
page.setInstance(mFragmentManager.findFragmentByTag(page.getTag()));
if (mCurrentId == page.getPageId()) {
mCurrentFragmentInfo = page;
}
}
if (mCurrentId == NONE) {
doChangeTab(mMainTabs.homePage().getPageId());
}
}
private void switchPage(int pageId) {
if (mCurrentId == pageId) {
return;
}
FragmentTransaction ft = null;
if (mCurrentFragmentInfo != null) {
Fragment fragment = mCurrentFragmentInfo.getInstance();
if (fragment != null) {
ft = mFragmentManager.beginTransaction();
hideOrDetach(ft, fragment);
}
}
if (ft != null) {
ft.commit();
}
doChangeTab(pageId);
}
private void hideOrDetach(FragmentTransaction ft, Fragment fragment) {
if (mOperationType == SHOW_HIDE) {
ft.hide(fragment);
} else {
ft.detach(fragment);
}
}
private void showOrAttach(FragmentTransaction fragmentTransaction, Fragment fragment) {
if (mOperationType == SHOW_HIDE) {
fragmentTransaction.show(fragment);
} else {
fragmentTransaction.attach(fragment);
}
}
private void doChangeTab(int fragmentId) {
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
FragmentInfo fragmentInfo = mMainTabs.getFragmentInfo(fragmentId);
Fragment fragment = fragmentInfo.getInstance();
if (fragment != null) {
showOrAttach(fragmentTransaction, fragment);
} else {
Fragment newFragment = fragmentInfo.newFragment(mContext);
fragmentInfo.setInstance(newFragment);
onFragmentCreated(fragmentId, newFragment);
fragmentTransaction.add(mContainerId, newFragment, fragmentInfo.getTag());
}
mCurrentFragmentInfo = fragmentInfo;
mCurrentId = fragmentId;
fragmentTransaction.commit();
}
@SuppressWarnings("WeakerAccess")
public void selectTabByPosition(int position) {
switchPage(mMainTabs.getIdByPosition(position));
}
public void selectTabById(int tabId) {
selectTabByPosition(mMainTabs.getPositionById(tabId));
}
@SuppressWarnings("unused")
public int getCurrentPosition() {
return mMainTabs.getPositionById(mCurrentId);
}
@SuppressWarnings("WeakerAccess,unused")
protected void onFragmentCreated(int id, Fragment newFragment) {
}
public void onSaveInstanceState(Bundle bundle) {
bundle.putInt(CURRENT_ID_KET, mCurrentId);
}
public static abstract class Tabs {
private final List<FragmentInfo> mPages;
protected Tabs() {
mPages = new ArrayList<>();
}
protected void add(FragmentInfo page) {
mPages.add(page);
}
FragmentInfo homePage() {
return mPages.get(0);
}
public int size() {
return mPages.size();
}
FragmentInfo getFragmentInfo(int id) {
for (FragmentInfo page : mPages) {
if (page.getPageId() == id) {
return page;
}
}
throw new IllegalArgumentException("MainPages not has this pageId :" + id);
}
/**
* @param clazz Fragment对应的clazz
* @return pagerId 没有则返回-1
*/
@SuppressWarnings("unused")
int getIdByClazz(Class<? extends Fragment> clazz) {
for (FragmentInfo page : mPages) {
if (page.getClazz() == clazz) {
return page.getPageId();
}
}
return -1;
}
List<FragmentInfo> getPages() {
return Collections.unmodifiableList(mPages);
}
private int getPositionById(int tabId) {
int size = mPages.size();
for (int i = 0; i < size; i++) {
if (mPages.get(i).getPageId() == tabId) {
return i;
}
}
return -1;
}
private int getIdByPosition(int position) {
return mPages.get(position).getPageId();
}
}
}

@ -0,0 +1,44 @@
package com.android.base.app.mvp
import android.support.annotation.CallSuper
import java.lang.ref.WeakReference
abstract class AbstractPresenter<V : IBaseView> : IPresenter<V> {
private var _view: WeakReference<V>? = null
protected val view: V?
get() = if (_view != null) {
_view?.get()
} else null
protected val isViewAttached: Boolean
get() = _view != null && _view?.get() != null
final override fun bindView(view: V?) {
if (view == null) {
throw NullPointerException("Presenter bindView --> view is null")
}
if (_view != null) {
throw UnsupportedOperationException("Presenter bindView --> the view already bind")
}
_view = WeakReference(view)
}
override fun onPostStart() {}
override fun onPause() {
}
override fun onResume() {
}
@CallSuper
override fun onDestroy() {
if (_view != null) {
_view?.clear()
_view = null
}
}
}

@ -0,0 +1,3 @@
package com.android.base.app.mvp
interface IBaseView

@ -0,0 +1,13 @@
package com.android.base.app.mvp
interface IPresenter<V : IBaseView> : Lifecycle {
/**
* bind a view
*
* @param view V
*/
fun bindView(view: V?)
}

@ -0,0 +1,31 @@
package com.android.base.app.mvp
/**
* @author Ztiany
* Email: 1169654504@qq.com
* Date : 2017-04-18 16:23
*/
interface Lifecycle {
/**
* start the Lifecycle , initialize something, will be called only once
*/
fun onStart()
/**
* will be called when view is ready.
*/
fun onPostStart()
fun onResume()
fun onPause()
/**
* destroy the Lifecycle and release resource, will be called only once
*/
fun onDestroy()
}

@ -0,0 +1,66 @@
package com.android.base.app.mvp
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.View
import com.android.base.app.fragment.FragmentDelegate
import com.android.base.app.fragment.FragmentDelegateOwner
class PresenterBinder constructor(private val lifecycle: Lifecycle) : FragmentDelegate<Fragment> {
private var isCalled: Boolean = false
private var host: Fragment? = null
override fun onAttachToFragment(fragment: Fragment) {
host = fragment
}
override fun onDetachFromFragment() {
host = null
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
if (!isCalled) {
lifecycle.onStart()
val activity = host?.activity
activity?.findViewById<View>(android.R.id.content)?.post { lifecycle.onPostStart() }
isCalled = true
}
}
override fun onResume() {
lifecycle.onResume()
}
override fun onPause() {
lifecycle.onPause()
}
override fun onDestroy() {
lifecycle.onDestroy()
}
companion object {
/**
* @param v The MVP of the V
* @param p The MVP of the P
* @param <V> The MVP of the V
</V> */
fun <V : IBaseView> bind(fragmentDelegateOwner: FragmentDelegateOwner, v: V, p: IPresenter<V>): PresenterBinder {
p.bindView(v)
val lifecycleDelegate = PresenterBinder(p)
fragmentDelegateOwner.addDelegate(lifecycleDelegate)
return lifecycleDelegate
}
}
}
fun <V : IBaseView> FragmentDelegateOwner.bindPresenter(v: V, p: IPresenter<V>): PresenterBinder {
p.bindView(v)
val lifecycleDelegate = PresenterBinder(p)
addDelegate(lifecycleDelegate)
return lifecycleDelegate
}

@ -0,0 +1,65 @@
package com.android.base.app.mvp
import android.support.annotation.CallSuper
import com.android.base.rx.LifecycleScopeProviderEx
import com.uber.autodispose.lifecycle.CorrespondingEventsFunction
import com.uber.autodispose.lifecycle.LifecycleEndedException
import com.uber.autodispose.lifecycle.LifecycleScopes
import io.reactivex.CompletableSource
import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
/**
* work with RxJava
*
* @author Ztiany
* Date : 2016-10-19 12:17
* Email: 1169654504@qq.com
*/
abstract class RxPresenter<V : IBaseView> : AbstractPresenter<V>(), LifecycleScopeProviderEx<RxPresenter.LifecycleEvent> {
private val lifecycleSubject = BehaviorSubject.create<LifecycleEvent>()
enum class LifecycleEvent {
START,
DESTROY
}
@CallSuper
override fun onStart() {
lifecycleSubject.onNext(LifecycleEvent.START)
}
@CallSuper
override fun onDestroy() {
lifecycleSubject.onNext(LifecycleEvent.DESTROY)
super@RxPresenter.onDestroy()
}
final override fun lifecycle(): Observable<LifecycleEvent> {
return lifecycleSubject
}
final override fun correspondingEvents(): CorrespondingEventsFunction<LifecycleEvent> {
return LIFECYCLE_CORRESPONDING_EVENTS
}
final override fun peekLifecycle(): LifecycleEvent? {
return lifecycleSubject.value
}
final override fun requestScope(): CompletableSource {
return LifecycleScopes.resolveScopeFromLifecycle<LifecycleEvent>(this)
}
companion object {
internal val LIFECYCLE_CORRESPONDING_EVENTS: CorrespondingEventsFunction<LifecycleEvent> = CorrespondingEventsFunction {
return@CorrespondingEventsFunction when (it) {
LifecycleEvent.START -> LifecycleEvent.DESTROY
else -> throw LifecycleEndedException("Cannot bind to LifecycleEvent when outside of it.")
}
}
}
}

@ -0,0 +1,60 @@
package com.android.base.app.mvvm
import android.arch.lifecycle.ViewModel
import android.support.annotation.CallSuper
import com.android.base.rx.LifecycleScopeProviderEx
import com.uber.autodispose.lifecycle.CorrespondingEventsFunction
import com.uber.autodispose.lifecycle.LifecycleEndedException
import com.uber.autodispose.lifecycle.LifecycleScopes
import io.reactivex.CompletableSource
import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
/**
* ArchViewModel work with Rx
*
* @author Ztiany
* Email: 1169654504@qq.com
* Date : 2017-04-18 16:25
*/
abstract class ArchViewModel : ViewModel(), LifecycleScopeProviderEx<ArchViewModel.ViewModelEvent> {
private val archLifecycleSubject = BehaviorSubject.createDefault(ViewModelEvent.CREATED)
enum class ViewModelEvent {
CREATED, CLEARED
}
@CallSuper
override fun onCleared() {
archLifecycleSubject.onNext(ViewModelEvent.CLEARED)
super.onCleared()
}
final override fun correspondingEvents(): CorrespondingEventsFunction<ViewModelEvent> {
return CORRESPONDING_EVENTS
}
final override fun lifecycle(): Observable<ViewModelEvent> {
return archLifecycleSubject.hide()
}
final override fun peekLifecycle(): ViewModelEvent? {
return archLifecycleSubject.value
}
final override fun requestScope(): CompletableSource {
return LifecycleScopes.resolveScopeFromLifecycle(this)
}
companion object {
private val CORRESPONDING_EVENTS = CorrespondingEventsFunction<ViewModelEvent> { event ->
when (event) {
ViewModelEvent.CREATED -> ViewModelEvent.CLEARED
else -> throw LifecycleEndedException(
"Cannot bind to ViewModel lifecycle after onCleared.")
}
}
}
}

@ -0,0 +1,32 @@
package com.android.base.app.mvvm
import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProvider
import android.arch.lifecycle.ViewModelProviders
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
inline fun <reified VM : ViewModel> Fragment.getViewModel(factory: ViewModelProvider.Factory? = null): VM {
return if (factory == null) {
ViewModelProviders.of(this)[VM::class.java]
} else {
ViewModelProviders.of(this, factory)[VM::class.java]
}
}
inline fun <reified VM : ViewModel> Fragment.getViewModelFromActivity(factory: ViewModelProvider.Factory? = null): VM {
val activity = this.activity ?: throw IllegalStateException("fragment is not attach to activity")
return if (factory == null) {
ViewModelProviders.of(activity)[VM::class.java]
} else {
ViewModelProviders.of(activity, factory)[VM::class.java]
}
}
inline fun <reified VM : ViewModel> FragmentActivity.getViewModel(factory: ViewModelProvider.Factory? = null): VM {
return if (factory == null) {
ViewModelProviders.of(this)[VM::class.java]
} else {
ViewModelProviders.of(this, factory)[VM::class.java]
}
}

@ -0,0 +1,39 @@
package com.android.base.app.ui;
import com.android.base.adapter.DataManager;
/**
* @author Ztiany
* Email: ztiany3@gmail.com
* Date : 2018-04-27 14:04
*/
public class AutoPageNumber extends PageNumber {
private final DataManager mDataManager;
private final RefreshListLayout mRefreshListLayout;
public AutoPageNumber(RefreshListLayout refreshListLayout, DataManager dataManager) {
mRefreshListLayout = refreshListLayout;
mDataManager = dataManager;
}
@Override
public int getCurrentPage() {
return calcPageNumber(mDataManager.getDataSize());
}
@Override
public int getLoadingPage() {
if (mRefreshListLayout.isRefreshing()) {
return getPageStart();
} else {
return getCurrentPage() + 1;
}
}
@Override
public int getItemCount() {
return mDataManager.getDataSize();
}
}

@ -0,0 +1,22 @@
package com.android.base.app.ui;
import com.android.base.R;
public class CommonId {
private CommonId() {
throw new UnsupportedOperationException();
}
public static final int REFRESH_ID = R.id.base_refresh_layout;
public static final int STATE_ID = R.id.base_state_layout;
public static final int LIST_ID = R.id.base_list_layout;
public static final int RETRY_TV_ID = R.id.base_retry_tv;
public static final int RETRY_IV_ID = R.id.base_retry_icon;
public static final int RETRY_BTN_ID = R.id.base_retry_btn;
public static final int TOOLBAR_ID = R.id.common_toolbar;
}

@ -0,0 +1,28 @@
package com.android.base.app.ui;
import android.support.annotation.StringRes;
/**
* 显示通用的 LoadingDialog Message
*
* @author Ztiany
* Email: 1169654504@qq.com
* Date : 2016-12-02 15:12
*/
public interface LoadingView {
void showLoadingDialog();
void showLoadingDialog(boolean cancelable);
void showLoadingDialog(CharSequence message, boolean cancelable);
void showLoadingDialog(@StringRes int messageId, boolean cancelable);
void dismissLoadingDialog();
void showMessage(CharSequence message);
void showMessage(@StringRes int messageId);
}

@ -0,0 +1,9 @@
package com.android.base.app.ui;
import static com.android.base.app.ui.StateLayoutConfig.*;
public interface OnRetryActionListener {
void onRetry(@RetryableState int state);
}

@ -0,0 +1,99 @@
package com.android.base.app.ui;
/**
* @author Ztiany
* @version 1.0
*/
public abstract class PageNumber {
private static int PAGE_START = 1;
private static int PAGE_SIZE = 20;
private int mPageStart;
private int mPageSize;
private Object mPageToken;
@SuppressWarnings("WeakerAccess,unused")
public PageNumber() {
this(PAGE_START, PAGE_SIZE);
}
@SuppressWarnings("WeakerAccess")
public PageNumber(int pageStart, int pageSize) {
mPageStart = pageStart;
mPageSize = pageSize;
}
@SuppressWarnings("unused")
public void setPageToken(Object pageToken) {
mPageToken = pageToken;
}
@SuppressWarnings("unchecked,unused")
public <T> T getPageToken() {
return (T) mPageToken;
}
@SuppressWarnings("unused")
public int getPageSize() {
return mPageSize;
}
@SuppressWarnings("WeakerAccess")
public int getPageStart() {
return mPageStart;
}
public boolean hasMore(int size) {
return size >= mPageSize;
}
/**
* 根据page size计算当前的页码
*/
int calcPageNumber(int dataSize) {
/* s=1 s=0
19/20 = 0 1 0
21/20 = 1 2 1
54/20 = 2 3 2
64/20 = 3 4 3
*/
int pageNumber;
int pageSize = mPageSize;
int pageStart = mPageStart;
if (pageStart == 0) {
pageNumber = (dataSize / pageSize) - 1;
pageNumber = pageNumber < 0 ? 0 : pageNumber;
} else if (pageStart == 1) {
pageNumber = (dataSize / pageSize);
pageNumber = pageNumber < 1 ? 1 : pageNumber;
} else {
throw new RuntimeException("pageStart must be 0 or 1");
}
return pageNumber;
}
@SuppressWarnings("unused")
public void changePageSetting(int pageStart, int pageSize) {
mPageStart = pageStart;
mPageSize = pageSize;
}
public static void setDefaultPageStart(int pageSize) {
PAGE_START = pageSize;
}
public static void setDefaultPageSize(int pageSize) {
PAGE_SIZE = pageSize;
}
public abstract int getCurrentPage();
@SuppressWarnings("unused")
public abstract int getLoadingPage();
@SuppressWarnings("unused")
public abstract int getItemCount();
}

@ -0,0 +1,31 @@
package com.android.base.app.ui;
import java.util.List;
/**
* 列表视图行为
*
* @author Ztiany
* Email: 1169654504@qq.com
* Date : 2017-03-29 22:16
*/
public interface RefreshListLayout<T> extends RefreshStateLayout {
void replaceData(List<T> data);
void addData(List<T> data);
PageNumber getPager();
boolean isEmpty();
void loadMoreCompleted(boolean hasMore);
void loadMoreFailed();
boolean isLoadingMore();
@Override
boolean isRefreshing();
}

@ -0,0 +1,40 @@
package com.android.base.app.ui;
/**
* <br/> 对下拉刷新的抽象
* <br/> Email: 1169654504@qq.com
*
* @author Ztiany
* @version 1.0
*/
public interface RefreshLoadMoreView {
void autoRefresh();
void refreshCompleted();
void loadMoreCompleted(boolean hasMore);
void loadMoreFailed();
void setRefreshHandler(RefreshHandler refreshHandler);
void setLoadMoreHandler(LoadMoreHandler loadMoreHandler);
boolean isRefreshing();
boolean isLoadingMore();
void setRefreshEnable(boolean enable);
void setLoadMoreEnable(boolean enable);
interface RefreshHandler {
void onRefresh();
}
interface LoadMoreHandler {
void onRefresh();
}
}

@ -0,0 +1,31 @@
package com.android.base.app.ui;
import android.view.View;
/**
* RefreshLoadMoreView Factory
*
* @author Ztiany
* Email: 1169654504@qq.com
* @version 1.0
*/
public class RefreshLoadViewFactory {
private static Factory sFactory;
public static RefreshLoadMoreView createRefreshView(View view) {
if (sFactory != null) {
return sFactory.createRefreshView(view);
}
throw new IllegalArgumentException("RefreshLoadViewFactory does not support create RefreshLoadMoreView . the view :" + view);
}
public static void registerFactory(Factory factory) {
sFactory = factory;
}
public interface Factory {
RefreshLoadMoreView createRefreshView(View view);
}
}

@ -0,0 +1,11 @@
package com.android.base.app.ui;
public interface RefreshStateLayout extends StateLayout{
void autoRefresh();
void refreshCompleted();
boolean isRefreshing();
}

@ -0,0 +1,32 @@
package com.android.base.app.ui;
/**
* <br/> 对下拉刷新的抽象
* <br/> Email: 1169654504@qq.com
*
* @author Ztiany
* @version 1.0
*/
public interface RefreshView {
void autoRefresh();
void refreshCompleted();
void setRefreshHandler(RefreshHandler refreshHandler);
boolean isRefreshing();
void setRefreshEnable(boolean enable);
abstract class RefreshHandler {
public boolean canRefresh() {
return true;
}
public abstract void onRefresh();
}
}

@ -0,0 +1,35 @@
package com.android.base.app.ui;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.View;
/**
* RefreshView Factory
*
* @author Ztiany
* Email: 1169654504@qq.com
* @version 1.0
*/
public class RefreshViewFactory {
private static Factory sFactory;
public static RefreshView createRefreshView(View view) {
if (sFactory != null) {
return sFactory.createRefreshView(view);
}
if (view instanceof SwipeRefreshLayout) {
return new SwipeRefreshView((SwipeRefreshLayout) view);
}
throw new IllegalArgumentException("RefreshViewFactory does not support create RefreshView . the view :" + view);
}
public static void registerFactory(Factory factory) {
sFactory = factory;
}
public interface Factory {
RefreshView createRefreshView(View view);
}
}

@ -0,0 +1,27 @@
package com.android.base.app.ui;
/**
* @author Ztiany
* Email: 1169654504@qq.com
* Date : 2017-07-08 14:52
*/
public interface StateLayout {
void showContentLayout();
void showLoadingLayout();
void showEmptyLayout();
void showErrorLayout();
void showRequesting();
void showBlank();
void showNetErrorLayout();
void showServerErrorLayout();
StateLayoutConfig getStateLayoutConfig();
}

@ -0,0 +1,63 @@
package com.android.base.app.ui;
import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes;
import android.support.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @author Ztiany
* Email: 1169654504@qq.com
* Date : 2017-04-20 23:32
*/
public interface StateLayoutConfig {
int CONTENT = 0x01;
int LOADING = 0x02;
int ERROR = 0x03;
int EMPTY = 0x04;
int NET_ERROR = 0x05;
int BLANK = 0x06;
int REQUESTING = 0x07;
int SERVER_ERROR = 0x08;
@IntDef({
EMPTY,
ERROR,
NET_ERROR,
})
@Retention(RetentionPolicy.SOURCE)
@interface RetryableState {
}
@IntDef({
EMPTY,
ERROR,
CONTENT,
LOADING,
NET_ERROR,
BLANK,
REQUESTING,
SERVER_ERROR,
})
@Retention(RetentionPolicy.SOURCE)
@interface ViewState {
}
StateLayoutConfig setStateMessage(@RetryableState int state, CharSequence message);
StateLayoutConfig setStateIcon(@RetryableState int state, Drawable drawable);
StateLayoutConfig setStateIcon(@RetryableState int state, @DrawableRes int drawableId);
StateLayoutConfig setStateAction(@RetryableState int state, CharSequence actionText);
StateLayoutConfig setStateRetryListener(OnRetryActionListener retryActionListener);
StateLayoutConfig disableOperationWhenRequesting(boolean disable);
}

@ -0,0 +1,51 @@
package com.android.base.app.ui;
import android.support.v4.widget.SwipeRefreshLayout;
class SwipeRefreshView implements RefreshView {
private SwipeRefreshLayout mSwipeRefreshLayout;
private RefreshHandler mRefreshHandler;
SwipeRefreshView(SwipeRefreshLayout swipeRefreshLayout) {
mSwipeRefreshLayout = swipeRefreshLayout;
}
/**
* SwipeRefreshLayout
*/
@Override
public void autoRefresh() {
mSwipeRefreshLayout.setRefreshing(true);
doRefresh();
}
@Override
public void refreshCompleted() {
mSwipeRefreshLayout.setRefreshing(false);
}
@Override
public void setRefreshHandler(final RefreshHandler refreshHandler) {
mRefreshHandler = refreshHandler;
mSwipeRefreshLayout.setOnRefreshListener(this::doRefresh);
}
private void doRefresh() {
if (mRefreshHandler.canRefresh()) {
mRefreshHandler.onRefresh();
} else {
mSwipeRefreshLayout.post(() -> mSwipeRefreshLayout.setRefreshing(false));
}
}
@Override
public boolean isRefreshing() {
return mSwipeRefreshLayout.isRefreshing();
}
@Override
public void setRefreshEnable(boolean enable) {
mSwipeRefreshLayout.setEnabled(enable);
}
}

@ -0,0 +1,153 @@
@file:JvmName("UIKit")
package com.android.base.app.ui
import com.android.base.app.BaseKit
import com.android.base.utils.common.Checker
fun <T> RefreshListLayout<T>.processListResultWithStatus(list: List<T>?, onEmpty: (() -> Unit)? = null) {
if (isLoadingMore) {
if (!Checker.isEmpty(list)) {
addData(list)
}
} else {
replaceData(list)
refreshCompleted()
}
if (pager != null) {
loadMoreCompleted(list != null && pager.hasMore(list.size))
}
if (isEmpty) {
if (onEmpty == null) {
showEmptyLayout()
} else {
onEmpty()
}
} else {
showContentLayout()
}
}
fun <T> RefreshListLayout<T>.processListResultWithoutStatus(list: List<T>?, onEmpty: (() -> Unit)? = null) {
if (isLoadingMore) {
if (!Checker.isEmpty(list)) {
addData(list)
}
} else {
replaceData(list)
refreshCompleted()
}
if (pager != null) {
loadMoreCompleted(list != null && pager.hasMore(list.size))
}
if (onEmpty != null && isEmpty) {
onEmpty()
}
}
fun <T> RefreshListLayout<T>.submitListResultWithStatus(list: List<T>?, hasMore: Boolean, onEmpty: (() -> Unit)? = null) {
if (isRefreshing) {
refreshCompleted()
}
replaceData(list)
loadMoreCompleted(hasMore)
if (isEmpty) {
if (onEmpty == null) {
showEmptyLayout()
} else {
onEmpty()
}
} else {
showContentLayout()
}
}
fun <T> RefreshListLayout<T>.submitListResultWithoutStatus(list: List<T>?, hasMore: Boolean, onEmpty: (() -> Unit)? = null) {
if (isRefreshing) {
refreshCompleted()
}
replaceData(list)
loadMoreCompleted(hasMore)
if (onEmpty != null && isEmpty) {
onEmpty()
}
}
fun RefreshListLayout<*>.processListErrorWithStatus(throwable: Throwable) {
if (isRefreshing) {
refreshCompleted()
}
if (isLoadingMore) {
loadMoreFailed()
}
if (isEmpty) {
val errorTypeClassifier = BaseKit.get().errorClassifier()
if (errorTypeClassifier != null) {
when {
errorTypeClassifier.isNetworkError(throwable) -> showNetErrorLayout()
errorTypeClassifier.isServerError(throwable) -> showServerErrorLayout()
else -> showErrorLayout()
}
} else {
showErrorLayout()
}
} else {
showContentLayout()
}
}
fun RefreshListLayout<*>.processListErrorWithoutStatus() {
if (isRefreshing) {
refreshCompleted()
}
if (isLoadingMore) {
loadMoreFailed()
}
}
fun RefreshListLayout<*>.showLoadingIfEmpty() {
if (isEmpty) {
if (isRefreshing) {
showBlank()
} else {
showLoadingLayout()
}
}
}
fun <T> RefreshStateLayout.processResultWithStatus(t: T?, onResult: ((T) -> Unit)) {
if (isRefreshing) {
refreshCompleted()
}
if (t == null || (t is Collection<*> && t.isEmpty()) || (t is Map<*, *> && t.isEmpty())) {
showEmptyLayout()
} else {
onResult.invoke(t)
showContentLayout()
}
}
fun RefreshStateLayout.processErrorWithStatus(throwable: Throwable) {
if (isRefreshing) {
refreshCompleted()
}
val errorTypeClassifier = BaseKit.get().errorClassifier()
if (errorTypeClassifier != null) {
when {
errorTypeClassifier.isNetworkError(throwable) -> showNetErrorLayout()
errorTypeClassifier.isServerError(throwable) -> showServerErrorLayout()
else -> showErrorLayout()
}
} else {
showErrorLayout()
}
}

@ -0,0 +1,106 @@
package com.android.base.concurrent;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.util.Log;
import java.io.File;
import java.io.FileFilter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;
/**
* @author MaTianyu
* @see <a href="https://github.com/litesuits/android-lite-go">android-lite-go</a>
*/
class GoUtil {
private static final String TAG = GoUtil.class.getSimpleName();
private static final String PATH_CPU = "/sys/devices/system/cpu/";
private static final String CPU_FILTER = "cpu[0-9]+";
private static int CPU_CORES = 0;
public static String formatDate(long millis) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.format(new Date(millis));
}
/**
* Get available processors.
*/
public static int getProcessorsCount() {
return Runtime.getRuntime().availableProcessors();
}
/**
* Gets the number of cores available in this device, across all processors.
* Requires: Ability to peruse the filesystem at "/sys/devices/system/cpu"
*
* @return The number of cores, or available processors if failed to get result
*/
public static int getCoresNumbers() {
if (CPU_CORES > 0) {
return CPU_CORES;
}
//Private Class to display only CPU devices in the directory listing
class CpuFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
//Check if filename is "cpu", followed by a single digit number
if (Pattern.matches(CPU_FILTER, pathname.getName())) {
return true;
}
return false;
}
}
try {
//Get directory containing CPU info
File dir = new File(PATH_CPU);
//Filter to only list the devices we care about
File[] files = dir.listFiles(new CpuFilter());
//Return the number of cores (virtual CPU devices)
CPU_CORES = files.length;
} catch (Exception e) {
e.printStackTrace();
}
if (CPU_CORES < 1) {
CPU_CORES = Runtime.getRuntime().availableProcessors();
}
if (CPU_CORES < 1) {
CPU_CORES = 1;
}
Log.i(TAG, "CPU cores: " + CPU_CORES);
return CPU_CORES;
}
public static AlertDialog.Builder dialogBuilder(Context context, String title, String msg) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
if (msg != null) {
builder.setMessage(msg);
}
if (title != null) {
builder.setTitle(title);
}
return builder;
}
public static Dialog showTips(Context context, String title, String des) {
return showTips(context, title, des, null, null);
}
public static Dialog showTips(Context context, String title, String des, String btn,
DialogInterface.OnDismissListener dismissListener) {
AlertDialog.Builder builder = dialogBuilder(context, title, des);
builder.setCancelable(true);
builder.setPositiveButton(btn, null);
Dialog dialog = builder.show();
dialog.setCanceledOnTouchOutside(true);
dialog.setOnDismissListener(dismissListener);
return dialog;
}
}

@ -0,0 +1,37 @@
package com.android.base.concurrent;
import java.util.concurrent.Executor;
/**
* Smart执行器
*
* @see <a href="https://github.com/litesuits/android-lite-go">android-lite-go</a>
*/
public class JobExecutor {
private static SmartExecutor smartExecutor;
public static void setSchedulePolicy(SchedulePolicy policy) {
if (smartExecutor != null) {
smartExecutor.setSchedulePolicy(policy);
}
}
public static void setOverloadPolicy(OverloadPolicy policy) {
if (smartExecutor != null) {
smartExecutor.setOverloadPolicy(policy);
}
}
static {
smartExecutor = new SmartExecutor();
smartExecutor.setSchedulePolicy(SchedulePolicy.LastInFirstRun);
smartExecutor.setOverloadPolicy(OverloadPolicy.DiscardOldTaskInQueue);
}
public static Executor getJobExecutor() {
return smartExecutor;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save