# yb-util **Repository Path**: miwisay/yb-util ## Basic Information - **Project Name**: yb-util - **Description**: 常用工具包 - **Primary Language**: Android - **License**: MulanPSL-2.0 - **Default Branch**: v1.3.7.14-alpha - **Homepage**: https://gitee.com/miwisay/yb-util - **GVP Project**: No ## Statistics - **Stars**: 7 - **Forks**: 1 - **Created**: 2019-02-24 - **Last Updated**: 2025-06-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: Android, java-util, yb ## README # YBUtil [![](https://jitpack.io/v/com.gitee.miwisay/yb-util.svg)](https://jitpack.io/#com.gitee.miwisay/yb-util) ### 依赖包 当前稳定版本:[1.3.7.13-low-alpha08] or [1.3.7.13-alpha-rc08] //*** 高版本 compileSdk >= 32 ******************************************** // Config info: // compileSdk 35 // Gradle Plugin Versio 8.6.1 // Gradle Version 8.7 // Kotlin Version 2.0.0 //yb 工具类 api 'com.gitee.miwisay.yb-util:yb-utils:1.3.7.14-rc01' //yb ui包 api 'com.gitee.miwisay.yb-util:yb-component:1.3.7.14-rc01' //yb mediaX包 api 'com.gitee.miwisay.yb-util:yb-mediax:1.3.7.14-rc01' /*yb 网络请求*/ api 'com.gitee.miwisay.yb-util:yb-networkutil:1.3.7.14-rc01' //********************************************************************** //*** 低版本 compileSdk < 32 ******************************************** //yb 工具类 api 'com.gitee.miwisay.yb-util:yb-utils:1.3.7.14-low-rc01' //yb ui包 api 'com.gitee.miwisay.yb-util:yb-component:1.3.7.14-low-rc01' //yb mediaX包 api 'com.gitee.miwisay.yb-util:yb-mediax:1.3.7.14-low-rc01' /*yb 网络请求*/ api 'com.gitee.miwisay.yb-util:yb-networkutil:1.3.7.14-low-rc01' //********************************************************************** //网络请求工具栏需要的第三方框架 api ("com.squareup.okhttp3:okhttp:4.9.1") #### ybcomponent(控件相关) RecycleView常规,右滑更多,多选适配器,控件圆角化(Shape),MaskImageView(遮罩百分比展示),小红点布局... #### ybnetworkutil(网络相关) ##### 【网络请求】com.ybear.ybnetworkutil.http.HttpClient ``` public static void main(String[] args) { Req mReq = HttpClient .create() .writeTimeout( 30 ) //写入超时 .readTimeout( 30 ) //请求超时 .connectTimeout( 30 ) //连接超时 /*.setHttpReboot( HttpReboot.get() ) //请求失败时可以重新发起请求,不过有Bug没时间改*/ .build() .addReqDataListener(new CallReqAdapter() { /* 全局监听,所有请求都会走一遍这个监听器 */ /** 请求信息 @param r {@link Request} */ @Override public void onRequest(@NonNull Request r) { super.onRequest(r); Log.d( "TAG_onRequest", "[" + r.getMethod() + "] " + r.toFullUrl() ); } /** 请求结果(String) @param url 请求的链接 @param result 返回的数据 */ @Override public void onResult(String url, String result) { super.onResult(url, result); Log.d( "TAG_onResult", "[Result] Url:" + url +" Result:" + result ); } /** 请求结果 @param call okhttp3的Call @param r 返回的结果 @throws IOException 异常 */ @Override public void onResponse(@NonNull Call call, @NonNull Response r) throws IOException { super.onResponse( call, r ); ResponseBody body = r.body(); Log.d( "TAG_onResponse", "[Result] Result:" + ( body == null ? "null" : body.string() ) ); } /** 请求失败 @param call okhttp3的Call @param e 异常 */ @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { super.onFailure(call, e); e.printStackTrace(); LogUtil.d( "TAG_onFailure", "[Failure] ErrorMsg:" + e.getMessage() ); } }); /* 【推荐】通过继承Request实现请求 */ TestApi api = new TestApi(); // api.setMethod( Method.POST ); //当提交方式为auto时根据当前设置自动调用 api.addParam( "参数1", "参数值" ); api.addParam( "参数2", true ); api.addParam( "参数3", 1 ); /* setCallback...方法用于接收请求到的数据 */ //与全局回调 onResponse 和 onFailure 一致 // api.setCallback( new Callback(){ ... } ); //与全局回调的 onResult 一致(不过这里不会返回Url) api.setCallbackString( new CallbackString(){ @Override public void getRequestString(@Nullable String result) { Log.d( "TAG_getRequestString", "[Result] Result:" + result ); } } ); mReq.req( api ) //请求地址和参数 .mediaTypeJson() //请求方式。form,json,js,xml,textPlain,textXml,textHtml .requestBody() //请求类型(比如:正常请求,上传文件等操作)。requestBody,formBody,multipartBody .post(); //提交方式。get,post,put,patch,head,delete,auto /* 通过String请求 */ ClientBuilder cbReq = mReq.req( "https://域名/api/testApi" ); // ClientBuilder cbReq = mReq.req( "https://域名/api/testApi", "参数1=参数值&参数2=true&参数3=1" ); // ClientBuilder cbReq = mReq.req( "https://域名/api/testApi", new HashMap<>() ); cbReq.mediaTypeJson().requestBody().post(); } /** 你的Api请求 */ public static class TestApi extends BaseApi { @Override public String api() { return "api/testApi(你的api)"; } } /** 基类请求。更换域名地址时,在这里修改就可以了 */ public static abstract class BaseApi extends Request { @Override public String url() { return "https://你的域名地址"; } } ``` ##### 【网络监听】com.ybear.ybnetworkutil.network ``` //Application public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); NetworkChangeManage ncm = NetworkChangeManage.get(); //初始化网络监听 ncm.init( getApplication() ); //可以在这里添加注册,不过推荐在BaseActivity中添加 //NetworkChangeManage.get().registerService(); //注册网络监听器 ncm.registerNetworkChange((isAvailable, type) -> Log.e( "onNetworkChange Tag", "isAvailable:" + isAvailable + " | type:" + type) ); } } //BaseActivity public class BaseActivity extends Activity { @Override protected void onResume() { super.onResume(); //如果是模块化开发,可以在BaseActivity中添加 NetworkChangeManage ncm = NetworkChangeManage.get(); if( !ncm.isRunningService() ) { ncm.registerService(); } } @Override protected void onPause() { super.onPause(); //如果是模块化开发,可以在BaseActivity中添加 //回到桌面时,解除注册网络监听服务(com.ybear.ybutils.utils.StackManage) if( StackManage.get().isGotoHome( this ) ) { NetworkChangeManage.get().unregisterService(); } } } ``` #### ybutils(工具类) ##### 【日志】com.ybear.ybutils.utils.log.LogUtil ``` /* 在Application中设置 */ //自动配置是否打印日志(打包时,正式环境(release)关闭日志) LogUtil.setDebugEnableOfAuto( getApplication() ); //手动配置是否打印日志(任何环境) LogUtil.setDebugEnable( getApplication(), true ); //半自动配置是否打印日志(当isAuto为true时,无视enable) LogUtil.setDebugEnable( getApplication(), true, true ); //保存日志的路径 LogUtil.setLogSavePath( getApplication(), true ); //启用/禁用回调日志 LogUtil.setCallLogEnable( true ); //全局Tag标签(LogUtil.v( "hello!" ); 不设置Tag时使用全局Tag) LogUtil.setTagOfGlobal( "TestTAG" ); //生成新日志文件的间隔(秒) LogUtil.setNewLogFileIntervalSecond( 120 ); //设置保存日志的类型文本(保存日志时会携带该文本) LogUtil.setLogSaveTypeText( new String[] { "V", "D", "I", "W", "E" } ); //保存日志时的编码 LogUtil.setLogSaveCharset( Charset.defaultCharset() ); //回调日志接口 LogUtil.setOnCallLogListener( data -> { /* 可选设置 */ // //可以在回调时决定是否保存当前日志(这是一个全局性的决定,等同于LogUtil.setLogSavePath( *, true );) // data.setEnableSave( true ); // /* 重新设置tag和message */ // data.setTag( data.getTag() ); // data.setMessage( data.getMessage() ); /* 自定义保存的日志,可以是完全加密后的日志。CustomMessage不为空时,只保存CustomMessage的内容 */ String custom = new String( Base64.encode( data.getMessage().getBytes( data.getCharset() ), Base64.DEFAULT ), data.getCharset() ); data.setCustomMessage( custom ); Log.e( "LOG_TAG", custom ); return data; } ); /* 打印日志 */ LogUtil.v( "TAG", "自定义TAG:文本" ); LogUtil.v( this, "自定义TAG:this" ); LogUtil.v( getClass(), "自定义TAG:getClass()" ); LogUtil.v( "TAG", "%s%s", "自定义TAG:", "文本,带参数" ); LogUtil.v( this, "%s%s", "自定义TAG:", "this,带参数" ); LogUtil.v( getClass(), "%s%s", "自定义TAG:", "getClass(),带参数" ); try { LogUtil.v( this, new NullPointerException("自定义TAG:this,Throwable空指针") ); LogUtil.v( getClass(), new NullPointerException("自定义TAG:getClass(),Throwable空指针") ); LogUtil.v( new NullPointerException("全局TAG:Throwable空指针") ); }catch(Exception e) { e.printStackTrace(); LogUtil.v( e ); } LogUtil.v( "全局TAG" ); LogUtil.v( "%s%s", new String[] { "全局TAG:","带参数" } ); LogUtil.w( getClass(), "生成新日志文件的间隔:%s", LogUtil.getNewLogFileIntervalSecond() ); LogUtil.w( getClass(), "保存日志路径:%s", LogUtil.getLogSavePath() ); //v,d,i,w,e //json log //是否跳过实体类输出为json LogUtil.setSkipLogPrintJson( true ); //添加指定无法实现 ILogPrintJson 接口的类,使其允许输出为json // LogUtil.addLogPrintJsonClass( TestEntity.class ); //test class TestEntity test = new TestEntity( //implements ILogPrintJson "LogPrintJson 1", //text new TestEntity( "LogPrintJson 2" ) //TestEntity ); //第一种输出json的办法(不会受限于 setSkipLogPrintJson) Log.d( "TAG", "LogPrintJson 1 -> " + test.toJSONString() ); //第二种输出json的办法(不会受限于 setSkipLogPrintJson) LogUtil.d( "TAG", "LogPrintJson 2 -> %s", test.toJSONString() ); //第三种输出json的办法(受限于 setSkipLogPrintJson) LogUtil.d( "TAG", "LogPrintJson 3 -> %s", test ); /* * 输出的日志 * >>> LogPrintJson 1 -> {"content":"LogPrintJson 1","testEntity":{"content":"LogPrintJson 2"}} * >>> LogPrintJson 2 -> {"content":"LogPrintJson 1","testEntity":{"content":"LogPrintJson 2"}} * >>> LogPrintJson 3 -> TestEntity{content='LogPrintJson 1', testEntity=TestEntity{content='LogPrintJson 2', testEntity=null}} * */ ``` ##### 【Handler】com.ybear.ybutils.utils.handler ``` Handler handler = HandlerManage.create( new CallbackAdapter( "Test string data" ) { @Override public boolean handleMessage(@NonNull Message msg, @Nullable String data) { LogUtil.e( "Handler -> handleMessage:" + msg.what + " | data:" + data ); return true; } @Override public void dispatchMessage(@NonNull Message msg, @Nullable String data) { LogUtil.e( "Handler -> dispatchMessage:" + msg.what + " | data:" + data ); } }); //handler.sendEmptyMessage( 111 ); handler.sendEmptyMessageAtTime( 2222, 10000 ); handler.sendEmptyMessageDelayed( 3333, 3000 ); Message msg = handler.obtainMessage(); msg.what = 4444; handler.sendMessageDelayed( msg, 5000 ); ``` ##### 【心跳管理器】com.ybear.ybutils.utils.LiveTime ``` //** Application ****************************** public class TestApplication extends Application { //测试id(每1秒执行一次,存在5秒后退出) public static final int LIVE_TIME_ID_TEXT_1_SECOND = 200; //测试id(每3秒执行一次,一直存活) public static final int LIVE_TIME_ID_TEXT_5_SECOND = 201; @Override public void onCreate() { super.onCreate(); /* 初始化 LiveTime */ LiveTime liveTime = LiveTime.get(); liveTime.addOnLiveTimeListener(new LiveTime.OnLiveTimeListener() { @Override public void onCreate(int id) { //id 创建时回调 LogUtil.d( "LiveTime.Listener -> onCreate -> id:" + id ); } @Override public boolean onLiveTime(int id) { //id 心跳时回调 switch ( id ) { case LIVE_TIME_ID_TEXT_1_SECOND: LogUtil.d( "LiveTime.Listener -> onLiveTime -> LIVE_TIME_ID_TEXT_1_SECOND" ); break; case LIVE_TIME_ID_TEXT_5_SECOND: LogUtil.d( "LiveTime.Listener -> onLiveTime -> LIVE_TIME_ID_TEXT_5_SECOND" ); break; } return false; } @Override public void onStop(int id) { //id 停止时回调 LogUtil.d( "LiveTime.Listener -> onStop -> id:" + id ); } @Override public void onDestroy(int id) { //id 销毁时回调 LogUtil.d( "LiveTime.Listener -> onDestroy -> id:" + id ); } }); /* LIVE_TIME_ID_TEXT_1_SECOND */ /* 心跳间隔 LIVE_TIME_ID_TEXT_1_SECOND 每1秒执行一次 */ liveTime.updateLiveTimeIntervalSecond( LIVE_TIME_ID_TEXT_1_SECOND, 1 ); /* LIVE_TIME_ID_TEXT_1_SECOND 存在5秒后退出 */ liveTime.updateLiveTimeSecond( LIVE_TIME_ID_TEXT_1_SECOND, 5 ); /* LIVE_TIME_ID_TEXT_5_SECOND */ /* 心跳间隔 LIVE_TIME_ID_TEXT_5_SECOND 每3秒执行一次 */ liveTime.updateLiveTimeIntervalSecond( LIVE_TIME_ID_TEXT_5_SECOND, 3 ); /* LIVE_TIME_ID_TEXT_5_SECOND 一直存活(默认为:0) */ // liveTime.updateLiveTimeSecond( LIVE_TIME_ID_TEXT_5_SECOND, 0 ); } } //** MainActivity or other path *************** public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SysUtil.setTheme( this, R.style.defaultActivity ); LiveTime liveTime = LiveTime.get(); //启动全部id liveTime.startLiveTime(); //或者 //启动指定id // liveTime.startLiveTime( // HandlerManage.create(), // TestApplication.LIVE_TIME_ID_TEXT_1_SECOND, // TestApplication.LIVE_TIME_ID_TEXT_5_SECOND // ); } @Override protected void onResume() { super.onResume(); /* 防止心跳突然停止 */ LiveTime liveTime = LiveTime.get(); //检查全部id liveTime.checkLiveTime(); // //或者 // liveTime.checkLiveTime( // HandlerManage.create(), // false, //是否强制退出 // TestApplication.LIVE_TIME_ID_TEXT_5_SECOND // ); // //或者 // liveTime.checkLiveTime( // HandlerManage.create(), // TestApplication.LIVE_TIME_ID_TEXT_1_SECOND, // TestApplication.LIVE_TIME_ID_TEXT_5_SECOND // ); } @Override protected void onDestroy() { super.onDestroy(); //释放心跳 LiveTime.get().releaseLiveTime(); //或者 // LiveTime.get().releaseLiveTime( // HandlerManage.create(), // TestApplication.LIVE_TIME_ID_TEXT_1_SECOND, // TestApplication.LIVE_TIME_ID_TEXT_5_SECOND // ); } } ``` ##### 【Toast】com.ybear.ybutils.utils.toast ``` /********** 基础展示 **********/ //短Toast ToastManage.get().showToast( this, "Test toast" ); //长Toast ToastManage.get().showToastForLong( this, "Test toast" ); /********** 进阶展示1 **********/ ToastX toastX = ToastManage.get() .makeText( this ) .setText( "Test toast" ) .setGravity( Gravity.CENTER ) .setDuration( ToastX.LENGTH_LONG or ToastX.LENGTH_SHORT ) .setMargin( 5.0F, 5.0F ); //展示 toastX.show(); //取消 toastX.cancel(); /********** 进阶展示2 **********/ Build build = new Build(); //小图标 build.setCompoundDrawablesWithIntrinsicBounds( R.mipmap.ic_launcher, 0, 0, 0 ); //图标内间距 build.setCompoundDrawablesPadding( 10 ); //文本大小 build.setTextSize( 12 ); //文本颜色 build.setTextColor( Color.BLACK ); //文本展示位置 build.setTextGravity( Gravity.CENTER ); //背景颜色 build.setBackgroundColor( Color.WHITE ); //Toast展示位置 build.setGravity( Gravity.CENTER ); //Toast内间距 build.setPadding( 10, 10, 10, 10 ); //Toast外间距 build.setMargin( 5.0F, 5.0F ); //圆角 build.setRadius( 50 ); ToastX toastX ToastManage.get().makeText( this, "Test toast", ToastX.LENGTH_LONG or ToastX.LENGTH_SHORT, build ); //展示 toastX.show(); //取消 toastX.cancel(); ``` ##### 【通知】com.ybear.ybutils.utils.notification ``` //点击通知时打开的页面 Intent intent = new Intent( this, TestActivity.class ); boolean notifyResult = NotificationX.get() .showNotification( hashCode(), "测试标题", "测试内容", intent, false ); ToastManage.get().showToast( this, "展示通知结果:" + notifyResult ); ``` ##### 【时间转换】com.ybear.ybutils.utils.time.DateTime ``` LogUtil.e("Time -> " + DateTime.parse( 1592474989 ) + " | " + DateTime.parse( DateTime.currentTimeMillis() ) + " | " + DateTime.parseOfList( DateTime.currentTimeMillis() ) + " | " + DateTime.now() + " | " + DateTime.nowOfList() ); ``` ##### 【对话框】com.ybear.ybcomponent.widget.dialog ``` //1.第一种调用方法 Dialog.with( this ) .setTitle("测试的标题") .setMessage("测试的内容") .setOnPositiveButtonListener((dialog, which) -> { dialog.dismiss(); ToastManage.get().showToast( this, "确定按钮" ); }).setOnNegativeButtonListener("关闭对话框", (dialog, which) -> { dialog.dismiss(); ToastManage.get().showToast( this, "关闭了对话框" ); }) //如果布局异常,可以使用 measure() 测量布局实际尺寸,否则一般情况下不需要使用 .measure() .create().show(); // 1.create(): 创建默认弹窗 // 2.create(Create(...)) 通过Create类创建 // 3.create(Int or View) 通过布局 or 控件创建 // 4.createOfFree(...) 根据布局/控件尺寸创建 // 5.createOfMatch(...) 最大尺寸展示Dialog //2.第二种调用方法(继承 DialogOption) class TestDialogOption(context: Context) : DialogOption( context ) { override fun onInit(context: Context): DialogConfig { val dialog = Dialog.with( context ) // //透明背景 // .transparentBackground() // //透明蒙层 // .transparentDimAmount() //默认蒙层 .defaultDimAmount() .animOfCenterAlpha() .setTitle("TestDialogOption title") .setMessage( "TestDialogOption content" ) .setOnPositiveButtonListener { dialog: DialogInterface, which: Int -> dialog.dismiss() }.setOnNegativeButtonListener( "关闭对话框" ) { dialog: DialogInterface, _: Int -> dialog.cancel() ToastManage.get().showToast(context, "关闭了对话框") } // //定义圆角 // dialog.setCornerRadius( Utils.dp2Px( context, 12 ) ) // dialog.setCornerRadiusTop(...) // dialog.setCornerRadiusBottom(...) // 这个必须设置,否则不会展示Dialog dialog.setDialogOption( this ) dialog.create() setCancelable( false ) setCanceledOnTouchOutside( false ) /* 监听器 */ // setOnShowListener(...) // setOnDismissListener(...) // setOnCancelListener(...) // setOnKeyListener(...) /* 常用方法 */ //Size 转换 // dp2Px( 10 ) // dp2Px( 10F ) //资源id转换 // getString( R.string.app_name ) // getColor( R.color.colorWhite ) //View 转 Bitmap // drawToBitmap( ImageView( context ) ) //创建一个Popup弹窗 // createPopupWindow( ImageView( context ) ) //点击的缩放效果 // ImageView( context ).setOnTouchListener(object : OnTouchListener { // override fun onTouch(v: View?, ev: MotionEvent?): Boolean { // //点击的缩放效果 // onTouchScaleAnimation( v, ev ) // return false // } // }) return dialog } } ``` ##### 【语言环境工具类】com.ybear.ybutils.utils.LocaleUtil ``` //改变当前语言环境 LocaleUtil.changeAppLanguage( this, Locale.ENGLISH.getLanguage() ); //当前语言。eg: zh, en, ko, jp... String curLanguage = LocaleUtil.getLanguage(); //当前语言(携带国家)。eg: zh-CN, zh-TW String curLanguageAndCountry = LocaleUtil.getLanguage( true ); //是否为ltr布局 boolean isLtr = LocaleUtil.isLtrLayout(); //是否为rtl布局 boolean isRtl = LocaleUtil.isRtlLayout(); LogUtil.d( "TAG_LocaleUtil", "curLanguage:%s, curLanguageAndCountry:%s, isLtr:%s, isRtl:%s", curLanguage, curLanguageAndCountry, isLtr, isRtl ); ``` //等待更新,SysUtil, AppUtil, Utils #### ybcomponent(控件类) ##### 【遮罩图片控件】com.ybear.ybcomponent.widget.MaskImageView ``` MaskImageView miv = findViewById( R.id.miv ); miv.setImageResource( R.drawable.ic_pause, 100, 100 ); miv.setForegroundMask( R.drawable.ic_next ); miv.setBackgroundMask( R.drawable.ic_flip_landscape ); miv.setBackgroundColor( Color.BLACK ); miv.setStartOfProgress( 0 ); miv.setDuration( 1000 ); //动画 CountDownTimer cdt = new CountDownTimer( 10000, 1000 ){ @Override public void onTick(long millisUntilFinished) { miv.post( () -> miv.startAnimOfProgress( 0, 100 ) ); LogUtil.d( "TAG", "MaskImageView -> " + millisUntilFinished ); } @Override public void onFinish() { } }; cdt.start(); ``` ##### 【侧滑控件】com.ybear.ybcomponent.widget.ItemSwipeLayout ``` //侧滑布局xml文件 ItemSwipeLayout isLayout = findViewById(R.id.main_isl_item_swipe); isLayout.setEnableSwipeDrag( true ); View[] views = new View[3]; int[] colors = { R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark }; for (int i = 0; i < views.length; i++) { TextView tv = new TextView( this ); tv.setText( ( "测试" + i ) ); tv.setBackgroundResource( colors[i] ); tv.setGravity(Gravity.CENTER); views[ i ] = tv; } isLayout.setSwipeViews( views ); isLayout.setEnableMultiSwipeDrag( false ); isLayout.setOnSwipeItemClickListener(new ItemSwipeLayout.OnSwipeItemClickListener() { @Override public void onClick(ItemSwipeLayout view, View childView, int position) { LogUtil.e("TAG", position + " " + childView); } }); ``` ##### 【底部菜单栏】com.ybear.ybcomponent.widget.menu.MenuView ``` MenuView menuView = findViewById(R.id.main_menu); menuView.setItemStyle(new ItemStyleBuild() .setIconSize( 60 ) .setTextColor(R.color.colorAccent) .setTextSelectColor(R.color.colorPrimary) ); menuView.setMenuItem( new MenuItem( getResources(), R.mipmap.ic_launcher, "选项1", R.mipmap.ic_launcher, "选中1" ), new MenuItem( getResources(), R.mipmap.ic_launcher, "", R.mipmap.ic_launcher, "选中2" ), new MenuItem( getResources(), R.mipmap.ic_launcher, "", R.mipmap.ic_launcher, "选中3" ), new MenuItem( getResources(), R.mipmap.ic_launcher, "选项4", R.mipmap.ic_launcher, "选中4" ) ); menuView.setEnableRedDot( true ); // menuView.setRedDotSize( 20 ); menuView.getRedDotLayout( 0 ).setTextSize( 12 ).setText( "9" ); menuView.setBackgroundColor( Color.WHITE ); menuView.setEnableTopLine( true ); menuView.setTopLineHeight(Utils.dp2Px( this, 2 ) ); menuView.setTopLineColor( Color.BLACK ); menuView.notifyMenuChanged(); menuView.setOnCheckedChangeListener((view, v, curPos, oldPos) -> { LogUtil.e("TAG", "id " + curPos); }); ```