17 files added
2 files renamed
33 files modified
15 files deleted
| | |
| | | buildTypes { |
| | | debug{ |
| | | minifyEnabled false |
| | | buildConfigField 'String','HOST_IP_ADDR','"http://192.168.100.82:8080/"' |
| | | buildConfigField 'String','ENVIRONMENT','"release"' |
| | | resValue "string", "app_name", "MVVM开源项目测试" |
| | | buildConfigField 'String','HOST_IP_ADDR','"http://192.168.110.116:8080/"' |
| | | resValue "string", "app_name", "MVVM OPEN TEST" |
| | | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
| | | } |
| | | release { |
| | | minifyEnabled false |
| | | buildConfigField 'String','HOST_IP_ADDR','"http://192.168.100.82:8080/"' |
| | | buildConfigField 'String','ENVIRONMENT','"release"' |
| | | resValue "string", "app_name", "MVVM开源项目" |
| | | buildConfigField 'String','HOST_IP_ADDR','"http://192.168.100.82:8080/' |
| | | resValue "string", "app_name", "MVVM OPEN" |
| | | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
| | | } |
| | | } |
| | |
| | | implementation 'com.guolindev.permissionx:permissionx:1.6.0' //权限依赖 |
| | | implementation 'com.github.bumptech.glide:glide:4.12.0' |
| | | annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' |
| | | implementation 'com.pangle.cn:ads-sdk-pro:4.0.2.2'//字节跳动 穿山甲广告 |
| | | implementation 'com.tencent.bugly:crashreport:latest.release' //其中latest.release指代最新Bugly SDK版本号,也可以指定明确的版本号,例如2.1.9 |
| | | implementation 'com.tencent.bugly:nativecrashreport:latest.release' //其中latest.release指代最新Bugly NDK版本号,也可以指定明确的版本号,例如3.0 |
| | | implementation 'com.facebook.rebound:rebound:0.3.6'//Rebound “弹簧”动画效果的第三方工具包,由FaceBook |
| | | implementation 'com.github.zhaolei9527:BottomMenu:v1.0.1'//底部菜单弹框 |
| | | implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.2.3'//图片选择 |
| | | implementation 'com.github.wildma:PictureSelector:1.1.1'//图片选择裁切工具 |
| | | } |
| | |
| | | android:label="@string/app_name" |
| | | android:supportsRtl="true" |
| | | android:networkSecurityConfig="@xml/network_security_config" |
| | | tools:replace="android:theme" |
| | | android:theme="@style/Theme.OpemMvvm" > |
| | | <activity |
| | | android:name=".ui.splash.SplashActivity" |
| | |
| | | <category android:name="android.intent.category.LAUNCHER" /> |
| | | </intent-filter> |
| | | </activity> |
| | | <activity android:name=".MainActivity" |
| | | <activity android:name=".ui.main.MainActivity" |
| | | android:launchMode="singleTask" |
| | | tools:ignore="WrongManifestParent" |
| | | android:exported="true"> |
| | |
| | | import androidx.annotation.NonNull; |
| | | import androidx.annotation.Nullable; |
| | | |
| | | import com.bytedance.sdk.openadsdk.TTAdConfig; |
| | | import com.bytedance.sdk.openadsdk.TTAdConstant; |
| | | import com.bytedance.sdk.openadsdk.TTAdSdk; |
| | | import com.runt.open.mvvm.listener.CrashHandler; |
| | | import com.runt.open.mvvm.util.MyLog; |
| | | import com.scwang.smart.refresh.footer.ClassicsFooter; |
| | |
| | | import com.scwang.smart.refresh.layout.api.RefreshLayout; |
| | | import com.scwang.smart.refresh.layout.listener.DefaultRefreshFooterCreator; |
| | | import com.scwang.smart.refresh.layout.listener.DefaultRefreshHeaderCreator; |
| | | import com.tencent.bugly.crashreport.CrashReport; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | |
| | | } |
| | | } |
| | | }); |
| | | CrashReport.initCrashReport(getApplicationContext(), "8d88679ae9", false);//注册bugly |
| | | TTAdConfig.Builder builder = new TTAdConfig.Builder() |
| | | .appId("5106813") |
| | | .useTextureView(true) //使用TextureView控件播放视频,默认为SurfaceView,当有SurfaceView冲突的场景,可以使用TextureView |
| | | .appName(getString(R.string.app_name)) |
| | | .titleBarTheme(TTAdConstant.TITLE_BAR_THEME_DARK) |
| | | .allowShowNotify(true) //是否允许sdk展示通知栏提示 |
| | | .allowShowPageWhenScreenLock(true) //是否在锁屏场景支持展示广告落地页 |
| | | .directDownloadNetworkType(TTAdConstant.NETWORK_STATE_WIFI) //允许直接下载的网络状态集合 |
| | | .supportMultiProcess(true) //是否支持多进程,true支持 |
| | | .asyncInit(true) ;//异步初始化sdk,开启可减少初始化耗时 |
| | | //.httpStack(new MyOkStack3())//自定义网络库,demo中给出了okhttp3版本的样例,其余请自行开发或者咨询工作人员。 |
| | | TTAdSdk.init(this, builder.build(), new TTAdSdk.InitCallback() { |
| | | @Override |
| | | public void success() { |
| | | MyLog.i(TAG,"TTAdSdk success"); |
| | | } |
| | | |
| | | @Override |
| | | public void fail(int i, String s) { |
| | | MyLog.e(TAG,"TTAdSdk fail"); |
| | | } |
| | | }); |
| | | CrashHandler crashHandler = CrashHandler.getInstance(); |
| | | crashHandler.init(getApplicationContext(), new CrashHandler.CrashListener() { |
| | | @Override |
| | |
| | | import android.Manifest; |
| | | import android.app.AlertDialog; |
| | | import android.content.Context; |
| | | import android.content.Intent; |
| | | import android.content.pm.ActivityInfo; |
| | | import android.graphics.drawable.Drawable; |
| | | import android.net.Uri; |
| | | import android.os.Build; |
| | | import android.os.Bundle; |
| | | import android.os.Environment; |
| | | import android.view.LayoutInflater; |
| | | import android.view.MotionEvent; |
| | | import android.view.View; |
| | | import android.view.ViewGroup; |
| | | import android.view.WindowManager; |
| | | import android.view.inputmethod.InputMethodManager; |
| | | import android.widget.EditText; |
| | |
| | | import androidx.lifecycle.ViewModelProvider; |
| | | import androidx.viewbinding.ViewBinding; |
| | | |
| | | import com.permissionx.guolindev.PermissionX; |
| | | import com.runt.open.mvvm.MyApplication; |
| | | import com.runt.open.mvvm.R; |
| | | import com.runt.open.mvvm.base.model.BaseViewModel; |
| | | import com.runt.open.mvvm.base.model.ViewModelFactory; |
| | | import com.runt.open.mvvm.listener.ResPonse; |
| | | import com.runt.open.mvvm.util.PreferencesUtils; |
| | | import com.runt.open.mvvm.widgets.TitleBarView; |
| | | |
| | | import java.io.File; |
| | | import java.lang.reflect.Method; |
| | |
| | | * activity 封装 |
| | | * Created by Administrator on 2021/10/27 0027. |
| | | */ |
| | | public abstract class BaseActivity<B extends ViewBinding,VM extends BaseViewModel> extends AppCompatActivity { |
| | | public abstract class BaseActivity<VB extends ViewBinding,VM extends BaseViewModel> extends AppCompatActivity { |
| | | |
| | | protected B binding; |
| | | protected VM viewModel; |
| | | protected VB mBinding; |
| | | protected VM mViewModel; |
| | | protected String TAG ; |
| | | public final String[] FILE_PERMISSIONS = new String []{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE}; |
| | | public final String[] LOCATION_PERMISSIONS = new String []{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION}; |
| | | public final String[] CAMERA_PERMISSIONS = new String[]{ FILE_PERMISSIONS[0],FILE_PERMISSIONS[1], Manifest.permission.CAMERA}; |
| | | public final String[] CAMERA_RECORD_PERMISSIONS = new String[]{ FILE_PERMISSIONS[0],FILE_PERMISSIONS[1], Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO}; |
| | | |
| | |
| | | RESULT_CODE_SUCESS = 4046/*成功*/, |
| | | RESULT_CODE_CANCEL = 4043/*取消*/; |
| | | protected Context mContext; |
| | | TitleBarView titleBarView; |
| | | |
| | | |
| | | @Override |
| | |
| | | setStatusBarTextColor(true); |
| | | final ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); |
| | | try { |
| | | Class<B> entityClass = (Class<B>) type.getActualTypeArguments()[0]; |
| | | Class<VB> entityClass = (Class<VB>) type.getActualTypeArguments()[0]; |
| | | Method method = entityClass.getMethod("inflate", LayoutInflater.class);//get method from name "inflate"; |
| | | binding = (B) method.invoke(entityClass,getLayoutInflater());//execute method to create a objct of viewbind; |
| | | mBinding = (VB) method.invoke(entityClass,getLayoutInflater());//execute method to create a objct of viewbind; |
| | | titleBarView = (TitleBarView) mBinding.getClass().getDeclaredField("titleBar").get(mBinding); |
| | | titleBarView.setLeftClick(v -> onBackPressed()); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | Class<VM> vmClass = (Class<VM>) type.getActualTypeArguments()[1]; |
| | | viewModel = new ViewModelProvider(this,getViewModelFactory()).get(vmClass); |
| | | setContentView(binding.getRoot()); |
| | | mViewModel = new ViewModelProvider(this,getViewModelFactory()).get(vmClass); |
| | | setContentView(mBinding.getRoot()); |
| | | mContext = this; |
| | | try { |
| | | //设置坚屏 一定要放到try catch里面,否则会崩溃 |
| | |
| | | } |
| | | TAG = this.getClass().getSimpleName(); |
| | | initViews(); |
| | | mViewModel.onCreate(this); |
| | | loadData(); |
| | | } |
| | | |
| | | public abstract void initViews(); |
| | | |
| | | public abstract void loadData(); |
| | | |
| | | public boolean isNull(Object object){ |
| | | return object == null || object.toString().trim().equals("") || object.equals("null"); |
| | | } |
| | | |
| | | protected void setTitle(String text){ |
| | | titleBarView.setTitleText(text); |
| | | } |
| | | |
| | | protected void onTitleLeftClick(){ |
| | | onBackKeyDown(); |
| | | } |
| | | |
| | | protected void setTitleRight(String text){ |
| | | titleBarView.setRightText(text); |
| | | titleBarView.setRightDra(null); |
| | | } |
| | | |
| | | protected void setTitleRight(Drawable drawable){ |
| | | titleBarView.setRightText(null); |
| | | titleBarView.setRightDra(drawable); |
| | | } |
| | | |
| | | |
| | |
| | | * 显示弹框 |
| | | * @param title |
| | | * @param msg |
| | | * @param resPonse |
| | | */ |
| | | public void showDialog(String title,String msg,ResPonse resPonse){ |
| | | showDialog(title,msg,"确认","取消",resPonse); |
| | | } |
| | | |
| | | public void showInputDialog(String title,String msg,String hint,ResPonse resPonse){ |
| | | showInputDialog(title,msg,hint,"确认","取消",resPonse); |
| | | } |
| | | |
| | | public void showInputDialog(String title, String msg, String hint,String btnOk,String btnCancel,final ResPonse resPonse){ |
| | | showDialog(title,msg,hint,btnOk,btnCancel,resPonse,true); |
| | | } |
| | | |
| | | /** |
| | | * 显示弹框 |
| | | * @param title |
| | | * @param msg |
| | | * @param btnOk |
| | | * @param btnCancel |
| | | * @param resPonse |
| | | */ |
| | | public void showDialog(String title, String msg, String btnOk,String btnCancel,final ResPonse resPonse){ |
| | | public void showDialog(String title, String msg, String btnOk,String btnCancel,final ResPonse resPonse){ |
| | | showDialog(title,msg,null,btnOk,btnCancel,resPonse,false); |
| | | } |
| | | |
| | |
| | | getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); |
| | | |
| | | setStatusBarTextColor(isBlack); |
| | | final ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) titleBarView.getLayoutParams(); |
| | | layoutParams.topMargin = layoutParams.topMargin+getStatusBarHeight(); |
| | | titleBarView.setLayoutParams(layoutParams); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | } |
| | | public void showToast(String message){ |
| | | Toast.makeText(this,message,Toast.LENGTH_SHORT).show(); |
| | | runOnUiThread(() -> Toast.makeText(mContext,message,Toast.LENGTH_SHORT).show()); |
| | | } |
| | | |
| | | public void showToast(@StringRes int msg){ |
| | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 拨打电话(直接拨打电话) |
| | | * @param phoneNum 电话号码 |
| | | */ |
| | | public void callPhone(String phoneNum){ |
| | | PermissionX.init(this) |
| | | .permissions(Manifest.permission.CALL_PHONE) |
| | | .request((allGranted, grantedList, deniedList) -> { |
| | | if(allGranted){ |
| | | Intent intent = new Intent(Intent.ACTION_CALL); |
| | | Uri data = Uri.parse("tel:" + phoneNum); |
| | | intent.setData(data); |
| | | startActivity(intent); |
| | | }else{ |
| | | showToast("权限被拒绝"); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | |
| | | public boolean getBooleanUserPrefrence(String key){ |
| | | return PreferencesUtils.getBoolean(this,key,false,PreferencesUtils.USER); |
| | |
| | | //设置当前可见Item左右可见page数,次范围内不会被销毁 |
| | | //禁用预加载 |
| | | try { |
| | | viewPager2 = (ViewPager2) binding.getClass().getDeclaredField("viewPager2").get(binding); |
| | | tabLayout = (TabLayout) binding.getClass().getDeclaredField("tabLayout").get(binding); |
| | | viewPager2 = (ViewPager2) mBinding.getClass().getDeclaredField("viewPager2").get(mBinding); |
| | | tabLayout = (TabLayout) mBinding.getClass().getDeclaredField("tabLayout").get(mBinding); |
| | | } catch (IllegalAccessException e) { |
| | | e.printStackTrace(); |
| | | } catch (NoSuchFieldException e) { |
New file |
| | |
| | | package com.runt.open.mvvm.base.activities; |
| | | |
| | | import androidx.annotation.NonNull; |
| | | import androidx.lifecycle.Observer; |
| | | import androidx.recyclerview.widget.LinearLayoutManager; |
| | | import androidx.recyclerview.widget.RecyclerView; |
| | | import androidx.viewbinding.ViewBinding; |
| | | |
| | | import com.runt.open.mvvm.R; |
| | | import com.runt.open.mvvm.base.adapter.BaseAdapter; |
| | | import com.runt.open.mvvm.base.model.LoadPageViewModel; |
| | | import com.scwang.smart.refresh.footer.ClassicsFooter; |
| | | import com.scwang.smart.refresh.header.ClassicsHeader; |
| | | import com.scwang.smart.refresh.layout.SmartRefreshLayout; |
| | | import com.scwang.smart.refresh.layout.api.RefreshLayout; |
| | | import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener; |
| | | |
| | | import java.lang.reflect.ParameterizedType; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * Created by Administrator on 2021/11/4 0004. |
| | | */ |
| | | public abstract class LoadPageActivity<VB extends ViewBinding,VM extends LoadPageViewModel,A extends BaseAdapter,RESULT> |
| | | extends BaseActivity<VB,VM> implements OnRefreshLoadMoreListener { |
| | | protected int page; |
| | | protected SmartRefreshLayout refresh; |
| | | //适配器 |
| | | protected A adapter; |
| | | |
| | | @Override |
| | | public void initViews() { |
| | | try { |
| | | Class<A> entityClass = (Class<A>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[2]; |
| | | this.adapter = entityClass.newInstance();//实例化泛型 |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | refresh.setRefreshHeader(new ClassicsHeader(mContext)); |
| | | refresh.setRefreshFooter(new ClassicsFooter(mContext)); |
| | | refresh.setOnRefreshLoadMoreListener(this); |
| | | RecyclerView recycler = mBinding.getRoot().findViewById(R.id.recycler); |
| | | recycler.setLayoutManager(new LinearLayoutManager(mContext)); |
| | | recycler.setAdapter(adapter); |
| | | refresh = mBinding.getRoot().findViewById(R.id.refresh); |
| | | refresh.setOnRefreshLoadMoreListener(this); |
| | | mViewModel.getLiveData().observe(this, (Observer<List<RESULT>>) list -> { |
| | | adapter.showNull = true; |
| | | if(page == 0){ |
| | | adapter.setData(list); |
| | | }else{ |
| | | adapter.addData(list); |
| | | } |
| | | refresh.finishRefresh(); |
| | | //加载完毕 |
| | | if(list.size() < 10 || page > 0 && list.size() == 0){ |
| | | refresh.finishLoadMoreWithNoMoreData(); |
| | | }else{ |
| | | refresh.finishLoadMore(); |
| | | } |
| | | }); |
| | | mViewModel.getLiveFailed().observe(this, o -> { |
| | | refresh.finishRefresh(); |
| | | refresh.finishLoadMore(); |
| | | //校正page数值 |
| | | int size = adapter.getData().size(); |
| | | if(size/mViewModel.SIZE+1 < page){ |
| | | page--; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public void loadData() { |
| | | refresh.autoRefresh(); |
| | | } |
| | | |
| | | protected abstract Map requestParams(); |
| | | |
| | | @Override |
| | | public void onRefresh(@NonNull RefreshLayout refreshLayout) { |
| | | page = 0; |
| | | mViewModel.requestData(page,requestParams()); |
| | | } |
| | | |
| | | @Override |
| | | public void onLoadMore(@NonNull RefreshLayout refreshLayout) { |
| | | page++; |
| | | mViewModel.requestData(page,requestParams()); |
| | | } |
| | | |
| | | public A getAdapter() { |
| | | return adapter; |
| | | } |
| | | } |
| | |
| | | package com.runt.open.mvvm.base.adapter; |
| | | |
| | | import android.graphics.drawable.Drawable; |
| | | import android.util.Log; |
| | | import android.view.LayoutInflater; |
| | | import android.view.ViewGroup; |
| | | |
| | |
| | | import androidx.recyclerview.widget.RecyclerView; |
| | | import androidx.viewbinding.ViewBinding; |
| | | |
| | | import com.runt.open.mvvm.base.activities.BaseActivity; |
| | | import com.runt.open.mvvm.databinding.LayoutNullBinding; |
| | | import com.runt.open.mvvm.util.DeviceUtil; |
| | | |
| | |
| | | * T 数据类型 |
| | | * V 适配器视图 |
| | | */ |
| | | public abstract class BaseAdapter<B extends ViewBinding,T> extends RecyclerView.Adapter { |
| | | public abstract class BaseAdapter<DATA, VB extends ViewBinding> extends RecyclerView.Adapter<RecyclerView.ViewHolder> { |
| | | |
| | | protected List<T> mData = new ArrayList<>(); |
| | | protected List<DATA> dataList = new ArrayList<>(); |
| | | protected OnItemClickListener<DATA> onItemClickListener; |
| | | public boolean showNull; |
| | | public float defaultMarginBottom,lastMarginBottom; |
| | | |
| | | protected Drawable nullDrawable; |
| | | protected String nullTxt="暂无数据"; |
| | | protected String TAG = "BaseAdapter"; |
| | | protected BaseActivity activity; |
| | | |
| | | public BaseAdapter(){ |
| | | public interface OnItemClickListener<DATA>{ |
| | | void onItemClick(int position,DATA data); |
| | | } |
| | | |
| | | public BaseAdapter(@NonNull List<T> data){ |
| | | mData = data; |
| | | public class ViewBindHolder extends RecyclerView.ViewHolder { |
| | | ViewBinding binding; |
| | | public ViewBindHolder(ViewBinding binding) { |
| | | super(binding.getRoot()); |
| | | this.binding = binding; |
| | | } |
| | | } |
| | | |
| | | public List<T> getData() { |
| | | return mData; |
| | | public void setOnItemClickListener(OnItemClickListener<DATA> onItemClickListener) { |
| | | this.onItemClickListener = onItemClickListener; |
| | | } |
| | | |
| | | public void setData(@NonNull List<T> data){ |
| | | mData = data; |
| | | public void setData(List<DATA> list){ |
| | | if(dataList != list) { |
| | | dataList.clear(); |
| | | if (list != null) { |
| | | dataList.addAll(list); |
| | | } |
| | | } |
| | | notifyDataSetChanged(); |
| | | } |
| | | |
| | | public void addData(DATA data){ |
| | | if(data != null){ |
| | | dataList.add(data); |
| | | } |
| | | notifyDataSetChanged(); |
| | | } |
| | | |
| | | public void addData(List<DATA> list){ |
| | | if (list != null && list.size() > 0) { |
| | | this.dataList.addAll(list); |
| | | } |
| | | notifyDataSetChanged(); |
| | | } |
| | | |
| | | public List<DATA> getData() { |
| | | return dataList; |
| | | } |
| | | |
| | | @NonNull |
| | | @Override |
| | | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |
| | | public ViewBindHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |
| | | if(viewType == 1 ){ |
| | | // get genericity "B" |
| | | Class<B> entityClass = (Class<B>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; |
| | | try { |
| | | /* for(Method method: entityClass.getMethods()){ |
| | | Class<VB> entityClass = (Class<VB>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]; |
| | | /*for(Method method: entityClass.getMethods()){ |
| | | StringBuilder sb = new StringBuilder(); |
| | | for(Class type : method.getParameterTypes()){ |
| | | sb.append(type.getSimpleName()+","); |
| | | } |
| | | Log.e(TAG,String.format("method:%s,return:%s,param:%s",method.getName(),method.getReturnType().getSimpleName(),sb.toString())); |
| | | Log.e("BaseAdapter",String.format("method:%s,return:%s,param:%s",method.getName(),method.getReturnType().getSimpleName(),sb.toString())); |
| | | }*/ |
| | | Method method = entityClass.getMethod("inflate", LayoutInflater.class,ViewGroup.class,boolean.class);//get method from name "inflate"; |
| | | B vBind = (B) method.invoke(entityClass,LayoutInflater.from(parent.getContext()),parent,false);//execute method to create a objct of viewbind; |
| | | VB vBind = (VB) method.invoke(entityClass,LayoutInflater.from(parent.getContext()),parent,false);//execute method to create a objct of viewbind; |
| | | return new ViewBindHolder(vBind); |
| | | } catch (SecurityException e) { |
| | | e.printStackTrace(); |
| | |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | return new NullViewHolder( LayoutNullBinding.inflate(LayoutInflater.from(parent.getContext()),parent,false)); |
| | | return new ViewBindHolder( LayoutNullBinding.inflate( LayoutInflater.from(parent.getContext()), parent, false ) ); |
| | | } |
| | | |
| | | @Override |
| | | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { |
| | | //MyLog.i(TAG,"onBindViewHolder position:"+position+" "+mData.size()+" "+getItemViewType(position)); |
| | | if(activity == null){ |
| | | activity = (BaseActivity) holder.itemView.getContext(); |
| | | } |
| | | if(getItemViewType(position)==0){ |
| | | bindView((NullViewHolder) holder); |
| | | }else { |
| | | bindView((ViewBindHolder) holder, mData.size() == 0 ? null : mData.get(position), position); |
| | | ViewBindHolder bindHolder = (ViewBindHolder) holder; |
| | | if(getItemViewType(position) == 0 || bindHolder.binding instanceof LayoutNullBinding){ |
| | | onBindEmptyView((LayoutNullBinding) bindHolder.binding); |
| | | }else{ |
| | | if (onItemClickListener != null) { |
| | | bindHolder.binding.getRoot().setOnClickListener(view -> { |
| | | if(onItemClickListener != null){ |
| | | onItemClickListener.onItemClick(position,getItem(position)); |
| | | } |
| | | }); |
| | | } |
| | | onBindView((VB) bindHolder.binding, position, getItem(position)); |
| | | setBottomMargin(bindHolder,position); |
| | | } |
| | | } |
| | | |
| | |
| | | * @param position |
| | | */ |
| | | protected void setBottomMargin(ViewBindHolder holder, int position){ |
| | | setBottomMargin(holder,position,23); |
| | | setBottomMargin(holder,position,lastMarginBottom); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param position 位置 |
| | | * @param dp 间距 |
| | | */ |
| | | protected void setBottomMargin(RecyclerView.ViewHolder holder, int position,int dp){ |
| | | setBottomMargin(holder,position,dp,0); |
| | | protected void setBottomMargin(RecyclerView.ViewHolder holder, int position,float dp){ |
| | | setBottomMargin(holder,position,dp,defaultMarginBottom); |
| | | } |
| | | protected void setBottomMargin(RecyclerView.ViewHolder holder, int position, int dp, int defaultDp){ |
| | | |
| | | protected void setBottomMargin(RecyclerView.ViewHolder holder, int position, float dp, float defaultDp){ |
| | | ViewGroup.MarginLayoutParams params1 = (ViewGroup.MarginLayoutParams) holder.itemView.getLayoutParams(); |
| | | if(position == mData.size() -1){ |
| | | if(position == dataList.size() -1){ |
| | | params1.setMargins(params1.leftMargin, params1.topMargin, params1.rightMargin, DeviceUtil.convertDpToPixel(dp,holder.itemView.getContext())); |
| | | }else{ |
| | | params1.setMargins(params1.leftMargin, params1.topMargin, params1.rightMargin, DeviceUtil.convertDpToPixel(defaultDp,holder.itemView.getContext())); |
| | | } |
| | | } |
| | | protected abstract void bindView(ViewBindHolder holder,T data,int position); |
| | | |
| | | |
| | | protected void bindView(NullViewHolder holder){ |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 空数据支持 |
| | | * @return |
| | | */ |
| | | @Override |
| | | public int getItemCount() { |
| | | //默认显示空视图,若不显示空视图则重写该方法,返回mData.size() |
| | | return mData == null || mData.size() == 0 ?1:mData.size(); |
| | | return (showNull && dataList.size() == 0 )? 1 : dataList.size(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 当下标为0,数据集合为0 返回0(意味当前应显示空数据视图)) |
| | | * @param position |
| | | * @return |
| | | */ |
| | | @Override |
| | | public int getItemViewType(int position) { |
| | | //当下标为0,数据集合为0 返回0(意味当前应显示空数据视图)) |
| | | //MyLog.i(TAG,"getItemViewType position:"+position+" mdata:"+mData.size()+" "+(position ==0 && mData.size()==0)); |
| | | return position == 0 && mData.size()==0?0:1; |
| | | return (showNull && position == 0 && dataList.size() == 0)? 0 : 1; |
| | | } |
| | | |
| | | public class ViewBindHolder extends RecyclerView.ViewHolder{ |
| | | public B binding; |
| | | public ViewBindHolder( B binding) { |
| | | super(binding.getRoot()); |
| | | this.binding = binding; |
| | | public DATA getItem(int position){ |
| | | if(position >= dataList.size()){ |
| | | return null; |
| | | }else { |
| | | return dataList.get(position); |
| | | } |
| | | } |
| | | |
| | | protected abstract void onBindView(VB binding, int position, DATA data); |
| | | |
| | | /** |
| | | * 空数据显示 |
| | | * Created by Administrator on 2021/10/28 0028. |
| | | */ |
| | | public class NullViewHolder extends RecyclerView.ViewHolder { |
| | | LayoutNullBinding binding; |
| | | protected void onBindEmptyView(LayoutNullBinding emptyBinding){ |
| | | Log.e("baseAdapter"," emptyBinding:"+emptyBinding); |
| | | |
| | | public NullViewHolder(LayoutNullBinding binding) { |
| | | super(binding.getRoot()); |
| | | this.binding = binding; |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | import androidx.annotation.Nullable; |
| | | import androidx.fragment.app.Fragment; |
| | | import androidx.lifecycle.ViewModel; |
| | | import androidx.lifecycle.ViewModelProvider; |
| | | import androidx.viewbinding.ViewBinding; |
| | | |
| | | import com.runt.open.mvvm.base.activities.BaseActivity; |
| | | import com.runt.open.mvvm.base.model.BaseViewModel; |
| | | import com.runt.open.mvvm.base.model.ViewModelFactory; |
| | | |
| | | import java.lang.reflect.Method; |
| | |
| | | * fragment 封装 |
| | | * Created by Administrator on 2021/10/28 0028. |
| | | */ |
| | | public abstract class BaseFragment<B extends ViewBinding,VM extends ViewModel> extends Fragment { |
| | | public abstract class BaseFragment<VB extends ViewBinding,VM extends BaseViewModel> extends Fragment { |
| | | |
| | | protected BaseActivity activity; |
| | | protected B binding; |
| | | protected VM viewModel; |
| | | protected BaseActivity mActivity; |
| | | protected VB mBinding; |
| | | protected VM mViewModel; |
| | | public static final int REQUEST_CODE_LOGOUT = 20009,/*退出*/ |
| | | REQUEST_CODE_ORDER = 10010,/*订单详情*/ |
| | | REQUEST_CODE_LOGIN = 20010,/*登录*/ |
| | | REQUEST_CODE_SCAN = 20011/*扫描请求*/, |
| | | REQUEST_CODE_PIC = 20012,/*选择图片*/ |
| | | REQUEST_CODE_PERMISSION = 20013,/*请求权限*/ |
| | | REQUEST_CODE_SIGN = 20014,/*签到*/ |
| | | REQUEST_CODE_WITHDRAW = 22014,/*提现*/ |
| | | RESULT_CODE_FAILD = 4044/*失败*/, |
| | | RESULT_CODE_SUCESS = 4046/*成功*/, |
| | | RESULT_CODE_CANCEL = 4043/*取消*/; |
| | | |
| | | @Nullable |
| | | @Override |
| | |
| | | // get genericity "B" |
| | | try { |
| | | final ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); |
| | | Class<B> entityClass = (Class<B>) type.getActualTypeArguments()[0]; |
| | | Class<VB> entityClass = (Class<VB>) type.getActualTypeArguments()[0]; |
| | | Method method = entityClass.getMethod("inflate", LayoutInflater.class,ViewGroup.class,boolean.class);//get method from name "inflate"; |
| | | binding = (B) method.invoke(entityClass,inflater,container,false);//execute method to create a objct of viewbind; |
| | | mBinding = (VB) method.invoke(entityClass,inflater,container,false);//execute method to create a objct of viewbind; |
| | | Class<VM> vmClass = (Class<VM>) type.getActualTypeArguments()[1]; |
| | | viewModel = new ViewModelProvider(this,getViewModelFactory()).get(vmClass); |
| | | mViewModel = new ViewModelProvider(this,getViewModelFactory()).get(vmClass); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return binding.getRoot(); |
| | | initViews(); |
| | | return mBinding.getRoot(); |
| | | } |
| | | |
| | | public void setOnClickListener(View.OnClickListener click,int... ids){ |
| | | for (int id: ids){ |
| | | getActivity().findViewById(id).setOnClickListener(click); |
| | | } |
| | | } |
| | | public ViewModelProvider.Factory getViewModelFactory(){ |
| | | return ViewModelFactory.getInstance(); |
| | | } |
| | |
| | | @Override |
| | | public void onActivityCreated(@Nullable Bundle savedInstanceState) { |
| | | super.onActivityCreated(savedInstanceState); |
| | | activity = (BaseActivity) getActivity(); |
| | | initViews(); |
| | | mActivity = (BaseActivity) getActivity(); |
| | | mViewModel.onCreate(mActivity); |
| | | loadData(); |
| | | } |
| | | |
| | | public abstract void initViews(); |
| | | |
| | | public abstract void loadData(); |
| | | |
| | | @Override |
| | | public void onDestroyView() { |
| | | super.onDestroyView(); |
| | | binding = null; |
| | | mBinding = null; |
| | | } |
| | | |
| | | } |
| | |
| | | package com.runt.open.mvvm.base.fragments; |
| | | |
| | | import androidx.lifecycle.ViewModel; |
| | | import androidx.viewbinding.ViewBinding; |
| | | import androidx.viewpager2.widget.ViewPager2; |
| | | |
| | | import com.google.android.material.tabs.TabLayout; |
| | | import com.google.android.material.tabs.TabLayoutMediator; |
| | | import com.runt.open.mvvm.base.adapter.FragmentAdapter; |
| | | import com.runt.open.mvvm.base.model.BaseViewModel; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | |
| | | * 带有tablayout fragment封装 |
| | | * Created by Administrator on 2021/11/3 0003. |
| | | */ |
| | | public abstract class BaseTabFragment<B extends ViewBinding,VM extends ViewModel> extends BaseFragment<B,VM> { |
| | | public abstract class BaseTabFragment<B extends ViewBinding,VM extends BaseViewModel> extends BaseFragment<B,VM> { |
| | | |
| | | TabLayout tabLayout; |
| | | FragmentAdapter fragmentAdapter; |
| | |
| | | |
| | | @Override |
| | | public void initViews() { |
| | | |
| | | fragmentAdapter = new FragmentAdapter(activity); |
| | | fragmentAdapter.setFragments(initFragments()); |
| | | setTabTitles(initTabTitles()); |
| | | //设置当前可见Item左右可见page数,次范围内不会被销毁 |
| | | //禁用预加载 |
| | | try { |
| | | viewPager2 = (ViewPager2) binding.getClass().getDeclaredField("viewPager2").get(binding); |
| | | tabLayout = (TabLayout) binding.getClass().getDeclaredField("tabLayout").get(binding); |
| | | viewPager2 = (ViewPager2) mBinding.getClass().getDeclaredField("viewPager2").get(mBinding); |
| | | tabLayout = (TabLayout) mBinding.getClass().getDeclaredField("tabLayout").get(mBinding); |
| | | } catch (IllegalAccessException e) { |
| | | e.printStackTrace(); |
| | | } catch (NoSuchFieldException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | ; |
| | | viewPager2.setOffscreenPageLimit(ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT); |
| | | viewPager2.setAdapter(fragmentAdapter); |
| | | viewPager2.setCurrentItem(0); |
| | | viewPager2.setUserInputEnabled(false); //true:滑动,false:禁止滑动 |
| | | new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> tab.setText(tabTitles.get(position))).attach(); |
| | | } |
| | | |
| | | @Override |
| | | public void loadData() { |
| | | fragmentAdapter = new FragmentAdapter(mActivity); |
| | | fragmentAdapter.setFragments(initFragments()); |
| | | viewPager2.setAdapter(fragmentAdapter); |
| | | } |
| | | |
| | | protected abstract List<String> initTabTitles(); |
| | | |
| | | protected abstract List<BaseFragment> initFragments(); |
New file |
| | |
| | | package com.runt.open.mvvm.base.fragments; |
| | | |
| | | import androidx.annotation.NonNull; |
| | | import androidx.lifecycle.Observer; |
| | | import androidx.recyclerview.widget.LinearLayoutManager; |
| | | import androidx.recyclerview.widget.RecyclerView; |
| | | import androidx.viewbinding.ViewBinding; |
| | | |
| | | import com.runt.open.mvvm.R; |
| | | import com.runt.open.mvvm.base.adapter.BaseAdapter; |
| | | import com.runt.open.mvvm.base.model.LoadPageViewModel; |
| | | import com.scwang.smart.refresh.footer.ClassicsFooter; |
| | | import com.scwang.smart.refresh.header.ClassicsHeader; |
| | | import com.scwang.smart.refresh.layout.SmartRefreshLayout; |
| | | import com.scwang.smart.refresh.layout.api.RefreshLayout; |
| | | import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener; |
| | | |
| | | import java.lang.reflect.ParameterizedType; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 分页fragment 封装 |
| | | * Created by Administrator on 2021/11/3 0003. |
| | | */ |
| | | public abstract class LoadPageFragment<VB extends ViewBinding,VM extends LoadPageViewModel,A extends BaseAdapter,RESULT> extends BaseFragment<VB,VM> implements OnRefreshLoadMoreListener { |
| | | |
| | | protected int page; |
| | | protected SmartRefreshLayout refresh; |
| | | //适配器 |
| | | protected A adapter; |
| | | |
| | | @Override |
| | | public void initViews() { |
| | | try { |
| | | Class<A> entityClass = (Class<A>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[2]; |
| | | this.adapter = entityClass.newInstance();//实例化泛型 |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | RecyclerView recycler = mBinding.getRoot().findViewById(R.id.recycler); |
| | | recycler.setLayoutManager(new LinearLayoutManager(getContext())); |
| | | recycler.setAdapter(adapter); |
| | | refresh = mBinding.getRoot().findViewById(R.id.refresh); |
| | | refresh.setRefreshHeader(new ClassicsHeader(getContext())); |
| | | refresh.setRefreshFooter(new ClassicsFooter(getContext())); |
| | | refresh.setOnRefreshLoadMoreListener(this); |
| | | mViewModel.getLiveData().observe(this, (Observer<List<RESULT>>) list -> { |
| | | adapter.showNull = true; |
| | | if(page == 0){ |
| | | adapter.setData(list); |
| | | }else{ |
| | | adapter.addData(list); |
| | | } |
| | | refresh.finishRefresh(); |
| | | //加载完毕 |
| | | if(list.size() < 10 || page > 0 && list.size() == 0){ |
| | | refresh.finishLoadMoreWithNoMoreData(); |
| | | }else{ |
| | | refresh.finishLoadMore(); |
| | | } |
| | | }); |
| | | mViewModel.getLiveFailed().observe(this, o -> { |
| | | refresh.finishRefresh(); |
| | | refresh.finishLoadMore(); |
| | | //校正page数值 |
| | | int size = adapter.getData().size(); |
| | | if(size/mViewModel.SIZE+1 < page){ |
| | | page--; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public void loadData() { |
| | | refresh.autoRefresh(); |
| | | } |
| | | |
| | | protected abstract Map requestParams(); |
| | | |
| | | @Override |
| | | public void onRefresh(@NonNull RefreshLayout refreshLayout) { |
| | | page = 0; |
| | | mViewModel.requestData(page,requestParams()); |
| | | } |
| | | |
| | | @Override |
| | | public void onLoadMore(@NonNull RefreshLayout refreshLayout) { |
| | | page++; |
| | | mViewModel.requestData(page,requestParams()); |
| | | } |
| | | |
| | | public A getAdapter() { |
| | | return adapter; |
| | | } |
| | | } |
| | |
| | | package com.runt.open.mvvm.base.model; |
| | | |
| | | import androidx.lifecycle.MutableLiveData; |
| | | import androidx.lifecycle.ViewModel; |
| | | |
| | | import com.runt.open.mvvm.data.LoadingCmd; |
| | | import com.runt.open.mvvm.base.activities.BaseActivity; |
| | | import com.runt.open.mvvm.retrofit.AndroidScheduler; |
| | | import com.runt.open.mvvm.retrofit.api.CommonApiCenter; |
| | | import com.runt.open.mvvm.retrofit.observable.HttpObserver; |
| | | import com.runt.open.mvvm.retrofit.utils.RetrofitUtils; |
| | | |
| | | import io.reactivex.Observable; |
| | | import io.reactivex.functions.Consumer; |
| | | import io.reactivex.schedulers.Schedulers; |
| | | |
| | | /** |
| | |
| | | */ |
| | | public class BaseViewModel extends ViewModel { |
| | | |
| | | MutableLiveData<LoadingCmd> loadLive = new MutableLiveData<>(); |
| | | protected BaseActivity mActivity; |
| | | protected CommonApiCenter commonApi = RetrofitUtils.getInstance().getCommonApi(); |
| | | |
| | | public MutableLiveData<LoadingCmd> getLoadLive() { |
| | | return loadLive; |
| | | public void onCreate(BaseActivity activity) { |
| | | this.mActivity = activity; |
| | | } |
| | | |
| | | /** |
| | |
| | | public <T> void httpObserverOnLoading(Observable<T> observable, HttpObserver observer){ |
| | | observable.subscribeOn(Schedulers.io())//指定网络请求在io后台线程中进行 |
| | | .doOnSubscribe(disposable -> { |
| | | loadLive.setValue(new LoadingCmd(LoadingCmd.CMD.LOADING,"请求数据中...")); |
| | | mActivity.showLoadingDialog(""); |
| | | }) |
| | | .observeOn(AndroidScheduler.mainThread()) |
| | | .doOnError(new Consumer<Throwable>() { |
| | | @Override |
| | | public void accept(Throwable throwable) throws Exception { |
| | | |
| | | } |
| | | }) |
| | | .doOnComplete(() -> { |
| | | loadLive.postValue(new LoadingCmd(LoadingCmd.CMD.DISSMISS)); |
| | | mActivity.dissmissLoadingDialog(); |
| | | }) |
| | | .subscribe(observer); |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.base.model; |
| | | |
| | | /** |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/7/27. |
| | | */ |
| | | public class ImpViewModel extends BaseViewModel{ |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.base.model; |
| | | |
| | | import androidx.lifecycle.MutableLiveData; |
| | | |
| | | import com.runt.open.mvvm.data.HttpApiResult; |
| | | import com.runt.open.mvvm.data.PageResult; |
| | | import com.runt.open.mvvm.retrofit.observable.HttpObserver; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 分页 |
| | | * Created by Administrator on 2021/11/3 0003. |
| | | */ |
| | | public abstract class LoadPageViewModel<RESULT extends PageResult> extends BaseViewModel { |
| | | |
| | | public final int SIZE = 10; |
| | | private MutableLiveData<List> liveData = new MutableLiveData<>(); |
| | | private MutableLiveData liveFailed = new MutableLiveData(); |
| | | |
| | | protected abstract String requestUrl(); |
| | | |
| | | public void requestData(int page,Map param){ |
| | | httpObserverOn( commonApi.getPageData(requestUrl(), page, SIZE, param), new HttpObserver<RESULT>() { |
| | | |
| | | @Override |
| | | protected void onSuccess(RESULT data) { |
| | | liveData.postValue(data.rows); |
| | | } |
| | | |
| | | @Override |
| | | protected void onFailed(HttpApiResult httpResult) { |
| | | mActivity.showToast(httpResult.msg); |
| | | liveFailed.postValue(1); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public MutableLiveData<List> getLiveData(){ |
| | | return liveData; |
| | | } |
| | | |
| | | public MutableLiveData getLiveFailed() { |
| | | return liveFailed; |
| | | } |
| | | } |
| | |
| | | /** |
| | | * Created by Administrator on 2021/11/15 0015. |
| | | */ |
| | | public class ApkUpGradeResult extends BaseApiResult<ApkUpGradeResult.AppInfo>{ |
| | | public class ApkUpGradeResult extends HttpApiResult<ApkUpGradeResult.AppInfo> { |
| | | |
| | | public class AppInfo { |
| | | //以下为声明的参数 |
File was renamed from app/src/main/java/com/runt/open/mvvm/data/BaseApiResult.java |
| | |
| | | /** |
| | | * Created by Administrator on 2021/10/28 0028. |
| | | */ |
| | | public class BaseApiResult<D extends Object> implements Serializable { |
| | | public class HttpApiResult<D extends Object> implements Serializable { |
| | | |
| | | public String msg; |
| | | public int code = 200; |
File was renamed from app/src/main/java/com/runt/open/mvvm/data/BasePageResult.java |
| | |
| | | /** |
| | | * Created by Administrator on 2021/10/28 0028. |
| | | */ |
| | | public class BasePageResult<T> extends BaseApiResult<String>{ |
| | | public class PageResult<T> extends HttpApiResult<String> { |
| | | public int pages; |
| | | public int total; |
| | | public int pageNum; |
| | |
| | | package com.runt.open.mvvm.data; |
| | | |
| | | import com.runt.open.mvvm.ui.login.UserBean; |
| | | import com.runt.open.mvvm.ui.main.home.Message; |
| | | |
| | | /** |
| | | * My father is Object, ites purpose of |
| | |
| | | */ |
| | | public class Results { |
| | | |
| | | public static class LoggedInUser extends BaseApiResult<UserBean> { } |
| | | public static class LoggedInUser extends HttpApiResult<UserBean> { } |
| | | |
| | | public static class StringApiResult extends BaseApiResult<String>{ } |
| | | public static class StringApiResult extends HttpApiResult<String> { } |
| | | |
| | | public class MessageResult extends PageResult<Message>{} |
| | | } |
| | |
| | | |
| | | import android.util.Log; |
| | | |
| | | import com.runt.open.mvvm.MyApplication; |
| | | import com.runt.open.mvvm.retrofit.net.NetWorkCost; |
| | | import com.runt.open.mvvm.retrofit.net.NetWorkListenear; |
| | | import com.runt.open.mvvm.retrofit.utils.HttpPrintUtils; |
| | | import com.runt.open.mvvm.util.GsonUtils; |
| | | import com.runt.open.mvvm.util.DeviceUtil; |
| | | |
| | | import org.json.JSONArray; |
| | | import org.json.JSONException; |
| | | import org.json.JSONObject; |
| | | |
| | | import java.io.EOFException; |
| | |
| | | import java.net.URLDecoder; |
| | | import java.nio.charset.Charset; |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | |
| | | import okhttp3.FormBody; |
| | |
| | | |
| | | final String TAG = "HttpLogging"; |
| | | |
| | | public HttpLoggingInterceptor() { |
| | | } |
| | | private boolean printLog ; |
| | | |
| | | public HttpLoggingInterceptor(){ |
| | | this(true); |
| | | } |
| | | public HttpLoggingInterceptor(boolean printLog) { |
| | | this.printLog = printLog; |
| | | } |
| | | |
| | | @Override |
| | | public Response intercept(Chain chain) throws IOException { |
| | | |
| | | Request request = chain.request(); |
| | | int hashCode = request.hashCode(); |
| | | ArrayList<String> logArrays = getRequestLog(request); |
| | | int position = logArrays.size() +2; |
| | | Response response; |
| | | Request requestTemp = chain.request(); |
| | | int hashCode = requestTemp.hashCode(); |
| | | if(printLog) { |
| | | Log.d(TAG, "hashcode:" + hashCode); |
| | | } |
| | | Request.Builder requestBuild = requestTemp.newBuilder() |
| | | .addHeader("appVersion", DeviceUtil.getAppVersionName(MyApplication.getApplication())) |
| | | .addHeader("os", DeviceUtil.isHarmonyOS()? "harmony" : "android"); |
| | | Request request = requestBuild.build().newBuilder().build(); |
| | | ArrayList<String> logArrays = new ArrayList<>(); |
| | | Response response = null; |
| | | try { |
| | | request = encryptRequest(request);//加密 |
| | | logArrays.addAll(getRequestLog(request)); |
| | | int position = logArrays.size() +2; |
| | | //request = encryptRequest(request);//加密 |
| | | response = chain.proceed(request); |
| | | logArrays.addAll(getResponseLog(response)); |
| | | Log.d(TAG,"hashcode:"+hashCode); |
| | | NetWorkCost netWorkCost = NetWorkListenear.workCostMap.get(hashCode); |
| | | if(netWorkCost != null) { |
| | | String cost = String.format("dns:%s,secure:%s,connect:%s,requestH:%s,requestB:%s,responseH:%s,responseB:%s", convertTimes(netWorkCost.dns), convertTimes(netWorkCost.secure), convertTimes(netWorkCost.connect), convertTimes(netWorkCost.requestHeader), convertTimes(netWorkCost.requestBody), convertTimes(netWorkCost.resposeHeader), convertTimes(netWorkCost.resposeBody)); |
| | | logArrays.add(position, "<-- costtimes : " + convertTimes(netWorkCost.total) + " (" + cost + ')'); |
| | | logArrays.add(position, "<-- costtimes : " + netWorkCost); |
| | | } |
| | | NetWorkListenear.workCostMap.remove(hashCode); |
| | | new Thread(){ |
| | | @Override |
| | | public void run() { |
| | | HttpPrintUtils.getInstance().printLog(logArrays, true);//线程安全方法,需在新线程执行,避免阻塞当前线程,导致程序无响应 |
| | | if(printLog) { |
| | | HttpPrintUtils.getInstance().printLog(logArrays, true);//线程安全方法,需在新线程执行,避免阻塞当前线程,导致程序无响应 |
| | | } |
| | | } |
| | | }.start(); |
| | | } catch (JSONException e) { |
| | | if(response == null){ |
| | | response = chain.proceed(request); |
| | | } |
| | | e.printStackTrace(); |
| | | } catch (Exception e) { |
| | | logArrays.add("<-- response url:" + URLDecoder.decode(request.url().toString(), "UTF-8")); |
| | | NetWorkCost netWorkCost = NetWorkListenear.workCostMap.get(hashCode); |
| | | String cost = String.format("dns:%s,secure:%s,connect:%s,requestH:%s,requestB:%s,responseH:%s,responseB:%s", convertTimes(netWorkCost.dns) ,convertTimes(netWorkCost.secure) , convertTimes(netWorkCost.connect),convertTimes(netWorkCost.requestHeader),convertTimes(netWorkCost.requestBody) ,convertTimes(netWorkCost.resposeHeader),convertTimes(netWorkCost.resposeBody) ); |
| | | logArrays.add("<-- costtimes : "+convertTimes(netWorkCost.total)+" (" +cost + ')'); |
| | | if (netWorkCost != null) { |
| | | netWorkCost.total = new Date().getTime() - netWorkCost.total; |
| | | logArrays.add("<-- costtimes : " + netWorkCost); |
| | | } |
| | | NetWorkListenear.workCostMap.remove(hashCode); |
| | | logArrays.add("<-- response failed " + e.getLocalizedMessage()); |
| | | logArrays.add("<-- " + e.toString()); |
| | | new Thread(){ |
| | | @Override |
| | | public void run() { |
| | | HttpPrintUtils.getInstance().printLog(logArrays, false);//线程安全方法,需在新线程执行,避免阻塞当前线程,导致程序无响应 |
| | | if(printLog) { |
| | | HttpPrintUtils.getInstance().printLog(logArrays, false);//线程安全方法,需在新线程执行,避免阻塞当前线程,导致程序无响应 |
| | | } |
| | | } |
| | | }.start(); |
| | | throw e;//抛出异常,用于请求接收信息 |
| | |
| | | return response; |
| | | } |
| | | |
| | | private ArrayList<String> getRequestLog(Request request) throws IOException { |
| | | private ArrayList<String> getRequestLog(Request request) throws IOException, JSONException { |
| | | RequestBody requestBody = request.body(); |
| | | ArrayList<String> logArrays = new ArrayList<>(); |
| | | String requestStartMessage = "--> " + request.method() + ' ' + URLDecoder.decode(request.url().toString() ,"UTF-8")+ ' ' ; |
| | |
| | | String str=buffer1.readString(charset).replaceAll("%(?![0-9a-fA-F]{2})","%25"); |
| | | param.put(part.headers().get(part.headers().name(0)),URLDecoder.decode(str, "UTF-8")); |
| | | } |
| | | logArrays.add(GsonUtils.retractJson(new JSONObject(param).toString())); |
| | | logArrays.add(new JSONObject(param).toString(4)); |
| | | }else if(requestBody instanceof FormBody){ |
| | | logArrays.add("---------->REQUEST BODY[FormBody]<----------"); |
| | | FormBody body = (FormBody) requestBody; |
| | | for(int i = 0 ; i < body.size() ; i ++ ){ |
| | | param.put(body.name(i),body.value(i)); |
| | | } |
| | | logArrays.add(GsonUtils.retractJson(new JSONObject(param).toString())); |
| | | logArrays.add(new JSONObject(param).toString(4)); |
| | | }else{ |
| | | Buffer buffer = new Buffer(); |
| | | requestBody.writeTo(buffer); |
| | | logArrays.add("---------->REQUEST BODY<----------"); |
| | | String str = buffer.readString(charset); |
| | | try{ |
| | | logArrays.add(GsonUtils.retractJson(URLDecoder.decode(str, "UTF-8"))); |
| | | }catch (Exception e){ |
| | | str = URLDecoder.decode(str, "UTF-8"); |
| | | }catch (Exception e){} |
| | | |
| | | if(str.indexOf("[") == 0){ |
| | | logArrays.add(new JSONArray(str).toString(4)); |
| | | }else if(str.indexOf("{") == 0){ |
| | | logArrays.add(new JSONObject(str).toString(4)); |
| | | }else{ |
| | | logArrays.add(str); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | |
| | | private ArrayList<String> getResponseLog(Response response) throws IOException { |
| | | private ArrayList<String> getResponseLog(Response response) throws IOException, JSONException { |
| | | ArrayList<String> logArrays = new ArrayList<>(); |
| | | ResponseBody responseBody = response.body(); |
| | | long contentLength = responseBody.contentLength(); |
| | |
| | | if (isPlaintext(buffer)) { |
| | | logArrays.add("---------->RESPONSE BODY<----------"); |
| | | if (contentLength != 0) { |
| | | logArrays.add(retractJson(buffer.clone().readString(charset))); |
| | | logArrays.add(new JSONObject(buffer.clone().readString((charset))).toString(4)); |
| | | } |
| | | |
| | | logArrays.add("<-- END HTTP (" + buffer.size() + "-byte body)"); |
| | |
| | | return logArrays; |
| | | } |
| | | |
| | | /** |
| | | * 字符串缩进 |
| | | * @param json |
| | | * @return |
| | | */ |
| | | private String retractJson(String json){ |
| | | int level = 0 ; |
| | | StringBuffer jsonForMatStr = new StringBuffer(); |
| | | for(int index=0;index<json.length();index++)//将字符串中的字符逐个按行输出 |
| | | { |
| | | //获取s中的每个字符 |
| | | char c = json.charAt(index); |
| | | // System.out.println(s.charAt(index)); |
| | | |
| | | //level大于0并且jsonForMatStr中的最后一个字符为\n,jsonForMatStr加入\t |
| | | if (level > 0 && '\n' == jsonForMatStr.charAt(jsonForMatStr.length() - 1)) { |
| | | jsonForMatStr.append(getLevelStr(level)); |
| | | // System.out.println("123"+jsonForMatStr); |
| | | } |
| | | //遇到"{"和"["要增加空格和换行,遇到"}"和"]"要减少空格,以对应,遇到","要换行 |
| | | switch (c) { |
| | | case '{': |
| | | case '[': |
| | | jsonForMatStr.append(c + "\n"); |
| | | level++; |
| | | break; |
| | | case ',': |
| | | jsonForMatStr.append(c + "\n"); |
| | | break; |
| | | case '}': |
| | | case ']': |
| | | jsonForMatStr.append("\n"); |
| | | level--; |
| | | jsonForMatStr.append(getLevelStr(level)); |
| | | jsonForMatStr.append(c); |
| | | break; |
| | | default: |
| | | jsonForMatStr.append(c); |
| | | break; |
| | | } |
| | | } |
| | | return jsonForMatStr.toString(); |
| | | } |
| | | |
| | | private String getLevelStr(int level) { |
| | | StringBuffer levelStr = new StringBuffer(); |
| | | for (int levelI = 0; levelI < level; levelI++) { |
| | | levelStr.append("\t");//\t或空格 |
| | | } |
| | | return levelStr.toString(); |
| | | } |
| | | |
| | | /** |
| | | * Returns true if the body in question probably contains human readable text. Uses a small sample |
| | |
| | | String contentEncoding = headers.get("Content-Encoding"); |
| | | return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity"); |
| | | } |
| | | |
| | | private String convertTimes(long ms){ |
| | | String m = null,s=null; |
| | | final int utilS = 1000; |
| | | final int utilM = utilS*60; |
| | | if(ms/utilM>0){ |
| | | m = ms/utilM+"m"; |
| | | } |
| | | if(ms%utilM/utilS>0){ |
| | | s = ms%utilM/utilS+"s"; |
| | | } |
| | | return (m!=null?m:"")+(s!=null?s:"")+ms%utilS+"ms"; |
| | | } |
| | | |
| | | } |
| | |
| | | package com.runt.open.mvvm.retrofit.api; |
| | | |
| | | |
| | | |
| | | import com.runt.open.mvvm.data.ApkUpGradeResult; |
| | | |
| | | import java.util.Map; |
| | | |
| | | import io.reactivex.Observable; |
| | | import okhttp3.MultipartBody; |
| | | import okhttp3.ResponseBody; |
| | | import retrofit2.Call; |
| | | import retrofit2.http.Field; |
| | | import retrofit2.http.FieldMap; |
| | | import retrofit2.http.FormUrlEncoded; |
| | | import retrofit2.http.GET; |
| | | import retrofit2.http.Multipart; |
| | | import retrofit2.http.POST; |
| | | import retrofit2.http.Part; |
| | | import retrofit2.http.Query; |
| | | import retrofit2.http.QueryMap; |
| | | import retrofit2.http.Url; |
| | |
| | | @GET("system/appupgrade/tourist/get/2") |
| | | Observable<ApkUpGradeResult> getAppUpdate(); |
| | | |
| | | @POST("updateName") |
| | | Observable<ApkUpGradeResult> updateName(@Field("username") String name); |
| | | |
| | | @Multipart |
| | | @POST("updatehead") |
| | | Call<ResponseBody> updateHead(@Part MultipartBody.Part file); |
| | | } |
| | |
| | | |
| | | import android.util.Log; |
| | | |
| | | import com.runt.open.mvvm.data.BaseApiResult; |
| | | import com.runt.open.mvvm.data.HttpApiResult; |
| | | import com.runt.open.mvvm.util.GsonUtils; |
| | | import com.google.gson.Gson; |
| | | import com.google.gson.JsonIOException; |
| | |
| | | response = decryptJsonStr(val);//解密 |
| | | } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { |
| | | e.printStackTrace(); |
| | | BaseApiResult apiResult = new BaseApiResult<>(); |
| | | HttpApiResult apiResult = new HttpApiResult<>(); |
| | | apiResult.code = 412; |
| | | apiResult.msg = "解密数据出错"+e.getMessage(); |
| | | response = new Gson().toJson(apiResult); |
| | | } catch (JSONException e) { |
| | | e.printStackTrace(); |
| | | BaseApiResult apiResult = new BaseApiResult<>(); |
| | | HttpApiResult apiResult = new HttpApiResult<>(); |
| | | apiResult.code = 414; |
| | | apiResult.msg = "非标准json"; |
| | | response = new Gson().toJson(apiResult); |
| | |
| | | |
| | | //网络消耗时间 |
| | | public long dns,connect,total,secure,requestHeader,requestBody,resposeHeader,resposeBody; |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "NetWorkCost{" + |
| | | "total=" + convertTimes(total) + |
| | | ", dns=" + convertTimes(dns) + |
| | | ", connect=" + convertTimes(connect) + |
| | | ", secure=" + convertTimes(secure) + |
| | | ", requestHeader=" + convertTimes(requestHeader) + |
| | | ", requestBody=" + convertTimes(requestBody) + |
| | | ", resposeHeader=" + convertTimes(resposeHeader) + |
| | | ", resposeBody=" + convertTimes(resposeBody) + |
| | | '}'; |
| | | } |
| | | |
| | | private String convertTimes(long ms){ |
| | | String m = null,s=null; |
| | | final int utilS = 1000; |
| | | final int utilM = utilS*60; |
| | | if(ms/utilM>0){ |
| | | m = ms/utilM+"m"; |
| | | } |
| | | if(ms%utilM/utilS>0){ |
| | | s = ms%utilM/utilS+"s"; |
| | | } |
| | | return (m!=null?m:"")+(s!=null?s:"")+ms%utilS+"ms"; |
| | | } |
| | | } |
| | |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-7-9. |
| | | */ |
| | | |
| | | |
| | | public class NetWorkListenear extends EventListener { |
| | | |
| | | private static final String TAG = "NetworkEventListener"; |
| | | final Charset UTF8 = Charset.forName("UTF-8"); |
| | | public static Map<Integer, NetWorkCost> workCostMap = new HashMap<>(); |
| | | |
| | | public static Factory get(){ |
| | | public static Factory get() { |
| | | Factory factory = new Factory() { |
| | | @NotNull |
| | | @Override |
| | |
| | | @Override |
| | | public void callStart(@NotNull Call call) { |
| | | super.callStart(call); |
| | | //mRequestId = mNextRequestId.getAndIncrement() + ""; |
| | | //getAndAdd,在多线程下使用cas保证原子性 |
| | | //Log.d(TAG, "callStart hashcode:"+call.request().hashCode()); |
| | | NetWorkCost netWorkCost = new NetWorkCost(); |
| | | netWorkCost.total = new Date().getTime(); |
| | | workCostMap.put(call.request().hashCode(),netWorkCost); |
| | | workCostMap.put(call.request().hashCode(), netWorkCost); |
| | | } |
| | | |
| | | @Override |
| | |
| | | public void dnsEnd(@NotNull Call call, @NotNull String domainName, @NotNull List<InetAddress> inetAddressList) { |
| | | super.dnsEnd(call, domainName, inetAddressList); |
| | | //Log.d(TAG, "dnsEnd"); |
| | | workCostMap.get(call.request().hashCode()).dns = new Date().getTime() - workCostMap.get(call.request().hashCode()).dns; |
| | | workCostMap.get(call.request().hashCode()).dns = new Date().getTime() - workCostMap.get(call.request().hashCode()).dns; |
| | | } |
| | | |
| | | @Override |
| | |
| | | @Override |
| | | public void connectFailed(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, @NotNull Proxy proxy, @Nullable Protocol protocol, @NotNull IOException ioe) { |
| | | super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe); |
| | | workCostMap.get(call.request().hashCode()).connect = new Date().getTime() - workCostMap.get(call.request().hashCode()).connect; |
| | | workCostMap.get(call.request().hashCode()).total = new Date().getTime() - workCostMap.get(call.request().hashCode()).total; |
| | | //Log.d(TAG, "connectFailed"); |
| | | NetWorkCost workCost = workCostMap.get(call.request().hashCode()); |
| | | workCost.connect = new Date().getTime() - workCost.connect; |
| | | workCost.total = new Date().getTime() - workCost.total; |
| | | //Log.d(TAG, "connectFailed hashcode:"+call.request().hashCode() +" "+workCost); |
| | | } |
| | | |
| | | @Override |
| | |
| | | public void responseBodyEnd(@NotNull Call call, long byteCount) { |
| | | super.responseBodyEnd(call, byteCount); |
| | | //Log.d(TAG, "responseBodyEnd"); |
| | | workCostMap.get(call.request().hashCode()).resposeBody = new Date().getTime() - workCostMap.get(call.request().hashCode()).resposeBody; |
| | | workCostMap.get(call.request().hashCode()).total = new Date().getTime() - workCostMap.get(call.request().hashCode()).total; |
| | | NetWorkCost workCost = workCostMap.get(call.request().hashCode()); |
| | | workCost.resposeBody = new Date().getTime() - workCost.resposeBody; |
| | | workCost.total = new Date().getTime() - workCost.total; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void callFailed(@NotNull Call call, @NotNull IOException ioe) { |
| | | super.callFailed(call, ioe); |
| | | workCostMap.get(call.request().hashCode()).total = new Date().getTime() - workCostMap.get(call.request().hashCode()).total; |
| | | //Log.d(TAG, "callFailed"); |
| | | NetWorkCost workCost = workCostMap.get(call.request().hashCode()); |
| | | if (workCost != null) { |
| | | workCost.total = new Date().getTime() - workCost.total; |
| | | } |
| | | //Log.d(TAG, "callFailed hashcode:"+call.request().hashCode() +" "+workCost); |
| | | } |
| | | |
| | | } |
| | |
| | | package com.runt.open.mvvm.retrofit.observable; |
| | | |
| | | import android.accounts.NetworkErrorException; |
| | | import android.util.Log; |
| | | |
| | | import androidx.annotation.NonNull; |
| | | import androidx.lifecycle.MutableLiveData; |
| | | import com.google.gson.Gson; |
| | | import com.runt.open.mvvm.data.HttpApiResult; |
| | | |
| | | |
| | | import com.runt.open.mvvm.data.BaseApiResult; |
| | | |
| | | import java.io.IOException; |
| | | import java.lang.reflect.ParameterizedType; |
| | | import java.net.ConnectException; |
| | | import java.net.SocketTimeoutException; |
| | | import java.net.UnknownHostException; |
| | | import java.util.concurrent.TimeoutException; |
| | | import java.util.regex.Pattern; |
| | | |
| | | import io.reactivex.SingleObserver; |
| | | import io.reactivex.observers.DisposableObserver; |
| | | import retrofit2.Response; |
| | | import io.reactivex.Observer; |
| | | import io.reactivex.disposables.Disposable; |
| | | import retrofit2.adapter.rxjava2.HttpException; |
| | | |
| | | /** |
| | | * 网络请求观察 |
| | | * Created by Administrator on 2021/11/11 0011. |
| | | */ |
| | | public abstract class HttpObserver<M extends BaseApiResult> extends DisposableObserver<Response<M>> implements SingleObserver<Response<M>> { |
| | | public abstract class HttpObserver<RESULT> implements Observer<HttpApiResult<RESULT>> { |
| | | |
| | | final String TAG = "HttpObserver"; |
| | | |
| | | MutableLiveData<M> resultLive; |
| | | |
| | | public HttpObserver(MutableLiveData<M> resultLive) { |
| | | this.resultLive = resultLive; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void onNext(Response<M> response) { |
| | | onExcuted(response); |
| | | public void onSubscribe(Disposable d) { |
| | | Log.d(TAG,"onSubscribe "+hashCode()); |
| | | } |
| | | |
| | | @Override |
| | | public void onError(Throwable throwable) { |
| | | Log.i("subscribe","onError"); |
| | | |
| | | try { |
| | | Log.e(TAG,this.getClass().getSimpleName()+" mes:"+throwable.getMessage()); |
| | | Class<M> entityClass = (Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; |
| | | M t = entityClass.newInstance();//实例化一个泛型 |
| | | t.code = 410; |
| | | if( throwable instanceof SocketTimeoutException){ |
| | | t.msg = "服务请求超时,请稍候再试";//设置错误信息 |
| | | }else if( throwable instanceof NetworkErrorException){ |
| | | t.msg = "网络连接不畅,请检查您的网络设置";//设置错误信息 |
| | | }else{ |
| | | t.msg = throwable.getMessage();//设置错误信息 |
| | | } |
| | | resultLive.setValue(t); |
| | | } catch (ClassCastException e) { |
| | | e.printStackTrace(); |
| | | M t = (M) new BaseApiResult<String>(); |
| | | t.code = 409; |
| | | t.msg = "实例化对象未指定泛型实体类"; |
| | | resultLive.setValue(t); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | M t = (M) new BaseApiResult<String>(); |
| | | t.code = 409; |
| | | t.msg = e.getMessage(); |
| | | resultLive.setValue(t); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onSuccess(Response<M> response) { |
| | | onExcuted(response); |
| | | } |
| | | |
| | | private void onExcuted(@NonNull Response<M> response){ |
| | | |
| | | if(response.body() != null){ |
| | | resultLive.setValue(response.body()); |
| | | public void onNext(HttpApiResult<RESULT> httpResult) { |
| | | Log.d(TAG,"onNext "+httpResult); |
| | | if (httpResult != null && httpResult.code == 0) { |
| | | onSuccess(httpResult.data); |
| | | }else{ |
| | | try { |
| | | String error = response.errorBody().string(); |
| | | Log.i("subscribe","onExcuted "+error); |
| | | onError(new Throwable(error)); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | onFailed(httpResult);//接口返回错误 |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onError(Throwable e) { |
| | | e.printStackTrace(); |
| | | Log.e(TAG,"onError "+e.getMessage()+" "+hashCode()); |
| | | int code = 600; |
| | | String msg = "网络请求失败,请检查网络或稍后重试"; |
| | | if( e instanceof ConnectException || e instanceof TimeoutException |
| | | || e instanceof SocketTimeoutException || e instanceof UnknownHostException){ |
| | | code = 601; |
| | | msg = "网络请求失败,请检查网络或稍后重试"; |
| | | }else if( e instanceof HttpException){ |
| | | String regEx = "[^0-9]"; |
| | | Log.i(TAG,"code:"+ Pattern.compile(regEx).matcher(e.getMessage()).replaceAll("")); |
| | | String error = Pattern.compile(regEx).matcher(e.getMessage()).replaceAll(""); |
| | | code = error.length()>0?Integer.parseInt(error):500; |
| | | msg = error.length()>0?"服务器请求失败":"登录信息验证失败"; |
| | | } |
| | | HttpApiResult httpResult = new Gson().fromJson("{'code':"+code+",'msg':'"+msg+"'}", HttpApiResult.class); |
| | | onFailed(httpResult); |
| | | } |
| | | |
| | | @Override |
| | | public void onComplete() { |
| | | Log.i("subscribe","onComplete"); |
| | | Log.i(TAG,"onComplete "+hashCode()); |
| | | } |
| | | |
| | | protected abstract void onSuccess(RESULT data); |
| | | |
| | | protected void onFailed(HttpApiResult httpResult){} |
| | | |
| | | } |
| | |
| | | package com.runt.open.mvvm.retrofit.utils; |
| | | |
| | | import com.runt.open.mvvm.BuildConfig; |
| | | import com.runt.open.mvvm.util.GsonUtils; |
| | | import com.runt.open.mvvm.util.MyLog; |
| | | import android.util.Log; |
| | | |
| | | import java.io.EOFException; |
| | | import java.util.ArrayList; |
| | | |
| | | import okio.Buffer; |
| | | |
| | | /** |
| | | * My father is Object, ites purpose of 单例模式 保证synchronized方法的线程安全性 |
| | | * |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-5-13. |
| | | */ |
| | | |
| | | public class HttpPrintUtils { |
| | | String TAG = "HttpPrintUtils"; |
| | | static HttpPrintUtils instance; |
| | | public static HttpPrintUtils getInstance(){ |
| | | if(instance == null){ |
| | | instance = new HttpPrintUtils(); |
| | | instance = new HttpPrintUtils(); |
| | | } |
| | | return instance; |
| | | } |
| | |
| | | logArrays.add("┗"+getEmptyStr((length-end.length())/2,"━")+end+getEmptyStr((length-end.length())/2,"━")+"┛\n"); |
| | | logArrays.add(" \n\n\n"); |
| | | //Logger.DEFAULT.log(sb.toString());//打印log,避免多个log语句,导致log输出时其他线程的log输出切入此输出阵列内 |
| | | if(BuildConfig.DEBUG) { |
| | | for(int i = 0 ; i < logArrays.size() ; i ++ ){ |
| | | String str = logArrays.get(i); |
| | | if (info) { |
| | | MyLog.i(TAG , str.replace("\n","")+" "+logArrays.size()+" "+i); |
| | | } else { |
| | | MyLog.e(TAG , str.replace("\n","")+" "+logArrays.size()+" "+i); |
| | | } |
| | | for(int i = 0 ; i < logArrays.size() ; i ++ ){ |
| | | String str = logArrays.get(i); |
| | | if (info) { |
| | | Log.i(TAG , str.replace("\n","")+" "+logArrays.size()+" "+i); |
| | | } else { |
| | | Log.e(TAG , str.replace("\n","")+" "+logArrays.size()+" "+i); |
| | | } |
| | | } |
| | | } |
| | |
| | | s = s.substring(0,totalLength*3)+"..."; |
| | | } |
| | | s = s.replace("\t"," ");//缩进替换空格 |
| | | if(s.indexOf("\":{\"")>-1 || s.indexOf("\":[{\"")>-1 || s.indexOf("\":[[")>-1){//内容非校正缩进,且为json字符规范 |
| | | splitStr(s.substring(0,s.indexOf("\":")+2)+ GsonUtils.retractJson(s.substring(s.indexOf("\":")+2)),totalLength,list); |
| | | if(s.length()> totalLength){ |
| | | outOflength(s,totalLength,list); |
| | | }else { |
| | | if(s.length()> totalLength){ |
| | | outOflength(s,totalLength,list); |
| | | }else { |
| | | logStr = "┃ " + s + getEmptyStr((totalLength - 16 - s.length()), " "); |
| | | //处理中文间距,保证打印无偏差 |
| | | list.add(logStr + getEmptyStr((totalLength - logStr.length() - 1 - hasCNchar(logStr)), " ") + "┃ "/*+logStr.length()+" "+logStr.getBytes().length+" "+(" ").getBytes().length +" "+hasCNchar(s)*/ + "\n"); |
| | | } |
| | | logStr = "┃ " + s + getEmptyStr((totalLength - 16 - s.length()), " "); |
| | | //处理中文间距,保证打印无偏差 |
| | | list.add(logStr + getEmptyStr((totalLength - logStr.length() - 1 - hasCNchar(logStr)), " ") + "┃ "/*+logStr.length()+" "+logStr.getBytes().length+" "+(" ").getBytes().length +" "+hasCNchar(s)*/ + "\n"); |
| | | } |
| | | } |
| | | } |
| | |
| | | sb.append(space); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | /** |
| | | * Returns true if the body in question probably contains human readable text. Uses a small sample |
| | | * of code points to detect unicode control characters commonly used in binary file signatures. |
| | | */ |
| | | static boolean isPlaintext(Buffer buffer) { |
| | | try { |
| | | Buffer prefix = new Buffer(); |
| | | long byteCount = buffer.size() < 64 ? buffer.size() : 64; |
| | | buffer.copyTo(prefix, 0, byteCount); |
| | | for (int i = 0; i < 16; i++) { |
| | | if (prefix.exhausted()) { |
| | | break; |
| | | } |
| | | int codePoint = prefix.readUtf8CodePoint(); |
| | | if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) { |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | } catch (EOFException e) { |
| | | return false; // Truncated UTF-8 sequence. |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | |
| | | public class RetrofitUtils { |
| | | public static String HOST_IP_ADDR; |
| | | static RetrofitUtils instance; |
| | | Retrofit retrofit/*log输出,驼峰转换*/,unHumpRetrofit/*log输出,不强制驼峰转换*/, |
| | | unLogRetrofit/*log不输出,驼峰转换*/,unLogHumpRetorfit/*log不输出,不强制驼峰转换*/; |
| | |
| | | //设置OKHttpClient |
| | | .client(client) |
| | | //设置baseUrl,注意,baseUrl必须后缀"/" |
| | | .baseUrl(BuildConfig.ENVIRONMENT.equals("develop")?HOST_IP_ADDR:BuildConfig.HOST_IP_ADDR) |
| | | .baseUrl(BuildConfig.HOST_IP_ADDR+"api/v1/") |
| | | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) |
| | | .build(); |
| | | } |
| | |
| | | MutableLiveData<Results.StringApiResult> verifyResult = new MutableLiveData<>(); |
| | | MutableLiveData<Results.StringApiResult> resetResult = new MutableLiveData<>(); |
| | | MutableLiveData<Results.StringApiResult> registerResult = new MutableLiveData<>(); |
| | | |
| | | HttpObserver<Results.LoggedInUser> logginObserver = new HttpObserver<Results.LoggedInUser>(){ |
| | | @Override |
| | | protected void onSuccess(Results.LoggedInUser data) { |
| | | loginResult.setValue(data); |
| | | } |
| | | }; |
| | | public MutableLiveData<Results.LoggedInUser> getLoginResult() { |
| | | return loginResult; |
| | | } |
| | |
| | | public void login(String username, String password) { |
| | | // can be launched in a separate asynchronous job |
| | | final Observable<Results.LoggedInUser> userObservable = loginApi.login(username, password); |
| | | httpObserverOnLoading(userObservable,new HttpObserver<Results.LoggedInUser>(loginResult){}); |
| | | httpObserverOnLoading(userObservable,logginObserver); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param code |
| | | */ |
| | | public void loginByCode(String phone,String code){ |
| | | httpObserverOnLoading(loginApi.loginByCode(phone,code), |
| | | new HttpObserver<Results.LoggedInUser>(loginResult){}); |
| | | httpObserverOnLoading(loginApi.loginByCode(phone,code),logginObserver); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param pass |
| | | */ |
| | | public void resetPwd(String phone,String sms,String pass){ |
| | | httpObserverOnLoading(loginApi.resetLoginPwd(phone, sms, pass), new HttpObserver<Results.StringApiResult>(resetResult) {}); |
| | | httpObserverOnLoading(loginApi.resetLoginPwd(phone, sms, pass),logginObserver); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param pass |
| | | */ |
| | | public void register(String phone,String sms,String pass){ |
| | | httpObserverOnLoading(loginApi.register(phone, sms, pass), new HttpObserver<Results.StringApiResult>(resetResult) {}); |
| | | httpObserverOnLoading(loginApi.register(phone, sms, pass), new HttpObserver<Results.StringApiResult>(){ |
| | | @Override |
| | | protected void onSuccess(Results.StringApiResult data) { |
| | | resetResult.setValue(data); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public void getVerifyCode(String url,String phone){ |
| | | String time = new Date().getTime()+""; |
| | | httpObserverOnLoading(loginApi.getVerifyCode(url, phone, randomString(phone, time), time), new HttpObserver<Results.StringApiResult>(verifyResult){}); |
| | | httpObserverOnLoading(loginApi.getVerifyCode(url, phone, randomString(phone, time), time), new HttpObserver<Results.StringApiResult>(){ |
| | | @Override |
| | | protected void onSuccess(Results.StringApiResult data) { |
| | | verifyResult.setValue(data); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | @Override |
| | | public void initViews() { |
| | | binding.txtGetVerify.setOnClickListener(onclick); |
| | | binding.txtForgot.setOnClickListener(onclick); |
| | | binding.txtLogin.setOnClickListener(onclick); |
| | | binding.txtRegister.setOnClickListener(onclick); |
| | | binding.txtPrivacy.setOnClickListener(onclick); |
| | | long getTime = getLongProjectPrefrence(VERIFY_CODE); |
| | | long cha = new Date().getTime() - getTime; |
| | | if(cha <1000*60){ |
| | | CodeTimer codeTimer = new CodeTimer(cha, 1000, binding.txtGetVerify); |
| | | codeTimer.startUp(); |
| | | } |
| | | changeView(); |
| | | binding.editPhone.setText(getStringProjectPrefrence(Configuration.KEY_USERNAME)); |
| | | viewModel.getVerifyResult().observe(this, stringApiResult -> { |
| | | mBinding.txtGetVerify.setOnClickListener(onclick); |
| | | mBinding.txtForgot.setOnClickListener(onclick); |
| | | mBinding.txtLogin.setOnClickListener(onclick); |
| | | mBinding.txtRegister.setOnClickListener(onclick); |
| | | mBinding.txtPrivacy.setOnClickListener(onclick); |
| | | mBinding.editPhone.setText(getStringProjectPrefrence(Configuration.KEY_USERNAME)); |
| | | mViewModel.getVerifyResult().observe(this, stringApiResult -> { |
| | | if(stringApiResult.code == 200){ |
| | | |
| | | }else{ |
| | | showToast(stringApiResult.msg); |
| | | } |
| | | }); |
| | | viewModel.getLoginResult().observe(this,loggedInUser -> { |
| | | mViewModel.getLoginResult().observe(this, loggedInUser -> { |
| | | if(loggedInUser.code == 200){ |
| | | putBooleanProjectPrefrence(Configuration.IS_LOGIN,true); |
| | | putStringProjectPrefrence(Configuration.KEY_USERNAME,binding.editPhone.getText().toString()); |
| | | putStringProjectPrefrence(Configuration.KEY_USERNAME, mBinding.editPhone.getText().toString()); |
| | | |
| | | UserBean user = new Gson().fromJson(new Gson().toJson(loggedInUser.data) ,UserBean.class); |
| | | UserBean.setUser(user); |
| | |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public void loadData() { |
| | | long getTime = getLongProjectPrefrence(VERIFY_CODE); |
| | | long cha = new Date().getTime() - getTime; |
| | | if(cha <1000*60){ |
| | | CodeTimer codeTimer = new CodeTimer(cha, 1000, mBinding.txtGetVerify); |
| | | codeTimer.startUp(); |
| | | } |
| | | changeView(); |
| | | } |
| | | |
| | | CustomClickListener onclick = new CustomClickListener() { |
| | | @Override |
| | | protected void onSingleClick(View view) { |
| | |
| | | break; |
| | | case R.id.txt_get_verify: |
| | | |
| | | String phone = binding.editPhone.getText().toString(); |
| | | String phone = mBinding.editPhone.getText().toString(); |
| | | if(!verifyPhone(phone)){//验证手机 |
| | | return; |
| | | } |
| | | if(type==2){//获取注册验证码 |
| | | viewModel.getRegisterSMS(phone); |
| | | mViewModel.getRegisterSMS(phone); |
| | | }else if(type ==1){ |
| | | viewModel.getForgetSMS( phone); |
| | | mViewModel.getForgetSMS( phone); |
| | | }else if(type == -1){ |
| | | viewModel.getLoginSMS( phone); |
| | | mViewModel.getLoginSMS( phone); |
| | | } |
| | | break; |
| | | case R.id.txt_privacy: |
| | |
| | | * 修改页面布局 |
| | | */ |
| | | private void changeView(){ |
| | | binding.button.setEnabled(true); |
| | | binding.txtRegister.setVisibility(View.VISIBLE); |
| | | binding.checkbox.setVisibility(View.GONE); |
| | | binding.txtPrivacy.setVisibility(View.GONE); |
| | | mBinding.button.setEnabled(true); |
| | | mBinding.txtRegister.setVisibility(View.VISIBLE); |
| | | mBinding.checkbox.setVisibility(View.GONE); |
| | | mBinding.txtPrivacy.setVisibility(View.GONE); |
| | | switch (type){ |
| | | case -1://短信登录 |
| | | binding.editVerifyCode.setVisibility(View.VISIBLE); |
| | | binding.txtGetVerify.setVisibility(View.VISIBLE); |
| | | binding.editPass.setVisibility(View.GONE); |
| | | binding.editPassRepeat.setVisibility(View.GONE); |
| | | binding.txtForgot.setVisibility(View.VISIBLE); |
| | | binding.txtLogin.setText(getResources().getString(R.string.login)); |
| | | binding.button.setText(getResources().getString(R.string.login)); |
| | | binding.checkbox.setVisibility(View.VISIBLE); |
| | | binding.txtPrivacy.setVisibility(View.VISIBLE); |
| | | mBinding.editVerifyCode.setVisibility(View.VISIBLE); |
| | | mBinding.txtGetVerify.setVisibility(View.VISIBLE); |
| | | mBinding.editPass.setVisibility(View.GONE); |
| | | mBinding.editPassRepeat.setVisibility(View.GONE); |
| | | mBinding.txtForgot.setVisibility(View.VISIBLE); |
| | | mBinding.txtLogin.setText(getResources().getString(R.string.login)); |
| | | mBinding.button.setText(getResources().getString(R.string.login)); |
| | | mBinding.checkbox.setVisibility(View.VISIBLE); |
| | | mBinding.txtPrivacy.setVisibility(View.VISIBLE); |
| | | break; |
| | | case 0://登录 |
| | | binding.editVerifyCode.setVisibility(View.GONE); |
| | | binding.txtGetVerify.setVisibility(View.GONE); |
| | | binding.editPass.setVisibility(View.VISIBLE); |
| | | binding.editPassRepeat.setVisibility(View.GONE); |
| | | binding.txtForgot.setVisibility(View.VISIBLE); |
| | | binding.txtLogin.setText(getResources().getString(R.string.msg_login)); |
| | | binding.button.setText(getResources().getString(R.string.login)); |
| | | mBinding.editVerifyCode.setVisibility(View.GONE); |
| | | mBinding.txtGetVerify.setVisibility(View.GONE); |
| | | mBinding.editPass.setVisibility(View.VISIBLE); |
| | | mBinding.editPassRepeat.setVisibility(View.GONE); |
| | | mBinding.txtForgot.setVisibility(View.VISIBLE); |
| | | mBinding.txtLogin.setText(getResources().getString(R.string.msg_login)); |
| | | mBinding.button.setText(getResources().getString(R.string.login)); |
| | | break; |
| | | case 1://忘记密码 |
| | | binding.txtForgot.setVisibility(View.INVISIBLE); |
| | | binding.editVerifyCode.setVisibility(View.VISIBLE); |
| | | binding.txtGetVerify.setVisibility(View.VISIBLE); |
| | | binding.editPass.setVisibility(View.VISIBLE); |
| | | binding.editPassRepeat.setVisibility(View.VISIBLE); |
| | | binding.txtLogin.setText(getResources().getString(R.string.login)); |
| | | binding.button.setText(getResources().getString(R.string.str_confirm)); |
| | | mBinding.txtForgot.setVisibility(View.INVISIBLE); |
| | | mBinding.editVerifyCode.setVisibility(View.VISIBLE); |
| | | mBinding.txtGetVerify.setVisibility(View.VISIBLE); |
| | | mBinding.editPass.setVisibility(View.VISIBLE); |
| | | mBinding.editPassRepeat.setVisibility(View.VISIBLE); |
| | | mBinding.txtLogin.setText(getResources().getString(R.string.login)); |
| | | mBinding.button.setText(getResources().getString(R.string.str_confirm)); |
| | | break; |
| | | case 2://注册 |
| | | binding.checkbox.setVisibility(View.VISIBLE); |
| | | binding.txtPrivacy.setVisibility(View.VISIBLE); |
| | | binding.txtRegister.setVisibility(View.INVISIBLE); |
| | | binding.editVerifyCode.setVisibility(View.VISIBLE); |
| | | binding.txtGetVerify.setVisibility(View.VISIBLE); |
| | | binding.editPass.setVisibility(View.VISIBLE); |
| | | binding.editPassRepeat.setVisibility(View.VISIBLE); |
| | | binding.txtLogin.setText(getResources().getString(R.string.login)); |
| | | binding.button.setText(getResources().getString(R.string.register)); |
| | | mBinding.checkbox.setVisibility(View.VISIBLE); |
| | | mBinding.txtPrivacy.setVisibility(View.VISIBLE); |
| | | mBinding.txtRegister.setVisibility(View.INVISIBLE); |
| | | mBinding.editVerifyCode.setVisibility(View.VISIBLE); |
| | | mBinding.txtGetVerify.setVisibility(View.VISIBLE); |
| | | mBinding.editPass.setVisibility(View.VISIBLE); |
| | | mBinding.editPassRepeat.setVisibility(View.VISIBLE); |
| | | mBinding.txtLogin.setText(getResources().getString(R.string.login)); |
| | | mBinding.button.setText(getResources().getString(R.string.register)); |
| | | break; |
| | | } |
| | | clearText(binding.editPassRepeat,binding.editPass,binding.editPhone,binding.editVerifyCode); |
| | | clearText(mBinding.editPassRepeat, mBinding.editPass, mBinding.editPhone, mBinding.editVerifyCode); |
| | | } |
| | | |
| | | private void clearText(EditText... editTextes){ |
| | |
| | | * 提交数据 |
| | | */ |
| | | public void submit(){ |
| | | String phone = binding.editPhone.getText().toString(); |
| | | String pass = binding.editPass .getText().toString(); |
| | | String veriCode = binding.editVerifyCode.getText().toString(); |
| | | String invite = binding.editPassRepeat.getText().toString(); |
| | | String phone = mBinding.editPhone.getText().toString(); |
| | | String pass = mBinding.editPass .getText().toString(); |
| | | String veriCode = mBinding.editVerifyCode.getText().toString(); |
| | | String invite = mBinding.editPassRepeat.getText().toString(); |
| | | if(!verifyPhone(phone)){//验证手机 |
| | | return; |
| | | } |
| | |
| | | showToast(R.string.input_verify_code); |
| | | return; |
| | | } |
| | | if(!binding.checkbox.isChecked()){ |
| | | if(!mBinding.checkbox.isChecked()){ |
| | | showToast("请阅读并勾选《隐私条款》"); |
| | | return; |
| | | } |
| | | viewModel.loginByCode(phone,veriCode); |
| | | mViewModel.loginByCode(phone,veriCode); |
| | | break; |
| | | case 0: |
| | | if(!verifyPassWord(pass)){//验证密码 |
| | | return; |
| | | } |
| | | viewModel.login(phone,pass); |
| | | mViewModel.login(phone,pass); |
| | | break; |
| | | case 1: |
| | | if(!verifyPassWord(pass)){//验证密码 |
| | |
| | | showToast(R.string.str_new_verify_failed); |
| | | return; |
| | | } |
| | | viewModel.resetPwd(phone,veriCode,pass); |
| | | mViewModel.resetPwd(phone,veriCode,pass); |
| | | break; |
| | | case 2://注册 |
| | | if(!verifyPassWord(pass)){//验证密码 |
| | |
| | | showToast(R.string.input_verify_code); |
| | | return; |
| | | } |
| | | if(!binding.checkbox.isChecked()){ |
| | | if(!mBinding.checkbox.isChecked()){ |
| | | showToast("请阅读并勾选《隐私条款》"); |
| | | return; |
| | | } |
| | | viewModel.register(phone,veriCode,pass); |
| | | mViewModel.register(phone,veriCode,pass); |
| | | break; |
| | | } |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.ui.main; |
| | | |
| | | import android.Manifest; |
| | | import android.content.Intent; |
| | | import android.content.res.ColorStateList; |
| | | import android.view.KeyEvent; |
| | | import android.view.View; |
| | | |
| | | import androidx.activity.result.ActivityResultLauncher; |
| | | import androidx.activity.result.contract.ActivityResultContracts; |
| | | import androidx.viewpager2.widget.ViewPager2; |
| | | |
| | | import com.google.android.material.bottomnavigation.BottomNavigationView; |
| | | import com.permissionx.guolindev.PermissionX; |
| | | import com.runt.open.mvvm.R; |
| | | import com.runt.open.mvvm.base.activities.BaseActivity; |
| | | import com.runt.open.mvvm.base.adapter.FragmentAdapter; |
| | | import com.runt.open.mvvm.base.fragments.BaseFragment; |
| | | import com.runt.open.mvvm.data.PhoneDevice; |
| | | import com.runt.open.mvvm.databinding.ActivityMainBinding; |
| | | import com.runt.open.mvvm.listener.CustomClickListener; |
| | | import com.runt.open.mvvm.listener.ResPonse; |
| | | import com.runt.open.mvvm.ui.login.RegisterLoginActivity; |
| | | import com.runt.open.mvvm.ui.login.UserBean; |
| | | import com.runt.open.mvvm.ui.main.home.HomeFragment; |
| | | import com.runt.open.mvvm.ui.main.mine.MineFragment; |
| | | import com.runt.open.mvvm.ui.main.service.ServiceFragment; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | public class MainActivity extends BaseActivity<ActivityMainBinding, MainViewModel> { |
| | | |
| | | private BaseFragment[] fragments = {new HomeFragment(),new ServiceFragment(),new MineFragment()} ; |
| | | ActivityResultLauncher<Intent> loginLaunch = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { |
| | | if(result.getResultCode() == RESULT_CODE_SUCESS){ |
| | | fragments[2].loadData();//登录后重新刷新 |
| | | }else if(result.getResultCode() != RESULT_CODE_SUCESS){ |
| | | mBinding.viewPager2.setCurrentItem(0); |
| | | } |
| | | }); |
| | | |
| | | @Override |
| | | public void initViews() { |
| | | |
| | | mBinding.titleBar.setRightDra(getResources().getDrawable(R.mipmap.icon_white_setting)); |
| | | mBinding.titleBar.setRightClick(new CustomClickListener() { |
| | | @Override |
| | | protected void onSingleClick(View view) { |
| | | //startActivityForResult(new Intent(mContext,SettingActivity.class),REQUEST_CODE_LOGOUT);//打开设置 |
| | | } |
| | | }); |
| | | final FragmentAdapter fragmentAdapter = new FragmentAdapter(this); |
| | | fragmentAdapter.setFragments(Arrays.asList(fragments)); |
| | | //设置当前可见Item左右可见page数,次范围内不会被销毁 |
| | | //禁用预加载 |
| | | mBinding.viewPager2.setOffscreenPageLimit(ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT); |
| | | mBinding.viewPager2.setAdapter(fragmentAdapter); |
| | | mBinding.viewPager2.setCurrentItem(0); |
| | | mBinding.viewPager2.setUserInputEnabled(false); //true:滑动,false:禁止滑动 |
| | | mBinding.viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { |
| | | |
| | | @Override |
| | | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { |
| | | setTitleStr(position); |
| | | } |
| | | |
| | | @Override |
| | | public void onPageSelected(int position) { |
| | | mBinding.navView.getMenu().getItem(position).setChecked(true); |
| | | if(position == 2 && UserBean.getUser() == null){ |
| | | loginLaunch.launch(new Intent(mContext, RegisterLoginActivity.class)); |
| | | } |
| | | } |
| | | }); |
| | | ColorStateList csl = getResources().getColorStateList(R.color.home_nav_color); |
| | | mBinding.navView.setItemTextColor(csl);//设置文本颜色 |
| | | mBinding.navView.setItemIconTintList(csl);//图标着色 |
| | | mBinding.navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);//监听 |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void loadData() { |
| | | checkPermission(); |
| | | } |
| | | |
| | | @Override |
| | | public boolean onKeyDown(int keyCode, KeyEvent event) { |
| | | if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { |
| | | backExit(); |
| | | return true; |
| | | } |
| | | return super.onKeyDown(keyCode, event); |
| | | } |
| | | |
| | | /** |
| | | * 底部导航监听 |
| | | */ |
| | | private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = item -> { |
| | | for(int i = 0 ; i < mBinding.navView.getMenu().size() ; i ++){ |
| | | if(item.getItemId() == mBinding.navView.getMenu().getItem(i).getItemId()){ |
| | | mBinding.viewPager2.setCurrentItem(i); |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | /** |
| | | * 设置标题 |
| | | * @param position |
| | | */ |
| | | private void setTitleStr(int position){ |
| | | switch (position){ |
| | | case 0: |
| | | setTitle("资讯"); |
| | | break; |
| | | case 1: |
| | | setTitle("服务"); |
| | | break; |
| | | case 2: |
| | | setTitle("个人中心"); |
| | | break; |
| | | } |
| | | } |
| | | private void showPermissionDialog(){ |
| | | |
| | | showDialog("警告", "软件需要权限才能运行", "申请权限", "退出", new ResPonse() { |
| | | @Override |
| | | public void doSuccess(Object obj) { |
| | | checkPermission(); |
| | | } |
| | | |
| | | @Override |
| | | public void doError(Object obj) { |
| | | finish(); |
| | | System.exit(0); |
| | | } |
| | | }); |
| | | } |
| | | private void checkPermission(){ |
| | | PermissionX.init(MainActivity.this) |
| | | .permissions(Manifest.permission.READ_PHONE_STATE) |
| | | .request((allGranted, grantedList, deniedList) -> { |
| | | if(allGranted){ |
| | | PhoneDevice.setDevice(mContext); |
| | | }else{ |
| | | showPermissionDialog(); |
| | | } |
| | | |
| | | }); |
| | | } |
| | | } |
| | |
| | | package com.runt.open.mvvm.ui.main.home; |
| | | |
| | | import android.os.Bundle; |
| | | import android.view.LayoutInflater; |
| | | import android.view.View; |
| | | import android.view.ViewGroup; |
| | | import android.widget.TextView; |
| | | import com.runt.open.mvvm.base.fragments.LoadPageFragment; |
| | | import com.runt.open.mvvm.databinding.RefreshRecyclerBinding; |
| | | |
| | | import androidx.annotation.NonNull; |
| | | import androidx.annotation.Nullable; |
| | | import androidx.fragment.app.Fragment; |
| | | import androidx.lifecycle.Observer; |
| | | import androidx.lifecycle.ViewModelProvider; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | import com.runt.open.mvvm.base.fragments.BaseFragment; |
| | | import com.runt.open.mvvm.databinding.FragmentHomeBinding; |
| | | |
| | | public class HomeFragment extends BaseFragment<FragmentHomeBinding,HomeViewModel> { |
| | | |
| | | public class HomeFragment extends LoadPageFragment<RefreshRecyclerBinding,HomeViewModel,MsgAdapter,Message> { |
| | | |
| | | |
| | | @Override |
| | | public void initViews() { |
| | | final TextView textView = binding.textHome; |
| | | viewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() { |
| | | @Override |
| | | public void onChanged(@Nullable String s) { |
| | | textView.setText(s); |
| | | } |
| | | }); |
| | | protected Map requestParams() { |
| | | return new HashMap(); |
| | | } |
| | | |
| | | } |
| | |
| | | package com.runt.open.mvvm.ui.main.home; |
| | | |
| | | import androidx.lifecycle.LiveData; |
| | | import androidx.lifecycle.MutableLiveData; |
| | | import androidx.lifecycle.ViewModel; |
| | | import com.runt.open.mvvm.base.model.LoadPageViewModel; |
| | | import com.runt.open.mvvm.data.Results; |
| | | |
| | | public class HomeViewModel extends ViewModel { |
| | | public class HomeViewModel extends LoadPageViewModel<Results.MessageResult> { |
| | | |
| | | private MutableLiveData<String> mText; |
| | | |
| | | public HomeViewModel() { |
| | | mText = new MutableLiveData<>(); |
| | | mText.setValue("This is home fragment"); |
| | | } |
| | | |
| | | public LiveData<String> getText() { |
| | | return mText; |
| | | @Override |
| | | protected String requestUrl() { |
| | | return "getMsgList"; |
| | | } |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.ui.main.home; |
| | | |
| | | /** |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/7/27. |
| | | */ |
| | | public class Message { |
| | | public String content,cTime,title; |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.ui.main.home; |
| | | |
| | | import android.view.View; |
| | | |
| | | import com.runt.open.mvvm.base.adapter.BaseAdapter; |
| | | import com.runt.open.mvvm.databinding.ItemMsgBinding; |
| | | import com.runt.open.mvvm.listener.CustomClickListener; |
| | | import com.runt.open.mvvm.util.HandleDate; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * My father is Object, ites purpose of |
| | | * |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-8-21. |
| | | */ |
| | | |
| | | public class MsgAdapter extends BaseAdapter<Message, ItemMsgBinding> { |
| | | |
| | | @Override |
| | | protected void onBindView(ItemMsgBinding binding, int position, Message message) { |
| | | binding.txtDetail.setText(message.content); |
| | | Date date = new Date(message.cTime); |
| | | binding.txtTime.setText(HandleDate.getTimeStateNew(date)); |
| | | binding.txtTitle.setText(message.title); |
| | | binding.getRoot().setOnClickListener(new CustomClickListener() { |
| | | @Override |
| | | protected void onSingleClick(View view) { |
| | | //context.startActivity(new Intent(context, MsgDetailActivity.class).putExtra("id", data.get("id").toString())); |
| | | } |
| | | }); |
| | | } |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.ui.main.mine; |
| | | |
| | | import android.app.Activity; |
| | | import android.content.Intent; |
| | | import android.view.View; |
| | | |
| | | import com.bumptech.glide.Glide; |
| | | import com.bumptech.glide.request.RequestOptions; |
| | | import com.permissionx.guolindev.PermissionX; |
| | | import com.runt.open.mvvm.BuildConfig; |
| | | import com.runt.open.mvvm.R; |
| | | import com.runt.open.mvvm.base.fragments.BaseFragment; |
| | | import com.runt.open.mvvm.databinding.FragmentMineBinding; |
| | | import com.runt.open.mvvm.listener.ResPonse; |
| | | import com.runt.open.mvvm.retrofit.observable.HttpObserver; |
| | | import com.runt.open.mvvm.ui.login.UserBean; |
| | | import com.runt.open.mvvm.util.MyLog; |
| | | import com.wildma.pictureselector.PictureSelector; |
| | | |
| | | import java.io.File; |
| | | |
| | | import okhttp3.ResponseBody; |
| | | import retrofit2.Call; |
| | | import retrofit2.Callback; |
| | | import retrofit2.Response; |
| | | |
| | | /** |
| | | * My father is Object, ites purpose of |
| | | * |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-9-15. |
| | | */ |
| | | |
| | | public class MineFragment extends BaseFragment<FragmentMineBinding,MineViewModel> implements View.OnClickListener { |
| | | |
| | | private final String TAG = "MineFragment"; |
| | | |
| | | @Override |
| | | public void initViews() { |
| | | } |
| | | |
| | | @Override |
| | | public void loadData() { |
| | | if(UserBean.getUser() != null){ |
| | | RequestOptions options = new RequestOptions() |
| | | .placeholder(R.mipmap.default_head)//图片加载出来前,显示的图片 |
| | | .fallback(R.mipmap.default_head) //url为空的时候,显示的图片 |
| | | .error(R.mipmap.default_head);//图片加载失败后,显示的图片 |
| | | Glide.with(getContext()).load(BuildConfig.HOST_IP_ADDR +UserBean.getUser().getHead()).apply(options).into(mBinding.img); |
| | | mBinding.txtName.setText(UserBean.getUser().getUsername()); |
| | | mBinding.txtCoin.setText(UserBean.getUser().getCoin()+""); |
| | | mBinding.txtSigns.setText(UserBean.getUser().getSign()+""); |
| | | mBinding.linGroup.setVisibility(View.VISIBLE); |
| | | }else{ |
| | | Glide.with(getContext()).load(R.mipmap.default_head).into(mBinding.img); |
| | | mBinding.txtName.setText("未登录"); |
| | | mBinding.linGroup.setVisibility(View.GONE); |
| | | } |
| | | setOnClickListener(this,R.id.lin_sign,R.id.lin_coin,R.id.img,R.id.txt_name); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void onClick(View view) { |
| | | switch (view.getId()){ |
| | | case R.id.img: |
| | | openAlthum(); |
| | | break; |
| | | case R.id.txt_name://名称 |
| | | mViewModel.updateName(new HttpObserver() { |
| | | @Override |
| | | protected void onSuccess(Object data) { |
| | | UserBean.getUser().setUsername(data.toString()); |
| | | mBinding.txtName.setText(data.toString()); |
| | | |
| | | } |
| | | }); |
| | | break; |
| | | /* case R.id.lin_coin://金币 |
| | | new BottomMenuFragment(getActivity()) |
| | | .addMenuItems(new MenuItem("查看记录")) |
| | | .addMenuItems(new MenuItem("申请提现")) |
| | | .setOnItemClickListener(new BottomMenuFragment.OnItemClickListener() { |
| | | @Override |
| | | public void onItemClick(TextView menu_item, int position) { |
| | | if(position == 0){ |
| | | startActivity(new Intent(mActivity, CoinRecordActivity.class) ); |
| | | }else { |
| | | if(mActivity.isNull(UserBean.getUser().getAlipay())){ |
| | | mActivity.showDialog("设置支付宝", "您还没有设置支付宝账号", "设置", "取消", new ResPonse() { |
| | | @Override |
| | | public void doSuccess(Object obj) { |
| | | startActivity(new Intent(mActivity, CoinSettingActivity.class) ); |
| | | } |
| | | }); |
| | | }else if(mActivity.isNull(UserBean.getUser().getRealname())){ |
| | | mActivity.showDialog("设置真实姓名", "您还没有设置真实姓名", "设置", "取消", new ResPonse() { |
| | | @Override |
| | | public void doSuccess(Object obj) { |
| | | startActivity(new Intent(mActivity, CoinSettingActivity.class) ); |
| | | } |
| | | }); |
| | | }else{ |
| | | startActivityForResult(new Intent(mActivity, WithDrawActivity.class),REQUEST_CODE_WITHDRAW ); |
| | | } |
| | | } |
| | | } |
| | | }) |
| | | .show(); |
| | | break; |
| | | case R.id.lin_sign://签到 |
| | | startActivityForResult(new Intent(getContext(), SignInActivity.class),REQUEST_CODE_SIGN); |
| | | break;*/ |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 打开相册 |
| | | */ |
| | | public void openAlthum(){ |
| | | PermissionX.init(this) |
| | | .permissions(mActivity.CAMERA_PERMISSIONS) |
| | | .request((allGranted, grantedList, deniedList) -> { |
| | | if(allGranted){ |
| | | PictureSelector |
| | | .create(this, PictureSelector.SELECT_REQUEST_CODE) |
| | | .selectPicture(true, 300, 300, 20, 20); |
| | | }else{ |
| | | mActivity.showDialog("警告", "软件需要权限才能运行", "申请权限", "取消", new ResPonse() { |
| | | @Override |
| | | public void doSuccess(Object obj) { |
| | | openAlthum(); |
| | | } |
| | | |
| | | @Override |
| | | public void doError(Object obj) { |
| | | } |
| | | }); |
| | | } |
| | | |
| | | }); |
| | | } |
| | | |
| | | |
| | | |
| | | @Override |
| | | public void onActivityResult(int requestCode, int resultCode, Intent data) { |
| | | super.onActivityResult(requestCode, resultCode, data); |
| | | /*结果回调*/ |
| | | if (requestCode == PictureSelector.SELECT_REQUEST_CODE) { |
| | | if (data != null) { |
| | | String picturePath = data.getStringExtra(PictureSelector.PICTURE_PATH); |
| | | MyLog.i("mineActivity","picturePath:"+picturePath); |
| | | final File file = new File(picturePath); |
| | | mViewModel.updateHead(file).enqueue(new Callback<ResponseBody>() { |
| | | @Override |
| | | public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { |
| | | /*UserBean.getUser().setHead(obj.toString()); |
| | | file.delete(); |
| | | Glide.with(getContext()).load(BuildConfig.HOST_IP_ADDR+UserBean.getUser().getHead()) .into(mBinding.img); //获取选取的图片*/ |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void onFailure(Call<ResponseBody> call, Throwable t) { |
| | | file.delete(); |
| | | } |
| | | }); |
| | | } |
| | | }else if(requestCode == REQUEST_CODE_SIGN && resultCode == Activity.RESULT_OK){ |
| | | loadData(); |
| | | }else if(requestCode == REQUEST_CODE_WITHDRAW && resultCode == Activity.RESULT_OK){ |
| | | loadData(); |
| | | } |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.ui.main.mine; |
| | | |
| | | import com.runt.open.mvvm.base.model.BaseViewModel; |
| | | import com.runt.open.mvvm.listener.ResPonse; |
| | | import com.runt.open.mvvm.retrofit.observable.HttpObserver; |
| | | import com.runt.open.mvvm.ui.login.UserBean; |
| | | |
| | | import java.io.File; |
| | | |
| | | import okhttp3.MediaType; |
| | | import okhttp3.MultipartBody; |
| | | import okhttp3.RequestBody; |
| | | import okhttp3.ResponseBody; |
| | | import retrofit2.Call; |
| | | |
| | | /** |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/7/27. |
| | | */ |
| | | public class MineViewModel extends BaseViewModel { |
| | | |
| | | public void updateName(HttpObserver observer){ |
| | | if(UserBean.getUser().getPhone().equals(UserBean.getUser().getUsername())) { |
| | | mActivity.showInputDialog("输入名称", UserBean.getUser().getUsername(), "名称只能修改一次", new ResPonse() { |
| | | @Override |
| | | public void doSuccess(Object obj) { |
| | | httpObserverOnLoading(commonApi.updateName(obj.toString()), observer); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | public Call<ResponseBody> updateHead(File file){ |
| | | return commonApi.updateHead(MultipartBody.Part.createFormData("head",file.getName(), RequestBody.create(MediaType.parse("text/plain"), file))); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.ui.main.service; |
| | | |
| | | import android.view.View; |
| | | |
| | | import com.runt.open.mvvm.R; |
| | | import com.runt.open.mvvm.base.activities.BaseActivity; |
| | | import com.runt.open.mvvm.base.fragments.BaseFragment; |
| | | import com.runt.open.mvvm.base.model.ImpViewModel; |
| | | import com.runt.open.mvvm.databinding.FragmentServiceBinding; |
| | | import com.runt.open.mvvm.listener.ResPonse; |
| | | |
| | | /** |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/7/27. |
| | | */ |
| | | public class ServiceFragment extends BaseFragment<FragmentServiceBinding, ImpViewModel> implements View.OnClickListener { |
| | | |
| | | @Override |
| | | public void initViews() { |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void loadData() { |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void onClick(View view) { |
| | | switch (view.getId()){ |
| | | case R.id.lin_uav: |
| | | mActivity.showDialog("联系服务商", "即将拨打服务商电话", "拨打","取消",new ResPonse() { |
| | | @Override |
| | | public void doSuccess(Object obj) { |
| | | mActivity.callPhone("15048325741"); |
| | | } |
| | | }); |
| | | break; |
| | | case R.id.lin_bozhong: |
| | | showDialog(); |
| | | break; |
| | | case R.id.lin_yumi: |
| | | showDialog(); |
| | | break; |
| | | case R.id.lin_xiaomai: |
| | | showDialog(); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | public void showDialog(){ |
| | | ((BaseActivity)getActivity()).showDialog("暂无服务商", "如果您是相关服务商可致电入住", "入住","取消",new ResPonse() { |
| | | @Override |
| | | public void doSuccess(Object obj) { |
| | | mActivity.showDialog("联系平台", "即将拨打平台电话", "拨打","取消",new ResPonse() { |
| | | @Override |
| | | public void doSuccess(Object obj) { |
| | | mActivity.callPhone("13000000000"); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | |
| | | |
| | | import android.content.Intent; |
| | | import android.os.Handler; |
| | | import android.os.Message; |
| | | import android.util.Log; |
| | | import android.view.View; |
| | | import android.view.WindowManager; |
| | | |
| | | import androidx.annotation.NonNull; |
| | | import androidx.lifecycle.Observer; |
| | | |
| | | import com.bytedance.sdk.openadsdk.TTSplashAd; |
| | | import com.runt.open.mvvm.MainActivity; |
| | | import com.runt.open.mvvm.base.activities.BaseActivity; |
| | | import com.runt.open.mvvm.base.model.ImpViewModel; |
| | | import com.runt.open.mvvm.databinding.ActivitySplashBinding; |
| | | import com.runt.open.mvvm.ui.main.MainActivity; |
| | | |
| | | |
| | | /** |
| | |
| | | * |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-4-16. |
| | | */ |
| | | public class SplashActivity extends BaseActivity<ActivitySplashBinding,SplashViewModel> { |
| | | public class SplashActivity extends BaseActivity<ActivitySplashBinding, ImpViewModel> { |
| | | |
| | | final String TAG = "WelcomeActivity"; |
| | | |
| | | Handler handler = new Handler(){ |
| | | boolean started = false; |
| | | @Override |
| | | public void handleMessage(@NonNull Message msg) { |
| | | super.handleMessage(msg); |
| | | if(!started) {//确保该语句只执行一次 |
| | | started = true; |
| | | Intent intent = new Intent(mContext, MainActivity.class); |
| | | startActivity(intent); |
| | | finish(); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | @Override |
| | | public void initViews() { |
| | | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, |
| | | WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏 |
| | | hideBottomUIMenu(); |
| | | viewModel.getSplashAd().observe(this, new Observer<TTSplashAd>() { |
| | | @Override |
| | | public void onChanged(TTSplashAd ttSplashAd) { |
| | | binding.splashAdContainer.addView(ttSplashAd.getSplashView()); |
| | | //设置SplashView的交互监听器 |
| | | ttSplashAd.setSplashInteractionListener(new TTSplashAd.AdInteractionListener() { |
| | | @Override |
| | | public void onAdClicked(View view, int type) { |
| | | Log.d(TAG, "onAdClicked"); |
| | | } |
| | | |
| | | @Override |
| | | public void onAdShow(View view, int type) { |
| | | Log.d(TAG, "onAdShow"); |
| | | } |
| | | |
| | | @Override |
| | | public void onAdSkip() { |
| | | Log.d(TAG, "onAdSkip"); |
| | | handler.sendMessage(new Message()); |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void onAdTimeOver() { |
| | | Log.d(TAG, "onAdTimeOver"); |
| | | handler.sendMessage(new Message()); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | viewModel.getTimeOut().observe(this, new Observer<Integer>() { |
| | | @Override |
| | | public void onChanged(Integer integer) { |
| | | handler.sendMessage(new Message()); |
| | | } |
| | | }); |
| | | viewModel.applyTdAd(mContext);;//请求广告 |
| | | } |
| | | |
| | | |
| | | @Override |
| | | protected void onDestroy() { |
| | | super.onDestroy(); |
| | | binding.splashAdContainer.removeAllViews(); |
| | | public void loadData() { |
| | | new Handler().postDelayed(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | Intent intent = new Intent(mContext, MainActivity.class); |
| | | startActivity(intent); |
| | | finish(); |
| | | } |
| | | },2000); |
| | | } |
| | | |
| | | } |
| | |
| | | package com.runt.open.mvvm.ui.splash; |
| | | |
| | | import android.content.Context; |
| | | import android.os.Handler; |
| | | import android.util.Log; |
| | | |
| | | import androidx.lifecycle.MutableLiveData; |
| | | |
| | | import com.bytedance.sdk.openadsdk.AdSlot; |
| | | import com.bytedance.sdk.openadsdk.TTAdNative; |
| | | import com.bytedance.sdk.openadsdk.TTAdSdk; |
| | | import com.bytedance.sdk.openadsdk.TTSplashAd; |
| | | import com.runt.open.mvvm.base.model.BaseViewModel; |
| | | |
| | | import java.util.Date; |
| | |
| | | final String TAG = "SplashViewModel"; |
| | | long cTime = new Date().getTime(),limitTime = 2000; |
| | | |
| | | private MutableLiveData<TTSplashAd> splashAd = new MutableLiveData<>(); |
| | | private MutableLiveData<Integer> timeOut = new MutableLiveData<>(); |
| | | |
| | | public MutableLiveData<TTSplashAd> getSplashAd() { |
| | | return splashAd; |
| | | } |
| | | |
| | | public MutableLiveData<Integer> getTimeOut() { |
| | | return timeOut; |
| | |
| | | },limitTime); |
| | | } |
| | | |
| | | /** |
| | | * 请求广告 |
| | | * @param context |
| | | */ |
| | | public void applyTdAd(Context context){ |
| | | countdown(); |
| | | TTAdNative mTTAdNative = TTAdSdk.getAdManager().createAdNative(context); |
| | | AdSlot adSlot = new AdSlot.Builder() |
| | | .setCodeId("887382769")//广告id |
| | | .setSupportDeepLink(true) |
| | | .setImageAcceptedSize(1080,1920) |
| | | //模板广告需要设置期望个性化模板广告的大小,单位dp,代码位是否属于个性化模板广告,请在穿山甲平台查看 |
| | | //.setExpressViewAcceptedSize(expressViewWidth, expressViewHeight) |
| | | .build(); |
| | | mTTAdNative.loadSplashAd(adSlot,new TTAdNative.SplashAdListener() { |
| | | @Override |
| | | public void onError(int i, String s) { |
| | | Log.i(TAG,"code:"+i+" message:"+s); |
| | | } |
| | | |
| | | @Override |
| | | public void onTimeout() { |
| | | Log.i(TAG,"超时"); |
| | | } |
| | | |
| | | @Override |
| | | public void onSplashAdLoad(TTSplashAd ttSplashAd) { |
| | | Log.d(TAG, "开屏广告请求成功"); |
| | | |
| | | long waitTime = limitTime - (new Date().getTime() - cTime); |
| | | if(waitTime > 0){//是否超过限定时间 没有超时则继续 |
| | | new Handler().postDelayed(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | splashAd.setValue(ttSplashAd); |
| | | } |
| | | }, waitTime > 0 ? waitTime : 0); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | } |
| | |
| | | initCompent(); |
| | | } |
| | | |
| | | @Override |
| | | public void loadData() { |
| | | |
| | | } |
| | | |
| | | |
| | | int count = 100; |
| | | int index = 100; |
| | | private void initCompent(){ |
| | | binding.browser.getSettings().setJavaScriptEnabled(true); |
| | | binding.browser.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); |
| | | mBinding.browser.getSettings().setJavaScriptEnabled(true); |
| | | mBinding.browser.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); |
| | | //跳转至拼接好的地址 |
| | | //mBaseHandler.sendMessage(msg);//http://192.168.5.156:8080/MyFinance/gd16/1.html |
| | | binding.browser.loadUrl(url); |
| | | binding.browser.setWebViewClient(new myWebViewClient()); |
| | | binding.browser.setWebChromeClient(new WebChromeClient(){ |
| | | mBinding.browser.loadUrl(url); |
| | | mBinding.browser.setWebViewClient(new myWebViewClient()); |
| | | mBinding.browser.setWebChromeClient(new WebChromeClient(){ |
| | | |
| | | @Override |
| | | public void onProgressChanged(WebView view,final int newProgress) { |
| | | MyLog.i("onProgressChanged","--newProgress:--"+newProgress); |
| | | MyLog.i("onProgressChanged","--binding.viewProgressbar:--"+binding.viewProgressbar.getWidth()); |
| | | MyLog.i("onProgressChanged","--binding.viewProgressbar:--"+ mBinding.viewProgressbar.getWidth()); |
| | | final LayoutAnimationController.AnimationParameters animation= new LayoutAnimationController.AnimationParameters(); //得到一个LayoutAnimationController对象; |
| | | animation.index =index++ ; |
| | | animation.count = count++ ; |
| | | if (newProgress == 100) { |
| | | MyAnimations.hideAnimaInSitu(binding.linProgressbar); |
| | | MyAnimations.makeViewMove(binding.viewProgressbar.getTranslationX(),0,0,0,binding.viewProgressbar); |
| | | MyAnimations.hideAnimaInSitu(mBinding.linProgressbar); |
| | | MyAnimations.makeViewMove(mBinding.viewProgressbar.getTranslationX(),0,0,0, mBinding.viewProgressbar); |
| | | new Handler().postDelayed(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | binding.linProgressbar.setVisibility(View.GONE); |
| | | mBinding.linProgressbar.setVisibility(View.GONE); |
| | | } |
| | | },MyAnimations.ANIMA_TIME); |
| | | } else { |
| | | |
| | | if (View.VISIBLE != binding.linProgressbar.getVisibility()) { |
| | | MyAnimations.showAnimaInSitu(binding.linProgressbar); |
| | | if (View.VISIBLE != mBinding.linProgressbar.getVisibility()) { |
| | | MyAnimations.showAnimaInSitu(mBinding.linProgressbar); |
| | | if(linProgressWidth==0){ |
| | | final ViewTreeObserver vto = binding.linProgressbar.getViewTreeObserver(); |
| | | final ViewTreeObserver vto = mBinding.linProgressbar.getViewTreeObserver(); |
| | | vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { |
| | | public boolean onPreDraw() { |
| | | linProgressWidth = binding.linProgressbar.getMeasuredWidth(); |
| | | binding.viewProgressbar.setTranslationX(0-linProgressWidth); |
| | | binding.linProgressbar.getViewTreeObserver().removeOnPreDrawListener(this); |
| | | linProgressWidth = mBinding.linProgressbar.getMeasuredWidth(); |
| | | mBinding.viewProgressbar.setTranslationX(0-linProgressWidth); |
| | | mBinding.linProgressbar.getViewTreeObserver().removeOnPreDrawListener(this); |
| | | return true; |
| | | } |
| | | }); |
| | | }else{ |
| | | binding.viewProgressbar.setTranslationX(0-linProgressWidth); |
| | | mBinding.viewProgressbar.setTranslationX(0-linProgressWidth); |
| | | } |
| | | |
| | | } |
| | | if(linProgressWidth!=0){ |
| | | MyAnimations.makeViewMove(binding.viewProgressbar.getTranslationX(),0-linProgressWidth+linProgressWidth/100*newProgress,0,0,binding.viewProgressbar,MyAnimations.ANIMA_TIME*3); |
| | | MyAnimations.makeViewMove(mBinding.viewProgressbar.getTranslationX(),0-linProgressWidth+linProgressWidth/100*newProgress,0,0, mBinding.viewProgressbar,MyAnimations.ANIMA_TIME*3); |
| | | new Handler().postDelayed(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | MyAnimations.makeViewMove(binding.viewProgressbar.getTranslationX(),binding.viewProgressbar.getTranslationX()+300,0,0,binding.viewProgressbar,MyAnimations.ANIMA_TIME*10); |
| | | MyAnimations.makeViewMove(mBinding.viewProgressbar.getTranslationX(), mBinding.viewProgressbar.getTranslationX()+300,0,0, mBinding.viewProgressbar,MyAnimations.ANIMA_TIME*10); |
| | | } |
| | | },MyAnimations.ANIMA_TIME*3); |
| | | } |
| | |
| | | |
| | | }); |
| | | |
| | | binding.browser.getSettings().setSavePassword(false); |
| | | mBinding.browser.getSettings().setSavePassword(false); |
| | | //Toast.makeText(mContext,"进入浏览器",Toast.LENGTH_SHORT).show(); |
| | | String Scale = String.valueOf(binding.browser.getScale()); |
| | | String Scale = String.valueOf(mBinding.browser.getScale()); |
| | | MyLog.i("Runt","--Scale:--"+Scale); |
| | | int screenDensity=getResources().getDisplayMetrics().densityDpi; |
| | | MyLog.i("Runt", "--screenDensity:--"+String.valueOf(screenDensity)); //60-160-240 |
| | |
| | | } |
| | | |
| | | private void hideProgressBar(){ |
| | | MyAnimations.hideAnimaInSitu(binding.linProgressbar); |
| | | MyAnimations.makeViewMove(binding.viewProgressbar.getTranslationX(),0,0,0,binding.viewProgressbar); |
| | | MyAnimations.hideAnimaInSitu(mBinding.linProgressbar); |
| | | MyAnimations.makeViewMove(mBinding.viewProgressbar.getTranslationX(),0,0,0, mBinding.viewProgressbar); |
| | | new Handler().postDelayed(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | binding.linProgressbar.setVisibility(View.GONE); |
| | | mBinding.linProgressbar.setVisibility(View.GONE); |
| | | } |
| | | },MyAnimations.ANIMA_TIME*2); |
| | | } |
| | |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | /** |
| | | * check the system is harmony os |
| | | * |
| | | * @return true if it is harmony os |
| | | */ |
| | | public static boolean isHarmonyOS() { |
| | | try { |
| | | Class clz = Class.forName("com.huawei.system.BuildEx"); |
| | | Method method = clz.getMethod("getOsBrand"); |
| | | return "harmony".equals(method.invoke(clz)); |
| | | } catch (ClassNotFoundException e) { |
| | | Log.e(TAG, "occured ClassNotFoundException"); |
| | | } catch (NoSuchMethodException e) { |
| | | Log.e(TAG, "occured NoSuchMethodException"); |
| | | } catch (Exception e) { |
| | | Log.e(TAG, "occur other problem"); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 获取鸿蒙系统版本号 |
| | | */ |
| | | public static String getHarmonyOsVersion() { |
| | | if (isHarmonyOS()) { |
| | | try { |
| | | Class cls = Class.forName("android.os.SystemProperties"); |
| | | Method method = cls.getMethod("get", String.class); |
| | | return method.invoke(cls, "ro.huawei.build.display.id").toString(); |
| | | //android.os.Build.DISPLAY |
| | | } catch ( Exception e) { |
| | | } |
| | | } |
| | | return "-1"; |
| | | } |
| | | |
| | | /** |
| | | * 获取属性 |
| | | * @param property |
| | | * @param defaultValue |
| | | * @return |
| | | */ |
| | | private static String getProp(String property, String defaultValue) { |
| | | try { |
| | | Class spClz = Class.forName("android.os.SystemProperties"); |
| | | Method method = spClz.getDeclaredMethod("get", String.class); |
| | | String value = (String) method.invoke(spClz, property); |
| | | if (TextUtils.isEmpty(value)) { |
| | | return defaultValue; |
| | | } |
| | | return value; |
| | | } catch (Throwable e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return defaultValue; |
| | | } |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.util; |
| | | |
| | | import java.sql.Timestamp; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/7/27. |
| | | */ |
| | | public class HandleDate { |
| | | |
| | | private static SimpleDateFormat secondsdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | private static SimpleDateFormat timesdf = new SimpleDateFormat("HH:mm:ss"); |
| | | private static SimpleDateFormat datesdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | private static SimpleDateFormat hoursdf = new SimpleDateFormat("yyyy-MM-dd HH"); |
| | | private static SimpleDateFormat minutesdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); |
| | | |
| | | /** |
| | | * 获取当前时间的String类型 |
| | | */ |
| | | public static long getDateToLong() { |
| | | return new Date().getTime(); |
| | | } |
| | | |
| | | /** |
| | | * 获取指定时间的long类型 |
| | | */ |
| | | public static long getDateToLong(Date date) { |
| | | |
| | | return date.getTime(); |
| | | } |
| | | |
| | | /** |
| | | * 获取指定时间的long类型 |
| | | * |
| | | * @throws ParseException |
| | | */ |
| | | public static long getDateToLong(String datestr) throws ParseException { |
| | | |
| | | return datesdf.parse(datestr).getTime(); |
| | | } |
| | | |
| | | /** |
| | | * 将long类型的时间转换成只有日期的int类型 |
| | | */ |
| | | public static int getDateToInt(long datetime) { |
| | | |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); |
| | | Date date = getLongToDate(datetime); |
| | | String format = sdf.format(date); |
| | | return Integer.parseInt(format); |
| | | } |
| | | |
| | | /** |
| | | * 将指定long类型的日期转换为date |
| | | */ |
| | | public static Date getLongToDate(Long datetime) { |
| | | return new Date(datetime); |
| | | } |
| | | |
| | | /** |
| | | * 将指定long类型的日期转换为string时间精确到秒 |
| | | */ |
| | | public static String getLongToSecond(Long datetime) { |
| | | Date date = new Date(datetime); |
| | | return secondsdf.format(date); |
| | | } |
| | | |
| | | /** |
| | | * 将指定long类型的日期转换为string时间 只显示 时分秒 |
| | | */ |
| | | public static String getLongToTime(Long datetime) { |
| | | String datestr = ""; |
| | | Date date = null; |
| | | try { |
| | | date = new Date(datetime); |
| | | datestr = timesdf.format(date); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return datestr; |
| | | } |
| | | |
| | | /** |
| | | * 将指定long类型的日期转换为string时间只显示日期 |
| | | */ |
| | | public static String getLongToDatestr(Long datetime) { |
| | | String datestr = ""; |
| | | Date date = null; |
| | | try { |
| | | date = new Date(datetime); |
| | | datestr = datesdf.format(date); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return datestr; |
| | | } |
| | | |
| | | /** |
| | | * 将指定long类型的日期转换为string时间精确到小时 |
| | | */ |
| | | public static String getLongToHour(Long datetime) { |
| | | String datestr = ""; |
| | | Date date = null; |
| | | try { |
| | | date = new Date(datetime); |
| | | datestr = hoursdf.format(date); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return datestr; |
| | | } |
| | | |
| | | /** |
| | | * 将指定long类型的日期转换为string日期 |
| | | */ |
| | | public static String getLongToSimpleDate(Long datetime) { |
| | | String datestr = ""; |
| | | Date date = null; |
| | | try { |
| | | date = new Date(datetime); |
| | | datestr = datesdf.format(date); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return datestr; |
| | | } |
| | | |
| | | /** |
| | | * 根据毫秒时间戳来格式化字符串 今天显示今天、昨天显示昨天、前天显示前天. 早于前天的显示具体年-月-日,如2017-06-12; |
| | | * |
| | | * @param timeStamp 毫秒值 |
| | | * @return 今天 昨天 前天 或者 yyyy-MM-dd HH:mm:ss类型字符串 |
| | | */ |
| | | public static String format(long timeStamp) { |
| | | long curTimeMillis = System.currentTimeMillis(); |
| | | Date curDate = new Date(curTimeMillis); |
| | | int todayHoursSeconds = curDate.getHours() * 60 * 60; |
| | | int todayMinutesSeconds = curDate.getMinutes() * 60; |
| | | int todaySeconds = curDate.getSeconds(); |
| | | int todayMillis = (todayHoursSeconds + todayMinutesSeconds + todaySeconds) * 1000; |
| | | long todayStartMillis = curTimeMillis - todayMillis; |
| | | if (timeStamp >= todayStartMillis) { |
| | | return "今天"; |
| | | } |
| | | int oneDayMillis = 24 * 60 * 60 * 1000; |
| | | long yesterdayStartMilis = todayStartMillis - oneDayMillis; |
| | | if (timeStamp >= yesterdayStartMilis) { |
| | | return "昨天"; |
| | | } |
| | | long yesterdayBeforeStartMilis = yesterdayStartMilis - oneDayMillis; |
| | | if (timeStamp >= yesterdayBeforeStartMilis) { |
| | | return "前天"; |
| | | } |
| | | // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | return sdf.format(new Date(timeStamp)); |
| | | } |
| | | |
| | | /** |
| | | * 根据时间戳来判断当前的时间是几天前,几分钟,刚刚 |
| | | * |
| | | * @param long_time |
| | | * @return |
| | | */ |
| | | |
| | | public static String getTimeStateNew(String long_time) { |
| | | String long_by_13 = "1000000000000"; |
| | | String long_by_10 = "1000000000"; |
| | | if (Long.valueOf(long_time) / Long.valueOf(long_by_13) < 1) { |
| | | if (Long.valueOf(long_time) / Long.valueOf(long_by_10) >= 1) { |
| | | long_time = long_time + "000"; |
| | | } |
| | | } |
| | | return getTimeStateNew(long_time); |
| | | } |
| | | public static String getTimeStateNew(Date long_time) { |
| | | return getTimeStateNew(long_time.getTime() ); |
| | | } |
| | | public static String getTimeStateNew(Long long_time) { |
| | | Timestamp time = new Timestamp(Long.valueOf(long_time)); |
| | | Timestamp now = new Timestamp(System.currentTimeMillis()); |
| | | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); |
| | | // System.out.println("传递过来的时间:"+format.format(time)); |
| | | // System.out.println("现在的时间:"+format.format(now)); |
| | | long day_conver = 1000 * 60 * 60 * 24; |
| | | long hour_conver = 1000 * 60 * 60; |
| | | long min_conver = 1000 * 60; |
| | | long time_conver = now.getTime() - time.getTime(); |
| | | long temp_conver; |
| | | // System.out.println("天数:"+time_conver/day_conver); |
| | | if ((time_conver / day_conver) < 3) { |
| | | temp_conver = time_conver / day_conver; |
| | | if (temp_conver <= 2 && temp_conver >= 1) { |
| | | return temp_conver + "天前"; |
| | | } else { |
| | | temp_conver = (time_conver / hour_conver); |
| | | if (temp_conver >= 1) { |
| | | return temp_conver + "小时前"; |
| | | } else { |
| | | temp_conver = (time_conver / min_conver); |
| | | if (temp_conver >= 1) { |
| | | return temp_conver + "分钟前"; |
| | | } else { |
| | | return "刚刚"; |
| | | } |
| | | } |
| | | } |
| | | } else { |
| | | return format.format(time); |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.widgets; |
| | | |
| | | import android.annotation.SuppressLint; |
| | | import android.content.Context; |
| | | import android.content.res.TypedArray; |
| | | import android.graphics.Bitmap; |
| | | import android.graphics.BitmapShader; |
| | | import android.graphics.Canvas; |
| | | import android.graphics.Color; |
| | | import android.graphics.Matrix; |
| | | import android.graphics.Paint; |
| | | import android.graphics.RectF; |
| | | import android.graphics.Shader; |
| | | import android.graphics.drawable.BitmapDrawable; |
| | | import android.graphics.drawable.ColorDrawable; |
| | | import android.graphics.drawable.Drawable; |
| | | import android.util.AttributeSet; |
| | | import android.widget.ImageView; |
| | | |
| | | import com.runt.open.mvvm.R; |
| | | |
| | | |
| | | /** |
| | | * My father is Object, ites purpose of 圆形图片带阴影 |
| | | * |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-2-23. |
| | | */ |
| | | @SuppressLint("AppCompatCustomView") |
| | | public class CircleImageView extends ImageView { |
| | | |
| | | private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP; |
| | | |
| | | private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; |
| | | private static final int COLORDRAWABLE_DIMENSION = 1; |
| | | |
| | | private static final int DEFAULT_BORDER_WIDTH = 0; |
| | | private static final int DEFAULT_BORDER_COLOR = Color.BLACK; |
| | | |
| | | private final RectF mDrawableRect = new RectF(); |
| | | private final RectF mBorderRect = new RectF(); |
| | | |
| | | private final Matrix mShaderMatrix = new Matrix(); |
| | | private final Paint mBitmapPaint = new Paint(); |
| | | private final Paint mBorderPaint = new Paint(); |
| | | |
| | | private int mBorderColor = DEFAULT_BORDER_COLOR; |
| | | private int mBorderWidth = DEFAULT_BORDER_WIDTH; |
| | | |
| | | private Bitmap mBitmap; |
| | | private BitmapShader mBitmapShader; |
| | | private int mBitmapWidth; |
| | | private int mBitmapHeight; |
| | | |
| | | private float mDrawableRadius; |
| | | private float mBorderRadius; |
| | | |
| | | private boolean mReady; |
| | | private boolean mSetupPending; |
| | | |
| | | public CircleImageView(Context context) { |
| | | super(context); |
| | | } |
| | | |
| | | public CircleImageView(Context context, AttributeSet attrs) { |
| | | this(context, attrs, 0); |
| | | } |
| | | |
| | | public CircleImageView(Context context, AttributeSet attrs, int defStyle) { |
| | | super(context, attrs, defStyle); |
| | | super.setScaleType(SCALE_TYPE); |
| | | |
| | | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0); |
| | | |
| | | mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH); |
| | | mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR); |
| | | |
| | | a.recycle(); |
| | | |
| | | mReady = true; |
| | | |
| | | if (mSetupPending) { |
| | | setup(); |
| | | mSetupPending = false; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public ScaleType getScaleType() { |
| | | return SCALE_TYPE; |
| | | } |
| | | |
| | | @Override |
| | | public void setScaleType(ScaleType scaleType) { |
| | | if (scaleType != SCALE_TYPE) { |
| | | throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType)); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | protected void onDraw(Canvas canvas) { |
| | | if (getDrawable() == null) { |
| | | return; |
| | | } |
| | | |
| | | canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint); |
| | | if(getBorderWidth()>0) { |
| | | canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
| | | super.onSizeChanged(w, h, oldw, oldh); |
| | | setup(); |
| | | } |
| | | |
| | | public int getBorderColor() { |
| | | return mBorderColor; |
| | | } |
| | | |
| | | public void setBorderColor(int borderColor) { |
| | | if (borderColor == mBorderColor) { |
| | | return; |
| | | } |
| | | |
| | | mBorderColor = borderColor; |
| | | mBorderPaint.setColor(mBorderColor); |
| | | invalidate(); |
| | | } |
| | | |
| | | public int getBorderWidth() { |
| | | return mBorderWidth; |
| | | } |
| | | |
| | | public void setBorderWidth(int borderWidth) { |
| | | if (borderWidth == mBorderWidth) { |
| | | return; |
| | | } |
| | | |
| | | mBorderWidth = borderWidth; |
| | | setup(); |
| | | } |
| | | |
| | | @Override |
| | | public void setImageBitmap(Bitmap bm) { |
| | | super.setImageBitmap(bm); |
| | | mBitmap = bm; |
| | | setup(); |
| | | } |
| | | |
| | | @Override |
| | | public void setImageDrawable(Drawable drawable) { |
| | | super.setImageDrawable(drawable); |
| | | mBitmap = getBitmapFromDrawable(drawable); |
| | | setup(); |
| | | } |
| | | |
| | | @Override |
| | | public void setImageResource(int resId) { |
| | | super.setImageResource(resId); |
| | | mBitmap = getBitmapFromDrawable(getDrawable()); |
| | | setup(); |
| | | } |
| | | |
| | | private Bitmap getBitmapFromDrawable(Drawable drawable) { |
| | | if (drawable == null) { |
| | | return null; |
| | | } |
| | | |
| | | if (drawable instanceof BitmapDrawable) { |
| | | return ((BitmapDrawable) drawable).getBitmap(); |
| | | } |
| | | |
| | | try { |
| | | Bitmap bitmap; |
| | | |
| | | if (drawable instanceof ColorDrawable) { |
| | | bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG); |
| | | } else { |
| | | bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG); |
| | | } |
| | | |
| | | Canvas canvas = new Canvas(bitmap); |
| | | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); |
| | | drawable.draw(canvas); |
| | | return bitmap; |
| | | } catch (OutOfMemoryError e) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private void setup() { |
| | | if (!mReady) { |
| | | mSetupPending = true; |
| | | return; |
| | | } |
| | | |
| | | if (mBitmap == null) { |
| | | return; |
| | | } |
| | | |
| | | mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); |
| | | |
| | | mBitmapPaint.setAntiAlias(true); |
| | | mBitmapPaint.setShader(mBitmapShader); |
| | | |
| | | mBorderPaint.setStyle(Paint.Style.STROKE); |
| | | mBorderPaint.setAntiAlias(true); |
| | | mBorderPaint.setColor(mBorderColor); |
| | | mBorderPaint.setStrokeWidth(mBorderWidth); |
| | | |
| | | mBitmapHeight = mBitmap.getHeight(); |
| | | mBitmapWidth = mBitmap.getWidth(); |
| | | |
| | | mBorderRect.set(0, 0, getWidth(), getHeight()); |
| | | mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2); |
| | | |
| | | mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth); |
| | | mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2); |
| | | |
| | | updateShaderMatrix(); |
| | | invalidate(); |
| | | } |
| | | |
| | | private void updateShaderMatrix() { |
| | | float scale; |
| | | float dx = 0; |
| | | float dy = 0; |
| | | |
| | | mShaderMatrix.set(null); |
| | | |
| | | if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) { |
| | | scale = mDrawableRect.height() / (float) mBitmapHeight; |
| | | dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; |
| | | } else { |
| | | scale = mDrawableRect.width() / (float) mBitmapWidth; |
| | | dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; |
| | | } |
| | | |
| | | mShaderMatrix.setScale(scale, scale); |
| | | mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth); |
| | | |
| | | mBitmapShader.setLocalMatrix(mShaderMatrix); |
| | | } |
| | | } |
New file |
| | |
| | | package com.runt.open.mvvm.widgets; |
| | | |
| | | import android.content.Context; |
| | | import android.graphics.Canvas; |
| | | import android.graphics.Color; |
| | | import android.graphics.Paint; |
| | | import android.graphics.Path; |
| | | import android.graphics.RectF; |
| | | import android.util.AttributeSet; |
| | | |
| | | import androidx.annotation.Nullable; |
| | | import androidx.appcompat.widget.AppCompatImageView; |
| | | |
| | | |
| | | /** |
| | | * My father is Object, ites purpose of 圆角 |
| | | * |
| | | * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-6-18. |
| | | */ |
| | | public class CornerImageView extends AppCompatImageView { |
| | | |
| | | private int cornerSize = 30; |
| | | |
| | | private Paint paint; |
| | | |
| | | public CornerImageView(Context context) { |
| | | |
| | | super(context); |
| | | |
| | | paint = new Paint(); |
| | | |
| | | paint.setColor(Color.WHITE); |
| | | |
| | | paint.setAntiAlias(true);//消除锯齿 |
| | | |
| | | } |
| | | |
| | | public CornerImageView(Context context, @Nullable AttributeSet attrs) { |
| | | |
| | | super(context, attrs); |
| | | |
| | | paint = new Paint(); |
| | | |
| | | paint.setColor(Color.WHITE); |
| | | |
| | | paint.setAntiAlias(true);//消除锯齿 |
| | | |
| | | } |
| | | |
| | | @Override |
| | | |
| | | public void draw(Canvas canvas) { |
| | | |
| | | super.draw(canvas); |
| | | |
| | | drawLeftTop(canvas); |
| | | |
| | | drawRightTop(canvas); |
| | | |
| | | drawLeftBottom(canvas); |
| | | |
| | | drawRightBottom(canvas); |
| | | |
| | | } |
| | | |
| | | private void drawLeftTop(Canvas canvas) { |
| | | |
| | | Path path = new Path(); |
| | | |
| | | path.moveTo(0, cornerSize); |
| | | |
| | | path.lineTo(0, 0); |
| | | |
| | | path.lineTo(cornerSize, 0); |
| | | |
| | | path.arcTo(new RectF(0, 0, cornerSize * 2, cornerSize * 2), -90, -90); |
| | | |
| | | path.close(); |
| | | |
| | | canvas.drawPath(path, paint); |
| | | |
| | | } |
| | | |
| | | private void drawLeftBottom(Canvas canvas) { |
| | | |
| | | Path path = new Path(); |
| | | |
| | | path.moveTo(0, getHeight() - cornerSize); |
| | | |
| | | path.lineTo(0, getHeight()); |
| | | |
| | | path.lineTo(cornerSize, getHeight()); |
| | | |
| | | path.arcTo(new RectF(0, // x |
| | | |
| | | getHeight() - cornerSize * 2,// y |
| | | |
| | | cornerSize * 2,// x |
| | | |
| | | getHeight()// getWidth()// y |
| | | |
| | | ), 90, 90); |
| | | |
| | | path.close(); |
| | | |
| | | canvas.drawPath(path, paint); |
| | | |
| | | } |
| | | |
| | | private void drawRightBottom(Canvas canvas) { |
| | | |
| | | Path path = new Path(); |
| | | |
| | | path.moveTo(getWidth() - cornerSize, getHeight()); |
| | | |
| | | path.lineTo(getWidth(), getHeight()); |
| | | |
| | | path.lineTo(getWidth(), getHeight() - cornerSize); |
| | | |
| | | RectF oval = new RectF(getWidth() - cornerSize * 2, getHeight() |
| | | |
| | | - cornerSize * 2, getWidth(), getHeight()); |
| | | |
| | | path.arcTo(oval, 0, 90); |
| | | |
| | | path.close(); |
| | | |
| | | canvas.drawPath(path, paint); |
| | | |
| | | } |
| | | |
| | | private void drawRightTop(Canvas canvas) { |
| | | |
| | | Path path = new Path(); |
| | | |
| | | path.moveTo(getWidth(), cornerSize); |
| | | |
| | | path.lineTo(getWidth(), 0); |
| | | |
| | | path.lineTo(getWidth() - cornerSize, 0); |
| | | |
| | | path.arcTo(new RectF(getWidth() - cornerSize * 2, 0, getWidth(), |
| | | |
| | | 0 + cornerSize * 2), -90, 90); |
| | | |
| | | path.close(); |
| | | |
| | | canvas.drawPath(path, paint); |
| | | |
| | | } |
| | | |
| | | public int getCornerSize() { |
| | | |
| | | return cornerSize; |
| | | |
| | | } |
| | | |
| | | public void setCornerSize(int cornerSize) { |
| | | |
| | | this.cornerSize = cornerSize; |
| | | |
| | | } |
| | | } |
| | |
| | | rightTextSize = array.getDimension(R.styleable.TitleBarView_rightTextSize, DimensionUtils.convertSpToPixel(getContext(),14)); |
| | | rightPadding = array.getDimension(R.styleable.TitleBarView_rightDrawablePadding,10); |
| | | |
| | | int leftTint = array.getColor(R.styleable.TitleBarView_leftTint,-1); |
| | | if(leftTint != -1) { |
| | | setTint(leftDra,leftTint); |
| | | } |
| | | int rightTint = array.getColor(R.styleable.TitleBarView_rightTint,-1); |
| | | if(rightTint != -1) { |
| | | setTint(rightDra,rightTint); |
| | | } |
| | | textPaint = new Paint(); |
| | | textPaint.setAntiAlias(true); // 是否抗锯齿 |
| | | //mTextPaint.setAlpha(50); // 设置alpha不透明度,范围为0~255 |
| | |
| | | float height2 = fm.bottom - fm.top + fm.leading;//行高 |
| | | return (int) height2; |
| | | } |
| | | |
| | | private void setTint(Drawable drawable, @ColorInt int color){ |
| | | if(drawable!= null){ |
| | | drawable.setTint(color); |
| | | } |
| | | } |
| | | } |
| | |
| | | <androidx.constraintlayout.widget.ConstraintLayout |
| | | xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | android:id="@+id/container" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" > |
| | | |
| | | <com.runt.open.mvvm.widgets.TitleBarView |
| | | android:id="@+id/title_bar" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="?attr/actionBarSize" |
| | | android:background="@color/white" |
| | | app:layout_constraintTop_toTopOf="parent" |
| | | app:layout_constraintLeft_toLeftOf="parent" |
| | | app:layout_constraintRight_toRightOf="parent" /> |
| | | style="@style/titlebar" |
| | | tools:ignore="MissingConstraints" |
| | | app:titleText="标题" /> |
| | | |
| | | <androidx.viewpager2.widget.ViewPager2 |
| | | android:id="@+id/view_pager_2" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="0dp" |
| | | app:layout_constraintTop_toBottomOf="@id/title_bar" |
| | | app:layout_constraintLeft_toLeftOf="parent" |
| | | app:layout_constraintRight_toRightOf="parent" |
| | | app:layout_constraintBottom_toTopOf="@id/nav_view" /> |
| | | <com.google.android.material.bottomnavigation.BottomNavigationView |
| | | android:id="@+id/nav_view" |
| | | android:layout_width="0dp" |
| | |
| | | app:layout_constraintRight_toRightOf="parent" |
| | | app:menu="@menu/bottom_nav_menu"/> |
| | | |
| | | <fragment |
| | | android:id="@+id/nav_host_fragment_activity_main" |
| | | android:name="androidx.navigation.fragment.NavHostFragment" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | app:defaultNavHost="true" |
| | | app:layout_constraintBottom_toTopOf="@id/nav_view" |
| | | app:layout_constraintLeft_toLeftOf="parent" |
| | | app:layout_constraintRight_toRightOf="parent" |
| | | app:layout_constraintTop_toTopOf="parent" |
| | | app:navGraph="@navigation/mobile_navigation" |
| | | /> |
| | | |
| | | </androidx.constraintlayout.widget.ConstraintLayout> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | android:background="@color/white" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:orientation="vertical"> |
| | | |
| | | <com.runt.open.mvvm.widgets.TitleBarView |
| | | android:id="@+id/title_bar" |
| | | style="@style/titlebar" |
| | | tools:ignore="MissingConstraints" |
| | | app:titleText="设置" |
| | | app:leftDrawable="@mipmap/icon_white_back" |
| | | app:leftTint="@color/black"/> |
| | | |
| | | <TextView |
| | | android:id="@+id/version" |
| | | app:layout_constraintTop_toBottomOf="@id/title_bar" |
| | | app:layout_constraintLeft_toLeftOf="parent" |
| | | app:layout_constraintRight_toRightOf="parent" |
| | | android:background="@drawable/bg_white" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="left" |
| | | android:text="@string/str_version" |
| | | android:textColor="@color/txt_color" |
| | | android:textSize="@dimen/title_size" |
| | | android:padding="15dp" /> |
| | | <TextView |
| | | android:id="@+id/about" |
| | | app:layout_constraintTop_toBottomOf="@id/version" |
| | | app:layout_constraintLeft_toLeftOf="parent" |
| | | app:layout_constraintRight_toRightOf="parent" |
| | | android:background="@drawable/bg_white" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:drawableRight="@mipmap/arrow_right" |
| | | android:gravity="left" |
| | | android:text="关于我们" |
| | | android:textColor="@color/txt_color" |
| | | android:textSize="@dimen/title_size" |
| | | android:padding="15dp" /> |
| | | <View |
| | | app:layout_constraintTop_toBottomOf="@id/title_bar" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="0.5dp" |
| | | android:background="@color/color_gray8" /> |
| | | <View |
| | | app:layout_constraintTop_toBottomOf="@id/version" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="0.5dp" |
| | | android:background="@color/color_gray8" /> |
| | | <View |
| | | app:layout_constraintTop_toBottomOf="@id/about" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="0.5dp" |
| | | android:background="@color/color_gray8" /> |
| | | |
| | | <View |
| | | app:layout_constraintBottom_toTopOf="@id/logout" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="1dp" |
| | | android:background="@color/color_gray8" /> |
| | | <TextView |
| | | android:id="@+id/logout" |
| | | app:layout_constraintBottom_toBottomOf="parent" |
| | | app:layout_constraintLeft_toLeftOf="parent" |
| | | app:layout_constraintRight_toRightOf="parent" |
| | | android:background="@drawable/bg_white" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:text="@string/logout" |
| | | android:textColor="@color/txt_color" |
| | | android:textSize="@dimen/title_size" |
| | | android:padding="15dp" /> |
| | | </androidx.constraintlayout.widget.ConstraintLayout> |
| | |
| | | android:scaleType="centerCrop" |
| | | /> |
| | | |
| | | <FrameLayout |
| | | android:id="@+id/splash_ad_container" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" /> |
| | | |
| | | </RelativeLayout> |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto"> |
| | | |
| | | <com.runt.open.mvvm.widgets.TitleBarView |
| | | android:id="@+id/title_bar" |
| | | app:layout_constraintTop_toTopOf="parent" |
| | | app:layout_constraintLeft_toLeftOf="parent" |
| | | app:layout_constraintRight_toRightOf="parent" |
| | | style="@style/titlebar" /> |
| | | style="@style/titlebar" |
| | | tools:ignore="MissingConstraints" /> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/lin_progressbar" |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | android:background="@color/white" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:orientation="vertical" |
| | | tools:ignore="MissingDefaultResource"> |
| | | |
| | | <ScrollView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent"> |
| | | |
| | | <LinearLayout |
| | | |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical"> |
| | | |
| | | <View |
| | | android:layout_width="match_parent" |
| | | android:layout_height="0.5dp" |
| | | android:background="@color/color_gray8" /> |
| | | <LinearLayout |
| | | android:id="@+id/lin_name" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:background="@drawable/bg_white" > |
| | | |
| | | <ImageView |
| | | android:id="@+id/img_top_bg" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="200dp" |
| | | android:scaleType="centerCrop" |
| | | android:src="@mipmap/user_back"/> |
| | | |
| | | <com.runt.open.mvvm.widgets.CircleImageView |
| | | android:id="@+id/img" |
| | | android:layout_width="100dp" |
| | | android:layout_height="100dp" |
| | | android:layout_gravity="center_horizontal" |
| | | android:layout_marginTop="-50dp" |
| | | android:scaleType="centerCrop" |
| | | android:src="@mipmap/default_head" /> |
| | | <TextView |
| | | android:id="@+id/txt_name" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_gravity="center_horizontal" |
| | | android:layout_marginTop="10dp" |
| | | android:text="名称" |
| | | android:textSize="20sp" |
| | | android:textColor="@color/txt_normal"/> |
| | | <TextView |
| | | android:id="@+id/txt_user_sign" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_gravity="center_horizontal" |
| | | android:layout_marginTop="10dp" |
| | | android:paddingLeft="15dp" |
| | | android:paddingRight="15dp" |
| | | android:gravity="center" |
| | | android:text="签名" |
| | | android:textSize="16sp" |
| | | android:visibility="gone" |
| | | android:drawablePadding="20dp" |
| | | android:textColor="@color/txt_enable"/> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_margin="15dp" |
| | | android:visibility="gone" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | style="@style/user_txt_left" |
| | | android:drawableLeft="@mipmap/location_e" |
| | | android:text="所在地:"/> |
| | | <TextView |
| | | android:id="@+id/txt_address" |
| | | style="@style/user_txt_value" |
| | | android:text="北京"/> |
| | | |
| | | </LinearLayout> |
| | | <LinearLayout |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginLeft="15dp" |
| | | android:visibility="gone" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | style="@style/user_txt_left" |
| | | android:drawableLeft="@mipmap/birth" |
| | | android:text="年龄:"/> |
| | | <TextView |
| | | android:id="@+id/txt_age" |
| | | style="@style/user_txt_value" |
| | | android:text="11"/> |
| | | |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/lin_group" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="@drawable/white_corner_border" |
| | | android:layout_margin="@dimen/default_margin_lr" |
| | | android:elevation="5dp"> |
| | | |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/lin_coin" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:paddingTop="20dp" |
| | | android:paddingBottom="20dp" |
| | | android:gravity="center" |
| | | android:layout_weight="1"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="我的金币" |
| | | android:textSize="18sp" |
| | | android:drawableLeft="@mipmap/icon_coin" |
| | | android:drawablePadding="10dp" |
| | | android:textColor="@color/txt_normal"/> |
| | | |
| | | <TextView |
| | | android:id="@+id/txt_coin" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="10dp" |
| | | android:textSize="25sp" |
| | | android:text="0" |
| | | android:textColor="@color/txt_normal"/> |
| | | |
| | | |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/lin_sign" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:paddingTop="20dp" |
| | | android:paddingBottom="20dp" |
| | | android:gravity="center" |
| | | android:layout_weight="1"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="连续签到" |
| | | android:textSize="18sp" |
| | | android:drawableLeft="@mipmap/sign" |
| | | android:drawablePadding="10dp" |
| | | android:textColor="@color/txt_normal"/> |
| | | |
| | | <TextView |
| | | android:id="@+id/txt_signs" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="10dp" |
| | | android:textSize="25sp" |
| | | android:text="0" |
| | | android:textColor="@color/txt_normal"/> |
| | | |
| | | |
| | | </LinearLayout> |
| | | |
| | | </LinearLayout> |
| | | |
| | | |
| | | |
| | | </LinearLayout> |
| | | <View |
| | | android:layout_width="match_parent" |
| | | android:layout_height="0.5dp" |
| | | android:background="@color/color_gray8" /> |
| | | |
| | | </LinearLayout> |
| | | </ScrollView> |
| | | |
| | | |
| | | </LinearLayout> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | android:background="@color/white" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:orientation="vertical" |
| | | tools:ignore="MissingDefaultResource"> |
| | | |
| | | <View |
| | | android:layout_width="match_parent" |
| | | android:layout_height="0.5dp" |
| | | android:background="@color/cut_off_line" /> |
| | | <ScrollView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent"> |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:paddingTop="20dp" |
| | | android:orientation="vertical"> |
| | | |
| | | <RelativeLayout |
| | | android:id="@+id/lin_uav" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:padding="1dp" |
| | | android:layout_margin="10dp" |
| | | android:gravity="center_vertical"> |
| | | |
| | | <com.runt.open.mvvm.widgets.CornerImageView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:adjustViewBounds="true" |
| | | android:src="@mipmap/bg_uav" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="@color/white" |
| | | android:textSize="20sp" |
| | | android:textStyle="bold" |
| | | android:layout_marginTop="15dp" |
| | | android:layout_marginLeft="15dp" |
| | | android:text="无人机喷洒农药/化肥" /> |
| | | |
| | | |
| | | </RelativeLayout> |
| | | |
| | | <RelativeLayout |
| | | android:id="@+id/lin_yumi" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:padding="1dp" |
| | | android:layout_margin="10dp" |
| | | android:gravity="center_vertical"> |
| | | |
| | | <com.runt.open.mvvm.widgets.CornerImageView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:adjustViewBounds="true" |
| | | android:src="@mipmap/bg_yumi" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="@color/white" |
| | | android:textSize="20sp" |
| | | android:textStyle="bold" |
| | | android:layout_marginTop="15dp" |
| | | android:layout_marginLeft="15dp" |
| | | android:text="玉米收割" /> |
| | | |
| | | |
| | | </RelativeLayout> |
| | | <RelativeLayout |
| | | android:id="@+id/lin_bozhong" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:padding="1dp" |
| | | android:layout_margin="10dp" |
| | | android:gravity="center_vertical"> |
| | | |
| | | <com.runt.open.mvvm.widgets.CornerImageView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:adjustViewBounds="true" |
| | | android:src="@mipmap/bg_bozhong" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="@color/white" |
| | | android:textSize="20sp" |
| | | android:textStyle="bold" |
| | | android:layout_marginTop="15dp" |
| | | android:layout_marginLeft="15dp" |
| | | android:text="播种/施肥" /> |
| | | </RelativeLayout> |
| | | <RelativeLayout |
| | | android:id="@+id/lin_xiaomai" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:padding="1dp" |
| | | android:layout_margin="10dp" |
| | | android:gravity="center_vertical"> |
| | | |
| | | <com.runt.open.mvvm.widgets.CornerImageView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:adjustViewBounds="true" |
| | | android:src="@mipmap/bg_xiaomai" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="@color/white" |
| | | android:textSize="20sp" |
| | | android:textStyle="bold" |
| | | android:layout_marginTop="15dp" |
| | | android:layout_marginLeft="15dp" |
| | | android:text="小麦收割" /> |
| | | </RelativeLayout> |
| | | </LinearLayout> |
| | | </ScrollView> |
| | | </LinearLayout> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:orientation="vertical" > |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:paddingTop="20dp" |
| | | android:paddingBottom="20dp" |
| | | android:paddingLeft="@dimen/default_margin_lr" |
| | | android:paddingRight="@dimen/default_margin_lr" |
| | | android:background="@drawable/bg_white" |
| | | android:gravity="center" |
| | | android:orientation="vertical" > |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="horizontal" |
| | | android:gravity="bottom" > |
| | | |
| | | <TextView |
| | | android:id="@+id/txt_title" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:textStyle="bold" |
| | | android:textSize="@dimen/title_size" |
| | | android:text="挖鼻通知" /> |
| | | <TextView |
| | | android:id="@+id/txt_time" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:alpha="0.6" |
| | | android:text="02-11" /> |
| | | |
| | | </LinearLayout> |
| | | |
| | | <androidx.appcompat.widget.AppCompatTextView |
| | | android:id="@+id/txt_detail" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="5dp" |
| | | android:alpha="0.6" |
| | | android:text="l;ajs;ldjf;ljas;ldfjasdfasdfasdfasdf很垃圾乐山大佛了计算的;了时代峻峰拉三等奖了可接受的来精神的分了就l;asjdflkjl;asjdf;lkajsdf" |
| | | android:singleLine="true"/> |
| | | |
| | | |
| | | |
| | | </LinearLayout> |
| | | <View |
| | | android:layout_width="match_parent" |
| | | android:layout_height="0.5dp" |
| | | android:background="@color/cut_off_line" /> |
| | | <LinearLayout |
| | | android:id="@+id/lin_ad" |
| | | android:orientation="vertical" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" ></LinearLayout> |
| | | |
| | | |
| | | </LinearLayout> |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <com.scwang.smart.refresh.layout.SmartRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | android:id="@+id/smart_refresh" |
| | | android:id="@+id/refresh" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent"> |
| | | |
| | |
| | | <attr name="rightText" format="string" /> |
| | | <attr name="rightTextSize" format="dimension" /> |
| | | <attr name="rightTextColor" format="color" /> |
| | | <attr name="leftTint" format="color" /> |
| | | <attr name="rightTint" format="color" /> |
| | | </declare-styleable> |
| | | |
| | | |
| | |
| | | </style> |
| | | |
| | | |
| | | <style name="user_txt_left"> |
| | | <item name="android:layout_width">wrap_content</item> |
| | | <item name="android:layout_height">wrap_content</item> |
| | | <item name="android:textSize">18sp</item> |
| | | <item name="android:textColor">@color/txt_enable</item> |
| | | <item name="android:drawablePadding">10dp</item> |
| | | <item name="android:layout_marginRight">10dp</item> |
| | | </style> |
| | | <style name="tip_txt_style"> |
| | | <item name="android:layout_width">match_parent</item> |
| | | <item name="android:layout_height">@dimen/title_height</item> |
| | | <item name="android:onClick">onTypeClick</item> |
| | | <item name="android:textSize">16sp</item> |
| | | <item name="android:textColor">@color/txt_normal</item> |
| | | <item name="android:drawableRight">@drawable/check_selector</item> |
| | | <item name="android:gravity">center_vertical</item> |
| | | </style> |
| | | <style name="user_txt_value"> |
| | | <item name="android:layout_width">wrap_content</item> |
| | | <item name="android:layout_height">wrap_content</item> |
| | | <item name="android:textSize">18sp</item> |
| | | <item name="android:textColor">@color/txt_normal</item> |
| | | </style> |
| | | |
| | | |
| | | <style name="btn_normal"> |
| | | <item name="android:layout_width">match_parent</item> |
| | | <item name="android:layout_height">wrap_content</item> |