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