主页固定显示
This commit is contained in:
@@ -1,16 +0,0 @@
|
||||
package com.ttstd.dialer.activity;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.ttstd.dialer.activity.main;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.mvvm.BaseMvvmActivity;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.databinding.ActivityMainBinding;
|
||||
import com.ttstd.dialer.fragment.contact.ContactFragment;
|
||||
import com.ttstd.dialer.fragment.home.HomeFragment;
|
||||
import com.ttstd.dialer.view.BaseFragmentPagerAdapter;
|
||||
import com.ttstd.dialer.view.ScaleCircleNavigator;
|
||||
|
||||
import net.lucode.hackware.magicindicator.ViewPagerHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBinding> {
|
||||
protected MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
private FragmentManager mFragmentManager = getSupportFragmentManager();
|
||||
private BaseFragmentPagerAdapter mBaseFragmentPagerAdapter;
|
||||
|
||||
private List<Fragment> mFragments = new ArrayList<>();
|
||||
private HomeFragment mHomeFragment;
|
||||
private ContactFragment mContactFragment;
|
||||
|
||||
private int mCurrentIndex = 0;
|
||||
|
||||
private ScaleCircleNavigator mScaleCircleNavigator;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setNightMode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setfitWindow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.activity_main;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDataBinding() {
|
||||
mViewModel.setContext(this);
|
||||
mViewModel.setVDBinding(mViewDataBinding);
|
||||
mViewModel.setLifecycle(getLifecycleSubject());
|
||||
mViewDataBinding.setClick(new BtnClick());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
mScaleCircleNavigator = new ScaleCircleNavigator(this);
|
||||
|
||||
if (mContactFragment == null) {
|
||||
mContactFragment = new ContactFragment();
|
||||
mFragments.add(mContactFragment);
|
||||
}
|
||||
|
||||
boolean contactHome = mMMKV.decodeBool(CommonConfig.CONTACT_HOME_PAGE, false);
|
||||
if (!contactHome) {
|
||||
mCurrentIndex += 1;
|
||||
}
|
||||
if (mHomeFragment == null) {
|
||||
mHomeFragment = new HomeFragment();
|
||||
mFragments.add(mHomeFragment);
|
||||
}
|
||||
|
||||
|
||||
mScaleCircleNavigator.setCircleCount(mFragments.size());
|
||||
mScaleCircleNavigator.notifyDataSetChanged();
|
||||
mScaleCircleNavigator.setNormalCircleColor(getColor(R.color.indicator_color_normal));
|
||||
mScaleCircleNavigator.setSelectedCircleColor(getColor(R.color.indicator_color_selected));
|
||||
mScaleCircleNavigator.setCircleClickListener(new ScaleCircleNavigator.OnCircleClickListener() {
|
||||
@Override
|
||||
public void onClick(int index) {
|
||||
mViewDataBinding.viewPager.setCurrentItem(index);
|
||||
}
|
||||
});
|
||||
mBaseFragmentPagerAdapter = new BaseFragmentPagerAdapter(mFragmentManager, mFragments);
|
||||
mViewDataBinding.viewPager.setAdapter(mBaseFragmentPagerAdapter);
|
||||
mViewDataBinding.viewPager.setOffscreenPageLimit(10);
|
||||
mViewDataBinding.viewPager.setCurrentItem(mCurrentIndex);
|
||||
|
||||
mViewDataBinding.magicIndicator.setNavigator(mScaleCircleNavigator);
|
||||
ViewPagerHelper.bind(mViewDataBinding.magicIndicator, mViewDataBinding.viewPager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ttstd.dialer.activity.main;
|
||||
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.ActivityMainBinding;
|
||||
|
||||
public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEvent> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.ttstd.dialer.annotations;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.GlideBuilder;
|
||||
import com.bumptech.glide.annotation.GlideModule;
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool;
|
||||
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
|
||||
import com.bumptech.glide.load.engine.cache.LruResourceCache;
|
||||
import com.bumptech.glide.module.AppGlideModule;
|
||||
|
||||
@GlideModule
|
||||
public class CustomGlideModule extends AppGlideModule {
|
||||
@Override
|
||||
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
|
||||
super.applyOptions(context, builder);
|
||||
//内存缓存
|
||||
int memoryCacheSizeBytes = 1024 * 1024 * 32; // 20mb
|
||||
builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));
|
||||
//Bitmap 池
|
||||
int bitmapPoolSizeBytes = 1024 * 1024 * 64; // 30mb
|
||||
builder.setBitmapPool(new LruBitmapPool(bitmapPoolSizeBytes));
|
||||
//磁盘缓存
|
||||
int diskCacheSizeBytes = 1024 * 1024 * 128; // 100MB
|
||||
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.ttstd.dialer.annotations;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.databinding.BindingAdapter;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.ttstd.dialer.R;
|
||||
|
||||
public class ImageViewAdapter {
|
||||
@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) {
|
||||
Glide.with(imageView.getContext())
|
||||
.load(url)
|
||||
.error(error)
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
@BindingAdapter({"imageAvatarUrl", "error"})
|
||||
public static void loadAvatarImage(ImageView imageView, String url, Drawable error) {
|
||||
Glide.with(imageView.getContext())
|
||||
.load(url)
|
||||
.error(error)
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.ttstd.dialer.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.ttstd.dialer.utils.ScreenUtils;
|
||||
|
||||
import me.jessyan.autosize.AutoSizeCompat;
|
||||
|
||||
public class BaseAlertDialogBuilder extends AlertDialog.Builder {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final float ALERT_BASE_WIDTH = 480f;
|
||||
private static final float ALERT_BASE_WIDTH_TABLE = 640f;
|
||||
|
||||
public BaseAlertDialogBuilder(@NonNull Context context) {
|
||||
super(adjustAutoSize(context));
|
||||
}
|
||||
|
||||
public BaseAlertDialogBuilder(@NonNull Context context, int themeResId) {
|
||||
super(adjustAutoSize(context), themeResId);
|
||||
}
|
||||
|
||||
private static Context adjustAutoSize(Context context) {
|
||||
return new ContextWrapper(context) {
|
||||
private Resources mResources;
|
||||
|
||||
{
|
||||
Resources oldResources = super.getResources();
|
||||
mResources = new Resources(oldResources.getAssets(), oldResources.getDisplayMetrics(), oldResources.getConfiguration());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
if (ScreenUtils.isTablet(context)) {
|
||||
AutoSizeCompat.autoConvertDensityBaseOnWidth(mResources, ALERT_BASE_WIDTH_TABLE);
|
||||
} else {
|
||||
AutoSizeCompat.autoConvertDensityBaseOnWidth(mResources, ALERT_BASE_WIDTH);
|
||||
}
|
||||
return mResources;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
100
app/src/main/java/com/ttstd/dialer/base/BaseApplication.java
Normal file
100
app/src/main/java/com/ttstd/dialer/base/BaseApplication.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package com.ttstd.dialer.base;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.alibaba.android.arouter.launcher.ARouter;
|
||||
import com.arialyy.aria.core.Aria;
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.tencent.bugly.crashreport.CrashReport;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.ttstd.dialer.BuildConfig;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
|
||||
|
||||
public class BaseApplication extends Application {
|
||||
private static final String TAG = "BaseApplication";
|
||||
|
||||
/**
|
||||
* ViewModel中因为经常旋转导致弱引用为空
|
||||
*/
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static Context context;
|
||||
|
||||
public static Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.e(TAG, "onCreate: ");
|
||||
context = getApplicationContext();
|
||||
if (!BuildConfig.DEBUG) {
|
||||
catchException();
|
||||
}
|
||||
// 在开始分析的地方调用,传入路径
|
||||
// 如果是放到外部路径,需要添加权限
|
||||
// 默认存储在/sdcard/Android/data/packagename/files
|
||||
// Debug.startMethodTracing("App" + System.currentTimeMillis());
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
Log.e(TAG, "init: ");
|
||||
if (SystemUtils.isMainProcessName(this, android.os.Process.myPid())) {
|
||||
String rootDir = MMKV.initialize(this);
|
||||
Log.e(TAG, "mmkv root: " + rootDir);
|
||||
|
||||
if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
|
||||
ARouter.openLog(); // 打印日志
|
||||
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
|
||||
}
|
||||
ARouter.init(this); // 尽可能早,推荐在Application中初始化
|
||||
|
||||
// 初始化 Toast 框架
|
||||
Toaster.init(this);
|
||||
|
||||
Log.e(TAG, "slowInit: ");
|
||||
Aria.init(this);
|
||||
CrashReport.initCrashReport(getApplicationContext(), "845e3ed68c", false);
|
||||
CrashReport.setDeviceId(this, Build.MODEL);
|
||||
xcrash.XCrash.init(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void catchException() {
|
||||
Thread.setDefaultUncaughtExceptionHandler(
|
||||
new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
Log.e("捕获异常子线程:", Thread.currentThread().getName() +
|
||||
"在:" + e.getStackTrace()[0].getClassName());
|
||||
}
|
||||
}
|
||||
);
|
||||
//下面是新增方法!
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Looper.loop(); //会先执行这个方法,然后在执行下面的异常捕获方法!
|
||||
} catch (Exception e) {
|
||||
Log.e("捕获异常主线程:", Thread.currentThread().getName() + "在:" + e.getStackTrace()[0].getClassName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.ttstd.dialer.base;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.OnApplyWindowInsetsListener;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.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();
|
||||
public boolean setNightMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否是入侵
|
||||
*/
|
||||
// protected abstract boolean setNightMode();
|
||||
public boolean setfitWindow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
protected abstract void initDataBinding();
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
*/
|
||||
protected abstract void initView();
|
||||
|
||||
/**
|
||||
* 初始化数据
|
||||
*/
|
||||
protected abstract void initData();
|
||||
|
||||
public void addNavigationBarBottomPadding(View view) {
|
||||
UltimateBarX.addNavigationBarBottomPadding(view);
|
||||
if (Build.VERSION.SDK_INT >= 35) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(view, new OnApplyWindowInsetsListener() {
|
||||
@Override
|
||||
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
|
||||
Insets systemInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
|
||||
v.setPadding(0, 0, 0, systemInsets.bottom);
|
||||
return insets;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.ttstd.dialer.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.ttstd.dialer.base.rx.BaseRxDialogFragment;
|
||||
|
||||
public abstract class BaseDialogFragment extends BaseRxDialogFragment {
|
||||
|
||||
protected boolean isViewInitiated;
|
||||
protected boolean isVisibleToUser;
|
||||
protected boolean isDataInitiated;
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
isViewInitiated = true;
|
||||
prepareFetchData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
this.isVisibleToUser = isVisibleToUser;
|
||||
prepareFetchData();
|
||||
}
|
||||
|
||||
public abstract void fetchData();
|
||||
|
||||
public boolean prepareFetchData() {
|
||||
return prepareFetchData(false);
|
||||
}
|
||||
|
||||
public boolean prepareFetchData(boolean forceUpdate) {
|
||||
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
|
||||
fetchData();
|
||||
//注释掉保证每次都更新数据
|
||||
// isDataInitiated = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
44
app/src/main/java/com/ttstd/dialer/base/BaseFragment.java
Normal file
44
app/src/main/java/com/ttstd/dialer/base/BaseFragment.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.ttstd.dialer.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.ttstd.dialer.base.rx.BaseRxFragment;
|
||||
|
||||
public abstract class BaseFragment extends BaseRxFragment {
|
||||
|
||||
protected boolean isViewInitiated;
|
||||
protected boolean isVisibleToUser;
|
||||
protected boolean isDataInitiated;
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
isViewInitiated = true;
|
||||
prepareFetchData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
this.isVisibleToUser = isVisibleToUser;
|
||||
prepareFetchData();
|
||||
}
|
||||
|
||||
public abstract void fetchData();
|
||||
|
||||
public boolean prepareFetchData() {
|
||||
return prepareFetchData(false);
|
||||
}
|
||||
|
||||
public boolean prepareFetchData(boolean forceUpdate) {
|
||||
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
|
||||
fetchData();
|
||||
//注释掉保证每次都更新数据
|
||||
// isDataInitiated = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.ttstd.dialer.base;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.OnApplyWindowInsetsListener;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.rx.BaseRxActivity;
|
||||
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
|
||||
|
||||
import me.jessyan.autosize.AutoSizeCompat;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修补autozie RecyclerView item大小不一致
|
||||
*/
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
AutoSizeCompat.autoConvertDensityOfGlobal(getResources());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置布局
|
||||
*/
|
||||
protected abstract int getLayoutId();
|
||||
|
||||
/**
|
||||
* @return 是否是黑色状态栏
|
||||
*/
|
||||
// protected abstract boolean setNightMode();
|
||||
public boolean setNightMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否是入侵
|
||||
*/
|
||||
// protected abstract boolean setNightMode();
|
||||
public boolean setfitWindow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param view android 15 edge-to-edge会覆盖导航栏
|
||||
*/
|
||||
public void addNavigationBarBottomPadding(View view) {
|
||||
UltimateBarX.addNavigationBarBottomPadding(view);
|
||||
if (Build.VERSION.SDK_INT >= 35) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(view, new OnApplyWindowInsetsListener() {
|
||||
@Override
|
||||
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
|
||||
Insets systemInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
|
||||
v.setPadding(0, 0, 0, systemInsets.bottom);
|
||||
return insets;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.ttstd.dialer.base.mvp;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.ttstd.dialer.base.BaseTransparentActivity;
|
||||
|
||||
@Deprecated
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ttstd.dialer.base.mvp;
|
||||
|
||||
@Deprecated
|
||||
public interface BasePresenter<V extends BaseView> {
|
||||
void attachView(V view);
|
||||
|
||||
void detachView();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.ttstd.dialer.base.mvp;
|
||||
|
||||
@Deprecated
|
||||
public interface BaseView {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.ttstd.dialer.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.dialer.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();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.ttstd.dialer.base.mvvm;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.databinding.ViewDataBinding;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
||||
|
||||
public abstract class BaseViewModel<VDB extends ViewDataBinding, T> extends ViewModel {
|
||||
|
||||
/**
|
||||
* 当前viewmodel对应的页面binding
|
||||
*/
|
||||
protected VDB binding;
|
||||
|
||||
public void setVDBinding(ViewDataBinding vdBinding) {
|
||||
binding = (VDB) vdBinding;
|
||||
}
|
||||
|
||||
public VDB getVDBinding() {
|
||||
if (binding == null) {
|
||||
throw new NullPointerException("BaseViewModel >> getVDBinding >> null!!!");
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
private WeakReference<Context> weakContext;
|
||||
|
||||
public void setContext(Context context) {
|
||||
if (weakContext == null) {
|
||||
weakContext = new WeakReference<>(context.getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
public Context getSafeContext() {
|
||||
if (weakContext == null) {
|
||||
throw new NullPointerException("BaseViewModel >> getCtx >> null!!!");
|
||||
}
|
||||
return weakContext.get();
|
||||
}
|
||||
|
||||
private BehaviorSubject<T> mBehaviorSubject;
|
||||
|
||||
public void setLifecycle(BehaviorSubject subject) {
|
||||
this.mBehaviorSubject = (BehaviorSubject<T>) subject;
|
||||
}
|
||||
|
||||
public BehaviorSubject<T> getLifecycle() {
|
||||
return mBehaviorSubject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package com.ttstd.dialer.base.mvvm.fragment;
|
||||
|
||||
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 com.ttstd.dialer.base.BaseFragment;
|
||||
|
||||
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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.ttstd.dialer.base.rx;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.CheckResult;
|
||||
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 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 BehaviorSubject<ActivityEvent> getLifecycleSubject() {
|
||||
return lifecycleSubject;
|
||||
}
|
||||
|
||||
@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 onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
lifecycleSubject.onNext(ActivityEvent.CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
lifecycleSubject.onNext(ActivityEvent.START);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
lifecycleSubject.onNext(ActivityEvent.RESUME);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.ttstd.dialer.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.DialogFragment;
|
||||
|
||||
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 BaseRxDialogFragment extends DialogFragment 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();
|
||||
}
|
||||
}
|
||||
|
||||
123
app/src/main/java/com/ttstd/dialer/base/rx/BaseRxFragment.java
Normal file
123
app/src/main/java/com/ttstd/dialer/base/rx/BaseRxFragment.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package com.ttstd.dialer.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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.ttstd.dialer.base.rx;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.CheckResult;
|
||||
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 BaseRxService extends Service implements LifecycleProvider<ActivityEvent> {
|
||||
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
|
||||
|
||||
public BehaviorSubject<ActivityEvent> getLifecycleSubject() {
|
||||
return lifecycleSubject;
|
||||
}
|
||||
|
||||
@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
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ttstd.dialer.config;
|
||||
|
||||
public class CommonConfig {
|
||||
public static final String MMKV_ID = "InterProcessKV";
|
||||
|
||||
public static final String CONTACT_HOME_PAGE = "contact_home_page_key";
|
||||
|
||||
}
|
||||
28
app/src/main/java/com/ttstd/dialer/contact/AppDatabase.java
Normal file
28
app/src/main/java/com/ttstd/dialer/contact/AppDatabase.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.ttstd.dialer.contact;
|
||||
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
import android.content.Context;
|
||||
|
||||
@Database(entities = {Contact.class}, version = 1, exportSchema = false)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
public abstract ContactDao contactDao();
|
||||
|
||||
private static volatile AppDatabase INSTANCE;
|
||||
|
||||
// 单例模式获取数据库实例
|
||||
public static AppDatabase getDatabase(final Context context) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (AppDatabase.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
|
||||
AppDatabase.class, "contact_database")
|
||||
.allowMainThreadQueries() // 为了简化示例,允许主线程查询
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
75
app/src/main/java/com/ttstd/dialer/contact/Contact.java
Normal file
75
app/src/main/java/com/ttstd/dialer/contact/Contact.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package com.ttstd.dialer.contact;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity(tableName = "contacts")
|
||||
public class Contact implements Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
private int id;
|
||||
private String name;
|
||||
private String phoneNumber;
|
||||
private String avatar;
|
||||
|
||||
public Contact(String name, String phoneNumber) {
|
||||
this.name = name;
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
public void setPhoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (obj instanceof Contact) {
|
||||
return Objects.equals(((Contact) obj).phoneNumber, phoneNumber)
|
||||
|| ((Contact) obj).id == id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString();
|
||||
}
|
||||
}
|
||||
|
||||
37
app/src/main/java/com/ttstd/dialer/contact/ContactDao.java
Normal file
37
app/src/main/java/com/ttstd/dialer/contact/ContactDao.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.ttstd.dialer.contact;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface ContactDao {
|
||||
@Insert
|
||||
void insert(Contact contact);
|
||||
|
||||
@Update
|
||||
void update(Contact contact);
|
||||
|
||||
@Delete
|
||||
void delete(Contact contact);
|
||||
|
||||
@Query("DELETE FROM contacts WHERE id = :id")
|
||||
void deleteById(int id);
|
||||
|
||||
@Query("DELETE FROM contacts")
|
||||
void deleteAll();
|
||||
|
||||
@Query("SELECT * FROM contacts ORDER BY name ASC")
|
||||
List<Contact> getAllContacts();
|
||||
|
||||
@Query("SELECT * FROM contacts WHERE id = :id")
|
||||
Contact getContactById(int id);
|
||||
|
||||
@Query("SELECT * FROM contacts WHERE name LIKE :searchQuery OR phoneNumber LIKE :searchQuery")
|
||||
List<Contact> searchContacts(String searchQuery);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.ttstd.dialer.contact;
|
||||
|
||||
import android.content.Context;
|
||||
import java.util.List;
|
||||
|
||||
public class ContactRepository {
|
||||
private ContactDao mContactDao;
|
||||
private List<Contact> mAllContacts;
|
||||
|
||||
// 构造函数,获取数据库访问对象
|
||||
public ContactRepository(Context context) {
|
||||
AppDatabase db = AppDatabase.getDatabase(context);
|
||||
mContactDao = db.contactDao();
|
||||
mAllContacts = mContactDao.getAllContacts();
|
||||
}
|
||||
|
||||
// 获取所有联系人
|
||||
public List<Contact> getAllContacts() {
|
||||
return mContactDao.getAllContacts();
|
||||
}
|
||||
|
||||
// 根据ID获取联系人
|
||||
public Contact getContactById(int id) {
|
||||
return mContactDao.getContactById(id);
|
||||
}
|
||||
|
||||
// 搜索联系人
|
||||
public List<Contact> searchContacts(String query) {
|
||||
return mContactDao.searchContacts("%" + query + "%");
|
||||
}
|
||||
|
||||
// 添加联系人
|
||||
public void insert(Contact contact) {
|
||||
mContactDao.insert(contact);
|
||||
}
|
||||
|
||||
// 更新联系人
|
||||
public void update(Contact contact) {
|
||||
mContactDao.update(contact);
|
||||
}
|
||||
|
||||
// 删除联系人
|
||||
public void delete(Contact contact) {
|
||||
mContactDao.delete(contact);
|
||||
}
|
||||
|
||||
// 根据ID删除联系人
|
||||
public void deleteById(int id) {
|
||||
mContactDao.deleteById(id);
|
||||
}
|
||||
|
||||
// 删除所有联系人
|
||||
public void deleteAll() {
|
||||
mContactDao.deleteAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.ttstd.dialer.contact;
|
||||
|
||||
import android.app.Application;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import java.util.List;
|
||||
|
||||
public class ContactViewModel extends AndroidViewModel {
|
||||
private ContactRepository mRepository;
|
||||
|
||||
public ContactViewModel(Application application) {
|
||||
super(application);
|
||||
mRepository = new ContactRepository(application);
|
||||
}
|
||||
|
||||
public List<Contact> getAllContacts() {
|
||||
return mRepository.getAllContacts();
|
||||
}
|
||||
|
||||
public Contact getContactById(int id) {
|
||||
return mRepository.getContactById(id);
|
||||
}
|
||||
|
||||
public List<Contact> searchContacts(String query) {
|
||||
return mRepository.searchContacts(query);
|
||||
}
|
||||
|
||||
public void insert(Contact contact) {
|
||||
mRepository.insert(contact);
|
||||
}
|
||||
|
||||
public void update(Contact contact) {
|
||||
mRepository.update(contact);
|
||||
}
|
||||
|
||||
public void delete(Contact contact) {
|
||||
mRepository.delete(contact);
|
||||
}
|
||||
|
||||
public void deleteById(int id) {
|
||||
mRepository.deleteById(id);
|
||||
}
|
||||
|
||||
public void deleteAll() {
|
||||
mRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.ttstd.dialer.fragment.contact;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
|
||||
import com.ttstd.dialer.databinding.FragmentContactBinding;
|
||||
|
||||
public class ContactFragment extends BaseMvvmFragment<ContactViewModel, FragmentContactBinding> {
|
||||
private static final String TAG ="ContactFragment";
|
||||
|
||||
private Activity mContext;
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_contact;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDataBinding() {
|
||||
mContext = getActivity();
|
||||
mViewModel.setContext(mContext);
|
||||
mViewModel.setVDBinding(mViewDataBinding);
|
||||
mViewModel.setLifecycle(getLifecycleSubject());
|
||||
mViewDataBinding.setClick(new BtnClick());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView(Bundle bundle) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData(Bundle savedInstanceState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchData() {
|
||||
|
||||
}
|
||||
|
||||
public class BtnClick{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ttstd.dialer.fragment.contact;
|
||||
|
||||
import com.trello.rxlifecycle4.android.FragmentEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.FragmentContactBinding;
|
||||
|
||||
public class ContactViewModel extends BaseViewModel<FragmentContactBinding, FragmentEvent> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package com.ttstd.dialer.fragment.home;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.BaseFragment;
|
||||
import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment;
|
||||
import com.ttstd.dialer.databinding.FragmentHomeBinding;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
import com.ttstd.dialer.utils.DataUtil;
|
||||
import com.ttstd.dialer.utils.LunarCalendarFestivalUtils;
|
||||
import com.ttstd.dialer.utils.TimeUtils;
|
||||
|
||||
/**
|
||||
* A simple {@link Fragment} subclass.
|
||||
* Use the {@link HomeFragment#newInstance} factory method to
|
||||
* create an instance of this fragment.
|
||||
*/
|
||||
public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBinding> {
|
||||
private static final String TAG = "HomeFragment";
|
||||
|
||||
private Activity mContext;
|
||||
private LunarCalendarFestivalUtils mFestivalUtils;
|
||||
|
||||
// TODO: Rename parameter arguments, choose names that match
|
||||
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
|
||||
private static final String ARG_PARAM1 = "param1";
|
||||
private static final String ARG_PARAM2 = "param2";
|
||||
|
||||
// TODO: Rename and change types of parameters
|
||||
private String mParam1;
|
||||
private String mParam2;
|
||||
|
||||
public HomeFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this factory method to create a new instance of
|
||||
* this fragment using the provided parameters.
|
||||
*
|
||||
* @param param1 Parameter 1.
|
||||
* @param param2 Parameter 2.
|
||||
* @return A new instance of fragment HomeFragment.
|
||||
*/
|
||||
// TODO: Rename and change types and number of parameters
|
||||
public static HomeFragment newInstance(String param1, String param2) {
|
||||
HomeFragment fragment = new HomeFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_PARAM1, param1);
|
||||
args.putString(ARG_PARAM2, param2);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getArguments() != null) {
|
||||
mParam1 = getArguments().getString(ARG_PARAM1);
|
||||
mParam2 = getArguments().getString(ARG_PARAM2);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_home;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDataBinding() {
|
||||
mContext = getActivity();
|
||||
mViewModel.setContext(mContext);
|
||||
mViewModel.setVDBinding(mViewDataBinding);
|
||||
mViewModel.setLifecycle(getLifecycleSubject());
|
||||
mViewDataBinding.setClick(new BtnClick());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView(Bundle bundle) {
|
||||
mFestivalUtils = new LunarCalendarFestivalUtils();
|
||||
setTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData(Bundle savedInstanceState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetchData() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
Log.e(TAG, "onStart: ");
|
||||
registerReceivers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
Log.e(TAG, "onStop: ");
|
||||
unregisterReceivers();
|
||||
}
|
||||
|
||||
public void registerReceivers() {
|
||||
registerTimeReceiver();
|
||||
}
|
||||
|
||||
private void unregisterReceivers() {
|
||||
if (null != mTimeReceiver) {
|
||||
mContext.unregisterReceiver(mTimeReceiver);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
private void registerTimeReceiver() {
|
||||
if (null == mTimeReceiver) {
|
||||
mTimeReceiver = new TimeReceiver();
|
||||
}
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_DATE_CHANGED);
|
||||
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
|
||||
filter.addAction(Intent.ACTION_TIME_CHANGED);
|
||||
filter.addAction(Intent.ACTION_TIME_TICK);
|
||||
mContext.registerReceiver(mTimeReceiver, filter);
|
||||
}
|
||||
|
||||
private TimeReceiver mTimeReceiver;
|
||||
|
||||
private class TimeReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
Log.e("TimeReceiver", "onReceive: " + action);
|
||||
switch (action) {
|
||||
case Intent.ACTION_DATE_CHANGED:
|
||||
case Intent.ACTION_TIMEZONE_CHANGED:
|
||||
case Intent.ACTION_TIME_CHANGED:
|
||||
case Intent.ACTION_TIME_TICK:
|
||||
setTime();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setTime() {
|
||||
if (isAdded()) {
|
||||
mViewDataBinding.tvTime.setText(DataUtil.formatDateHour());
|
||||
// mViewDataBinding.tvDate.setText(DataUtil.formatDateDay());
|
||||
// mViewDataBinding.tvWeek.setText(TimeUtils.getWeek());
|
||||
// mViewDataBinding.tvLunar.setText(mFestivalUtils.getLunarCalendar());
|
||||
}
|
||||
}
|
||||
|
||||
public void openAppStore(String pkg) {
|
||||
Uri uri = Uri.parse("market://details?id=" + pkg);
|
||||
Intent storeIntent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
storeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
try {
|
||||
startActivity(storeIntent);
|
||||
} catch (Exception e1) {
|
||||
Log.e(TAG, "openWeixin storeIntent: " + e1.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public class BtnClick {
|
||||
public void openContact(View view) {
|
||||
Intent intent = new Intent();
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "openSettings: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void openSettings(View view) {
|
||||
Intent intent = new Intent(Settings.ACTION_SETTINGS);
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "openSettings: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void openDouyin(View view) {
|
||||
if (ApkUtils.isInstalled(mContext, "com.ss.android.ugc.aweme")) {
|
||||
ApkUtils.openPackage(mContext, "com.ss.android.ugc.aweme");
|
||||
} else {
|
||||
Toaster.show("抖音未安装,请安装后使用");
|
||||
}
|
||||
}
|
||||
|
||||
public void openWeixin(View view) {
|
||||
if (ApkUtils.isInstalled(mContext, "com.tencent.mm")) {
|
||||
Intent intent = new Intent();
|
||||
ComponentName cmp = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI");
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
intent.setComponent(cmp);
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "launchWeChat: " + e.getMessage());
|
||||
Toaster.show("打开微信失败");
|
||||
openAppStore("com.tencent.mm");
|
||||
}
|
||||
} else {
|
||||
Toaster.show("微信未安装,请安装后使用");
|
||||
openAppStore("com.tencent.mm");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ttstd.dialer.fragment.home;
|
||||
|
||||
import com.trello.rxlifecycle4.android.FragmentEvent;
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.databinding.FragmentHomeBinding;
|
||||
|
||||
public class HomeViewModel extends BaseViewModel<FragmentHomeBinding, FragmentEvent> {
|
||||
|
||||
}
|
||||
86
app/src/main/java/com/ttstd/dialer/utils/ApkUtils.java
Normal file
86
app/src/main/java/com/ttstd/dialer/utils/ApkUtils.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.hjq.toast.Toaster;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ApkUtils {
|
||||
|
||||
private static final String TAG = "ApkUtils";
|
||||
|
||||
public static boolean isInstalled(Context context, String packageName) {
|
||||
if (TextUtils.isEmpty(packageName)) return false;
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
try {
|
||||
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
|
||||
return packageInfo != null;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean openPackage(Context context, String packageName) {
|
||||
Context pkgContext = getPackageContext(context, packageName);
|
||||
Intent intent = getAppOpenIntentByPackageName(context, packageName);
|
||||
if (pkgContext != null && intent != null) {
|
||||
pkgContext.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
Log.e(TAG, "openPackage: can not open " + packageName);
|
||||
Toaster.show("打开失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Context getPackageContext(Context context, String packageName) {
|
||||
Context pkgContext = null;
|
||||
if (context.getPackageName().equals(packageName)) {
|
||||
pkgContext = context;
|
||||
} else {
|
||||
// 创建第三方应用的上下文环境
|
||||
try {
|
||||
pkgContext = context.createPackageContext(packageName,
|
||||
Context.CONTEXT_IGNORE_SECURITY
|
||||
| Context.CONTEXT_INCLUDE_CODE);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return pkgContext;
|
||||
}
|
||||
|
||||
|
||||
public static Intent getAppOpenIntentByPackageName(Context context, String packageName) {
|
||||
//Activity完整名
|
||||
String mainAct = null;
|
||||
//根据包名寻找
|
||||
PackageManager pkgMag = context.getPackageManager();
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
List<ResolveInfo> list = pkgMag.queryIntentActivities(intent,
|
||||
PackageManager.GET_ACTIVITIES);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ResolveInfo info = list.get(i);
|
||||
if (info.activityInfo.packageName.equals(packageName)) {
|
||||
mainAct = info.activityInfo.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (TextUtils.isEmpty(mainAct)) {
|
||||
return null;
|
||||
}
|
||||
intent.setComponent(new ComponentName(packageName, mainAct));
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
29
app/src/main/java/com/ttstd/dialer/utils/DataUtil.java
Normal file
29
app/src/main/java/com/ttstd/dialer/utils/DataUtil.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class DataUtil {
|
||||
private static SimpleDateFormat day = new SimpleDateFormat("M月d日");
|
||||
private static SimpleDateFormat hour = new SimpleDateFormat("HH:mm");
|
||||
private static SimpleDateFormat minute = new SimpleDateFormat("mm");
|
||||
|
||||
/**
|
||||
* 格式化日期(精确到天)
|
||||
*/
|
||||
public static String formatDateDay() {
|
||||
return day.format(new Date());
|
||||
}
|
||||
|
||||
public static String formatDateTime() {
|
||||
return day.format(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日期(hour)
|
||||
*/
|
||||
public static String formatDateHour() {
|
||||
return hour.format(new Date());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 获取输入公历日期的生肖、天干地支、农历年、农历月、农历日、公历节日、农历节日、24节气等数据
|
||||
* DATE 2020.08.13
|
||||
* https://www.cnblogs.com/weihbs/p/13955786.html
|
||||
*/
|
||||
public class LunarCalendarFestivalUtils {
|
||||
//生肖年
|
||||
private String animal;
|
||||
//干支年
|
||||
private String ganZhiYear;
|
||||
//阴历年
|
||||
private String lunarYear;
|
||||
//阴历月
|
||||
private String lunarMonth;
|
||||
//阴历日
|
||||
private String lunarDay;
|
||||
//阳历节日
|
||||
private String solarFestival;
|
||||
//阴历节日
|
||||
private String lunarFestival;
|
||||
//节气
|
||||
private String lunarTerm;
|
||||
|
||||
public LunarCalendarFestivalUtils() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date date = new Date(System.currentTimeMillis());
|
||||
String timeStamp = sdf.format(date);
|
||||
initLunarCalendarInfo(timeStamp);
|
||||
}
|
||||
|
||||
public String getLunarCalendar() {
|
||||
return lunarMonth + "月" + lunarDay;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询日期的年份生肖
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getAnimal() {
|
||||
return animal;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询日期年份的天干地支
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getGanZhiYear() {
|
||||
return ganZhiYear;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询日期的农历年份
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getLunarYear() {
|
||||
return lunarYear;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询日期的农历月份
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getLunarMonth() {
|
||||
return lunarMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询日期的农历日
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getLunarDay() {
|
||||
return lunarDay;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询日期的公历节日(不是节日返回空)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getSolarFestival() {
|
||||
return solarFestival;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询日期的农历节日(不是节日返回空)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getLunarFestival() {
|
||||
return lunarFestival;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询日期的节气数据(不是节气返回空)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getLunarTerm() {
|
||||
return lunarTerm;
|
||||
}
|
||||
|
||||
|
||||
final static long[] lunarInfo = new long[]{
|
||||
0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
|
||||
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
|
||||
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
|
||||
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
|
||||
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
|
||||
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950, 0x06aa0,
|
||||
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
|
||||
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
|
||||
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
|
||||
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
|
||||
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
|
||||
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
|
||||
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
|
||||
0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
|
||||
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0
|
||||
};
|
||||
//阳历天数
|
||||
final static int[] solarMonths = new int[]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
//生肖
|
||||
final static String[] animals = new String[]{"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"};
|
||||
//天干
|
||||
final static String[] tGan = new String[]{"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"};
|
||||
//地支
|
||||
final static String[] dZhi = new String[]{"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};
|
||||
//二十四节气
|
||||
final static String[] solarTerms = new String[]{"小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏",
|
||||
"小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至"};
|
||||
//二十四节气日期偏移度
|
||||
private static final double D = 0.2422;
|
||||
//特殊年份节气日期偏移
|
||||
private final static Map<Integer, Integer[]> INCREASE_OFFSETMAP = new HashMap<Integer, Integer[]>();//+1偏移
|
||||
private final static Map<Integer, Integer[]> DECREASE_OFFSETMAP = new HashMap<Integer, Integer[]>();//-1偏移
|
||||
|
||||
static {
|
||||
INCREASE_OFFSETMAP.put(0, new Integer[]{1982});//小寒
|
||||
DECREASE_OFFSETMAP.put(0, new Integer[]{2019});//小寒
|
||||
INCREASE_OFFSETMAP.put(1, new Integer[]{2082});//大寒
|
||||
DECREASE_OFFSETMAP.put(3, new Integer[]{2026});//雨水
|
||||
INCREASE_OFFSETMAP.put(5, new Integer[]{2084});//春分
|
||||
INCREASE_OFFSETMAP.put(9, new Integer[]{2008});//小满
|
||||
INCREASE_OFFSETMAP.put(10, new Integer[]{1902});//芒种
|
||||
INCREASE_OFFSETMAP.put(11, new Integer[]{1928});//夏至
|
||||
INCREASE_OFFSETMAP.put(12, new Integer[]{1925, 2016});//小暑
|
||||
INCREASE_OFFSETMAP.put(13, new Integer[]{1922});//大暑
|
||||
INCREASE_OFFSETMAP.put(14, new Integer[]{2002});//立秋
|
||||
INCREASE_OFFSETMAP.put(16, new Integer[]{1927});//白露
|
||||
INCREASE_OFFSETMAP.put(17, new Integer[]{1942});//秋分
|
||||
INCREASE_OFFSETMAP.put(19, new Integer[]{2089});//霜降
|
||||
INCREASE_OFFSETMAP.put(20, new Integer[]{2089});//立冬
|
||||
INCREASE_OFFSETMAP.put(21, new Integer[]{1978});//小雪
|
||||
INCREASE_OFFSETMAP.put(22, new Integer[]{1954});//大雪
|
||||
DECREASE_OFFSETMAP.put(23, new Integer[]{1918, 2021});//冬至
|
||||
}
|
||||
|
||||
//定义一个二维数组,第一维数组存储的是20世纪的节气C值,第二维数组存储的是21世纪的节气C值,0到23个,依次代表立春、雨水...大寒节气的C值
|
||||
private static final double[][] CENTURY_ARRAY = {
|
||||
{6.11, 20.84, 4.6295, 19.4599, 6.3826, 21.4155, 5.59, 20.888, 6.318, 21.86, 6.5, 22.2, 7.928, 23.65, 8.35, 23.95, 8.44, 23.822, 9.098, 24.218, 8.218, 23.08, 7.9, 22.6},
|
||||
{5.4055, 20.12, 3.87, 18.73, 5.63, 20.646, 4.81, 20.1, 5.52, 21.04, 5.678, 21.37, 7.108, 22.83, 7.5, 23.13, 7.646, 23.042, 8.318, 23.438, 7.438, 22.36, 7.18, 21.94}
|
||||
};
|
||||
//农历月份
|
||||
final static String lunarNumber[] = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"};
|
||||
//农历年
|
||||
final static String[] lunarYears = new String[]{"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
|
||||
final static String[] chineseTen = new String[]{"初", "十", "廿", "卅"};
|
||||
//农历节日
|
||||
final static String[] lunarHoliday = new String[]{"0101 春节", "0115 元宵节", "0202 龙头节", "0505 端午节", "0707 七夕节", "0715 中元节",
|
||||
"0815 中秋节", "0909 重阳节", "1001 寒衣节", "1015 下元节", "1208 腊八节", "1223 小年"};
|
||||
//公立节日
|
||||
final static String[] solarHoliday = new String[]{"0101 元旦", "0214 情人节", "0308 妇女节", "0312 植树节", "0315 消费者权益日",
|
||||
"0401 愚人节", "0422 地球日", "0423 读书日", "0501 劳动节", "0504 青年节", "0512 护士节", "0518 博物馆日", "0519 旅游日", "0601 儿童节",
|
||||
"0701 建党节", "0801 建军节", "0910 教师节", "1001 国庆节", "1024 联合国日", "1204 宪法日", "1224 平安夜", "1225 圣诞节"};
|
||||
//格式化日期
|
||||
static SimpleDateFormat chineseDateFormat = new SimpleDateFormat("yyyy年MM月dd日", Locale.CHINA);
|
||||
static SimpleDateFormat solarDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
/**
|
||||
* 返回农历y年的总天数
|
||||
*
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
private int lunarYearDays(int y) {
|
||||
int i, sum = 348;
|
||||
for (i = 0x8000; i > 0x8; i >>= 1) {
|
||||
sum += ((lunarInfo[y - 1900] & i) != 0 ? 1 : 0);
|
||||
}
|
||||
return (sum + leapDays(y));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回农历y年闰月的天数
|
||||
*/
|
||||
private int leapDays(int y) {
|
||||
if (leapMonth(y) != 0) {
|
||||
return ((lunarInfo[y - 1900] & 0x10000) != 0 ? 30 : 29);
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断y年的农历中那个月是闰月,不是闰月返回0
|
||||
*
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
private int leapMonth(int y) {
|
||||
return (int) (lunarInfo[y - 1900] & 0xf);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回农历y年m月的总天数
|
||||
*
|
||||
* @param y
|
||||
* @param m
|
||||
* @return
|
||||
*/
|
||||
private int monthDays(int y, int m) {
|
||||
return ((lunarInfo[y - 1900] & (0x10000 >> m)) != 0 ? 30 : 29);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取阴历年
|
||||
*
|
||||
* @param year
|
||||
* @return
|
||||
*/
|
||||
private String getLunarYearString(String year) {
|
||||
int y1 = Integer.parseInt(year.charAt(0) + "");
|
||||
int y2 = Integer.parseInt(year.charAt(1) + "");
|
||||
int y3 = Integer.parseInt(year.charAt(2) + "");
|
||||
int y4 = Integer.parseInt(year.charAt(3) + "");
|
||||
return lunarYears[y1] + lunarYears[y2] + lunarYears[y3] + lunarYears[y4];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取阴历日
|
||||
*/
|
||||
private String getLunarDayString(int day) {
|
||||
int n = day % 10 == 0 ? 9 : day % 10 - 1;
|
||||
if (day > 30)
|
||||
return "";
|
||||
if (day == 10)
|
||||
return "初十";
|
||||
else
|
||||
return chineseTen[day / 10] + lunarNumber[n];
|
||||
}
|
||||
|
||||
/**
|
||||
* 特例,特殊的年分的节气偏移量,由于公式并不完善,所以算出的个别节气的第几天数并不准确,在此返回其偏移量
|
||||
*
|
||||
* @param year 年份
|
||||
* @param n 节气编号
|
||||
* @return 返回其偏移量
|
||||
*/
|
||||
private int specialYearOffset(int year, int n) {
|
||||
int offset = 0;
|
||||
offset += getOffset(DECREASE_OFFSETMAP, year, n, -1);
|
||||
offset += getOffset(INCREASE_OFFSETMAP, year, n, 1);
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 节气偏移量计算
|
||||
*
|
||||
* @param map
|
||||
* @param year
|
||||
* @param n
|
||||
* @param offset
|
||||
* @return
|
||||
*/
|
||||
private int getOffset(Map<Integer, Integer[]> map, int year, int n, int offset) {
|
||||
int off = 0;
|
||||
Integer[] years = map.get(n);
|
||||
if (null != years) {
|
||||
for (int i : years) {
|
||||
if (i == year) {
|
||||
off = offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某年的第n个节气为几日(从0小寒起算)
|
||||
*
|
||||
* @param year
|
||||
* @param n
|
||||
* @return
|
||||
*/
|
||||
private int sTerm(int year, int n) {
|
||||
double centuryValue = 0;//节气的世纪值,每个节气的每个世纪值都不同
|
||||
int centuryIndex = -1;
|
||||
if (year >= 1901 && year <= 2000) {//20世纪
|
||||
centuryIndex = 0;
|
||||
} else if (year >= 2001 && year <= 2100) {//21世纪
|
||||
centuryIndex = 1;
|
||||
} else {
|
||||
throw new RuntimeException("不支持此年份:" + year + ",目前只支持1901年到2100年的时间范围");
|
||||
}
|
||||
centuryValue = CENTURY_ARRAY[centuryIndex][n];
|
||||
int dateNum = 0;
|
||||
|
||||
int y = year % 100;//步骤1:取年分的后两位数
|
||||
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {//闰年
|
||||
if (n == 0 || n == 1 || n == 2 || n == 3) {
|
||||
//注意:凡闰年3月1日前闰年数要减一,即:L=[(Y-1)/4],因为小寒、大寒、立春、雨水这两个节气都小于3月1日,所以 y = y-1
|
||||
y = y - 1;//步骤2
|
||||
}
|
||||
}
|
||||
dateNum = (int) (y * D + centuryValue) - (int) (y / 4);//步骤3,使用公式[Y*D+C]-L计算
|
||||
dateNum += specialYearOffset(year, n);//步骤4,加上特殊的年分的节气偏移量
|
||||
return dateNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 母亲节和父亲节
|
||||
*
|
||||
* @param year
|
||||
* @param month
|
||||
* @param day
|
||||
* @return
|
||||
*/
|
||||
private String getMotherOrFatherDay(int year, int month, int day) {
|
||||
if (month != 5 && month != 6) return null;
|
||||
if ((month == 5 && (day < 8 || day > 14)) || (month == 6 && (day < 15 || day > 21)))
|
||||
return null;
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(year, month - 1, 1);
|
||||
int weekDate = calendar.get(Calendar.DAY_OF_WEEK);
|
||||
weekDate = (weekDate == 1) ? 7 : weekDate - 1;
|
||||
switch (month) {
|
||||
case 5:
|
||||
if (day == 15 - weekDate) {
|
||||
return "母亲节";
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (day == 22 - weekDate) {
|
||||
return "父亲节";
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 感恩节
|
||||
*
|
||||
* @param year
|
||||
* @param month
|
||||
* @param day
|
||||
* @return
|
||||
*/
|
||||
private String thanksgiving(int year, int month, int day) {
|
||||
if (month != 11) return null;
|
||||
if ((month == 11 && (day < 19 || day > 28))) return null;
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(year, month - 1, 1);
|
||||
int weekDate = calendar.get(Calendar.DAY_OF_WEEK);
|
||||
weekDate = (weekDate == 1) ? 7 : weekDate - 1;
|
||||
switch (month) {
|
||||
case 11:
|
||||
if (day == 29 - weekDate + 4) {
|
||||
return "感恩节";
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取复活节
|
||||
*
|
||||
* @param year
|
||||
* @param month
|
||||
* @param day
|
||||
* @return
|
||||
*/
|
||||
private String getEasterDay(int year, int month, int day) {
|
||||
int n = year - 1900;
|
||||
int a = n % 19;
|
||||
int q = n / 4;
|
||||
int b = (7 * a + 1) / 19;
|
||||
int m = (11 * a + 4 - b) % 29;
|
||||
int w = (n + q + 31 - m) % 7;
|
||||
int answer = 25 - m - w;
|
||||
String easterDay = "";
|
||||
if (answer > 0) {
|
||||
easterDay = year + "-" + 4 + "-" + answer;
|
||||
} else {
|
||||
easterDay = year + "-" + 3 + "-" + (31 + answer);
|
||||
}
|
||||
String searchDay = year + "-" + month + "-" + day;
|
||||
if (searchDay.equals(easterDay)) {
|
||||
return "复活节";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入公历日期初始化当前日期的生肖、天干地支、农历年、农历月、农历日、公历节日、农历节日、24节气
|
||||
* 输入日期的格式为(YYYY-MM-DD)
|
||||
*
|
||||
* @param currentDate
|
||||
*/
|
||||
public void initLunarCalendarInfo(String currentDate) {
|
||||
String[] splitDate = currentDate.split("-");
|
||||
//设置生肖
|
||||
int year = Integer.parseInt(splitDate[0]);
|
||||
this.animal = animals[(year - 4) % 12];
|
||||
//设置天干地支
|
||||
int num = year - 1900 + 36;
|
||||
this.ganZhiYear = (tGan[num % 10] + dZhi[num % 12]);
|
||||
///////////设置阴历/////////////////////////////////////////////////////////
|
||||
//基准日期
|
||||
Date baseDate = null;
|
||||
//当前日期
|
||||
Date nowaday = null;
|
||||
try {
|
||||
baseDate = chineseDateFormat.parse("1900年1月31日");
|
||||
nowaday = solarDateFormat.parse(currentDate);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 获取当前日期与1900年1月31日相差的天数
|
||||
int offset = (int) ((nowaday.getTime() - baseDate.getTime()) / 86400000L);
|
||||
|
||||
//用offset减去每农历年的天数,计算当天是农历第几天 iYear最终结果是农历的年份
|
||||
int iYear, daysOfYear = 0;
|
||||
for (iYear = 1900; iYear < 10000 && offset > 0; iYear++) {
|
||||
daysOfYear = lunarYearDays(iYear);
|
||||
offset -= daysOfYear;
|
||||
}
|
||||
if (offset < 0) {
|
||||
offset += daysOfYear;
|
||||
iYear--;
|
||||
}
|
||||
this.lunarYear = getLunarYearString(iYear + "");
|
||||
int leapMonth = leapMonth(iYear); // 闰哪个月,1-12
|
||||
boolean leap = false;
|
||||
|
||||
// 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天
|
||||
int iMonth, daysOfMonth = 0;
|
||||
for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++) {
|
||||
// 闰月
|
||||
if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap) {
|
||||
--iMonth;
|
||||
leap = true;
|
||||
daysOfMonth = leapDays(iYear);
|
||||
} else
|
||||
daysOfMonth = monthDays(iYear, iMonth);
|
||||
|
||||
offset -= daysOfMonth;
|
||||
// 解除闰月
|
||||
if (leap && iMonth == (leapMonth + 1))
|
||||
leap = false;
|
||||
}
|
||||
// offset为0时,并且刚才计算的月份是闰月,要校正
|
||||
if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {
|
||||
if (leap) {
|
||||
leap = false;
|
||||
} else {
|
||||
leap = true;
|
||||
--iMonth;
|
||||
}
|
||||
}
|
||||
// offset小于0时,也要校正
|
||||
if (offset < 0) {
|
||||
offset += daysOfMonth;
|
||||
--iMonth;
|
||||
}
|
||||
// 设置对应的阴历月份
|
||||
this.lunarMonth = lunarNumber[iMonth - 1];
|
||||
if ("一".equals(this.lunarMonth)) {
|
||||
this.lunarMonth = "正";
|
||||
}
|
||||
if ("十二".equals(this.lunarMonth)) {
|
||||
this.lunarMonth = "腊";
|
||||
}
|
||||
if (leap) {
|
||||
this.lunarMonth = "闰" + this.lunarMonth;
|
||||
}
|
||||
|
||||
//设置阴历日
|
||||
int iDay = offset + 1;
|
||||
this.lunarDay = getLunarDayString(iDay);
|
||||
|
||||
//设置节气
|
||||
int month = Integer.parseInt(splitDate[1]);
|
||||
int day = Integer.parseInt(splitDate[2]);
|
||||
if (day == sTerm(year, (month - 1) * 2)) {
|
||||
this.lunarTerm = solarTerms[(month - 1) * 2];
|
||||
} else if (day == sTerm(year, (month - 1) * 2 + 1)) {
|
||||
this.lunarTerm = solarTerms[(month - 1) * 2 + 1];
|
||||
} else {
|
||||
this.lunarTerm = "";
|
||||
}
|
||||
|
||||
//设置阳历节日
|
||||
String solarFestival = "";
|
||||
for (int i = 0; i < solarHoliday.length; i++) {
|
||||
// 返回公历节假日名称
|
||||
String sd = solarHoliday[i].split(" ")[0]; // 节假日的日期
|
||||
String sdv = solarHoliday[i].split(" ")[1]; // 节假日的名称
|
||||
String smonth_v = splitDate[1];
|
||||
String sday_v = splitDate[2];
|
||||
String smd = smonth_v + sday_v;
|
||||
if (sd.trim().equals(smd.trim())) {
|
||||
solarFestival = sdv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//判断节日是否是父亲节或母亲节
|
||||
String motherOrFatherDay = getMotherOrFatherDay(year, month, day);
|
||||
if (motherOrFatherDay != null) {
|
||||
solarFestival = motherOrFatherDay;
|
||||
}
|
||||
//判断节日是否是复活节
|
||||
String easterDay = getEasterDay(year, month, day);
|
||||
if (easterDay != null) {
|
||||
solarFestival = easterDay;
|
||||
}
|
||||
//判断节日是否是感恩节
|
||||
String thanksgiving = thanksgiving(year, month, day);
|
||||
if (thanksgiving != null) {
|
||||
solarFestival = thanksgiving;
|
||||
}
|
||||
this.solarFestival = solarFestival;
|
||||
|
||||
//设置阴历节日
|
||||
String lunarFestival = "";
|
||||
for (int i = 0; i < lunarHoliday.length; i++) {
|
||||
//阴历闰月节日
|
||||
if (leap) {
|
||||
break;
|
||||
}
|
||||
// 返回农历节假日名称
|
||||
String ld = lunarHoliday[i].split(" ")[0]; // 节假日的日期
|
||||
String ldv = lunarHoliday[i].split(" ")[1]; // 节假日的名称
|
||||
String lmonth_v = iMonth + "";
|
||||
String lday_v = iDay + "";
|
||||
String lmd = "";
|
||||
if (iMonth < 10) {
|
||||
lmonth_v = "0" + iMonth;
|
||||
}
|
||||
if (iDay < 10) {
|
||||
lday_v = "0" + iDay;
|
||||
}
|
||||
lmd = lmonth_v + lday_v;
|
||||
if ("12".equals(lmonth_v)) { // 除夕夜需要特殊处理
|
||||
if ((daysOfMonth == 29 && iDay == 29) || (daysOfMonth == 30 && iDay == 30)) {
|
||||
lunarFestival = "除夕";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ld.trim().equals(lmd.trim())) {
|
||||
lunarFestival = ldv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ("清明".equals(this.lunarTerm)) {
|
||||
lunarFestival = "清明节";
|
||||
}
|
||||
this.lunarFestival = lunarFestival;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 测试方法
|
||||
// * @param args
|
||||
// */
|
||||
// public static void main(String[] args) {
|
||||
// LunarCalendarFestivalUtils festival = new LunarCalendarFestivalUtils();
|
||||
// festival.initLunarCalendarInfo("2021-06-25");
|
||||
// System.out.println("农历"+festival.getLunarYear()+"年"+festival.getLunarMonth()+"月"+festival.getLunarDay()+"日");
|
||||
// System.out.println(festival.getGanZhiYear()+"【"+festival.getAnimal()+"】年");
|
||||
// System.out.println(festival.getLunarTerm());
|
||||
// System.out.println(festival.getSolarFestival());
|
||||
// System.out.println(festival.getLunarFestival());
|
||||
// }
|
||||
}
|
||||
77
app/src/main/java/com/ttstd/dialer/utils/ScreenUtils.java
Normal file
77
app/src/main/java/com/ttstd/dialer/utils/ScreenUtils.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class ScreenUtils {
|
||||
private static final String TAG = "ScreenUtils";
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
|
||||
*/
|
||||
public static int dip2px(Context context, float dpValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
|
||||
*/
|
||||
public static int px2dip(Context context, float pxValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (pxValue / scale + 0.5f);
|
||||
}
|
||||
|
||||
public static int dp2px(Resources resources, float dp) {
|
||||
final float scale = resources.getDisplayMetrics().density;
|
||||
return (int) (dp * scale + 0.5f);
|
||||
}
|
||||
|
||||
public static int sp2px(Resources resources, float sp) {
|
||||
final float scale = resources.getDisplayMetrics().scaledDensity;
|
||||
return (int) (sp * scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用需反射调用
|
||||
*/
|
||||
public static boolean isTablet() {
|
||||
try {
|
||||
// 1. 反射获取 SystemProperties 类
|
||||
Class<?> systemPropertiesClass = Class.forName("android.os.SystemProperties");
|
||||
// 2. 获取 get(String key) 方法
|
||||
Method getMethod = systemPropertiesClass.getDeclaredMethod("get", String.class);
|
||||
// 3. 调用方法获取属性值
|
||||
String characteristics = (String) getMethod.invoke(null, "ro.build.characteristics");
|
||||
Log.e(TAG, "isTablet: " + characteristics);
|
||||
// 4. 判断是否包含 "tablet" 标识
|
||||
return characteristics != null && characteristics.contains("tablet");
|
||||
} catch (Exception e) {
|
||||
// 反射失败时的处理(如属性不存在或权限问题)
|
||||
Log.e(TAG, "Reflection failed: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isTablet(Context context) {
|
||||
boolean isTablet = (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
|
||||
Log.e(TAG, "isTablet: " + isTablet);
|
||||
return isTablet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否是平板
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 是平板则返回true,反之返回false
|
||||
*/
|
||||
public static boolean isPad(Context context) {
|
||||
return (context.getResources().getConfiguration().screenLayout
|
||||
& Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
|
||||
}
|
||||
}
|
||||
24
app/src/main/java/com/ttstd/dialer/utils/SystemUtils.java
Normal file
24
app/src/main/java/com/ttstd/dialer/utils/SystemUtils.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SystemUtils {
|
||||
|
||||
public static boolean isMainProcessName(Context cxt, int pid) {
|
||||
String packageName = cxt.getPackageName();
|
||||
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
|
||||
if (runningApps == null) {
|
||||
return false;
|
||||
}
|
||||
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
|
||||
if (procInfo.pid == pid) {
|
||||
return procInfo.processName.equals(packageName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
19
app/src/main/java/com/ttstd/dialer/utils/TimeUtils.java
Normal file
19
app/src/main/java/com/ttstd/dialer/utils/TimeUtils.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.ttstd.dialer.utils;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
public class TimeUtils {
|
||||
// 根据日期取得星期几
|
||||
public static String getWeek() {
|
||||
Date date = new Date();
|
||||
String[] weeks = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
int weekIndex = cal.get(Calendar.DAY_OF_WEEK) - 1;
|
||||
if (weekIndex < 0) {
|
||||
weekIndex = 0;
|
||||
}
|
||||
return weeks[weekIndex];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package com.ttstd.dialer.view;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 加载显示Fragment的ViewPagerAdapter基类
|
||||
* 提供可以刷新的方法
|
||||
*
|
||||
* @author Fly
|
||||
* @e-mail 1285760616@qq.com
|
||||
* @time 2018/3/22
|
||||
*/
|
||||
public class BaseFragmentPagerAdapter extends FragmentPagerAdapter {
|
||||
private List<Fragment> mFragmentList;
|
||||
private FragmentManager mFragmentManager;
|
||||
/**下面两个值用来保存Fragment的位置信息,用以判断该位置是否需要更新*/
|
||||
private SparseArray<String> mFragmentPositionMap;
|
||||
private SparseArray<String> mFragmentPositionMapAfterUpdate;
|
||||
|
||||
public BaseFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
|
||||
super(fm);
|
||||
mFragmentManager = fm;
|
||||
mFragmentList = fragments;
|
||||
mFragmentPositionMap = new SparseArray<>();
|
||||
mFragmentPositionMapAfterUpdate = new SparseArray<>();
|
||||
setFragmentPositionMap();
|
||||
setFragmentPositionMapForUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存更新之前的位置信息,用<hashCode, position>的键值对结构来保存
|
||||
*/
|
||||
private void setFragmentPositionMap() {
|
||||
mFragmentPositionMap.clear();
|
||||
for (int i = 0; i < mFragmentList.size(); i++) {
|
||||
mFragmentPositionMap.put(Long.valueOf(getItemId(i)).intValue(), String.valueOf(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存更新之后的位置信息,用<hashCode, position>的键值对结构来保存
|
||||
*/
|
||||
private void setFragmentPositionMapForUpdate() {
|
||||
mFragmentPositionMapAfterUpdate.clear();
|
||||
for (int i = 0; i < mFragmentList.size(); i++) {
|
||||
mFragmentPositionMapAfterUpdate.put(Long.valueOf(getItemId(i)).intValue(), String.valueOf(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在此方法中找到需要更新的位置返回POSITION_NONE,否则返回POSITION_UNCHANGED即可
|
||||
*/
|
||||
@Override
|
||||
public int getItemPosition(Object object) {
|
||||
int hashCode = object.hashCode();
|
||||
//查找object在更新后的列表中的位置
|
||||
String position = mFragmentPositionMapAfterUpdate.get(hashCode);
|
||||
//更新后的列表中不存在该object的位置了
|
||||
if (position == null) {
|
||||
return POSITION_NONE;
|
||||
} else {
|
||||
//如果更新后的列表中存在该object的位置, 查找该object之前的位置并判断位置是否发生了变化
|
||||
int size = mFragmentPositionMap.size();
|
||||
for (int i = 0; i < size ; i++) {
|
||||
int key = mFragmentPositionMap.keyAt(i);
|
||||
if (key == hashCode) {
|
||||
String index = mFragmentPositionMap.get(key);
|
||||
if (position.equals(index)) {
|
||||
//位置没变依然返回POSITION_UNCHANGED
|
||||
return POSITION_UNCHANGED;
|
||||
} else {
|
||||
//位置变了
|
||||
return POSITION_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return POSITION_UNCHANGED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定的Fragment替换/更新为新的Fragment
|
||||
* @param oldFragment 旧Fragment
|
||||
* @param newFragment 新Fragment
|
||||
*/
|
||||
public void replaceFragment(Fragment oldFragment, Fragment newFragment) {
|
||||
int position = mFragmentList.indexOf(oldFragment);
|
||||
if (position == -1) {
|
||||
return;
|
||||
}
|
||||
//从Transaction移除旧的Fragment
|
||||
removeFragmentInternal(oldFragment);
|
||||
//替换List中对应的Fragment
|
||||
mFragmentList.set(position, newFragment);
|
||||
//刷新Adapter
|
||||
notifyItemChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定位置的Fragment替换/更新为新的Fragment,同{@link #replaceFragment(Fragment oldFragment, Fragment newFragment)}
|
||||
* @param position 旧Fragment的位置
|
||||
* @param newFragment 新Fragment
|
||||
*/
|
||||
public void replaceFragment(int position, Fragment newFragment) {
|
||||
Fragment oldFragment = mFragmentList.get(position);
|
||||
removeFragmentInternal(oldFragment);
|
||||
mFragmentList.set(position, newFragment);
|
||||
notifyItemChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定的Fragment
|
||||
* @param fragment 目标Fragment
|
||||
*/
|
||||
public void removeFragment(Fragment fragment) {
|
||||
//先从List中移除
|
||||
mFragmentList.remove(fragment);
|
||||
//然后从Transaction移除
|
||||
removeFragmentInternal(fragment);
|
||||
//最后刷新Adapter
|
||||
notifyItemChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定位置的Fragment,同 {@link #removeFragment(Fragment fragment)}
|
||||
* @param position
|
||||
*/
|
||||
public void removeFragment(int position) {
|
||||
Fragment fragment = mFragmentList.get(position);
|
||||
//然后从List中移除
|
||||
mFragmentList.remove(fragment);
|
||||
//先从Transaction移除
|
||||
removeFragmentInternal(fragment);
|
||||
//最后刷新Adapter
|
||||
notifyItemChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Fragment
|
||||
* @param fragment 目标Fragment
|
||||
*/
|
||||
public void addFragment(Fragment fragment) {
|
||||
mFragmentList.add(fragment);
|
||||
notifyItemChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定位置插入一个Fragment
|
||||
* @param position 插入位置
|
||||
* @param fragment 目标Fragment
|
||||
*/
|
||||
public void insertFragment(int position, Fragment fragment) {
|
||||
mFragmentList.add(position, fragment);
|
||||
notifyItemChanged();
|
||||
}
|
||||
|
||||
public void notifyItemChanged() {
|
||||
//刷新之前重新收集位置信息
|
||||
setFragmentPositionMapForUpdate();
|
||||
notifyDataSetChanged();
|
||||
setFragmentPositionMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Transaction移除Fragment
|
||||
* @param fragment 目标Fragment
|
||||
*/
|
||||
private void removeFragmentInternal(Fragment fragment) {
|
||||
FragmentTransaction transaction = mFragmentManager.beginTransaction();
|
||||
transaction.remove(fragment);
|
||||
transaction.commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
/**
|
||||
* 此方法不用position做返回值即可破解fragment tag异常的错误
|
||||
*/
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
// 获取当前数据的hashCode,其实这里不用hashCode用自定义的可以关联当前Item对象的唯一值也可以,只要不是直接返回position
|
||||
return mFragmentList.get(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return mFragmentList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mFragmentList.size();
|
||||
}
|
||||
|
||||
public List<Fragment> getFragments() {
|
||||
return mFragmentList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
package com.ttstd.dialer.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PointF;
|
||||
import android.util.SparseArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import net.lucode.hackware.magicindicator.NavigatorHelper;
|
||||
import net.lucode.hackware.magicindicator.abs.IPagerNavigator;
|
||||
import net.lucode.hackware.magicindicator.buildins.ArgbEvaluatorHolder;
|
||||
import net.lucode.hackware.magicindicator.buildins.UIUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
// _oo0oo_
|
||||
// o8888888o
|
||||
// 88" . "88
|
||||
// (| -_- |)
|
||||
// 0\ = /0
|
||||
// ___/`---'\___
|
||||
// .' \\| |// '.
|
||||
// / \\||| : |||// \
|
||||
// / _||||| -:- |||||- \
|
||||
// | | \\\ - /// | |
|
||||
// | \_| ''\---/'' |_/ |
|
||||
// \ .-\__ '-' ___/-. /
|
||||
// ___'. .' /--.--\ `. .'___
|
||||
// ."" '< `.___\_<|>_/___.' >' "".
|
||||
// | | : `- \`.;`\ _ /`;.`/ - ` : | |
|
||||
// \ \ `_. \_ __\ /__ _/ .-` / /
|
||||
// =====`-.____`.___ \_____/___.-`___.-'=====
|
||||
// `=---='
|
||||
//
|
||||
//
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// 佛祖保佑 永无BUG
|
||||
|
||||
/**
|
||||
* 类似CircleIndicator的效果
|
||||
* Created by hackware on 2016/9/3.
|
||||
*/
|
||||
|
||||
public class ScaleCircleNavigator extends View implements IPagerNavigator, NavigatorHelper.OnNavigatorScrollListener {
|
||||
private int mMinRadius;
|
||||
private int mMaxRadius;
|
||||
private int mNormalCircleColor = Color.LTGRAY;
|
||||
private int mSelectedCircleColor = Color.GRAY;
|
||||
private int mCircleSpacing;
|
||||
private int mCircleCount;
|
||||
|
||||
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private List<PointF> mCirclePoints = new ArrayList<PointF>();
|
||||
private SparseArray<Float> mCircleRadiusArray = new SparseArray<Float>();
|
||||
|
||||
// 事件回调
|
||||
private boolean mTouchable;
|
||||
private OnCircleClickListener mCircleClickListener;
|
||||
private float mDownX;
|
||||
private float mDownY;
|
||||
private int mTouchSlop;
|
||||
|
||||
private boolean mFollowTouch = true; // 是否跟随手指滑动
|
||||
private NavigatorHelper mNavigatorHelper = new NavigatorHelper();
|
||||
private Interpolator mStartInterpolator = new LinearInterpolator();
|
||||
|
||||
public ScaleCircleNavigator(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
mMinRadius = UIUtil.dip2px(context, 3);
|
||||
mMaxRadius = UIUtil.dip2px(context, 4);
|
||||
mCircleSpacing = UIUtil.dip2px(context, 8);
|
||||
mNavigatorHelper.setNavigatorScrollListener(this);
|
||||
mNavigatorHelper.setSkimOver(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
|
||||
}
|
||||
|
||||
private int measureWidth(int widthMeasureSpec) {
|
||||
int mode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int result = 0;
|
||||
switch (mode) {
|
||||
case MeasureSpec.EXACTLY:
|
||||
result = width;
|
||||
break;
|
||||
case MeasureSpec.AT_MOST:
|
||||
case MeasureSpec.UNSPECIFIED:
|
||||
if (mCircleCount <= 0) {
|
||||
result = getPaddingLeft() + getPaddingRight();
|
||||
} else {
|
||||
result = (mCircleCount - 1) * mMinRadius * 2 + mMaxRadius * 2 + (mCircleCount - 1) * mCircleSpacing + getPaddingLeft() + getPaddingRight();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int measureHeight(int heightMeasureSpec) {
|
||||
int mode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int result = 0;
|
||||
switch (mode) {
|
||||
case MeasureSpec.EXACTLY:
|
||||
result = height;
|
||||
break;
|
||||
case MeasureSpec.AT_MOST:
|
||||
case MeasureSpec.UNSPECIFIED:
|
||||
result = mMaxRadius * 2 + getPaddingTop() + getPaddingBottom();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
for (int i = 0, j = mCirclePoints.size(); i < j; i++) {
|
||||
PointF point = mCirclePoints.get(i);
|
||||
float radius = mCircleRadiusArray.get(i, (float) mMinRadius);
|
||||
mPaint.setColor(ArgbEvaluatorHolder.eval((radius - mMinRadius) / (mMaxRadius - mMinRadius), mNormalCircleColor, mSelectedCircleColor));
|
||||
canvas.drawCircle(point.x, getHeight() / 2.0f, radius, mPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareCirclePoints() {
|
||||
mCirclePoints.clear();
|
||||
if (mCircleCount > 0) {
|
||||
int y = Math.round(getHeight() / 2.0f);
|
||||
int centerSpacing = mMinRadius * 2 + mCircleSpacing;
|
||||
int startX = mMaxRadius + getPaddingLeft();
|
||||
for (int i = 0; i < mCircleCount; i++) {
|
||||
PointF pointF = new PointF(startX, y);
|
||||
mCirclePoints.add(pointF);
|
||||
startX += centerSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (mTouchable) {
|
||||
mDownX = x;
|
||||
mDownY = y;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mCircleClickListener != null) {
|
||||
if (Math.abs(x - mDownX) <= mTouchSlop && Math.abs(y - mDownY) <= mTouchSlop) {
|
||||
float max = Float.MAX_VALUE;
|
||||
int index = 0;
|
||||
for (int i = 0; i < mCirclePoints.size(); i++) {
|
||||
PointF pointF = mCirclePoints.get(i);
|
||||
float offset = Math.abs(pointF.x - x);
|
||||
if (offset < max) {
|
||||
max = offset;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
mCircleClickListener.onClick(index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
mNavigatorHelper.onPageScrolled(position, positionOffset, positionOffsetPixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
mNavigatorHelper.onPageSelected(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
mNavigatorHelper.onPageScrollStateChanged(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
prepareCirclePoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
prepareCirclePoints();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachToMagicIndicator() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachFromMagicIndicator() {
|
||||
}
|
||||
|
||||
public void setMinRadius(int minRadius) {
|
||||
mMinRadius = minRadius;
|
||||
prepareCirclePoints();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setMaxRadius(int maxRadius) {
|
||||
mMaxRadius = maxRadius;
|
||||
prepareCirclePoints();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setNormalCircleColor(int normalCircleColor) {
|
||||
mNormalCircleColor = normalCircleColor;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setSelectedCircleColor(int selectedCircleColor) {
|
||||
mSelectedCircleColor = selectedCircleColor;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setCircleSpacing(int circleSpacing) {
|
||||
mCircleSpacing = circleSpacing;
|
||||
prepareCirclePoints();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setStartInterpolator(Interpolator startInterpolator) {
|
||||
mStartInterpolator = startInterpolator;
|
||||
if (mStartInterpolator == null) {
|
||||
mStartInterpolator = new LinearInterpolator();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCircleCount(int count) {
|
||||
mCircleCount = count; // 此处不调用invalidate,让外部调用notifyDataSetChanged
|
||||
mNavigatorHelper.setTotalCount(mCircleCount);
|
||||
}
|
||||
|
||||
public void setTouchable(boolean touchable) {
|
||||
mTouchable = touchable;
|
||||
}
|
||||
|
||||
public void setFollowTouch(boolean followTouch) {
|
||||
mFollowTouch = followTouch;
|
||||
}
|
||||
|
||||
public void setSkimOver(boolean skimOver) {
|
||||
mNavigatorHelper.setSkimOver(skimOver);
|
||||
}
|
||||
|
||||
public void setCircleClickListener(OnCircleClickListener circleClickListener) {
|
||||
if (!mTouchable) {
|
||||
mTouchable = true;
|
||||
}
|
||||
mCircleClickListener = circleClickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
|
||||
if (mFollowTouch) {
|
||||
float radius = mMinRadius + (mMaxRadius - mMinRadius) * mStartInterpolator.getInterpolation(enterPercent);
|
||||
mCircleRadiusArray.put(index, radius);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
|
||||
if (mFollowTouch) {
|
||||
float radius = mMaxRadius + (mMinRadius - mMaxRadius) * mStartInterpolator.getInterpolation(leavePercent);
|
||||
mCircleRadiusArray.put(index, radius);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelected(int index, int totalCount) {
|
||||
if (!mFollowTouch) {
|
||||
mCircleRadiusArray.put(index, (float) mMaxRadius);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeselected(int index, int totalCount) {
|
||||
if (!mFollowTouch) {
|
||||
mCircleRadiusArray.put(index, (float) mMinRadius);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnCircleClickListener {
|
||||
void onClick(int index);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user