commit
60d4aa0b20
@ -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,封装通用流程。 |
||||||
|
* 2,onBackPressed 事件分发,优先交给 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 press,false表示由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: 支持显示{CONTENT、LOADING、ERROR、EMPTY}四种布局、支持下拉刷新 |
||||||
|
* 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…
Reference in new issue