update mvvm

This commit is contained in:
2024-05-27 09:53:34 +08:00
parent 66d4a00ba8
commit 68f0e5a4f7
34 changed files with 1494 additions and 391 deletions

View File

@@ -50,7 +50,7 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
viewBinding{
viewBinding {
enabled = true
}
@@ -142,14 +142,18 @@ dependencies {
//okhttp
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
//log打印
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
// gson converter
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// 标准转换器,去掉 Retrofit以Mutipart上传参数时String参数会多一对双引号
implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
//RxJava
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
@@ -189,6 +193,8 @@ dependencies {
implementation 'com.gitee.zackratos:UltimateBarX:0.8.0'
// //验证码输入
// implementation 'com.jacktuotuo.customview:verificationcodeview:1.0.5'
//动态权限框架
// implementation 'com.hjq:xxpermissions:6.0'
// 权限请求框架https://github.com/getActivity/XXPermissions
implementation 'com.github.getActivity:XXPermissions:18.63'
// 吐司框架https://github.com/getActivity/Toaster
implementation 'com.github.getActivity:Toaster:12.6'
}

View File

@@ -6,15 +6,16 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.ttstd.template.base.BaseApplication"
android:name=".base.BaseApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network"
android:theme="@style/AppTheme">
<activity
android:name="com.ttstd.template.activity.main.MainActivity"
android:name=".activity.main.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -23,6 +24,13 @@
</intent-filter>
</activity>
<activity
android:name=".activity.template.mvp.TemplateActivity"
android:exported="true" />
<activity
android:name=".activity.template.mvvm.MvvmActivity"
android:exported="true" />
<provider
android:name="androidx.core.content.FileProvider"
@@ -37,9 +45,9 @@
<!-- 这个 Demo 主要展示副单位的用法, 如果只使用副单位 (pt、in、mm) 就可以直接以像素作为单位填写设计图的尺寸, 不需再把像素转化为 dp-->
<meta-data
android:name="design_width_in_dp"
android:value="1080"/>
android:value="1080" />
<meta-data
android:name="design_height_in_dp"
android:value="1920"/>
android:value="1920" />
</application>
</manifest>

View File

@@ -1,10 +1,22 @@
package com.ttstd.template.activity.main;
import android.content.Intent;
import android.view.View;
import android.widget.Button;
import com.ttstd.template.R;
import com.ttstd.template.activity.template.mvp.TemplateActivity;
import com.ttstd.template.activity.template.mvvm.MvvmActivity;
import com.ttstd.template.base.BaseActivity;
public class MainActivity extends BaseActivity {
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends BaseActivity {
@BindView(R.id.bt_mvp)
Button bt_mvp;
@BindView(R.id.bt_mvvm)
Button bt_mvvm;
@Override
public int getLayoutId() {
@@ -12,8 +24,30 @@ public class MainActivity extends BaseActivity {
}
@Override
public void initView() {
protected boolean setNightMode() {
return true;
}
@Override
protected boolean setfitWindow() {
return true;
}
@Override
public void initView() {
ButterKnife.bind(this);
bt_mvp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, TemplateActivity.class));
}
});
bt_mvvm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, MvvmActivity.class));
}
});
}
@Override

View File

@@ -1,30 +1,39 @@
package com.ttstd.template.activity.template;
package com.ttstd.template.activity.template.mvp;
import android.content.res.Configuration;
import androidx.annotation.NonNull;
import com.ttstd.template.R;
import com.ttstd.template.base.BaseLightActivity;
import com.ttstd.template.base.mvp.BaseMvpActivity;
import butterknife.ButterKnife;
public class TemplateActivity extends BaseLightActivity implements TemplateContact.TemplateView {
public class TemplateActivity extends BaseMvpActivity implements TemplateContact.TemplateView {
private static final String TAG = TemplateActivity.class.getSimpleName();
private TemplatePresenter mPresenter;
@Override
public int getLayoutId() {
return R.layout.activity_video;
return R.layout.activity_main;
}
@Override
protected boolean setNightMode() {
return false;
}
@Override
protected boolean setfitWindow() {
return false;
}
@Override
public void initView() {
ButterKnife.bind(this);
mPresenter = new TemplatePresenter(this);
mPresenter.setLifecycle(lifecycleSubject);
mPresenter.setLifecycle(getLifecycleSubject());
mPresenter.attachView(this);
}

View File

@@ -1,7 +1,7 @@
package com.ttstd.template.activity.template;
package com.ttstd.template.activity.template.mvp;
import com.ttstd.template.base.BasePresenter;
import com.ttstd.template.base.BaseView;
import com.ttstd.template.base.mvp.BasePresenter;
import com.ttstd.template.base.mvp.BaseView;
public class TemplateContact {
interface Presenter extends BasePresenter<TemplateView> {

View File

@@ -1,4 +1,4 @@
package com.ttstd.template.activity.template;
package com.ttstd.template.activity.template.mvp;
import android.content.Context;

View File

@@ -0,0 +1,55 @@
package com.ttstd.template.activity.template.mvvm;
import android.util.Log;
import androidx.lifecycle.Observer;
import com.ttstd.template.R;
import com.ttstd.template.base.mvvm.BaseMvvmActivity;
import com.ttstd.template.databinding.ActivityMvvmBinding;
public class MvvmActivity extends BaseMvvmActivity<MvvmViewModel, ActivityMvvmBinding> {
private static final String TAG = MvvmActivity.class.getSimpleName();
@Override
protected int getLayoutId() {
return R.layout.activity_mvvm;
}
@Override
protected boolean setNightMode() {
return true;
}
@Override
protected boolean setfitWindow() {
return true;
}
@Override
protected void initDataBinding() {
mViewModel.setCtx(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewModel.getData().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e(TAG, "onChanged: " + s);
}
});
}
@Override
protected void initView() {
}
@Override
protected void initData() {
mViewModel.setData("mvvm");
mViewModel.getIp();
}
}

View File

@@ -0,0 +1,43 @@
package com.ttstd.template.activity.template.mvvm;
import android.util.Log;
import androidx.lifecycle.MutableLiveData;
import com.ttstd.template.base.mvvm.BaseViewModel;
import com.ttstd.template.databinding.ActivityMvvmBinding;
import com.ttstd.template.network.NetInterfaceManager;
public class MvvmViewModel extends BaseViewModel<ActivityMvvmBinding> {
private static final String TAG = MvvmViewModel.class.getSimpleName();
@Override
public void onDestroy() {
}
@Override
public ActivityMvvmBinding getVDBinding() {
return binding;
}
private MutableLiveData<String> mData = new MutableLiveData<>();
public MutableLiveData<String> getData() {
return mData;
}
public void setData(String s) {
mData.setValue(s);
mData.postValue(s);
}
public void getIp() {
NetInterfaceManager.getInstance().getPublicIp(getLifecycle(), new NetInterfaceManager.PublicIpCallbak() {
@Override
public void getPublicIp(String ip) {
Log.e(TAG, "getPublicIp: " + ip);
}
});
}
}

View File

@@ -1,134 +1,32 @@
package com.ttstd.template.base;
import android.app.ActivityManager;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.ContentView;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import com.ttstd.template.R;
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
public final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// StatusBarUtil.init(this);
UltimateBarX.statusBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(true)
.apply();
UltimateBarX.navigationBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(true)
.apply();
setContentView(this.getLayoutId());
initView();
initData();
//最近任务和应用图标不一样
if (Build.VERSION.SDK_INT > 27) {
ActivityManager.TaskDescription description = new ActivityManager.TaskDescription(getString(R.string.app_name), R.mipmap.ic_launcher, getColor(R.color.colorPrimary));
this.setTaskDescription(description);
}
}
/**
* 设置布局
*/
public abstract int getLayoutId();
/**
* 初始化视图
*/
public abstract void initView();
/**
* 初始化数据
*/
public abstract void initData();
public abstract class BaseActivity extends BaseTransparentActivity {
public BaseActivity() {
super();
}
@ContentView
public BaseActivity(@LayoutRes int contentLayoutId) {
super(contentLayoutId);
}
@Override
@NonNull
@CheckResult
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
@CallSuper
protected void onStart() {
super.onStart();
lifecycleSubject.onNext(ActivityEvent.START);
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
initView();
initData();
}
@Override
@CallSuper
protected void onResume() {
super.onResume();
lifecycleSubject.onNext(ActivityEvent.RESUME);
}
/**
* 初始化视图
*/
protected abstract void initView();
@Override
@CallSuper
protected void onPause() {
lifecycleSubject.onNext(ActivityEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
protected void onStop() {
lifecycleSubject.onNext(ActivityEvent.STOP);
super.onStop();
}
@Override
@CallSuper
protected void onDestroy() {
lifecycleSubject.onNext(ActivityEvent.DESTROY);
super.onDestroy();
}
}
/**
* 初始化数据
*/
protected abstract void initData();
}

View File

@@ -0,0 +1,62 @@
package com.ttstd.template.base;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import com.ttstd.template.R;
import com.ttstd.template.base.rx.BaseRxActivity;
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
public abstract class BaseDataBindingActivity extends BaseRxActivity {
public BaseDataBindingActivity() {
super();
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// StatusBarUtil.init(this);
UltimateBarX.statusBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(setNightMode())
.fitWindow(setfitWindow())
.apply();
UltimateBarX.navigationBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(setNightMode())
.fitWindow(setfitWindow())
.apply();
initDataBinding();
initView();
initData();
}
/**
* @return 是否是黑色状态栏
*/
protected abstract boolean setNightMode();
/**
* @return 是否是入侵
*/
protected abstract boolean setfitWindow();
protected abstract void initDataBinding();
/**
* 初始化视图
*/
protected abstract void initView();
/**
* 初始化数据
*/
protected abstract void initData();
}

View File

@@ -1,72 +1,15 @@
package com.ttstd.template.base;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ttstd.template.base.rx.BaseRxFragment;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseFragment extends Fragment implements LifecycleProvider<FragmentEvent> {
public final BehaviorSubject<FragmentEvent> lifecycleSubject = BehaviorSubject.create();
public abstract class BaseFragment extends BaseRxFragment {
protected boolean isViewInitiated;
protected boolean isVisibleToUser;
protected boolean isDataInitiated;
@Override
@NonNull
@CheckResult
public final Observable<FragmentEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull FragmentEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindFragment(lifecycleSubject);
}
@Override
@CallSuper
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
lifecycleSubject.onNext(FragmentEvent.ATTACH);
}
@Override
@CallSuper
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
@@ -92,65 +35,10 @@ public abstract class BaseFragment extends Fragment implements LifecycleProvider
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
fetchData();
//注释掉保证每次都更新数据
isDataInitiated = true;
// isDataInitiated = true;
return true;
}
return false;
}
@Override
@CallSuper
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE_VIEW);
}
@Override
@CallSuper
public void onStart() {
super.onStart();
lifecycleSubject.onNext(FragmentEvent.START);
}
@Override
@CallSuper
public void onResume() {
super.onResume();
lifecycleSubject.onNext(FragmentEvent.RESUME);
}
@Override
@CallSuper
public void onPause() {
lifecycleSubject.onNext(FragmentEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
public void onStop() {
lifecycleSubject.onNext(FragmentEvent.STOP);
super.onStop();
}
@Override
@CallSuper
public void onDestroyView() {
lifecycleSubject.onNext(FragmentEvent.DESTROY_VIEW);
super.onDestroyView();
}
@Override
@CallSuper
public void onDestroy() {
lifecycleSubject.onNext(FragmentEvent.DESTROY);
super.onDestroy();
}
@Override
@CallSuper
public void onDetach() {
lifecycleSubject.onNext(FragmentEvent.DETACH);
super.onDetach();
}
}

View File

@@ -0,0 +1,273 @@
package com.ttstd.template.base;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
/**
* @author: lml
* @date: 2021/12/15
*/
public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseFragment {
protected String mTag = this.getClass().getSimpleName();
/**
* 是否顯示了
*/
protected boolean mIsVisible;
/**
* 是否準備好了-Created
*/
protected boolean mHasPrepare;
protected VM mViewModel;
protected VDB mViewDataBinding;
protected Class<VM> vmClass;
//
// protected Toolbar toolbar;
// protected View statusBarView;
//
protected Bundle bundle;//来自getArguments()
protected Bundle savedInstanceState;
// protected Context context;
/**
* 上下文
*/
private WeakReference<Context> ctx;
public Context getCtx() {
return ctx == null ? null : ctx.get();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// this.context = context;
ctx = new WeakReference<>(context);
}
/**
* onCreate、onResume里不能调用
*
* @return
*/
public boolean isAttached() {
boolean flag = getCtx() != null && isAdded();
Log.e(" >> isAttached >>", "flag = " + flag);
return flag;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//ViewDataBinding
mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
mViewDataBinding.setLifecycleOwner(this);
//ViewModel
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
mViewModel = new ViewModelProvider(this).get(vmClass);
//
return mViewDataBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// if (initStatusBarToolBar()) {
// toolbar = getToolbar();
// }
//注册eventbus
// if (getClass().isAnnotationPresent(BindEventBus.class))
// EventBusManager.register(this);
//
// fitsLayoutOverlap();
initDataBinding();
initView(bundle = getArguments());
//
initData(this.savedInstanceState = savedInstanceState);
//
if (mIsVisible) {
onEnter();
}
mHasPrepare = true;
//
// LiveDataBus.get().with(ConstantUtils.DATA_BUS_LOADING_FRAGMENT, Boolean.class).observe(getActivity(), bool -> {
// L.e(" >> LiveDataBus >> DATA_BUS_LOADING_FRAGMENT: %s", bool);
// if(bool) {
// showLoading(R.string.str_please_wait);
// } else {
// hideLoading();
// }
// });
}
@Override
public void onDestroyView() {
super.onDestroyView();
mHasPrepare = false;
mViewDataBinding = null;
//移除eventbus
// if (getClass().isAnnotationPresent(BindEventBus.class))
// EventBusManager.unregister(this);
//
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mIsVisible == getUserVisibleHint())
return;
mIsVisible = getUserVisibleHint();
if (mIsVisible) {
if (!mHasPrepare)
return;
onEnter();
} else {
onExit();
}
}
@LayoutRes
protected abstract int getLayoutId();
// protected abstract Toolbar getToolbar();
// protected View getStatusView() {
// return null;
// }
protected abstract void initDataBinding();
protected abstract void initView(Bundle bundle);
protected abstract void initData(Bundle savedInstanceState);
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// fitsLayoutOverlap();
}
// protected boolean isImmersionBarEnabled() {
// return true;
// }
// protected boolean initStatusBarToolBar() {
// return true;
// }
// private void fitsLayoutOverlap() {
// if (!isImmersionBarEnabled()) return;
// if (statusBarView != null) {
// ImmersionBar.setStatusBarView(getActivity(), statusBarView);
// }
// if (toolbar != null) {
// ImmersionBar.setTitleBar(getActivity(), toolbar);
// }
// }
protected void hideInputMethod(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
protected void hideInputMethod(Activity activity, EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
protected void showInputMethod(EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
}
// private CustomDialog mWaitDialog;
//
// public void showLoading(@StringRes int contentID) {
// showLoading(contentID, R.color.white);
// }
//
// public void showLoading(@StringRes int contentID, @ColorRes int color) {
// hideLoading();
// DialogX.init(getActivity());
// if (color == R.color.white) {
// mWaitDialog = DialogXUtil.getInstance().showLoading(getActivity(), getString(contentID), getResources().getColor(color));
// } else {
// mWaitDialog = DialogXUtil.getInstance().showLoading_black(getActivity(), getString(contentID), getResources().getColor(color));
// }
// }
//
// public void updateLoadingTip(@StringRes int messageID, int percent) {
// try {
// if (mWaitDialog != null && mWaitDialog.isShow()) {
// TextView tvTip = mWaitDialog.getCustomView().findViewById(R.id.tv_load_tip);
// if (tvTip != null)
// tvTip.setText(getResources().getString(messageID) + (percent == -1 ? "" : percent + "%"));
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//
// public boolean isShowLoading() {
// return mWaitDialog != null && mWaitDialog.isShow();
// }
//
// public void hideLoading() {
// try {
// boolean isShow = isShowLoading();
// L.d(" >> hideLoading :: isShow: %s", isShow);
// if (isShow)
// mWaitDialog.dismiss();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
/**
* 進入界面
*/
protected void onEnter() {
}
/**
* 離開界面
*/
protected void onExit() {
}
}

View File

@@ -0,0 +1,56 @@
package com.ttstd.template.base;
import android.app.Service;
import android.content.Intent;
import androidx.annotation.NonNull;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseService extends Service implements LifecycleProvider<ActivityEvent> {
public final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
public BaseService() {
super();
}
@Override
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
public void onCreate() {
super.onCreate();
lifecycleSubject.onNext(ActivityEvent.CREATE);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycleSubject.onNext(ActivityEvent.STOP);
}
}

View File

@@ -0,0 +1,51 @@
package com.ttstd.template.base;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import com.ttstd.template.R;
import com.ttstd.template.base.rx.BaseRxActivity;
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
public abstract class BaseTransparentActivity extends BaseRxActivity {
public BaseTransparentActivity() {
super();
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// StatusBarUtil.init(this);
UltimateBarX.statusBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(setNightMode())
.fitWindow(setfitWindow())
.apply();
UltimateBarX.navigationBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(setNightMode())
.fitWindow(setfitWindow())
.apply();
}
/**
* 设置布局
*/
protected abstract int getLayoutId();
/**
* @return 是否是黑色状态栏
*/
protected abstract boolean setNightMode();
/**
* @return 是否是入侵
*/
protected abstract boolean setfitWindow();
}

View File

@@ -1,4 +0,0 @@
package com.ttstd.template.base;
public interface BaseView {
}

View File

@@ -0,0 +1,34 @@
package com.ttstd.template.base.mvp;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import com.ttstd.template.base.BaseTransparentActivity;
public abstract class BaseMvpActivity extends BaseTransparentActivity {
public BaseMvpActivity() {
super();
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
initView();
initData();
}
/**
* 初始化视图
*/
protected abstract void initView();
/**
* 初始化数据
*/
protected abstract void initData();
}

View File

@@ -1,4 +1,4 @@
package com.ttstd.template.base;
package com.ttstd.template.base.mvp;
public interface BasePresenter<V extends BaseView> {
void attachView(V view);

View File

@@ -0,0 +1,4 @@
package com.ttstd.template.base.mvp;
public interface BaseView {
}

View File

@@ -0,0 +1,55 @@
package com.ttstd.template.base.mvvm;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.ttstd.template.base.BaseTransparentActivity;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
public abstract class BaseMvvmActivity<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseTransparentActivity {
private static final String TAG = BaseMvvmActivity.class.getSimpleName();
protected VM mViewModel;
protected VDB mViewDataBinding;
protected Class<VM> vmClass;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//ViewDataBinding
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId());
mViewDataBinding.setLifecycleOwner(this);
//ViewModel
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
boolean isAbstract = Modifier.isAbstract(vmClass.getModifiers());
Log.e(TAG, "isLocalClass:" + vmClass.getSimpleName().equals(ViewModel.class.getSimpleName()) + " isAbstract:" + isAbstract);
if (!isAbstract) {//不是一个抽象类
mViewModel = new ViewModelProvider(this).get(vmClass);
}
initDataBinding();
initView();
initData();
}
protected abstract void initDataBinding();
/**
* 初始化视图
*/
protected abstract void initView();
/**
* 初始化数据
*/
protected abstract void initData();
}

View File

@@ -0,0 +1,73 @@
package com.ttstd.template.base.mvvm;
import android.content.Context;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModel;
import com.trello.rxlifecycle4.android.ActivityEvent;
import java.lang.ref.WeakReference;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
/**
* 所有viewmodel的基类
*/
public abstract class BaseViewModel<VDB extends ViewDataBinding> extends ViewModel implements ViewDataBindingCallback {
/**
* 当前viewmodel对应的页面binding
*/
protected VDB binding;
@Override
public void setVDBinding(ViewDataBinding vdBinding) {
binding = (VDB)vdBinding;
}
@Override
public VDB getVDBinding() {
if (binding == null) {
throw new NullPointerException("BaseViewModel >> getVDBinding >> null!!!");
}
return binding;
}
/**
* 上下文
*/
private WeakReference<Context> ctx;
@Override
public void setCtx(Context context) {
if(ctx == null) {
ctx = new WeakReference<>(context);
}
}
@Override
public Context getCtx() {
if (ctx == null) {
throw new NullPointerException("BaseViewModel >> getCtx >> null!!!");
}
return ctx.get();
}
public abstract void onDestroy();
private BehaviorSubject<ActivityEvent> mBehaviorSubject;
@Override
public void setLifecycle(BehaviorSubject subject) {
this.mBehaviorSubject =subject;
}
@Override
public BehaviorSubject<ActivityEvent> getLifecycle() {
return mBehaviorSubject;
}
}

View File

@@ -0,0 +1,26 @@
package com.ttstd.template.base.mvvm;
import android.content.Context;
import androidx.databinding.ViewDataBinding;
import com.trello.rxlifecycle4.android.ActivityEvent;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public interface ViewDataBindingCallback<VDB extends ViewDataBinding> {
void setVDBinding(VDB binding);
VDB getVDBinding() throws NullPointerException;
void setCtx(Context context);
Context getCtx() throws NullPointerException;
void setLifecycle(BehaviorSubject<ActivityEvent> subject);
BehaviorSubject<ActivityEvent> getLifecycle();
}

View File

@@ -1,11 +1,9 @@
package com.ttstd.template.base;
package com.ttstd.template.base.rx;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.ContentView;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
@@ -15,23 +13,19 @@ import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import com.ttstd.template.R;
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
/**
* {@link com.trello.rxlifecycle4.components.RxActivity}
* copied form RxActivity}
*/
public abstract class BaseRxActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
public abstract class BaseLightActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
public final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
public BaseLightActivity() {
super();
}
@ContentView
public BaseLightActivity(@LayoutRes int contentLayoutId) {
super(contentLayoutId);
public BehaviorSubject<ActivityEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@@ -60,38 +54,8 @@ public abstract class BaseLightActivity extends AppCompatActivity implements Lif
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(ActivityEvent.CREATE);
// StatusBarUtil.init(this);
UltimateBarX.statusBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(true)
.apply();
UltimateBarX.navigationBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(true)
.apply();
setContentView(this.getLayoutId());
initView();
initData();
}
/**
* 设置布局
*/
public abstract int getLayoutId();
/**
* 初始化视图
*/
public abstract void initView();
/**
* 初始化数据
*/
public abstract void initData();
@Override
@CallSuper
protected void onStart() {
@@ -127,3 +91,4 @@ public abstract class BaseLightActivity extends AppCompatActivity implements Lif
super.onDestroy();
}
}

View File

@@ -0,0 +1,123 @@
package com.ttstd.template.base.rx;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
/**
* {@link com.trello.rxlifecycle4.components.RxFragment}
* copied form RxFragment}
*/
public class BaseRxFragment extends Fragment implements LifecycleProvider<FragmentEvent> {
private final BehaviorSubject<FragmentEvent> lifecycleSubject = BehaviorSubject.create();
public BehaviorSubject<FragmentEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@NonNull
@CheckResult
public final Observable<FragmentEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull FragmentEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindFragment(lifecycleSubject);
}
@Override
@CallSuper
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
lifecycleSubject.onNext(FragmentEvent.ATTACH);
}
@Override
@CallSuper
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE);
}
@Override
@CallSuper
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE_VIEW);
}
@Override
@CallSuper
public void onStart() {
super.onStart();
lifecycleSubject.onNext(FragmentEvent.START);
}
@Override
@CallSuper
public void onResume() {
super.onResume();
lifecycleSubject.onNext(FragmentEvent.RESUME);
}
@Override
@CallSuper
public void onPause() {
lifecycleSubject.onNext(FragmentEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
public void onStop() {
lifecycleSubject.onNext(FragmentEvent.STOP);
super.onStop();
}
@Override
@CallSuper
public void onDestroyView() {
lifecycleSubject.onNext(FragmentEvent.DESTROY_VIEW);
super.onDestroyView();
}
@Override
@CallSuper
public void onDestroy() {
lifecycleSubject.onNext(FragmentEvent.DESTROY);
super.onDestroy();
}
@Override
@CallSuper
public void onDetach() {
lifecycleSubject.onNext(FragmentEvent.DETACH);
super.onDetach();
}
}

View File

@@ -0,0 +1,109 @@
package com.ttstd.template.bean;
import androidx.annotation.NonNull;
import com.google.gson.Gson;
import com.google.gson.JsonParser;
import java.io.Serializable;
public class WhoisBean implements Serializable {
private static final long serialVersionUID = -6537021620041268080L;
String ip;
String pro;
String proCode;
String city;
String cityCode;
String region;
String regionCode;
String addr;
String regionNames;
String err;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPro() {
return pro;
}
public void setPro(String pro) {
this.pro = pro;
}
public String getProCode() {
return proCode;
}
public void setProCode(String proCode) {
this.proCode = proCode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getRegionCode() {
return regionCode;
}
public void setRegionCode(String regionCode) {
this.regionCode = regionCode;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getRegionNames() {
return regionNames;
}
public void setRegionNames(String regionNames) {
this.regionNames = regionNames;
}
public String getErr() {
return err;
}
public void setErr(String err) {
this.err = err;
}
@NonNull
@Override
public String toString() {
return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString();
}
}

View File

@@ -4,19 +4,33 @@ import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import com.tencent.mmkv.MMKV;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.template.bean.BaseResponse;
import com.ttstd.template.bean.WhoisBean;
import com.ttstd.template.comm.CommonConfig;
import com.ttstd.template.network.api.GetWhoisApi;
import com.ttstd.template.network.interceptor.RepeatRequestInterceptor;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
import okhttp3.Cache;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
@@ -57,7 +71,12 @@ public class NetInterfaceManager {
builder.readTimeout(timeOut, TimeUnit.SECONDS);// 设置读取数据超时时间
builder.retryOnConnectionFailure(true);// 设置进行连接失败重试
builder.addInterceptor(new RepeatRequestInterceptor());
builder.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(@NotNull String s) {
Log.e("HttpLoggingInterceptor", "log: " + s);
}
}).setLevel(HttpLoggingInterceptor.Level.BASIC));
// 设置缓存文件路径
String cacheDirectory = getCacheDir() + "/OkHttpCache";
Cache cache = new Cache(new File(cacheDirectory), cacheSize);
@@ -111,6 +130,9 @@ public class NetInterfaceManager {
return okHttpClient;
}
public static final String WHOIS_IP = "whois_ip_addr";
/*
*
* Observable
@@ -155,4 +177,52 @@ public class NetInterfaceManager {
void onComplete();
}
public interface PublicIpCallbak {
void getPublicIp(String ip);
}
public void getPublicIp(BehaviorSubject<ActivityEvent> lifecycle, PublicIpCallbak callbak) {
Retrofit retrofit = new Retrofit.Builder()
.client(NetInterfaceManager.getInstance().getOkHttpClient())
.baseUrl(UrlAddress.PCONLINE_WHOIS)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build();
retrofit.create(GetWhoisApi.class)
.getWhois(true)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
.subscribe(new Observer<WhoisBean>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("getPublicIp", "onSubscribe: ");
}
@Override
public void onNext(@NonNull WhoisBean whoisBean) {
Log.e("getPublicIp", "onNext: " + whoisBean);
if (whoisBean != null && !TextUtils.isEmpty(whoisBean.getIp())) {
callbak.getPublicIp(whoisBean.getIp());
MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE).encode(WHOIS_IP, whoisBean.getIp());
} else {
callbak.getPublicIp("unknown");
}
}
@Override
public void onError(@NonNull Throwable e) {
Log.e("getPublicIp", "onError: ");
callbak.getPublicIp("unknown");
onComplete();
}
@Override
public void onComplete() {
Log.e("getPublicIp", "onComplete: ");
}
});
}
}

View File

@@ -4,77 +4,7 @@ public class UrlAddress {
/*主页接口*/
public static final String ROOT_URL = "https://led.zuoyepad.com/android/";
/*设备激活*/
public static final String SN_ACTIVATION = "sn/snActivation";
/*获取设备是否激活*/
public static final String GET_SN_IS_ACTIVATION = "sn/getSnIsActivation";
/*获取设备激活二维码链接*/
public static final String ACTIVATION_QRCODE = "pay/getActivationQrcode";
/*获取文件*/
public static final String GET_FILES = "file/getFiles";
/*获取操作指南*/
public static final String GET_OPERATION_GUIDE = "file/getFiles";
/*上传屏幕截图*/
public final static String UPLOAD_SCREEN_SNAPSHOT = "sn/uploadScreenshot";
/*浏览器网址管控*/
public final static String SET_BROWSER_URL = "control/getBrowser";
/*浏览器书签管控*/
public final static String SET_BROWSER_LABEL = "control/getLabel";
/*上传控制面版截图*/
public static final String UPLOAD_CONTROL_SCREENSHOT = "sn/uploadControlScreenshot";
/*设备信息接口*/
public static final String SNINFO = "sn/getSnInfo";
/*获取用户头像和信息*/
public static final String GET_USER_AVATAR_INFO = "sn/getUserAvatarInfo";
/*获取设备类型*/
public static final String GET_SN_TYPE = "sn/getSnType";
/*获取正在运行的app*/
public static final String RUN_NEW_APP = "app/runNewApp";
/*获取所有应用*/
public final static String GET_ALL_PACKAGE = "app/queryAllApp";
/*绑定设备消息*/
public final static String BIND_DEVICES = "sn/bindSn";
/*获取系统设置*/
public final static String GET_SETTINGS = "control/getSetting";
/*获取强制下载*/
public final static String GET_FORCE_INSTALL = "app/getForceDownload";
/*发送卸载或者安装信息*/
public final static String SEND_INSTALLEDORREMOVED = "app/addAppInstall";
/*发送设备基本信息*/
public final static String UPDATE_SNINFO = "sn/updateAdminSn";
/*根据包名获取更新*/
public final static String GET_NEWESTAPPUPDATE = "app/newestAppUpdate";
/*获取禁用包名*/
public static final String GET_APP_ICON = "getAppIcon";
/*获取灰度更新*/
public static final String GET_TEST_APP_INFO = "app/getTestAppInfo";
/*获取wifi*/
public static final String GET_WIFI_ALIAS_PW = "getWifi";
/*获取屏幕管控*/
public final static String GET_SCREEN_LOCK = "sn/getScreenshot";
/*获取锁屏密码*/
public static final String LOCK_SCREEN_PWD = "sn/getLockScreenPwd";
/*解除锁屏*/
public static final String UPDATE_LOCK_SCREEN = "sn/updateLockScreen";
/*发送绑定验证码*/
public static final String SEND_BIND_VER_CODE = "Sn/sendBindVerCode";
/*手动绑定设备*/
public static final String EQUIPMENT_BIND = "Sn/equipmentBind";
/*上传应用图标*/
public static final String UPLOAD_APP_IMG = "collectData/uploadAppImg";
/*获取应用库是否有图标*/
public static final String GET_IS_APP_IMG = "collectData/getIsAppImg";
/*通过ip获取信息*/
public static final String PCONLINE_WHOIS = "http://whois.pconline.com.cn/";
public static final String PCONLINE_WHOIS = "https://whois.pconline.com.cn/";
public static final String WHOIS = "ipJson.jsp";
}

View File

@@ -0,0 +1,22 @@
package com.ttstd.template.network.api;
import com.ttstd.template.bean.WhoisBean;
import com.ttstd.template.network.UrlAddress;
import io.reactivex.rxjava3.core.Observable;
import retrofit2.http.GET;
import retrofit2.http.Query;
/**
* @author : fanhuitong
* e-mail :
* @date : 2021/10/1814:39
* desc :
* version: 1.0
*/
public interface GetWhoisApi {
@GET(UrlAddress.WHOIS)
Observable<WhoisBean> getWhois(
@Query("json") boolean json
);
}

View File

@@ -0,0 +1,74 @@
package com.ttstd.template.rv;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class EquallyDividedItemDecoration extends RecyclerView.ItemDecoration {
private static final String TAG = EquallyDividedItemDecoration.class.getSimpleName();
private int mSpanCount;//横条目数量
private int mHalfRowSpacing;//行间距的一半
private int mHalfColumnSpacing;// 列间距的一半
public EquallyDividedItemDecoration(int spanCount, int halfRowSpacing) {
mSpanCount = spanCount;
mHalfRowSpacing = halfRowSpacing;
mHalfColumnSpacing = halfRowSpacing;
}
public EquallyDividedItemDecoration(int spanCount, int halfRowSpacing, int halfColumnSpacing) {
mSpanCount = spanCount;
mHalfRowSpacing = halfRowSpacing;
mHalfColumnSpacing = halfColumnSpacing;
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildAdapterPosition(view); // 获取view 在adapter中的位置。
Log.d(TAG, "getItemOffsets: position = " + position);
int itemCount = parent.getAdapter().getItemCount();//item全部数量
Log.d(TAG, "getItemOffsets: itemCount = " + itemCount);
int column = position % mSpanCount; // view 所在的列
Log.d(TAG, "getItemOffsets: column = " + column);
if (column == 0) {
outRect.left = 2 * mHalfRowSpacing;
outRect.right = mHalfRowSpacing;
} else if (column == mSpanCount - 1) {
outRect.left = mHalfRowSpacing;
outRect.right = 2 * mHalfRowSpacing;
} else {
outRect.left = mHalfRowSpacing;
outRect.right = mHalfRowSpacing;
}
int row = (position / 3);//所在行
Log.d(TAG, "getItemOffsets: row = " + row);
int maxRow = (int) Math.ceil((double) itemCount / mSpanCount);//一共多少行
Log.d(TAG, "getItemOffsets: maxRow = " + maxRow);
if (row == 0) {
outRect.top = 2 * mHalfColumnSpacing;
outRect.bottom = mHalfColumnSpacing;
} else if (row == maxRow - 1) {
outRect.top = mHalfColumnSpacing;
outRect.bottom = 2 * mHalfColumnSpacing;
} else {
outRect.top = mHalfColumnSpacing;
outRect.bottom = mHalfColumnSpacing;
}
Log.d(TAG, "getItemOffsets: outRect.left = " + outRect.left);
Log.d(TAG, "getItemOffsets: outRect.right = " + outRect.right);
Log.d(TAG, "getItemOffsets: outRect.top = " + outRect.top);
Log.d(TAG, "getItemOffsets: outRect.bottom = " + outRect.bottom);
}
}

View File

@@ -0,0 +1,121 @@
package com.ttstd.template.utils;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
/**
* Glide 加载 简单判空封装 防止异步加载数据时调用Glide 抛出异常
* Created by Li_Xavier on 2017/6/20 0020.
*/
public class GlideLoadUtils {
private String TAG = "ImageLoader";
/**
* 借助内部类 实现线程安全的单例模式
* 属于懒汉式单例因为Java机制规定内部类SingletonHolder只有在getInstance()
* 方法第一次调用的时候才会被加载实现了lazy而且其加载过程是线程安全的。
* 内部类加载的时候实例化一次instance。
*/
public GlideLoadUtils() {
}
private static class GlideLoadUtilsHolder {
private final static GlideLoadUtils INSTANCE = new GlideLoadUtils();
}
public static GlideLoadUtils getInstance() {
return GlideLoadUtilsHolder.INSTANCE;
}
/**
* Glide 加载 简单判空封装 防止异步加载数据时调用Glide 抛出异常
*
* @param context
* @param url 加载图片的url地址 String
* @param imageView 加载图片的ImageView 控件
* @param default_image 图片展示错误的本地图片 id
*/
public void glideLoad(Context context, String url, ImageView imageView, int default_image) {
if (context != null) {
Glide.with(context).load(url).centerCrop().error(default_image).into(imageView);
} else {
Log.i(TAG, "Picture loading failed,context is null");
}
}
public void glideLoadc(Context context, String url, ImageView imageView, Drawable default_image) {
if (context != null) {
Glide.with(context).load(url).centerCrop().error(default_image).into(imageView);
} else {
Log.i(TAG, "Picture loading failed,context is null");
}
}
public void glideLoad(Activity activity, String url, ImageView imageView, Drawable drawable) {
if (activity != null && !activity.isDestroyed()) {
Glide.with(activity.getApplicationContext()).load(url).error(drawable).into(imageView);
} else {
Log.i(TAG, "Picture loading failed,context is null");
}
}
public void glideLoad(Context context, String url, ImageView imageView) {
if (context != null) {
Glide.with(context).load(url).into(imageView);
} else {
Log.i(TAG, "Picture loading failed,context is null");
}
}
public void glideLoad(Context context, int drawableId, ImageView imageView) {
if (context != null) {
Glide.with(context).load(context.getDrawable(drawableId)).centerCrop().into(imageView);
} else {
Log.i(TAG, "Picture loading failed,context is null");
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public void glideLoad(Activity activity, String url, ImageView imageView, int default_image) {
if (!activity.isDestroyed()) {
// Glide.with(activity).load(url).centerCrop().dontAnimate().error(default_image).into(imageView);
Glide.with(activity).load(url).into(new SimpleTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
imageView.setImageDrawable(resource);
}
});
} else {
Log.i(TAG, "Picture loading failed,activity is Destroyed");
}
}
public void glideLoad(Fragment fragment, String url, ImageView imageView, int default_image) {
if (fragment != null && fragment.getActivity() != null) {
Glide.with(fragment).load(url).centerCrop().error(default_image).into(imageView);
} else {
Log.i(TAG, "Picture loading failed,fragment is null");
}
}
public void glideLoad(android.app.Fragment fragment, String url, ImageView imageView, int default_image) {
if (fragment != null && fragment.getActivity() != null) {
Glide.with(fragment).load(url).centerCrop().error(default_image).into(imageView);
} else {
Log.i(TAG, "Picture loading failed,android.app.Fragment is null");
}
}
}

View File

@@ -0,0 +1,54 @@
package com.ttstd.template.view;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import androidx.databinding.BindingAdapter;
import com.bumptech.glide.Glide;
import com.ttstd.template.R;
import com.ttstd.template.utils.GlideLoadUtils;
public class DataBindingAdapter {
@BindingAdapter("android:src")
public static void setSrc(ImageView view, Bitmap bitmap) {
view.setImageBitmap(bitmap);
}
@BindingAdapter("android:src")
public static void setSrc(ImageView view, int resId) {
view.setImageResource(resId);
}
@BindingAdapter("imageUrl")
public static void setSrc(ImageView imageView, String url) {
Glide.with(imageView.getContext())
.load(url)
.error(R.mipmap.ic_launcher)
.centerCrop()
.into(imageView);
}
/**
* 自定义设置图片属性 - 在匹配时自定义命名空间会被忽略
*/
@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView imageView, String url, Drawable error) {
GlideLoadUtils.getInstance().glideLoadc(imageView.getContext(), url, imageView, error);
}
@BindingAdapter({"loadUrl"})
public static void loadUrl(ImageView imageView, String url) {
if (TextUtils.isEmpty(url)) {
imageView.setVisibility(View.GONE);
} else {
imageView.setVisibility(View.VISIBLE);
Glide.with(imageView.getContext())
.load(url)
.into(imageView);
}
}
}

View File

@@ -6,9 +6,22 @@
android:layout_height="match_parent"
tools:context=".activity.main.MainActivity">
<FrameLayout
android:id="@+id/content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="@+id/bt_mvp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="mvp" />
<Button
android:id="@+id/bt_mvvm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="mvvm" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".activity.main.MainActivity">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -6,13 +6,36 @@
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:fitsSystemWindows">true</item>
</style>
<!-- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">-->
<!-- &lt;!&ndash; Customize your theme here. &ndash;&gt;-->
<!-- <item name="colorPrimary">@color/colorPrimary</item>-->
<!-- <item name="colorPrimaryDark">@color/colorPrimaryDark</item>-->
<!-- <item name="colorAccent">@color/colorAccent</item>-->
<!-- </style>-->
<!-- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">-->
<!-- &lt;!&ndash; Customize your theme here. &ndash;&gt;-->
<!-- <item name="colorPrimary">@color/colorPrimary</item>-->
<!-- <item name="colorPrimaryDark">@color/colorPrimaryDark</item>-->
<!-- <item name="colorAccent">@color/colorAccent</item>-->
<!-- </style>-->
</resources>
<style name="activity_styles" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- <item name="android:windowFullscreen">true</item>-->
<!--设置dialog的背景-->
<item name="android:windowBackground">@android:color/transparent</item>
<!--设置Dialog的windowFrame框为无-->
<item name="android:windowFrame">@null</item>
<!--设置无标题-->
<item name="windowNoTitle">true</item>
<!--是否浮现在activity之上-->
<!--为false会导致windowCloseOnTouchOutside
失效-->
<item name="android:windowIsFloating">true</item>
<!--是否半透明-->
<item name="android:windowIsTranslucent">true</item>
<!--设置窗口内容不覆盖-->
<item name="android:windowContentOverlay">@null</item>
<!--设置动画在这里使用让它继承系统的Animation.Dialog-->
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
<!--背景是否模糊显示-->
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowCloseOnTouchOutside">true</item>
</style>
</resources>

View File

@@ -1,18 +1,19 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
maven {url 'http://developer.huawei.com/repo/'}
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' }
maven { url 'http://developer.huawei.com/repo/' }
maven { url 'https://maven.aliyun.com/repository/central' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -20,13 +21,19 @@ buildscript {
}
allprojects {
configurations.all {
resolutionStrategy {
force 'androidx.constraintlayout:constraintlayout:2.0.4'
}
}
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
maven {url 'http://developer.huawei.com/repo/'}
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' }
maven { url 'http://developer.huawei.com/repo/' }
maven { url 'https://maven.aliyun.com/repository/central' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
}
}