desc:导入common lib

main
xiaowusky 2 years ago
parent 8a68fa6aaa
commit c40c005350

@ -47,6 +47,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-reactivestreams:2.4.1'
implementation project(path: ':library-ijkplayer')
implementation project(path: ':library-serialPort')
implementation project(path: ':library-common')
//excel
implementation rootProject.ext.dependencies.jxl
//

@ -0,0 +1 @@
/build

@ -0,0 +1,72 @@
apply from: "${rootProject.rootDir}/buildCommon/commonLibConfig.gradle"
project.ext.setLibDefaultConfig project
android {
defaultConfig {
ndk {
abiFilters 'armeabi', 'armeabi-v7a', 'x86'
}
}
sourceSets {
main {
jni.srcDirs = []
jniLibs.srcDirs = ['libs']
}
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField 'String', 'BASE_URL_CONFIG_PATH', '"baseUrl.properties"'
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField 'String', 'BASE_URL_CONFIG_PATH', '"baseUrl.properties"'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
implementation 'com.airbnb.android:lottie:5.0.2'
implementation(rootProject.ext.dependencies.mmkv)
// kotlin
implementation(rootProject.ext.dependencies.kotlin)
implementation(rootProject.ext.dependencies.kotlin_android)
// okhttp
implementation(rootProject.ext.dependencies.okhttp)
// okhttp
implementation(rootProject.ext.dependencies.okhttp_logger)
// retrofit
implementation rootProject.ext.dependencies.retrofit
// retrofitgson
implementation rootProject.ext.dependencies.converter_gson
// rxjava
implementation rootProject.ext.dependencies.rxjava
// rxAndroid
implementation rootProject.ext.dependencies.rxandroid
// retrofitrxjava
implementation rootProject.ext.dependencies.retrofit_rxjava
//luban
implementation rootProject.ext.dependencies.luban
// glide
implementation rootProject.ext.dependencies.glide
// glideOkHttp3
implementation rootProject.ext.dependencies.gilde_integration
// lifecycle_process
implementation rootProject.ext.dependencies.lifecycle_process
// glide
kapt rootProject.ext.dependencies.annotationProcessor
}

@ -0,0 +1,940 @@
# compiler: R8
# compiler_version: 3.0.73
# pg_map_id: 11aaacb
# common_typos_disable
com.common.commonlib.CommonApplication -> com.common.commonlib.CommonApplication:
# {"id":"sourceFile","fileName":"CommonApplication.kt"}
1:1:void <init>():16:16 -> <init>
1:1:void onCreate():18:18 -> onCreate
2:2:void intLibs(android.content.Context):26:26 -> onCreate
2:2:void onCreate():19 -> onCreate
3:3:void initNet():32:32 -> onCreate
3:3:void intLibs(android.content.Context):28 -> onCreate
3:3:void onCreate():19 -> onCreate
com.common.commonlib.image.module.OkHttpLibraryGlideModule -> com.common.commonlib.image.module.OkHttpLibraryGlideModule:
# {"id":"sourceFile","fileName":"OkHttpLibraryGlideModule.kt"}
1:1:void <init>():19:19 -> <init>
1:1:void registerComponents(android.content.Context,com.bumptech.glide.Glide,com.bumptech.glide.Registry):23:23 -> registerComponents
com.common.commonlib.net.manager.RequestManager -> a.a:
# {"id":"sourceFile","fileName":"RequestManager.kt"}
com.common.commonlib.net.manager.RequestManager INSTANCE -> a
1:17:void <clinit>():22:38 -> <clinit>
1:1:void <init>():20:20 -> <init>
1:1:void loggingInterceptor$lambda-0(java.lang.String):39:39 -> a
2:59:void com.common.commonlib.log.Logger.d(java.lang.String,java.lang.Object):0:57 -> a
2:59:void loggingInterceptor$lambda-0(java.lang.String):39 -> a
60:138:void com.common.commonlib.log.Logger.d(java.lang.String,java.lang.Object,boolean):0:78 -> a
60:138:void com.common.commonlib.log.Logger.d(java.lang.String,java.lang.Object):57 -> a
60:138:void loggingInterceptor$lambda-0(java.lang.String):39 -> a
com.common.commonlib.net.bean.BaseResponse -> com.common.commonlib.net.bean.BaseResponse:
# {"id":"sourceFile","fileName":"BaseResponse.kt"}
1:1:void <init>():12:12 -> <init>
com.common.commonlib.net.bean.BaseResponse$Creator -> com.common.commonlib.net.bean.BaseResponse$a:
# {"id":"sourceFile","fileName":"BaseResponse.kt"}
1:1:com.common.commonlib.net.bean.BaseResponse createFromParcel(android.os.Parcel):0:0 -> createFromParcel
1:1:java.lang.Object createFromParcel(android.os.Parcel):0 -> createFromParcel
1:1:com.common.commonlib.net.bean.BaseResponse[] newArray(int):0:0 -> newArray
1:1:java.lang.Object[] newArray(int):0 -> newArray
com.common.commonlib.utils.BaseUtils -> com.common.commonlib.utils.BaseUtils:
# {"id":"sourceFile","fileName":"BaseUtils.kt"}
1:1:void <init>():10:10 -> <init>
1:1:void enableBasicLog():39:39 -> enableBasicLog
2:2:void com.common.commonlib.net.manager.RequestManager.openBasicLog():102:102 -> enableBasicLog
2:2:void enableBasicLog():39 -> enableBasicLog
1:1:void enableFullLog():32:32 -> enableFullLog
2:2:void com.common.commonlib.net.manager.RequestManager.openFullLog():95:95 -> enableFullLog
2:2:void enableFullLog():32 -> enableFullLog
1:1:void enableHeadersLog():46:46 -> enableHeadersLog
2:2:void com.common.commonlib.net.manager.RequestManager.openHeadLog():109:109 -> enableHeadersLog
2:2:void enableHeadersLog():46 -> enableHeadersLog
1:1:java.lang.String getExternalStorageDirectory(android.content.Context):25:25 -> getExternalStorageDirectory
1:1:java.lang.String getExternalStoragePath(android.content.Context):21:21 -> getExternalStoragePath
1:1:boolean isListEmpty(java.util.List):14:14 -> isListEmpty
com.common.commonlib.utils.DateUtils -> com.common.commonlib.utils.DateUtils:
# {"id":"sourceFile","fileName":"DateUtils.kt"}
1:1:void <init>():10:10 -> <init>
1:18:java.lang.String getDurationTimeByInt(int):61:78 -> getDurationTimeByInt
1:1:java.util.Date getNow():42:42 -> getNow
1:2:java.lang.String getNowTimeFormat(java.lang.String):47:48 -> getNowTimeFormat
com.common.commonlib.utils.DisplayUtils -> com.common.commonlib.utils.DisplayUtils:
# {"id":"sourceFile","fileName":"DisplayUtils.kt"}
1:1:void <init>():8:8 -> <init>
1:1:int dp2px(android.content.Context,float):14:14 -> dp2px
1:1:int px2dp(android.content.Context,float):22:22 -> px2dp
com.common.commonlib.utils.FTPUtils -> com.common.commonlib.utils.FTPUtils:
# {"id":"sourceFile","fileName":"FTPUtils.kt"}
1:1:void <init>():17:17 -> <init>
1:5:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):103:107 -> ftpDown
6:6:int org.apache.commons.net.ftp.FTP.getReplyCode():670:670 -> ftpDown
6:6:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):108 -> ftpDown
7:7:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):109:109 -> ftpDown
8:8:void org.apache.commons.net.ftp.FTPClient.setBufferSize(int):3451:3451 -> ftpDown
8:8:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):110 -> ftpDown
9:9:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):111:111 -> ftpDown
10:10:void org.apache.commons.net.ftp.FTP.setControlEncoding(java.lang.String):418:418 -> ftpDown
10:10:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):111 -> ftpDown
11:15:void org.apache.commons.net.ftp.FTPClient.enterLocalPassiveMode():1233:1237 -> ftpDown
11:15:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):112 -> ftpDown
16:19:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):113:116 -> ftpDown
20:20:int org.apache.commons.net.ftp.FTP.quit():864:864 -> ftpDown
20:20:boolean org.apache.commons.net.ftp.FTPClient.logout():1109 -> ftpDown
20:20:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):117 -> ftpDown
21:34:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):118:131 -> ftpDown
35:42:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):124:131 -> ftpDown
43:44:com.common.commonlib.utils.FTPUtils$RESULT ftpDown(java.lang.String,int,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):130:131 -> ftpDown
1:6:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):51:56 -> ftpUpload
7:7:int org.apache.commons.net.ftp.FTP.getReplyCode():670:670 -> ftpUpload
7:7:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):57 -> ftpUpload
8:8:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):58:58 -> ftpUpload
9:9:void org.apache.commons.net.ftp.FTPClient.setBufferSize(int):3451:3451 -> ftpUpload
9:9:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):59 -> ftpUpload
10:10:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):60:60 -> ftpUpload
11:11:void org.apache.commons.net.ftp.FTP.setControlEncoding(java.lang.String):418:418 -> ftpUpload
11:11:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):60 -> ftpUpload
12:12:int org.apache.commons.net.ftp.FTP.type(int):1081:1081 -> ftpUpload
12:12:boolean org.apache.commons.net.ftp.FTPClient.setFileType(int):1519 -> ftpUpload
12:12:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):61 -> ftpUpload
13:13:int org.apache.commons.net.ftp.FTP.sendCommand(org.apache.commons.net.ftp.FTPCmd,java.lang.String):608:608 -> ftpUpload
13:13:int org.apache.commons.net.ftp.FTP.type(int):1081 -> ftpUpload
13:13:boolean org.apache.commons.net.ftp.FTPClient.setFileType(int):1519 -> ftpUpload
13:13:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):61 -> ftpUpload
14:16:boolean org.apache.commons.net.ftp.FTPClient.setFileType(int):1519:1521 -> ftpUpload
14:16:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):61 -> ftpUpload
17:21:void org.apache.commons.net.ftp.FTPClient.enterLocalPassiveMode():1233:1237 -> ftpUpload
17:21:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):62 -> ftpUpload
22:22:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):63:63 -> ftpUpload
23:23:boolean org.apache.commons.net.ftp.FTPClient.storeFile(java.lang.String,java.io.InputStream):1976:1976 -> ftpUpload
23:23:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):64 -> ftpUpload
24:36:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):65:77 -> ftpUpload
37:44:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):70:77 -> ftpUpload
45:48:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):74:77 -> ftpUpload
49:50:com.common.commonlib.utils.FTPUtils$RESULT ftpUpload(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String):76:77 -> ftpUpload
com.common.commonlib.utils.FTPUtils$RESULT -> com.common.commonlib.utils.FTPUtils$RESULT:
# {"id":"sourceFile","fileName":"FTPUtils.kt"}
1:1:void <clinit>():31:31 -> <clinit>
1:1:void <init>(java.lang.String,int):31:31 -> <init>
com.common.commonlib.utils.MMKVUtils -> com.common.commonlib.utils.MMKVUtils:
# {"id":"sourceFile","fileName":"MMKVUtils.kt"}
1:1:void <init>():12:12 -> <init>
1:1:boolean getBoolean(java.lang.String,java.lang.Object[]):43:43 -> getBoolean
1:1:byte[] getByteArray(java.lang.String,java.lang.Object[]):39:39 -> getByteArray
1:1:float getFloat(java.lang.String,java.lang.Object[]):47:47 -> getFloat
1:1:int getInt(java.lang.String,java.lang.Object[]):51:51 -> getInt
1:4:com.tencent.mmkv.MMKV getKV(java.lang.Object[]):67:70 -> getKV
5:5:com.tencent.mmkv.MMKV getKV(java.lang.Object[]):69:69 -> getKV
6:6:com.tencent.mmkv.MMKV getKV(java.lang.Object[]):68:68 -> getKV
1:1:long getLong(java.lang.String,java.lang.Object[]):55:55 -> getLong
1:1:java.lang.String getString(java.lang.String,java.lang.Object[]):59:59 -> getString
1:1:java.util.Set getStringSet(java.lang.String,java.lang.Object[]):63:63 -> getStringSet
1:11:void put(java.lang.String,java.lang.Object,java.lang.Object[]):23:33 -> put
12:12:void com.common.commonlib.log.Logger.e(java.lang.String,java.lang.Object):0:0 -> put
12:12:void put(java.lang.String,java.lang.Object,java.lang.Object[]):33 -> put
13:109:void com.common.commonlib.log.Logger.e(java.lang.String,java.lang.Object,boolean):0:96 -> put
13:109:void com.common.commonlib.log.Logger.e(java.lang.String,java.lang.Object):65 -> put
13:109:void put(java.lang.String,java.lang.Object,java.lang.Object[]):33 -> put
com.common.commonlib.utils.PhotoUtils -> com.common.commonlib.utils.PhotoUtils:
# {"id":"sourceFile","fileName":"PhotoUtils.kt"}
1:1:void <init>():18:18 -> <init>
1:5:void compressPicture(android.content.Context,java.io.File,java.lang.String,top.zibin.luban.OnCompressListener):73:77 -> compressPicture
1:10:java.io.File generatePhotoFileByTime(android.content.Context,java.lang.String):25:34 -> generatePhotoFileByTime
1:6:java.lang.String getImageParentPath(android.content.Context,java.lang.String):54:59 -> getImageParentPath
1:9:java.lang.String initPicPath(android.content.Context,java.lang.String):39:47 -> initPicPath
1:1:void tans2PNGSave(java.io.File,java.lang.String,com.common.commonlib.utils.PhotoUtils$TransformCallBack):86:86 -> tans2PNGSave
com.common.commonlib.utils.PhotoUtils$TransformCallBack -> com.common.commonlib.utils.PhotoUtils$TransformCallBack:
# {"id":"sourceFile","fileName":"PhotoUtils.kt"}
com.common.commonlib.utils.PhotoUtils$tans2PNGSave$1 -> com.common.commonlib.utils.PhotoUtils$tans2PNGSave$1:
# {"id":"sourceFile","fileName":"PhotoUtils.kt"}
1:12:java.lang.Object invokeSuspend(java.lang.Object):86:97 -> invokeSuspend
13:22:java.lang.Object invokeSuspend(java.lang.Object):95:104 -> invokeSuspend
23:30:java.lang.Object invokeSuspend(java.lang.Object):97:104 -> invokeSuspend
com.common.commonlib.utils.PhotoUtils$tans2PNGSave$1$1 -> com.common.commonlib.utils.PhotoUtils$tans2PNGSave$1$1:
# {"id":"sourceFile","fileName":"PhotoUtils.kt"}
1:4:java.lang.Object invokeSuspend(java.lang.Object):100:103 -> invokeSuspend
com.common.commonlib.utils.SpManager -> com.common.commonlib.utils.SpManager:
# {"id":"sourceFile","fileName":"SpManager.kt"}
1:1:void <init>():13:13 -> <init>
1:1:boolean getBoolean(java.lang.String):30:30 -> getBoolean
1:1:int getInt(java.lang.String):39:39 -> getInt
1:1:long getLong(java.lang.String):57:57 -> getLong
1:1:java.lang.String getString(java.lang.String):48:48 -> getString
1:2:void init(android.content.Context,java.lang.String,int):20:21 -> init
1:2:void putBoolean(java.lang.String,boolean):25:26 -> putBoolean
1:2:void putInt(java.lang.String,int):34:35 -> putInt
1:2:void putLong(java.lang.String,long):52:53 -> putLong
1:2:void putString(java.lang.String,java.lang.String):43:44 -> putString
com.common.commonlib.utils.StringUtils -> com.common.commonlib.utils.StringUtils:
# {"id":"sourceFile","fileName":"StringUtils.kt"}
1:1:void <init>():8:8 -> <init>
1:4:java.util.List getListByString(java.lang.String):29:32 -> getListByString
1:6:java.lang.String getStringSeparateByLine(java.util.List):14:19 -> getStringSeparateByLine
com.common.commonlib.view.CommonTitleView -> com.common.commonlib.view.CommonTitleView:
android.content.Context mContext -> a
java.lang.String title -> g
int bgColor -> o
int fixedHeight -> i
int showStyle -> f
android.widget.ImageView ivRight -> d
android.widget.ImageView ivLeft -> b
com.common.commonlib.view.CommonTitleView$RightIconListener rightIconListener -> q
android.view.View rlRightHotZone -> k
android.view.View rlLeftHotZone -> j
android.widget.TextView tv_title -> c
android.view.View rlEditZone -> l
android.widget.TextView tv_edit -> e
android.graphics.drawable.Drawable leftDrawable -> m
com.common.commonlib.view.CommonTitleView$EditListener editListener -> r
boolean isFixedHeight -> h
android.graphics.drawable.Drawable rightDrawable -> n
com.common.commonlib.view.CommonTitleView$LeftIconListener leftIconListener -> p
1:1:void <init>(android.content.Context):52:52 -> <init>
2:2:void <init>(android.content.Context,android.util.AttributeSet):56:56 -> <init>
3:5:void <init>(android.content.Context,android.util.AttributeSet,int):60:62 -> <init>
1:10:void initAttrs(android.util.AttributeSet):89:98 -> a
1:10:void init(android.util.AttributeSet):66 -> a
11:25:void init(android.util.AttributeSet):67:81 -> a
26:86:void initStyle():114:174 -> a
26:86:void init(android.util.AttributeSet):83 -> a
87:96:void initStyle():156:165 -> a
87:96:void init(android.util.AttributeSet):83 -> a
97:102:void initStyle():140:145 -> a
97:102:void init(android.util.AttributeSet):83 -> a
103:124:void initStyle():132:153 -> a
103:124:void init(android.util.AttributeSet):83 -> a
125:130:void initStyle():124:129 -> a
125:130:void init(android.util.AttributeSet):83 -> a
131:136:void initStyle():116:121 -> a
131:136:void init(android.util.AttributeSet):83 -> a
137:137:void init(android.util.AttributeSet):84:84 -> a
1:3:int getEditState():234:236 -> getEditState
1:12:void onClick(android.view.View):187:198 -> onClick
1:5:void onMeasure(int,int):105:109 -> onMeasure
1:1:void setEditListener(com.common.commonlib.view.CommonTitleView$EditListener):230:230 -> setEditListener
1:5:void setEditText(int):245:249 -> setEditText
1:1:void setLeftIconListener(com.common.commonlib.view.CommonTitleView$LeftIconListener):210:210 -> setLeftIconListener
1:1:void setRightIconListener(com.common.commonlib.view.CommonTitleView$RightIconListener):220:220 -> setRightIconListener
1:2:void setTitle(java.lang.String):180:181 -> setTitle
com.common.commonlib.view.MikeView -> com.common.commonlib.view.MikeView:
int mImageW -> n
int mImageH -> o
int mCurrentPosition -> l
int mBackgroundRound -> m
int SCREEN_WIDTH -> j
int mVoiceRectStart -> k
int SCREEN_HEIGHT -> i
int mVoiceRectMarginTop -> d
int mVoiceRectH -> b
int mInterval -> c
android.graphics.Bitmap mVoiceRecording -> h
int mVoiceRectW -> a
android.graphics.Paint mBackgroundPaint -> g
android.graphics.Paint mVoiceRectPaint -> f
android.graphics.Paint mImagePaint -> e
int mMarginTop -> p
int mTextMarginBottom -> q
1:1:void <init>(android.content.Context):55:55 -> <init>
2:2:void <init>(android.content.Context,android.util.AttributeSet):59:59 -> <init>
3:3:void <init>(android.content.Context,android.util.AttributeSet,int):63:63 -> <init>
4:39:void <init>(android.content.Context,android.util.AttributeSet,int):29:64 -> <init>
1:22:void init():68:89 -> a
23:31:android.graphics.Bitmap scaleBitmap(android.graphics.Bitmap,int,int):149:157 -> a
23:31:void init():89 -> a
32:34:void init():89:91 -> a
1:21:void onDraw(android.graphics.Canvas):96:116 -> onDraw
1:2:void setIndex(int):128:129 -> setIndex
3:3:void setIndex(double):134:134 -> setIndex
com.common.commonlib.view.SlideRecyclerView -> com.common.commonlib.view.SlideRecyclerView:
android.view.VelocityTracker mVelocityTracker -> a
int mPosition -> j
float mFirstY -> g
int mMenuViewWidth -> k
android.widget.Scroller mScroller -> d
float mFirstX -> f
float mLastX -> e
android.view.ViewGroup mFlingView -> i
boolean mIsSlide -> h
int mTouchSlop -> b
android.graphics.Rect mTouchFrame -> c
1:1:void <init>(android.content.Context):41:41 -> <init>
2:2:void <init>(android.content.Context,android.util.AttributeSet):45:45 -> <init>
3:5:void <init>(android.content.Context,android.util.AttributeSet,int):49:51 -> <init>
1:4:void releaseVelocity():164:167 -> a
1:2:void closeMenu():213:214 -> closeMenu
1:3:void computeScroll():202:204 -> computeScroll
1:2:boolean onInterceptTouchEvent(android.view.MotionEvent):56:57 -> onInterceptTouchEvent
3:6:void obtainVelocity(android.view.MotionEvent):172:175 -> onInterceptTouchEvent
3:6:boolean onInterceptTouchEvent(android.view.MotionEvent):58 -> onInterceptTouchEvent
7:48:boolean onInterceptTouchEvent(android.view.MotionEvent):59:100 -> onInterceptTouchEvent
49:91:boolean onInterceptTouchEvent(android.view.MotionEvent):61:103 -> onInterceptTouchEvent
1:2:boolean onTouchEvent(android.view.MotionEvent):108:109 -> onTouchEvent
3:6:void obtainVelocity(android.view.MotionEvent):172:175 -> onTouchEvent
3:6:boolean onTouchEvent(android.view.MotionEvent):110 -> onTouchEvent
7:56:boolean onTouchEvent(android.view.MotionEvent):111:160 -> onTouchEvent
1:14:int pointToPosition(int,int):179:192 -> pointToPosition
com.common.commonlib.view.SpaceItemDecoration -> com.common.commonlib.view.SpaceItemDecoration:
1:1:void <init>():16:16 -> <init>
1:7:void getItemOffsets(android.graphics.Rect,android.view.View,androidx.recyclerview.widget.RecyclerView,androidx.recyclerview.widget.RecyclerView$State):29:35 -> getItemOffsets
com.common.commonlib.view.activity.BaseActivity -> com.common.commonlib.view.activity.BaseActivity:
1:1:void <init>():18:18 -> <init>
1:1:void onCreate(android.os.Bundle):22:22 -> onCreate
2:7:void initStatusBar():27:32 -> onCreate
2:7:void onCreate(android.os.Bundle):23 -> onCreate
com.common.commonlib.view.activity.PermissionCompatActivity -> com.common.commonlib.view.activity.PermissionCompatActivity:
android.app.Dialog mPermissionDialog -> a
1:1:void <init>():35:35 -> <init>
1:1:void onCreate(android.os.Bundle):44:44 -> onCreate
1:6:void onRequestPermissionsResult(int,java.lang.String[],int[]):81:86 -> onRequestPermissionsResult
7:28:void showPermissionDialog():102:123 -> onRequestPermissionsResult
7:28:void onRequestPermissionsResult(int,java.lang.String[],int[]):93 -> onRequestPermissionsResult
29:29:void onRequestPermissionsResult(int,java.lang.String[],int[]):96:96 -> onRequestPermissionsResult
1:1:void onRestart():49:49 -> onRestart
com.common.commonlib.view.activity.PermissionCompatActivity$1 -> b.a:
com.common.commonlib.view.activity.PermissionCompatActivity this$0 -> a
1:1:void <init>(com.common.commonlib.view.activity.PermissionCompatActivity):114:114 -> <init>
1:1:void onClick(android.content.DialogInterface,int):118:118 -> onClick
2:3:void com.common.commonlib.view.activity.PermissionCompatActivity.cancelPermissionDialog():128:129 -> onClick
2:3:void com.common.commonlib.view.activity.PermissionCompatActivity.access$000(com.common.commonlib.view.activity.PermissionCompatActivity):35 -> onClick
2:3:void onClick(android.content.DialogInterface,int):118 -> onClick
4:4:void onClick(android.content.DialogInterface,int):119:119 -> onClick
com.common.commonlib.view.activity.PermissionCompatActivity$2 -> b.b:
com.common.commonlib.view.activity.PermissionCompatActivity this$0 -> a
1:1:void <init>(com.common.commonlib.view.activity.PermissionCompatActivity):105:105 -> <init>
1:1:void onClick(android.content.DialogInterface,int):108:108 -> onClick
2:3:void com.common.commonlib.view.activity.PermissionCompatActivity.cancelPermissionDialog():128:129 -> onClick
2:3:void com.common.commonlib.view.activity.PermissionCompatActivity.access$000(com.common.commonlib.view.activity.PermissionCompatActivity):35 -> onClick
2:3:void onClick(android.content.DialogInterface,int):108 -> onClick
4:7:void onClick(android.content.DialogInterface,int):109:112 -> onClick
com.common.commonlib.view.fragment.BaseFragment -> com.common.commonlib.view.fragment.BaseFragment:
1:1:void <init>():10:10 -> <init>
1:2:void onAttach(android.content.Context):15:16 -> onAttach
com.common.commonlib.view.fragment.BaseNavigationFragment -> com.common.commonlib.view.fragment.BaseNavigationFragment:
# {"id":"sourceFile","fileName":"BaseNavigationFragment.kt"}
android.view.View lastView -> b
boolean isNavigationViewInit -> a
1:1:void <init>():8:8 -> <init>
1:1:boolean getIsNavigationViewInit():53:53 -> getIsNavigationViewInit
1:10:android.view.View onCreateView(android.view.LayoutInflater,android.view.ViewGroup,android.os.Bundle):16:25 -> onCreateView
1:6:void onViewCreated(android.view.View,android.os.Bundle):29:34 -> onViewCreated
1:1:void setIsNavigationViewInit(boolean):57:57 -> setIsNavigationViewInit
com.common.commonlib.view.viewpagerlayoutmanager.AutoPlayRecyclerView -> com.common.commonlib.view.viewpagerlayoutmanager.AutoPlayRecyclerView:
com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper autoPlaySnapHelper -> a
1:1:void <init>(android.content.Context):17:17 -> <init>
2:2:void <init>(android.content.Context,android.util.AttributeSet):21:21 -> <init>
3:8:void <init>(android.content.Context,android.util.AttributeSet,int):25:30 -> <init>
1:9:boolean dispatchTouchEvent(android.view.MotionEvent):35:43 -> dispatchTouchEvent
10:12:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.start():91:93 -> dispatchTouchEvent
10:12:boolean dispatchTouchEvent(android.view.MotionEvent):44 -> dispatchTouchEvent
13:13:boolean dispatchTouchEvent(android.view.MotionEvent):38:38 -> dispatchTouchEvent
14:16:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.pause():84:86 -> dispatchTouchEvent
14:16:boolean dispatchTouchEvent(android.view.MotionEvent):39 -> dispatchTouchEvent
1:2:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):60:61 -> setLayoutManager
3:3:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):38:38 -> setLayoutManager
3:3:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):61 -> setLayoutManager
4:5:void com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper.destroyCallbacks():155:156 -> setLayoutManager
4:5:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.destroyCallbacks():76 -> setLayoutManager
4:5:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):42 -> setLayoutManager
4:5:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):61 -> setLayoutManager
6:8:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.destroyCallbacks():77:79 -> setLayoutManager
6:8:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):42 -> setLayoutManager
6:8:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):61 -> setLayoutManager
9:12:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):44:47 -> setLayoutManager
9:12:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):61 -> setLayoutManager
13:17:void com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper.setupCallbacks():144:148 -> setLayoutManager
13:17:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):49 -> setLayoutManager
13:17:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):61 -> setLayoutManager
18:21:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):50:53 -> setLayoutManager
18:21:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):61 -> setLayoutManager
22:27:void com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager.setInfinite(boolean):822:827 -> setLayoutManager
22:27:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):56 -> setLayoutManager
22:27:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):61 -> setLayoutManager
28:40:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):58:70 -> setLayoutManager
28:40:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):61 -> setLayoutManager
41:41:void com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper.setupCallbacks():145:145 -> setLayoutManager
41:41:void com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):49 -> setLayoutManager
41:41:void setLayoutManager(androidx.recyclerview.widget.RecyclerView$LayoutManager):61 -> setLayoutManager
com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper -> com.common.commonlib.view.viewpagerlayoutmanager.b:
android.os.Handler handler -> e
java.lang.Runnable autoPlayRunnable -> g
int direction -> i
int timeInterval -> f
boolean runnableAdded -> h
1:6:void <init>(int,int):28:33 -> <init>
1:1:void checkDirection(int):109:109 -> a
1:1:void checkTimeInterval(int):114:114 -> b
com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper$1 -> com.common.commonlib.view.viewpagerlayoutmanager.a:
com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper this$0 -> b
androidx.recyclerview.widget.RecyclerView$LayoutManager val$layoutManager -> a
1:1:void <init>(com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper,androidx.recyclerview.widget.RecyclerView$LayoutManager):58:58 -> <init>
1:4:void run():61:64 -> run
5:5:int com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.access$000(com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper):16:16 -> run
5:5:void run():65 -> run
6:8:void run():64:66 -> run
9:9:android.os.Handler com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.access$300(com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper):16:16 -> run
9:9:void run():66 -> run
10:10:java.lang.Runnable com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.access$100(com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper):16:16 -> run
10:10:void run():66 -> run
11:11:int com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper.access$200(com.common.commonlib.view.viewpagerlayoutmanager.AutoPlaySnapHelper):16:16 -> run
11:11:void run():66 -> run
12:12:void run():66:66 -> run
com.common.commonlib.view.viewpagerlayoutmanager.CarouselLayoutManager -> com.common.commonlib.view.viewpagerlayoutmanager.CarouselLayoutManager:
float getDistanceRatio() -> f
1:1:float setInterval():83:83 -> k
com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper -> com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper:
androidx.recyclerview.widget.RecyclerView$OnScrollListener mScrollListener -> d
android.widget.Scroller mGravityScroller -> b
androidx.recyclerview.widget.RecyclerView mRecyclerView -> a
boolean snapToCenter -> c
1:14:void <init>():9:22 -> <init>
1:1:boolean access$002(com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper,boolean):9:9 -> a
2:5:int com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager.getOffsetToCenter():798:801 -> a
2:5:void snapToCenterView(com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager,com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$OnPageChangeListener):124 -> a
6:13:void snapToCenterView(com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager,com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$OnPageChangeListener):126:133 -> a
1:5:boolean onFling(int,int):60:64 -> onFling
6:6:boolean com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager.getInfinite():818:818 -> onFling
6:6:boolean onFling(int,int):69 -> onFling
7:30:boolean onFling(int,int):69:92 -> onFling
com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper$1 -> com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper$a:
com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper this$0 -> b
boolean mScrolled -> a
1:3:void <init>(com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper):23:25 -> <init>
1:13:void onScrollStateChanged(androidx.recyclerview.widget.RecyclerView,int):29:41 -> onScrollStateChanged
14:14:boolean com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper.access$000(com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper):9:9 -> onScrollStateChanged
14:14:void onScrollStateChanged(androidx.recyclerview.widget.RecyclerView,int):41 -> onScrollStateChanged
15:18:void onScrollStateChanged(androidx.recyclerview.widget.RecyclerView,int):42:45 -> onScrollStateChanged
1:1:void onScrolled(androidx.recyclerview.widget.RecyclerView,int,int):53:53 -> onScrolled
com.common.commonlib.view.viewpagerlayoutmanager.CircleLayoutManager -> com.common.commonlib.view.viewpagerlayoutmanager.CircleLayoutManager:
int radius -> q
float getDistanceRatio() -> f
float maxRemoveOffset() -> i
float minRemoveOffset() -> j
float setInterval() -> k
1:1:void setUp():181:181 -> l
com.common.commonlib.view.viewpagerlayoutmanager.CircleScaleLayoutManager -> com.common.commonlib.view.viewpagerlayoutmanager.CircleScaleLayoutManager:
int radius -> q
float getDistanceRatio() -> f
float maxRemoveOffset() -> i
float minRemoveOffset() -> j
float setInterval() -> k
1:1:void setUp():194:194 -> l
com.common.commonlib.view.viewpagerlayoutmanager.GalleryLayoutManager -> com.common.commonlib.view.viewpagerlayoutmanager.GalleryLayoutManager:
float getDistanceRatio() -> f
1:1:float setInterval():138:138 -> k
com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper -> com.common.commonlib.view.viewpagerlayoutmanager.e:
androidx.recyclerview.widget.RecyclerView$LayoutManager mLayoutManager -> a
1:1:void <init>(androidx.recyclerview.widget.RecyclerView$LayoutManager,com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper$1):26:26 -> <init>
2:2:void <init>(androidx.recyclerview.widget.RecyclerView$LayoutManager):40:40 -> <init>
3:6:void <init>(androidx.recyclerview.widget.RecyclerView$LayoutManager):38:41 -> <init>
int getDecoratedMeasurement(android.view.View) -> a
int getStartAfterPadding() -> a
int getDecoratedMeasurementInOther(android.view.View) -> b
int getTotalSpace() -> b
int getTotalSpaceInOther() -> c
com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper$1 -> com.common.commonlib.view.viewpagerlayoutmanager.c:
1:1:void <init>(androidx.recyclerview.widget.RecyclerView$LayoutManager):233:233 -> <init>
1:1:int getStartAfterPadding():246:246 -> a
2:3:int getDecoratedMeasurement(android.view.View):252:253 -> a
1:2:int getDecoratedMeasurementInOther(android.view.View):260:261 -> b
3:4:int getTotalSpace():293:294 -> b
1:2:int getTotalSpaceInOther():299:300 -> c
com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper$2 -> com.common.commonlib.view.viewpagerlayoutmanager.d:
1:1:void <init>(androidx.recyclerview.widget.RecyclerView$LayoutManager):327:327 -> <init>
1:1:int getStartAfterPadding():340:340 -> a
2:3:int getDecoratedMeasurement(android.view.View):346:347 -> a
1:2:int getDecoratedMeasurementInOther(android.view.View):354:355 -> b
3:4:int getTotalSpace():387:388 -> b
1:2:int getTotalSpaceInOther():393:394 -> c
com.common.commonlib.view.viewpagerlayoutmanager.PageSnapHelper -> com.common.commonlib.view.viewpagerlayoutmanager.PageSnapHelper:
1:1:void <init>():5:5 -> <init>
1:5:boolean onFling(int,int):9:13 -> onFling
6:6:boolean com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager.getInfinite():818:818 -> onFling
6:6:boolean onFling(int,int):18 -> onFling
7:28:boolean onFling(int,int):18:39 -> onFling
com.common.commonlib.view.viewpagerlayoutmanager.RotateLayoutManager -> com.common.commonlib.view.viewpagerlayoutmanager.RotateLayoutManager:
float getDistanceRatio() -> f
1:1:float setInterval():94:94 -> k
com.common.commonlib.view.viewpagerlayoutmanager.ScaleLayoutManager -> com.common.commonlib.view.viewpagerlayoutmanager.ScaleLayoutManager:
float getDistanceRatio() -> f
1:1:float setInterval():109:109 -> k
com.common.commonlib.view.viewpagerlayoutmanager.ScrollHelper -> com.common.commonlib.view.viewpagerlayoutmanager.f:
1:5:void smoothScrollToPosition(androidx.recyclerview.widget.RecyclerView,com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager,int):10:14 -> a
com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager -> com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager:
boolean mRecycleChildrenOnDetach -> n
int mPendingScrollPosition -> l
boolean mInfinite -> o
boolean mReverseLayout -> i
float mInterval -> g
boolean mShouldReverseLayout -> j
com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper mOrientationHelper -> f
int mOrientation -> h
boolean mSmoothScrollbarEnabled -> k
float mOffset -> e
int mSpaceInOther -> d
com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$SavedState mPendingSavedState -> m
int mDecoratedMeasurementInOther -> b
int mSpaceMain -> c
int mDecoratedMeasurement -> a
int mDistanceToBottom -> p
1:5:android.view.View getMeasureView(androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State,int):386:390 -> a
6:14:int computeScrollExtent():520:528 -> a
15:36:int scrollBy(int,androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State):560:581 -> a
37:38:void layoutItems(androidx.recyclerview.widget.RecyclerView$Recycler):590:591 -> a
37:38:int scrollBy(int,androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State):584 -> a
39:44:int getOffsetToPosition(int):805:810 -> a
1:7:int computeScrollOffset():506:512 -> b
8:19:float getOffsetOfRightAdapterPosition():778:789 -> b
8:19:int computeScrollOffset():515 -> b
20:20:int computeScrollOffset():516:516 -> b
1:9:int computeScrollRange():532:540 -> c
1:1:boolean canScrollHorizontally():200:200 -> canScrollHorizontally
1:1:boolean canScrollVertically():208:208 -> canScrollVertically
1:1:int computeHorizontalScrollExtent(androidx.recyclerview.widget.RecyclerView$State):487:487 -> computeHorizontalScrollExtent
1:1:int computeHorizontalScrollOffset(androidx.recyclerview.widget.RecyclerView$State):477:477 -> computeHorizontalScrollOffset
1:1:int computeHorizontalScrollRange(androidx.recyclerview.widget.RecyclerView$State):497:497 -> computeHorizontalScrollRange
1:1:int computeVerticalScrollExtent(androidx.recyclerview.widget.RecyclerView$State):492:492 -> computeVerticalScrollExtent
1:1:int computeVerticalScrollOffset(androidx.recyclerview.widget.RecyclerView$State):482:482 -> computeVerticalScrollOffset
1:1:int computeVerticalScrollRange(androidx.recyclerview.widget.RecyclerView$State):502:502 -> computeVerticalScrollRange
1:14:int getCurrentPosition():726:739 -> d
1:2:int getCurrentPositionOffset():769:770 -> e
1:2:void ensureLayoutState():445:446 -> ensureLayoutState
3:3:com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper.createVerticalHelper(androidx.recyclerview.widget.RecyclerView$LayoutManager):327:327 -> ensureLayoutState
3:3:com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper.createOrientationHelper(androidx.recyclerview.widget.RecyclerView$LayoutManager,int):220 -> ensureLayoutState
3:3:void ensureLayoutState():446 -> ensureLayoutState
4:4:com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper.createOrientationHelper(androidx.recyclerview.widget.RecyclerView$LayoutManager,int):222:222 -> ensureLayoutState
4:4:void ensureLayoutState():446 -> ensureLayoutState
5:5:com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper.createHorizontalHelper(androidx.recyclerview.widget.RecyclerView$LayoutManager):233:233 -> ensureLayoutState
5:5:com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper com.common.commonlib.view.viewpagerlayoutmanager.OrientationHelper.createOrientationHelper(androidx.recyclerview.widget.RecyclerView$LayoutManager,int):218 -> ensureLayoutState
5:5:void ensureLayoutState():446 -> ensureLayoutState
6:6:void ensureLayoutState():446:446 -> ensureLayoutState
float getDistanceRatio() -> f
1:3:android.view.View findViewByPosition(int):744:746 -> findViewByPosition
1:1:float getMaxOffset():677:677 -> g
1:1:androidx.recyclerview.widget.RecyclerView$LayoutParams generateDefaultLayoutParams():134:134 -> generateDefaultLayoutParams
1:1:int getOrientation():218:218 -> getOrientation
1:1:boolean getRecycleChildrenOnDetach():146:146 -> getRecycleChildrenOnDetach
1:1:boolean getReverseLayout():285:285 -> getReverseLayout
1:1:float getMinOffset():681:681 -> h
1:1:float maxRemoveOffset():710:710 -> i
1:1:float minRemoveOffset():718:718 -> j
float setInterval() -> k
void setUp() -> l
1:2:void onAdapterChanged(androidx.recyclerview.widget.RecyclerView$Adapter,androidx.recyclerview.widget.RecyclerView$Adapter):463:464 -> onAdapterChanged
1:4:boolean onAddFocusables(androidx.recyclerview.widget.RecyclerView,java.util.ArrayList,int,int):403:406 -> onAddFocusables
5:17:int getMovement(int):425:437 -> onAddFocusables
5:17:boolean onAddFocusables(androidx.recyclerview.widget.RecyclerView,java.util.ArrayList,int,int):407 -> onAddFocusables
18:21:boolean onAddFocusables(androidx.recyclerview.widget.RecyclerView,java.util.ArrayList,int,int):411:414 -> onAddFocusables
1:4:void onDetachedFromWindow(androidx.recyclerview.widget.RecyclerView,androidx.recyclerview.widget.RecyclerView$Recycler):168:171 -> onDetachedFromWindow
1:7:void onLayoutChildren(androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State):334:340 -> onLayoutChildren
8:11:void resolveShouldLayoutReverse():271:274 -> onLayoutChildren
8:11:void onLayoutChildren(androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State):341 -> onLayoutChildren
12:12:void resolveShouldLayoutReverse():272:272 -> onLayoutChildren
12:12:void onLayoutChildren(androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State):341 -> onLayoutChildren
13:48:void onLayoutChildren(androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State):344:379 -> onLayoutChildren
49:50:void layoutItems(androidx.recyclerview.widget.RecyclerView$Recycler):590:591 -> onLayoutChildren
49:50:void onLayoutChildren(androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State):382 -> onLayoutChildren
1:3:void onLayoutCompleted(androidx.recyclerview.widget.RecyclerView$State):396:398 -> onLayoutCompleted
1:3:void onRestoreInstanceState(android.os.Parcelable):189:191 -> onRestoreInstanceState
1:7:android.os.Parcelable onSaveInstanceState():177:183 -> onSaveInstanceState
1:4:int scrollHorizontallyBy(int,androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State):545:548 -> scrollHorizontallyBy
1:4:void scrollToPosition(int):469:472 -> scrollToPosition
1:4:int scrollVerticallyBy(int,androidx.recyclerview.widget.RecyclerView$Recycler,androidx.recyclerview.widget.RecyclerView$State):553:556 -> scrollVerticallyBy
1:10:void setOrientation(int):229:238 -> setOrientation
1:1:void setRecycleChildrenOnDetach(boolean):163:163 -> setRecycleChildrenOnDetach
1:6:void setReverseLayout(boolean):289:294 -> setReverseLayout
1:1:void setSmoothScrollbarEnabled(boolean):882:882 -> setSmoothScrollbarEnabled
1:23:void smoothScrollToPosition(androidx.recyclerview.widget.RecyclerView,androidx.recyclerview.widget.RecyclerView$State,int):306:328 -> smoothScrollToPosition
com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$OnPageChangeListener -> com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$a:
com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$SavedState -> com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$SavedState:
float offset -> b
boolean isReverseLayout -> c
int position -> a
1:1:void <clinit>():892:892 -> <clinit>
1:1:void <init>():908:908 -> <init>
2:5:void <init>(android.os.Parcel):912:915 -> <init>
6:9:void <init>(com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$SavedState):918:921 -> <init>
1:3:void writeToParcel(android.os.Parcel,int):931:933 -> writeToParcel
com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$SavedState$1 -> com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$SavedState$a:
1:1:void <init>():893:893 -> <init>
1:1:com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$SavedState createFromParcel(android.os.Parcel):896:896 -> createFromParcel
1:1:java.lang.Object createFromParcel(android.os.Parcel):893 -> createFromParcel
1:1:com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager$SavedState[] newArray(int):901:901 -> newArray
1:1:java.lang.Object[] newArray(int):893 -> newArray
org.apache.commons.net.MalformedServerReplyException -> c.a:
1:1:void <init>(java.lang.String):53:53 -> <init>
org.apache.commons.net.ProtocolCommandEvent -> c.b:
1:5:void <init>(java.lang.Object,java.lang.String,java.lang.String):57:61 -> <init>
6:10:void <init>(java.lang.Object,int,java.lang.String):81:85 -> <init>
org.apache.commons.net.ProtocolCommandListener -> c.c:
void protocolCommandSent(org.apache.commons.net.ProtocolCommandEvent) -> a
void protocolReplyReceived(org.apache.commons.net.ProtocolCommandEvent) -> b
org.apache.commons.net.ProtocolCommandSupport -> c.d:
1:3:void <init>(java.lang.Object):50:52 -> <init>
1:5:void fireCommandSent(java.lang.String,java.lang.String):71:75 -> a
6:10:void fireReplyReceived(int,java.lang.String):95:99 -> a
11:11:int getListenerCount():131:131 -> a
org.apache.commons.net.SocketClient -> c.e:
javax.net.SocketFactory _socketFactory_ -> d
javax.net.ServerSocketFactory _serverSocketFactory_ -> e
javax.net.SocketFactory __DEFAULT_SOCKET_FACTORY -> f
java.io.OutputStream _output_ -> c
javax.net.ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY -> g
java.net.Socket _socket_ -> a
java.io.InputStream _input_ -> b
1:5:void <clinit>():64:68 -> <clinit>
1:1:void <init>():124:124 -> <init>
2:19:void <init>():114:131 -> <init>
1:1:void connect(java.lang.String,int):203:203 -> a
2:10:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):175:183 -> a
2:10:void connect(java.lang.String,int):203 -> a
11:13:void org.apache.commons.net.SocketClient._connectAction_():153:155 -> a
11:13:void org.apache.commons.net.ftp.FTP._connectAction_():378 -> a
11:13:void org.apache.commons.net.ftp.FTPClient._connectAction_():924 -> a
11:13:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
11:13:void connect(java.lang.String,int):203 -> a
14:14:void org.apache.commons.net.ftp.FTP._connectAction_():379:379 -> a
14:14:void org.apache.commons.net.ftp.FTPClient._connectAction_():924 -> a
14:14:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
14:14:void connect(java.lang.String,int):203 -> a
15:15:java.lang.String org.apache.commons.net.ftp.FTP.getControlEncoding():427:427 -> a
15:15:void org.apache.commons.net.ftp.FTP._connectAction_():379 -> a
15:15:void org.apache.commons.net.ftp.FTPClient._connectAction_():924 -> a
15:15:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
15:15:void connect(java.lang.String,int):203 -> a
16:18:void org.apache.commons.net.ftp.FTP._connectAction_():379:381 -> a
16:18:void org.apache.commons.net.ftp.FTPClient._connectAction_():924 -> a
16:18:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
16:18:void connect(java.lang.String,int):203 -> a
19:19:java.lang.String org.apache.commons.net.ftp.FTP.getControlEncoding():427:427 -> a
19:19:void org.apache.commons.net.ftp.FTP._connectAction_():381 -> a
19:19:void org.apache.commons.net.ftp.FTPClient._connectAction_():924 -> a
19:19:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
19:19:void connect(java.lang.String,int):203 -> a
20:20:void org.apache.commons.net.ftp.FTP._connectAction_():381:381 -> a
20:20:void org.apache.commons.net.ftp.FTPClient._connectAction_():924 -> a
20:20:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
20:20:void connect(java.lang.String,int):203 -> a
21:21:void org.apache.commons.net.ftp.FTP.__getReply():294:294 -> a
21:21:void org.apache.commons.net.ftp.FTP._connectAction_():400 -> a
21:21:void org.apache.commons.net.ftp.FTPClient._connectAction_():924 -> a
21:21:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
21:21:void connect(java.lang.String,int):203 -> a
22:22:void org.apache.commons.net.ftp.FTP._connectAction_():402:402 -> a
22:22:void org.apache.commons.net.ftp.FTPClient._connectAction_():924 -> a
22:22:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
22:22:void connect(java.lang.String,int):203 -> a
23:23:void org.apache.commons.net.ftp.FTP.__getReply():294:294 -> a
23:23:void org.apache.commons.net.ftp.FTP._connectAction_():403 -> a
23:23:void org.apache.commons.net.ftp.FTPClient._connectAction_():924 -> a
23:23:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
23:23:void connect(java.lang.String,int):203 -> a
24:24:void org.apache.commons.net.ftp.FTPClient._connectAction_():925:925 -> a
24:24:void org.apache.commons.net.SocketClient.connect(java.net.InetAddress,int):183 -> a
24:24:void connect(java.lang.String,int):203 -> a
org.apache.commons.net.ftp.FTP -> d.a:
java.lang.String _replyString -> k
boolean _newReplyString -> j
java.io.BufferedReader _controlInput_ -> n
int _replyCode -> h
java.util.ArrayList _replyLines -> i
java.lang.String _controlEncoding -> l
java.io.BufferedWriter _controlOutput_ -> o
org.apache.commons.net.ProtocolCommandSupport _commandSupport_ -> m
1:7:void <init>():265:271 -> <init>
1:1:java.lang.String org.apache.commons.net.ftp.FTPCmd.getCommand():113:113 -> a
1:1:int org.apache.commons.net.ftp.FTP.sendCommand(org.apache.commons.net.ftp.FTPCmd,java.lang.String):608 -> a
1:1:int sendCommand(org.apache.commons.net.ftp.FTPCmd):582 -> a
2:2:int org.apache.commons.net.ftp.FTP.sendCommand(org.apache.commons.net.ftp.FTPCmd,java.lang.String):608:608 -> a
2:2:int sendCommand(org.apache.commons.net.ftp.FTPCmd):582 -> a
3:47:void __getReply(boolean):311:355 -> a
48:48:boolean org.apache.commons.net.ftp.FTP.__lenientCheck(java.lang.String):285:285 -> a
48:48:void __getReply(boolean):361 -> a
49:62:void __getReply(boolean):351:364 -> a
63:76:java.lang.String org.apache.commons.net.ftp.FTP.getReplyString():720:733 -> a
63:76:void __getReply(boolean):364 -> a
77:77:org.apache.commons.net.ProtocolCommandSupport org.apache.commons.net.ftp.FTP.getCommandSupport():1789:1789 -> a
77:77:void org.apache.commons.net.SocketClient.fireReplyReceived(int,java.lang.String):778 -> a
77:77:void __getReply(boolean):364 -> a
78:78:void org.apache.commons.net.SocketClient.fireReplyReceived(int,java.lang.String):778:778 -> a
78:78:void __getReply(boolean):364 -> a
79:79:org.apache.commons.net.ProtocolCommandSupport org.apache.commons.net.ftp.FTP.getCommandSupport():1789:1789 -> a
79:79:void org.apache.commons.net.SocketClient.fireReplyReceived(int,java.lang.String):779 -> a
79:79:void __getReply(boolean):364 -> a
80:80:void org.apache.commons.net.SocketClient.fireReplyReceived(int,java.lang.String):779:779 -> a
80:80:void __getReply(boolean):364 -> a
81:82:void __getReply(boolean):366:367 -> a
83:83:void __getReply(boolean):337:337 -> a
84:84:void __getReply(boolean):325:325 -> a
85:85:void __getReply(boolean):317:317 -> a
86:96:java.lang.String __buildMessage(java.lang.String,java.lang.String):488:498 -> a
97:98:void __send(java.lang.String):504:505 -> a
99:103:boolean org.apache.commons.net.SocketClient.isConnected():346:350 -> a
99:103:void __send(java.lang.String):509 -> a
104:108:void __send(java.lang.String):511:515 -> a
109:132:int eprt(java.net.InetAddress,int):964:987 -> a
133:133:int org.apache.commons.net.ftp.FTP.sendCommand(org.apache.commons.net.ftp.FTPCmd,java.lang.String):608:608 -> a
133:133:int eprt(java.net.InetAddress,int):987 -> a
1:7:int sendCommand(java.lang.String,java.lang.String):473:479 -> b
8:8:org.apache.commons.net.ProtocolCommandSupport org.apache.commons.net.ftp.FTP.getCommandSupport():1789:1789 -> b
8:8:void org.apache.commons.net.SocketClient.fireCommandSent(java.lang.String,java.lang.String):791 -> b
8:8:int sendCommand(java.lang.String,java.lang.String):481 -> b
9:9:void org.apache.commons.net.SocketClient.fireCommandSent(java.lang.String,java.lang.String):791:791 -> b
9:9:int sendCommand(java.lang.String,java.lang.String):481 -> b
10:10:org.apache.commons.net.ProtocolCommandSupport org.apache.commons.net.ftp.FTP.getCommandSupport():1789:1789 -> b
10:10:void org.apache.commons.net.SocketClient.fireCommandSent(java.lang.String,java.lang.String):792 -> b
10:10:int sendCommand(java.lang.String,java.lang.String):481 -> b
11:11:void org.apache.commons.net.SocketClient.fireCommandSent(java.lang.String,java.lang.String):792:792 -> b
11:11:int sendCommand(java.lang.String,java.lang.String):481 -> b
12:12:void org.apache.commons.net.ftp.FTP.__getReply():294:294 -> b
12:12:int sendCommand(java.lang.String,java.lang.String):483 -> b
13:13:int sendCommand(java.lang.String,java.lang.String):484:484 -> b
14:14:int sendCommand(java.lang.String,java.lang.String):474:474 -> b
org.apache.commons.net.ftp.FTPClient -> d.b:
java.util.HashMap __featuresMap -> x
java.util.regex.Pattern __PARMS_PAT -> y
boolean __remoteVerificationEnabled -> u
int __bufferSize -> v
int __fileType -> t
boolean __passiveNatWorkaround -> w
int __passivePort -> r
java.lang.String __passiveHost -> s
int __dataConnectionMode -> p
int __dataTimeout -> q
1:1:void <clinit>():401:401 -> <clinit>
1:1:void <init>():458:458 -> <init>
2:72:void <init>():396:466 -> <init>
1:1:java.lang.String org.apache.commons.net.ftp.FTPCmd.getCommand():113:113 -> a
1:1:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
2:8:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):633:639 -> a
2:8:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
9:12:java.io.OutputStream org.apache.commons.net.ftp.FTPClient.getBufferedOutputStream(java.io.OutputStream):3644:3647 -> a
9:12:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):639 -> a
9:12:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
13:14:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):641:642 -> a
13:14:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
15:15:int org.apache.commons.net.ftp.FTPClient.getBufferSize():3459:3459 -> a
15:15:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):653 -> a
15:15:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
16:30:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):653:667 -> a
16:30:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
31:31:void org.apache.commons.net.ftp.FTP.__getReply():294:294 -> a
31:31:int org.apache.commons.net.ftp.FTP.getReply():692 -> a
31:31:boolean org.apache.commons.net.ftp.FTPClient.completePendingCommand():1813 -> a
31:31:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):672 -> a
31:31:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
32:32:int org.apache.commons.net.ftp.FTP.getReply():693:693 -> a
32:32:boolean org.apache.commons.net.ftp.FTPClient.completePendingCommand():1813 -> a
32:32:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):672 -> a
32:32:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
33:33:boolean org.apache.commons.net.ftp.FTPClient.completePendingCommand():1813:1813 -> a
33:33:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):672 -> a
33:33:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
34:34:void org.apache.commons.net.io.Util.closeQuietly(java.net.Socket):370:370 -> a
34:34:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):659 -> a
34:34:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
35:35:boolean org.apache.commons.net.ftp.FTPClient._storeFile(java.lang.String,java.lang.String,java.io.InputStream):663:663 -> a
35:35:boolean __storeFile(org.apache.commons.net.ftp.FTPCmd,java.lang.String,java.io.InputStream):624 -> a
36:42:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1854:1860 -> a
36:42:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
43:46:java.io.InputStream org.apache.commons.net.ftp.FTPClient.getBufferedInputStream(java.io.InputStream):3651:3654 -> a
43:46:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1860 -> a
43:46:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
47:48:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1861:1862 -> a
47:48:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
49:49:int org.apache.commons.net.ftp.FTPClient.getBufferSize():3459:3459 -> a
49:49:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1873 -> a
49:49:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
50:50:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1873:1873 -> a
50:50:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
51:51:void org.apache.commons.net.io.Util.closeQuietly(java.io.Closeable):354:354 -> a
51:51:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1877 -> a
51:51:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
52:52:void org.apache.commons.net.io.Util.closeQuietly(java.net.Socket):370:370 -> a
52:52:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1878 -> a
52:52:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
53:53:void org.apache.commons.net.ftp.FTP.__getReply():294:294 -> a
53:53:int org.apache.commons.net.ftp.FTP.getReply():692 -> a
53:53:boolean org.apache.commons.net.ftp.FTPClient.completePendingCommand():1813 -> a
53:53:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1885 -> a
53:53:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
54:54:int org.apache.commons.net.ftp.FTP.getReply():693:693 -> a
54:54:boolean org.apache.commons.net.ftp.FTPClient.completePendingCommand():1813 -> a
54:54:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1885 -> a
54:54:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
55:55:boolean org.apache.commons.net.ftp.FTPClient.completePendingCommand():1813:1813 -> a
55:55:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1885 -> a
55:55:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
56:56:void org.apache.commons.net.io.Util.closeQuietly(java.io.Closeable):354:354 -> a
56:56:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1877 -> a
56:56:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
57:57:void org.apache.commons.net.io.Util.closeQuietly(java.net.Socket):370:370 -> a
57:57:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1878 -> a
57:57:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
58:58:boolean org.apache.commons.net.ftp.FTPClient._retrieveFile(java.lang.String,java.lang.String,java.io.OutputStream):1880:1880 -> a
58:58:boolean retrieveFile(java.lang.String,java.io.OutputStream):1845 -> a
1:16:void __initDefaults():473:488 -> b
1:1:void org.apache.commons.net.SocketClient.disconnect():312:312 -> c
1:1:void org.apache.commons.net.ftp.FTP.disconnect():443 -> c
1:1:void disconnect():986 -> c
2:2:void org.apache.commons.net.SocketClient.closeQuietly(java.net.Socket):323:323 -> c
2:2:void org.apache.commons.net.SocketClient.disconnect():312 -> c
2:2:void org.apache.commons.net.ftp.FTP.disconnect():443 -> c
2:2:void disconnect():986 -> c
3:3:void org.apache.commons.net.SocketClient.disconnect():313:313 -> c
3:3:void org.apache.commons.net.ftp.FTP.disconnect():443 -> c
3:3:void disconnect():986 -> c
4:4:void org.apache.commons.net.SocketClient.closeQuietly(java.io.Closeable):332:332 -> c
4:4:void org.apache.commons.net.SocketClient.disconnect():313 -> c
4:4:void org.apache.commons.net.ftp.FTP.disconnect():443 -> c
4:4:void disconnect():986 -> c
5:5:void org.apache.commons.net.SocketClient.disconnect():314:314 -> c
5:5:void org.apache.commons.net.ftp.FTP.disconnect():443 -> c
5:5:void disconnect():986 -> c
6:6:void org.apache.commons.net.SocketClient.closeQuietly(java.io.Closeable):332:332 -> c
6:6:void org.apache.commons.net.SocketClient.disconnect():314 -> c
6:6:void org.apache.commons.net.ftp.FTP.disconnect():443 -> c
6:6:void disconnect():986 -> c
7:9:void org.apache.commons.net.SocketClient.disconnect():315:317 -> c
7:9:void org.apache.commons.net.ftp.FTP.disconnect():443 -> c
7:9:void disconnect():986 -> c
10:13:void org.apache.commons.net.ftp.FTP.disconnect():444:447 -> c
10:13:void disconnect():986 -> c
14:14:void disconnect():987:987 -> c
15:15:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):784:784 -> c
16:16:java.net.InetAddress org.apache.commons.net.SocketClient.getRemoteAddress():658:658 -> c
16:16:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):789 -> c
17:25:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):789:797 -> c
26:26:java.net.InetAddress org.apache.commons.net.SocketClient.getLocalAddress():633:633 -> c
26:26:java.net.InetAddress org.apache.commons.net.ftp.FTPClient.getHostAddress():1396 -> c
26:26:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):797 -> c
27:27:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):797:797 -> c
28:28:java.net.InetAddress org.apache.commons.net.SocketClient.getLocalAddress():633:633 -> c
28:28:java.net.InetAddress org.apache.commons.net.ftp.FTPClient.getHostAddress():1396 -> c
28:28:java.net.InetAddress org.apache.commons.net.ftp.FTPClient.getReportHostAddress():1412 -> c
28:28:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):808 -> c
29:66:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):808:845 -> c
67:67:java.net.InetAddress org.apache.commons.net.SocketClient.getLocalAddress():633:633 -> c
67:67:java.net.InetAddress org.apache.commons.net.ftp.FTPClient.getHostAddress():1396 -> c
67:67:java.net.InetAddress org.apache.commons.net.ftp.FTPClient.getReportHostAddress():1412 -> c
67:67:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):812 -> c
68:68:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):812:812 -> c
69:79:int org.apache.commons.net.ftp.FTP.port(java.net.InetAddress,int):922:932 -> c
69:79:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):812 -> c
80:80:int org.apache.commons.net.ftp.FTP.sendCommand(org.apache.commons.net.ftp.FTPCmd,java.lang.String):608:608 -> c
80:80:int org.apache.commons.net.ftp.FTP.port(java.net.InetAddress,int):932 -> c
80:80:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):812 -> c
81:114:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):812:845 -> c
115:139:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):821:845 -> c
140:156:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):829:845 -> c
157:157:int org.apache.commons.net.ftp.FTP.epsv():1028:1028 -> c
157:157:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):859 -> c
158:158:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):861:861 -> c
159:177:void org.apache.commons.net.ftp.FTPClient._parseExtendedPassiveModeReply(java.lang.String):589:607 -> c
159:177:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):861 -> c
178:178:java.net.InetAddress org.apache.commons.net.SocketClient.getRemoteAddress():658:658 -> c
178:178:void org.apache.commons.net.ftp.FTPClient._parseExtendedPassiveModeReply(java.lang.String):617 -> c
178:178:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):861 -> c
179:180:void org.apache.commons.net.ftp.FTPClient._parseExtendedPassiveModeReply(java.lang.String):617:618 -> c
179:180:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):861 -> c
181:181:void org.apache.commons.net.ftp.FTPClient._parseExtendedPassiveModeReply(java.lang.String):611:611 -> c
181:181:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):861 -> c
182:182:void org.apache.commons.net.ftp.FTPClient._parseExtendedPassiveModeReply(java.lang.String):600:600 -> c
182:182:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):861 -> c
183:183:int org.apache.commons.net.ftp.FTP.pasv():1007:1007 -> c
183:183:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):869 -> c
184:184:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872:872 -> c
185:209:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):546:570 -> c
185:209:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
210:210:java.net.InetAddress org.apache.commons.net.SocketClient.getRemoteAddress():658:658 -> c
210:210:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):571 -> c
210:210:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
211:213:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):572:574 -> c
211:213:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
214:214:org.apache.commons.net.ProtocolCommandSupport org.apache.commons.net.ftp.FTP.getCommandSupport():1789:1789 -> c
214:214:void org.apache.commons.net.SocketClient.fireReplyReceived(int,java.lang.String):778 -> c
214:214:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):574 -> c
214:214:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
215:215:void org.apache.commons.net.SocketClient.fireReplyReceived(int,java.lang.String):778:778 -> c
215:215:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):574 -> c
215:215:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
216:216:org.apache.commons.net.ProtocolCommandSupport org.apache.commons.net.ftp.FTP.getCommandSupport():1789:1789 -> c
216:216:void org.apache.commons.net.SocketClient.fireReplyReceived(int,java.lang.String):779 -> c
216:216:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):574 -> c
216:216:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
217:217:void org.apache.commons.net.SocketClient.fireReplyReceived(int,java.lang.String):779:779 -> c
217:217:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):574 -> c
217:217:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
218:222:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):576:580 -> c
218:222:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
223:256:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):875:908 -> c
257:257:boolean org.apache.commons.net.SocketClient.verifyRemote(java.net.Socket):675:675 -> c
257:257:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):908 -> c
258:258:java.net.InetAddress org.apache.commons.net.SocketClient.getRemoteAddress():658:658 -> c
258:258:boolean org.apache.commons.net.SocketClient.verifyRemote(java.net.Socket):676 -> c
258:258:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):908 -> c
259:259:boolean org.apache.commons.net.SocketClient.verifyRemote(java.net.Socket):678:678 -> c
259:259:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):908 -> c
260:262:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):910:912 -> c
263:263:java.net.InetAddress org.apache.commons.net.SocketClient.getRemoteAddress():658:658 -> c
263:263:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):912 -> c
264:264:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):912:912 -> c
265:265:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):562:562 -> c
265:265:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
266:266:void org.apache.commons.net.ftp.FTPClient._parsePassiveModeReply(java.lang.String):548:548 -> c
266:266:java.net.Socket _openDataConnection_(java.lang.String,java.lang.String):872 -> c
1:1:int org.apache.commons.net.ftp.FTP.sendCommand(org.apache.commons.net.ftp.FTPCmd,java.lang.String):608:608 -> d
1:1:int org.apache.commons.net.ftp.FTP.user(java.lang.String):753 -> d
1:1:boolean login(java.lang.String,java.lang.String):1034 -> d
2:8:boolean login(java.lang.String,java.lang.String):1036:1042 -> d
9:9:int org.apache.commons.net.ftp.FTP.sendCommand(org.apache.commons.net.ftp.FTPCmd,java.lang.String):608:608 -> d
9:9:int org.apache.commons.net.ftp.FTP.pass(java.lang.String):771 -> d
9:9:boolean login(java.lang.String,java.lang.String):1046 -> d
10:10:boolean login(java.lang.String,java.lang.String):1046:1046 -> d
org.apache.commons.net.ftp.FTPCmd -> d.c:
org.apache.commons.net.ftp.FTPCmd STOR -> e
org.apache.commons.net.ftp.FTPCmd QUIT -> d
org.apache.commons.net.ftp.FTPCmd PASV -> c
org.apache.commons.net.ftp.FTPCmd FEAT -> b
org.apache.commons.net.ftp.FTPCmd[] $VALUES -> f
org.apache.commons.net.ftp.FTPCmd EPSV -> a
1:40:void <clinit>():25:64 -> <clinit>
41:41:void <clinit>():24:24 -> <clinit>
1:1:void <init>(java.lang.String,int):24:24 -> <init>
1:1:org.apache.commons.net.ftp.FTPCmd valueOf(java.lang.String):24:24 -> valueOf
1:1:org.apache.commons.net.ftp.FTPCmd[] values():24:24 -> values
org.apache.commons.net.ftp.FTPConnectionClosedException -> d.d:
1:1:void <init>(java.lang.String):53:53 -> <init>
org.apache.commons.net.ftp.FTPReply -> d.e:
boolean isPositiveCompletion(int) -> a
boolean isPositivePreliminary(int) -> b
org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory -> e.a:
1:1:void <clinit>():46:46 -> <clinit>
org.apache.commons.net.io.CRLFLineReader -> f.a:
1:1:void <init>(java.io.Reader):43:43 -> <init>
1:19:java.lang.String readLine():54:72 -> readLine
20:20:java.lang.String readLine():70:70 -> readLine
org.apache.commons.net.io.CopyStreamException -> f.b:
1:3:void <init>(java.lang.String,long,java.io.IOException):47:49 -> <init>
org.apache.commons.net.io.CopyStreamListener -> f.c:
org.apache.commons.net.io.FromNetASCIIInputStream -> f.d:
boolean _noConversionRequired -> b
byte[] _lineSeparatorBytes -> c
int __length -> a
1:6:void <clinit>():42:47 -> <clinit>
1:1:void <init>(java.io.InputStream):73:73 -> <init>
2:2:void <init>(java.io.InputStream):51:51 -> <init>
1:16:int __read():81:96 -> a
1:4:int available():214:217 -> available
5:5:int available():215:215 -> available
1:5:int read():122:126 -> read
6:6:int read(byte[]):144:144 -> read
7:37:int read(byte[],int,int):165:195 -> read
org.apache.commons.net.io.ToNetASCIIOutputStream -> f.e:
boolean __lastWasCR -> a
1:2:void <init>(java.io.OutputStream):47:48 -> <init>
1:11:void write(int):70:80 -> write
12:12:void write(byte[]):97:97 -> write
13:13:void write(byte[],int,int):116:116 -> write
org.apache.commons.net.io.Util -> f.f:
1:39:long copyStream(java.io.InputStream,java.io.OutputStream,int,long,org.apache.commons.net.io.CopyStreamListener,boolean):96:134 -> a
org.apache.commons.net.util.ListenerList -> g.a:
1:2:void <init>():35:36 -> <init>
1:1:int getListenerCount():51:51 -> a
1:1:java.util.Iterator iterator():63:63 -> iterator

@ -0,0 +1,135 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# 有了verbose这句话混淆后就会生成映射文件
# 包含有类名->混淆后类名的映射关系
# 然后使用printmapping指定映射文件的名称
-verbose
-printmapping priguardMapping.txt
# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
# 保护代码中的Annotation不被混淆
# 这在JSON实体映射时非常重要比如fastJson
-keepattributes *Annotation*
# 避免混淆泛型
# 这在JSON实体映射时非常重要比如fastJson
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 对R文件下的所有类及其方法都不能被混淆
-keepclassmembers class **.R$* {
*;
}
# 保留了继承自ActivityApplication这些类的子类
# 因为这些子类有可能被外部调用
# 比如第一行就保证了所有Activity的子类不要被混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
# 枚举类不能被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保留自定义控件(继承自View)不能被混淆
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(***);
*** get* ();
}
# 保留Parcelable序列化的类不能被混淆
-keep class * implements android.os.Parcelable{
public static final android.os.Parcelable$Creator *;
}
# 保留Serializable 序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# AndroidX混淆
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**
# 保留BaseFragment公共成员和方法不被混淆
-keep class com.common.commonlib.utils.*{*;}
# 保留BaseFragment公共成员和方法不被混淆
-keep class com.common.commonlib.view.fragment.BaseFragment{
public<fields>;
public<methods>;
}
# 保留BaseNavigationFragment公共成员和方法不被混淆
-keep class com.common.commonlib.view.fragment.BaseNavigationFragment{
public<fields>;
public<methods>;
}
# 保留BaseActivity公共成员和方法不被混淆
-keep class com.common.commonlib.view.activity.BaseActivity{
public<fields>;
public<methods>;
}
# 保留CommonTitleView的内部类不被混淆
-keep class com.common.commonlib.view.CommonTitleView$*{
*;
}
# 保留BaseNavigationFragment公共成员和方法不被混淆
-keep class com.common.commonlib.view.SlideRecyclerView{
public<fields>;
public<methods>;
}
# 保留glide的配置类
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep class com.common.commonlib.image.module.OkHttpLibraryGlideModule

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.common.commonlib">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</manifest>

@ -0,0 +1,2 @@
debug1 = https://www.wanandroid.com
debug2 = https://www.baidu

@ -0,0 +1,57 @@
package com.common.commonlib
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
import com.common.commonlib.utils.BaseUtils
import com.tencent.mmkv.MMKV
/**
* 基础Application
*
* @author wangym
* @since 2021/7/28
*/
@SuppressLint("StaticFieldLeak")
open class CommonApplication : Application(), LifecycleObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
intLibs(this)
}
private fun intLibs(context: Context) {
commonContext = context
// 初始化MMKV
MMKV.initialize(this)
initNet()
}
private fun initNet() {
BaseUtils.enableFullLog()
}
companion object {
private var commonContext: Context? = null
fun getContext(): Context? {
return commonContext
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
open fun onAppForeground(){
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
open fun onAppBackground(){
}
}

@ -0,0 +1,3 @@
package com.common.commonlib.bean
data class GridPicBean(val imagePath:String, val isConverting: Boolean)

@ -0,0 +1,62 @@
package com.common.commonlib.image.config
import com.bumptech.glide.MemoryCategory
/**
* 图片加载基础配置
*
* @author wangym
* @since 2021/7/28
*/
object GlideBaseConfig {
enum class MemoryType(var memoryCategory: MemoryCategory) {
/**
* Tells Glide's memory cache and bitmap pool to use at most half of their initial maximum size.
*/
LOW(MemoryCategory.LOW),
/**
* Tells Glide's memory cache and bitmap pool to use at most their initial maximum size.
*/
NORMAL(MemoryCategory.NORMAL),
/**
* Tells Glide's memory cache and bitmap pool to use at most one and a half times their initial
* maximum size.
*/
HIGH(MemoryCategory.HIGH)
}
enum class LoadMode {
/**
* default loading type
*/
DEFAULT,
/**
* cache all resource
*/
ALL,
/**
* skip loading from disk
*/
SKIP_DISK,
/**
* skip loading from memory cache
*/
SKIP_MEM,
/**
* skip loading form all cache
*/
SKIP_CACHE,
/**
* don't load form internet
*/
SKIP_NET
}
}

@ -0,0 +1,30 @@
package com.common.commonlib.image.config
import android.graphics.drawable.Drawable
/**
* 占位符配置
*
* @author wangym
* @since 2021/7/28
*/
class GlideHolderConfig {
/**
* holder占位图
*/
var holderDrawable: Drawable? = null
var holderId: Int = -1
/**
* 加载失败时且没有配置fallback占位图时展示
*/
var errorDrawable: Drawable? = null
var errorId: Int = -1
var errorBuilder: Any? = null
/**
* 加载失败时是否允许失败作为正常流程展示
*/
var fallBackDrawable: Drawable? = null
var fallBackId: Int = -1
}

@ -0,0 +1,18 @@
package com.common.commonlib.image.config
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
/**
* 转换配置
* @param time 动画时长
*
* @author wangym
* @since 2021/7/28
*/
class GlideTransitionConfig(time: Int?) {
val transition: DrawableTransitionOptions = if (time != null) {
DrawableTransitionOptions.withCrossFade(time)
} else {
DrawableTransitionOptions.withCrossFade()
}
}

@ -0,0 +1,64 @@
package com.common.commonlib.image.loader
import android.graphics.drawable.Drawable
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.common.commonlib.image.config.GlideBaseConfig
/**
* 加载配置辅助类
*
* @author wangym
* @since 2021/7/28
*/
object ConfigHelper {
/**
* 带有过渡效果的加载
* @param requestBuilder 请求构建器
* @param transformations 过渡效果
*
* @return 请求构建器
*/
fun loadWithTransformation(
requestBuilder: RequestBuilder<Drawable>,
transformations: List<BitmapTransformation>
): RequestBuilder<Drawable> {
var temp = requestBuilder
for (item in transformations) {
temp = temp.transform(item)
}
return temp
}
/**
* 带有缓存模式的加载
* @param requestBuilder 请求构建器
* @param loadingMode 加载缓存模式
*
* @return 请求构建器
*/
fun loadWithMod(
requestBuilder: RequestBuilder<Drawable>,
loadingMode: GlideBaseConfig.LoadMode
): RequestBuilder<Drawable> {
var request = requestBuilder
request = when (loadingMode) {
GlideBaseConfig.LoadMode.DEFAULT ->
request
GlideBaseConfig.LoadMode.ALL ->
request.diskCacheStrategy(DiskCacheStrategy.ALL)
GlideBaseConfig.LoadMode.SKIP_CACHE ->
request.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
GlideBaseConfig.LoadMode.SKIP_DISK ->
request.diskCacheStrategy(DiskCacheStrategy.NONE)
GlideBaseConfig.LoadMode.SKIP_MEM ->
request.skipMemoryCache(true)
GlideBaseConfig.LoadMode.SKIP_NET ->
request.onlyRetrieveFromCache(true)
}
return request
}
}

@ -0,0 +1,71 @@
package com.common.commonlib.image.loader
import android.graphics.drawable.Drawable
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.common.commonlib.image.config.GlideHolderConfig
/**
* 全局配置适配器
*
* @author wangym
* @since 2021/7/28
*/
object GlobalConfigHelper {
/**
* 全局占位符配置
*/
var globalHolderConfig: GlideHolderConfig? = null
/**
* 全局过渡效果
*/
var globalTransition: DrawableTransitionOptions? = null
fun loadWithGlobalConfig(requestBuilder: RequestBuilder<Drawable>): RequestBuilder<Drawable> {
return loadWithGlobalConfig(requestBuilder, globalHolderConfig, globalTransition)
}
fun loadWithGlobalConfig(
requestBuilder: RequestBuilder<Drawable>,
holderConfig: GlideHolderConfig?
): RequestBuilder<Drawable> {
return loadWithGlobalConfig(requestBuilder, holderConfig, globalTransition)
}
private fun loadWithGlobalConfig(
requestBuilder: RequestBuilder<Drawable>,
holderConfig: GlideHolderConfig?,
transition: DrawableTransitionOptions?
): RequestBuilder<Drawable> {
var request = requestBuilder
if (holderConfig != null) {
if (holderConfig.holderDrawable != null) {
request = request.placeholder(holderConfig.holderDrawable)
}
if (holderConfig.holderId != -1) {
request = request.placeholder(holderConfig.holderId)
}
if (holderConfig.errorDrawable != null) {
request = request.error(holderConfig.errorDrawable)
}
if (holderConfig.errorId != -1) {
request = request.error(holderConfig.errorId)
}
if (holderConfig.errorBuilder != null) {
request = request.error(holderConfig.errorBuilder)
}
if (holderConfig.fallBackDrawable != null) {
request = request.fallback(holderConfig.fallBackDrawable)
}
if (holderConfig.fallBackId != -1) {
request = request.fallback(holderConfig.fallBackId)
}
}
if (transition != null) {
request = request.transition(transition)
}
return request
}
}

@ -0,0 +1,85 @@
package com.common.commonlib.image.loader
import android.content.Context
import android.widget.ImageView
import com.common.commonlib.image.config.GlideBaseConfig
import com.common.commonlib.image.config.GlideHolderConfig
/**
* 图片加载器接口
*
* @author wangym
* @since 2021/7/28
*/
interface ILoader {
/**
* 初始化
* @param context 上下文
* @param memoryCategory 缓存类型
*/
fun init(context: Context, memoryCategory: GlideBaseConfig.MemoryType)
/**
* 加载图片
* @param context 上下文
* @param url 图片链接
* @param view 图片控件
*/
fun load(context: Context, url: String?, view: ImageView)
/**
* 加载圆角图片
* @param context 上下文
* @param url 图片链接
* @param view 图片控件
* @param radius 圆角半径
*/
fun loadRound(context: Context, url: String?, view: ImageView, radius: Float)
/**
* 加载图片
* @param context 上下文
* @param url 图片链接
* @param view 图片控件
* @param loadingMode 加载模式
*/
fun loadWithMode(
context: Context,
url: String?,
view: ImageView,
loadingMode: GlideBaseConfig.LoadMode
)
/**
* 加载图片
* @param context 上下文
* @param url 图片链接
* @param view 图片控件
* @param placeGlideHolder 占位图
*/
fun loadWithPlaceHolder(
context: Context,
url: String,
view: ImageView,
placeGlideHolder: GlideHolderConfig
)
/**
* 清理内存缓存
* @param context 上下文
*/
fun clearMemoryCache(context: Context)
/**
* 清理磁盘缓存
* @param context 上下文
*/
fun clearDiskCache(context: Context)
/**
* 清理缓存
* @param context 上下文
*/
fun clearAllCache(context: Context)
}

@ -0,0 +1,158 @@
package com.common.commonlib.image.loader
import android.content.Context
import android.graphics.drawable.Drawable
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners
import com.common.commonlib.image.config.GlideBaseConfig
import com.common.commonlib.image.config.GlideHolderConfig
import com.common.commonlib.image.config.GlideTransitionConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
/**
* 图片加载类
*
* @author wangym
* @since 2021/7/28
*/
object ImageLoader : ILoader {
/**
* 初始化Glide基础配置
*/
override fun init(
context: Context, memoryCategory: GlideBaseConfig.MemoryType
) {
Glide.get(context).setMemoryCategory(memoryCategory.memoryCategory)
}
/**
* 设置全局占位符配置
* @param globalHolderConfig 占位符配置
*/
fun setGlobalConfig(globalHolderConfig: GlideHolderConfig) {
GlobalConfigHelper.globalHolderConfig = globalHolderConfig
}
/**
* 设置全局过渡效果
* @param transitionConfig 全局过渡配置
*/
fun setGlobalTransition(transitionConfig: GlideTransitionConfig) {
GlobalConfigHelper.globalTransition = transitionConfig.transition
}
/**
* 加载图片
* @param context 上下文
* @param url 图片链接
* @param view 图片控件
*/
override fun load(context: Context, url: String?, view: ImageView) {
loadWithMode(context, url, view, GlideBaseConfig.LoadMode.DEFAULT)
}
/**
* 加载圆角图片
* @param context 上下文
* @param url 图片链接
* @param view 图片控件
* @param radius 圆角弧度
*/
override fun loadRound(context: Context, url: String?, view: ImageView, radius: Float) {
var request = GlobalConfigHelper.loadWithGlobalConfig(Glide.with(context).load(url))
request = ConfigHelper.loadWithMod(request, GlideBaseConfig.LoadMode.DEFAULT)
val transformations = ArrayList<BitmapTransformation>()
transformations.add(GranularRoundedCorners(radius, radius, radius, radius))
request = ConfigHelper.loadWithTransformation(request, transformations)
loadIntoView(request, view)
}
/**
* 以模式加载图片
* @param context 上下文
* @param url 图片链接
* @param view 图片控件
* @param loadingMode 加载模式
*/
override fun loadWithMode(
context: Context,
url: String?,
view: ImageView,
loadingMode: GlideBaseConfig.LoadMode
) {
var request = GlobalConfigHelper.loadWithGlobalConfig(Glide.with(context).load(url))
request = ConfigHelper.loadWithMod(request, loadingMode)
loadIntoView(request, view)
}
/**
* 加载图片带有占位图
* @param context 上下文
* @param url 图片链接
* @param view 图片控件
* @param placeGlideHolder 占位图
*/
override fun loadWithPlaceHolder(
context: Context,
url: String,
view: ImageView,
placeGlideHolder: GlideHolderConfig
) {
loadIntoView(
GlobalConfigHelper.loadWithGlobalConfig(
Glide.with(context).load(url),
placeGlideHolder
), view
)
}
/**
* 加载进控件
* @param requestBuilder 请求构建器
* @param view 图片控件
*/
private fun loadIntoView(requestBuilder: RequestBuilder<Drawable>, view: ImageView) {
requestBuilder.into(view)
}
/**
* 清理内存缓存
* @param context 上下文
*/
override fun clearMemoryCache(context: Context) {
GlobalScope.launch(Dispatchers.Main) {
Glide.get(context).clearMemory()
}
}
/**
* 清理磁盘缓存
* @param context 上下文
*/
override fun clearDiskCache(context: Context) = runBlocking<Unit> {
launch(Dispatchers.Default) {
Glide.get(context).clearDiskCache()
}
}
/**
* 清理缓存
* @param context 上下文
*/
override fun clearAllCache(context: Context) = runBlocking<Unit> {
GlobalScope.launch(Dispatchers.Main) {
Glide.get(context).clearMemory()
}
launch(Dispatchers.Default) {
Glide.get(context).clearDiskCache()
}
}
}

@ -0,0 +1,25 @@
package com.common.commonlib.image.module
import android.content.Context
import com.bumptech.glide.Glide
import com.bumptech.glide.Registry
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.module.LibraryGlideModule
import java.io.InputStream
/**
* okhttp配置
*
* @author wangym
* @since 2021/7/28
*/
@GlideModule
class OkHttpLibraryGlideModule : LibraryGlideModule() {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
// 替换网路请求使用okhttp
registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory())
}
}

@ -0,0 +1,23 @@
package com.common.commonlib.log
/**
* 日志敏感信息处理
*
* @author wangym
* @since 2021/7/28
*/
object LogSecureHelper {
/**
* 一个间隔一个将数据替换成*
*/
fun cast2Star(source: String): String {
val sourceTemp = source.toCharArray()
for (item in sourceTemp.withIndex()) {
if (item.index / 2 == 0) {
sourceTemp[item.index] = '*'
}
}
return sourceTemp.toString()
}
}

@ -0,0 +1,103 @@
package com.common.commonlib.log
import android.util.Log
/**
* Log工具类
*
* @author wangym
* @since 2021/7/28
*/
object Logger {
var commonTag = ""
var needSecurity = false
fun init(tag: String, security: Boolean) {
commonTag = tag
needSecurity = security
}
fun w(msg: Any?) {
w(commonTag, msg.toString(), needSecurity)
}
fun d(msg: Any?) {
d(commonTag, msg.toString(), needSecurity)
}
fun i(msg: Any?) {
i(commonTag, msg.toString(), needSecurity)
}
fun e(msg: Any?) {
e(commonTag, msg.toString(), needSecurity)
}
fun w(msg: Any?, needSecurity: Boolean) {
w(commonTag, msg.toString(), needSecurity)
}
fun d(msg: Any?, needSecurity: Boolean) {
d(commonTag, msg.toString(), needSecurity)
}
fun i(msg: Any?, needSecurity: Boolean) {
i(commonTag, msg.toString(), needSecurity)
}
fun e(msg: Any?, needSecurity: Boolean) {
e(commonTag, msg.toString(), needSecurity)
}
fun w(tag: String, msg: Any?) {
w(tag, msg.toString(), needSecurity)
}
fun d(tag: String, msg: Any?) {
d(tag, msg.toString(), needSecurity)
}
fun i(tag: String, msg: Any?) {
i(tag, msg.toString(), needSecurity)
}
fun e(tag: String, msg: Any?) {
e(tag, msg.toString(), needSecurity)
}
fun w(tag: String, msg: Any?, needSecurity: Boolean) {
Log.w(
tag, if (needSecurity)
LogSecureHelper.cast2Star(msg.toString())
else
msg.toString()
)
}
fun d(tag: String, msg: Any?, needSecurity: Boolean) {
Log.d(
tag, if (needSecurity)
LogSecureHelper.cast2Star(msg.toString())
else
msg.toString()
)
}
fun i(tag: String, msg: Any?, needSecurity: Boolean) {
Log.i(
tag, if (needSecurity)
LogSecureHelper.cast2Star(msg.toString())
else
msg.toString()
)
}
fun e(tag: String, msg: Any?, needSecurity: Boolean) {
Log.e(
tag, if (needSecurity)
LogSecureHelper.cast2Star(msg.toString())
else
msg.toString()
)
}
}

@ -0,0 +1,124 @@
package com.common.commonlib.net
import android.net.ParseException
import android.util.Log
import com.common.commonlib.log.Logger
import com.common.commonlib.net.callback.RequestCallBack
import com.common.commonlib.net.interfaces.ITypedCommon
import com.common.commonlib.net.manager.RequestManager
import com.google.gson.JsonParseException
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Observer
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import okhttp3.Interceptor
import org.json.JSONException
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.SSLHandshakeException
/**
* 请求基类
*
* @author wangym
* @since 2021/7/28
*/
open class BaseObserve<T> : ITypedCommon {
var api: T
constructor(clazz: Class<T>) {
api = RequestManager.create(clazz, null, getType())
}
constructor(clazz: Class<T>, interceptors: List<Interceptor>) {
api = RequestManager.create(clazz, interceptors, getType())
}
/**
* 提供方法给外部获取API
*/
fun getAPI(): T {
return api
}
/**
* 调用接口
* @param observable 可订阅
* @param callBack 请求回调
*/
fun <R> observe(observable: Observable<R>?, callBack: RequestCallBack<R>?) {
if (observable == null) {
Logger.e("BaseObserve", "observe observable is null")
return
}
observable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<R> {
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: R) {
callBack?.onResult(t)
}
override fun onError(e: Throwable) {
e.message?.let { Log.e(TAG, it) }
callBack?.onError(dealError(e))
}
override fun onComplete() {
callBack?.onComplete()
}
})
}
/**
* 处理错误
* @param e 异常
*/
private fun dealError(e: Throwable): String? {
if (e is HttpException) {
return when (e.code()) {
UNAUTHORIZED -> "登录验证过期"
INTERNAL_SERVER_ERROR -> "服务器错误"
FORBIDDEN, NOT_FOUND -> "无效的请求"
REQUEST_TIMEOUT, GATEWAY_TIMEOUT, BAD_GATEWAY, SERVICE_UNAVAILABLE -> e.message()
else -> e.message()
}
} else if (e is ConnectException) {
return "网络连接异常,请检查您的网络状态"
} else if (e is SocketTimeoutException) {
return "网络连接超时,请检查您的网络状态,稍后重试"
} else if (e is UnknownHostException) {
return "网络异常,请检查您的网络状态"
} else if (e is JsonParseException || e is JSONException || e is ParseException) {
return "数据解析错误"
} else if (e is SSLHandshakeException) {
return "网络异常,请检查您的网络状态"
} else if (e is RuntimeException) {
return "运行时异常"
}
return e.message
}
companion object {
const val TAG: String = "BaseLoader"
const val UNAUTHORIZED = 401
const val FORBIDDEN = 403
const val NOT_FOUND = 404
const val REQUEST_TIMEOUT = 408
const val INTERNAL_SERVER_ERROR = 500
const val BAD_GATEWAY = 502
const val SERVICE_UNAVAILABLE = 503
const val GATEWAY_TIMEOUT = 504
}
override fun getType(): String {
return BaseObserve<T>::javaClass.name
}
}

@ -0,0 +1,24 @@
package com.common.commonlib.net.bean
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
/**
* 基础响应类
*
* @author wangym
* @since 2021/7/28
*/
@Parcelize
open class BaseResponse : Parcelable {
var errorCode: Int = 0
var errorMessage: String = ""
fun getMErrorCode(): Int {
return errorCode
}
fun getMErrorMessage(): String {
return errorMessage
}
}

@ -0,0 +1,12 @@
package com.common.commonlib.net.bean
/**
* 请求参数
*
* @author wangym
* @since 2021/7/28
*/
data class RequestParam(var baseUrl: String) {
var params: Map<String, String>? = null
}

@ -0,0 +1,13 @@
package com.common.commonlib.net.callback
/**
* 请求回调
*
* @author wangym
* @since 2021/7/28
*/
interface RequestCallBack<T> {
fun onResult(result: T)
fun onError(error: String?)
fun onComplete()
}

@ -0,0 +1,19 @@
package com.common.commonlib.net.callback
import com.common.commonlib.log.Logger
/**
* 不关注结果的的回调
*
* @author wangym
* @since 2021/8/2
*/
abstract class RequestNoResultCallBack<T> : RequestCallBack<T> {
override fun onResult(result: T) {
Logger.d("result ignore")
}
abstract override fun onError(error: String?)
abstract override fun onComplete()
}

@ -0,0 +1,19 @@
package com.common.commonlib.net.callback
import com.common.commonlib.log.Logger
/**
* 不需要关注onComplete的请求回调
*
* @author wangym
* @since 2021/8/2
*/
abstract class RequestResultCallBack<T> : RequestCallBack<T> {
abstract override fun onResult(result: T)
abstract override fun onError(error: String?)
override fun onComplete() {
Logger.d("RequestResultCallBack: onComplete")
}
}

@ -0,0 +1,26 @@
package com.common.commonlib.net.interceptor
import com.common.commonlib.net.interfaces.ITypedCommon
import okhttp3.Interceptor
/**
* 通用拦截器基类
*
* @author Alex Wang
* @since 2022-2-24
*/
abstract class CommonInterceptor : Interceptor, ITypedCommon {
/**
* 获取拦截器类型
*/
override fun getType(): String {
return CommonInterceptor::class.java.simpleName
}
/**
* 获取拦截器key
*/
open fun getKey(): String {
return CommonInterceptor::class.java.simpleName
}
}

@ -0,0 +1,86 @@
package com.common.commonlib.net.interceptor
import android.text.TextUtils
import android.util.Log
import com.common.commonlib.BuildConfig
import com.common.commonlib.CommonApplication
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import java.io.BufferedInputStream
import java.util.*
/**
* HOST拦截器:动态替换HOST
*
* @author wangym
* @since 2021/7/28
*/
class HostInterceptor : Interceptor {
/**
* 拦截替换BaseUrl
*/
override fun intercept(chain: Interceptor.Chain): Response {
val oldRequest: Request = chain.request()
val requestBuilder: Request.Builder = oldRequest.newBuilder()
val headKey = BASE_URL_KEY
val headValue: String? = oldRequest.header(headKey)
// 判断是否需要替换BaseUrl,不需要则直接跳转
if (TextUtils.isEmpty(headValue)) {
return chain.proceed(chain.request())
}
// 该HEAD只用于替换BaseUrl,将对应的自定义HEAD去除
requestBuilder.removeHeader(headKey)
// 尝试获取新BaseUrl
val newUrlValue = getValueFromProperties(headValue)
val newBaseUrl: HttpUrl? = newUrlValue.toHttpUrlOrNull()
// 获取到新BaseUrl进行替换
return if (newBaseUrl != null) {
val oldHttpUrl: HttpUrl = oldRequest.url
val newUrl = oldHttpUrl.newBuilder()
.scheme(newBaseUrl.scheme)
.host(newBaseUrl.host)
.port(newBaseUrl.port)
.build()
chain.proceed(requestBuilder.url(newUrl).build())
} else {
chain.proceed(chain.request())
}
}
/**
* 通过KEY从配置文件中获取值
*/
private fun getValueFromProperties(key: String?): String {
val prop = Properties()
var value = ""
try {
val context = CommonApplication.getContext()
if (context == null) {
Log.e(TAG, "got context null, please init context!")
return value
}
val mAssets = context.assets
if (mAssets == null) {
Log.e(TAG, "got assets null, please check the assets folder")
}
// 通过输入缓冲流进行读取配置文件
val inputStream =
BufferedInputStream(mAssets.open((BuildConfig.BASE_URL_CONFIG_PATH)))
// 加载输入流
prop.load(inputStream)
// 根据关键字获取value值
value = prop.getProperty(key);
} catch (e: Exception) {
Log.e(TAG, e.toString())
}
return value
}
companion object {
const val TAG = "BaseUrlInterceptor"
const val BASE_URL_KEY = "baseurl"
}
}

@ -0,0 +1,73 @@
package com.common.commonlib.net.interceptor
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
/**
* 公共请求拦截器
*
* @author wangym
* @since 2021/7/28
*/
open class RequestHeadInterceptor : CommonInterceptor() {
/**
* 请求头部参数
*/
var mHeaderHashMap: HashMap<String, String> = HashMap()
/**
* 拦截请求并添加参数
*/
override fun intercept(chain: Interceptor.Chain): Response {
val oldRequest: Request = chain.request()
val requestBuilder: Request.Builder = oldRequest.newBuilder()
requestBuilder.method(oldRequest.method, oldRequest.body)
if (mHeaderHashMap.size > 0) {
for (item: Map.Entry<String, String> in mHeaderHashMap.entries) {
requestBuilder.addHeader(item.key, item.value)
}
}
return chain.proceed(requestBuilder.build())
}
/**
* builder构造函数
*/
class Builder {
private var mHttpCommonInterceptor: RequestHeadInterceptor? = null
init {
mHttpCommonInterceptor = RequestHeadInterceptor()
}
fun addHeadParams(key: String, value: String): Builder {
mHttpCommonInterceptor?.mHeaderHashMap?.set(key, value)
return this
}
fun addHeadParams(key: String, value: Int): Builder {
mHttpCommonInterceptor?.mHeaderHashMap?.set(key, value.toString())
return this
}
fun addHeadParams(key: String, value: Long): Builder {
mHttpCommonInterceptor?.mHeaderHashMap?.set(key, value.toString())
return this
}
fun addHeadParams(key: String, value: Float): Builder {
mHttpCommonInterceptor?.mHeaderHashMap?.set(key, value.toString())
return this
}
fun addHeadParams(key: String, value: Double): Builder {
mHttpCommonInterceptor?.mHeaderHashMap?.set(key, value.toString())
return this
}
fun build(): RequestHeadInterceptor {
return mHttpCommonInterceptor!!
}
}
}

@ -0,0 +1,23 @@
package com.common.commonlib.net.interceptor
import okhttp3.Interceptor
import okhttp3.Response
/**
* 响应头部拦截器
*
* @author wangym
* @since 2021/7/28
*/
open class ResponseHeadInterceptor(private var callBack: ResponseCallBack?) : CommonInterceptor() {
override fun intercept(chain: Interceptor.Chain): Response {
// 获取响应
val response = chain.proceed(chain.request())
callBack?.onResult(response)
return response
}
interface ResponseCallBack {
fun onResult(response: Response)
}
}

@ -0,0 +1,14 @@
package com.common.commonlib.net.interfaces
/**
* 获取请求类别基础接口
*
* @author Alex Wang
* @since 2022-2-25
*/
interface ITypedCommon {
/**
* 获取请求类别
*/
fun getType(): String
}

@ -0,0 +1,104 @@
package com.common.commonlib.net.manager
import com.common.commonlib.net.interceptor.CommonInterceptor
/**
* 通用拦截器管理类
* 用于请求的通用拦截器管理
*
* @author Alex Wang
* @since 2022-2-24
*/
object CommonInterceptorManager {
const val TAG = "CommonInterceptorManager"
/**
* 拦截器
*/
private val mInterceptors: HashMap<String, HashMap<Any, CommonInterceptor>> = HashMap()
/**
* 通过类型获取该类型拦截器列表
*
* @param type 拦截器类型
*
* @return 拦截器列表
*/
fun getInterceptorsByType(type: String): Map<Any, CommonInterceptor>? {
return mInterceptors[type]
}
/**
* 通过类型获取该类型拦截器列表
*
* @param type 拦截器类型
*
* @return 拦截器列表
*/
fun getInterceptorsByType(type: String, key: String): CommonInterceptor? {
return mInterceptors[type]?.get(key)
}
/**
* 通过类型列表获取通用拦截器
* @param typeList 拦截器类型
*
* @return 拦截器map
*/
fun getInterceptorsByTypeList(typeList: ArrayList<String>): HashMap<Any, CommonInterceptor> {
val result = HashMap<Any, CommonInterceptor>()
for (item in typeList) {
if (!getInterceptorsByType(item).isNullOrEmpty()) {
result.putAll(getInterceptorsByType(item)!!)
}
}
return result
}
/**
* 添加新的通用拦截器
*
* @param interceptor 拦截器
*/
fun addInterceptorByType(interceptor: CommonInterceptor) {
val type = interceptor.getType()
val key = interceptor.getKey()
var map = mInterceptors[type]
if (map.isNullOrEmpty()) {
map = HashMap()
mInterceptors[type] = map
}
map[key] = interceptor
}
/**
* 删除通用拦截器
*
* @param type 请求类型
* @param key 拦截器key
*/
fun removeInterceptorByType(type: String, key: String) {
val map = mInterceptors[type]
if (!map.isNullOrEmpty()) {
map.remove(key)
}
}
/**
* 删除通用拦截器
*
* @param type 请求类型
*/
fun removeInterceptorByType(type: String) {
mInterceptors.remove(type)
}
/**
* 删除通用拦截器
*
* @param interceptor 拦截器
*/
fun removeInterceptorByType(interceptor: CommonInterceptor) {
removeInterceptorByType(interceptor.getType(), interceptor.getKey())
}
}

@ -0,0 +1,105 @@
package com.common.commonlib.net.manager
import com.common.commonlib.log.Logger.d
import com.common.commonlib.net.interceptor.HostInterceptor
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
/**
* 网络请求管理器
*
* @author wangym
* @since 2021/7/28
*/
object RequestManager {
const val TAG = "RequestManager"
private var logLevel = HttpLoggingInterceptor.Level.NONE
/**
* 默认超时时间(s)
*/
private const val defaultTimeOut: Long = 5
/**
* 默认读取超时时间(s)
*/
private const val defaultReadTimeOut: Long = 10
private var mRetrofit: Retrofit? = null
private val loggingInterceptor = HttpLoggingInterceptor {
d(TAG, it)
}
/**
* 创建retrofit
*
* @param service 类型
* @param interceptors 拦截器
* @param loaderType 请求类别
*/
fun <T> create(service: Class<T>, interceptors: List<Interceptor>?, loaderType: String): T {
init(interceptors, loaderType)
return mRetrofit!!.create(service)
}
private fun init(interceptors: List<Interceptor>?, loaderType: String) {
val builder: OkHttpClient.Builder = OkHttpClient.Builder()
builder.connectTimeout(defaultTimeOut, TimeUnit.SECONDS)
builder.readTimeout(defaultReadTimeOut, TimeUnit.SECONDS)
// 动态HOST替换拦截器
builder.addInterceptor(HostInterceptor())
// 添加通用拦截器
val interceptorList = CommonInterceptorManager.getInterceptorsByType(loaderType)
if (!interceptorList.isNullOrEmpty()) {
d(TAG, "got common interceptor list")
for (item in interceptorList.values) {
builder.addInterceptor(item)
}
}
// 添加接口自定义的拦截器
if (!interceptors.isNullOrEmpty()) {
for (intercept in interceptors) {
builder.addInterceptor(intercept)
}
}
// 添加日志拦截器 日志拦截器,需要添加在所有拦截器之后
loggingInterceptor.level = logLevel
builder.addInterceptor(loggingInterceptor)
mRetrofit = Retrofit.Builder()
.client(builder.build())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://www")
.build()
}
/**
* 打开完整日志
*/
fun openFullLog() {
logLevel = HttpLoggingInterceptor.Level.BODY
}
/**
* 打开基础日志
*/
fun openBasicLog() {
logLevel = HttpLoggingInterceptor.Level.BASIC
}
/**
* 打开头部日志
*/
fun openHeadLog() {
logLevel = HttpLoggingInterceptor.Level.HEADERS
}
}

@ -0,0 +1,48 @@
package com.common.commonlib.utils
import android.content.Context
import android.os.Environment
import com.common.commonlib.net.manager.RequestManager
/**
* 基础工具类
*/
object BaseUtils {
private const val TAG: String = "BaseUtils"
fun <T> isListEmpty(list: List<T>?): Boolean {
return list == null || list.isEmpty()
}
/**
* 获取应用中文件存储
*/
fun getExternalStoragePath(context: Context): String? {
return context.getExternalFilesDir(null)?.path
}
fun getExternalStorageDirectory(context: Context): String? {
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path
}
/**
* 打开网络请求日志
*/
fun enableFullLog() {
RequestManager.openFullLog()
}
/**
* 打开网络请求基础日志
*/
fun enableBasicLog() {
RequestManager.openBasicLog()
}
/**
* 打开网络请求日志
*/
fun enableHeadersLog() {
RequestManager.openHeadLog()
}
}

@ -0,0 +1,83 @@
package com.common.commonlib.utils
import android.annotation.SuppressLint
import java.text.SimpleDateFormat
import java.util.*
/**
* 时间工具类
*/
object DateUtils {
/**
* 如果使用大写HH标识使用24小时显示格式,如果使用小写hh就表示使用12小时制格式
*/
const val DATE_TO_STRING_DETAIL_PATTERN = "yyyy-MM-dd HH:mm:ss"
/**
* -- 显示格式
* */
const val DATE_TO_STRING_SHORT_PATTERN = "yyyy.MM.dd"
/**
* -- 显示格式
* */
const val DATE_TO_STRING_LONG_PATTERN = "yyyy_MM_dd HH:mm:ss"
/**
* 60
*/
const val SECONDS = 60
/**
* 60分钟
*/
const val MINUS = 60
/**
* 得到现在时间
*
* @return
*/
private fun getNow(): Date {
return Date()
}
@SuppressLint("SimpleDateFormat")
fun getNowTimeFormat(format: String): String {
val formatter = SimpleDateFormat(format)
return formatter.format(getNow())
}
/**
* 将int型的数据转化成 XX:XX:XX" 形式的字符串
*/
fun getDurationTimeByInt(source: Int): String {
var temp = source
var h = 0
var m = 0
var s = 0
var result = ""
if (temp >= SECONDS * MINUS) {
h = temp / (SECONDS * MINUS)
temp -= h * (SECONDS * MINUS)
}
if (temp >= SECONDS) {
m = temp / SECONDS
temp -= m * SECONDS
}
s = temp
result = when {
h > 0 -> {
"$h:$m:$s\""
}
m > 0 -> {
"$m:$s\""
}
else -> {
"$s\""
}
}
return result
}
}

@ -0,0 +1,25 @@
package com.common.commonlib.utils
import android.content.Context
/**
* 界面工具类
*/
object DisplayUtils {
/**
* dp px
*/
fun dp2px(context: Context, dpVale: Float): Int {
val density = context.resources.displayMetrics.density
return ((dpVale * density + 0.5f).toInt())
}
/**
* px dp
*/
fun px2dp(context: Context, pxVale: Float): Int {
val density = context.resources.displayMetrics.density
return ((pxVale / density + 0.5f).toInt())
}
}

@ -0,0 +1,136 @@
package com.common.commonlib.utils
import org.apache.commons.net.ftp.FTP
import org.apache.commons.net.ftp.FTPClient
import org.apache.commons.net.ftp.FTPReply
import java.io.BufferedOutputStream
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
/**
* FTP工具类
*
* @author wangym
* @since 2021/7/28
*/
object FTPUtils {
/**
* 默认缓冲池大小
*/
private const val DEFAULT_BUFFER_SIZE = 1024 * 10
/**
* 默认编码模式
*/
private const val DEFAULT_ENCODING = "UTF-8"
/**
* 结果
*/
enum class RESULT { SUCCESS, FAILED }
/**
* ftp上传
* @param url ftp地址
* @param port ftp连接端口号
* @param username 登录用户名
* @param password 登录密码
* @param fileNamePath 本地文件保存路径
* @param fileName 本地文件名
* @return
*/
fun ftpUpload(
url: String?,
port: String,
username: String?,
password: String?,
fileNamePath: String,
fileName: String
): RESULT {
val ftpClient = FTPClient()
val fis: FileInputStream?
var returnMessage = RESULT.FAILED
try {
ftpClient.connect(url, port.toInt())
val loginResult = ftpClient.login(username, password)
val returnCode = ftpClient.replyCode
if (loginResult && FTPReply.isPositiveCompletion(returnCode)) { // 如果登录成功
ftpClient.bufferSize = DEFAULT_BUFFER_SIZE
ftpClient.controlEncoding = DEFAULT_ENCODING
ftpClient.setFileType(FTP.BINARY_FILE_TYPE)
ftpClient.enterLocalPassiveMode()
fis = FileInputStream(fileNamePath + fileName)
ftpClient.storeFile(fileName, fis)
returnMessage = RESULT.SUCCESS //上传成功
} else { // 如果登录失败
returnMessage = RESULT.FAILED
}
} catch (e: IOException) {
e.printStackTrace()
returnMessage = RESULT.FAILED
} finally {
try {
ftpClient.disconnect()
} catch (e: IOException) {
e.printStackTrace()
throw RuntimeException("关闭FTP连接发生异常", e)
}
}
return returnMessage
}
/**
* ftp下载
* @param url
* @param port
* @param username
* @param password
* @param filePath 存放文件的路径
* @param FTP_file 要下载的文件名
* @param SD_file 本地文件名
*/
fun ftpDown(
url: String?,
port: Int,
username: String?,
password: String?,
filePath: String,
FTP_file: String?,
SD_file: String
): RESULT {
val buffOut: BufferedOutputStream?
val ftpClient = FTPClient()
var returnMessage = RESULT.FAILED
try {
ftpClient.connect(url, port)
val loginResult = ftpClient.login(username, password)
val returnCode = ftpClient.replyCode
if (loginResult && FTPReply.isPositiveCompletion(returnCode)) { // 如果登录成功
ftpClient.bufferSize = DEFAULT_BUFFER_SIZE
ftpClient.controlEncoding = DEFAULT_ENCODING
ftpClient.enterLocalPassiveMode()
buffOut = BufferedOutputStream(FileOutputStream(filePath + SD_file), 8 * 1024)
ftpClient.retrieveFile(FTP_file, buffOut)
buffOut.flush()
buffOut.close()
ftpClient.logout()
ftpClient.disconnect()
returnMessage = RESULT.SUCCESS //上传成功
} else { // 如果登录失败
returnMessage = RESULT.FAILED
}
} catch (e: IOException) {
e.printStackTrace()
throw java.lang.RuntimeException("FTP客户端出错", e)
} finally {
try {
ftpClient.disconnect()
} catch (e: IOException) {
e.printStackTrace()
throw java.lang.RuntimeException("关闭FTP连接发生异常", e)
}
}
return returnMessage
}
}

@ -0,0 +1,37 @@
/**
* Kotlin扩展属性和扩展函数
*/
package com.common.commonlib.utils
import android.app.Activity
import android.content.res.Resources
import android.util.TypedValue
import android.view.View
/**
* Kt扩展属性判断Activity是否存活
*/
val Activity?.isAlive: Boolean
get() = !(this?.isDestroyed ?: true || this?.isFinishing ?: true)
/**
* Boolean转Visibility
*/
fun Boolean.toVisibility() = if (this) View.VISIBLE else View.GONE
/**
* Float dp2px
*/
val Float.dp: Float
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
Resources.getSystem().displayMetrics
)
/**
* Int dp2px
*/
val Int.dp: Float
get() = this.toFloat().dp

@ -0,0 +1,73 @@
package com.common.commonlib.utils
import com.common.commonlib.log.Logger.e
import com.tencent.mmkv.MMKV
/**
* MMKV的工具类
*
* @author wangym
* @since 2021-12-14
*/
object MMKVUtils {
const val TAG = "MMKVUtils"
/**
* 保存数据
* @param key
* @param value
* @param args 第一位自定义保存ID
* 第二位是否支持跨进程
*/
fun <T> put(key: String, value: T, vararg args: Any) {
val kv: MMKV = getKV(*args)
when (value) {
is String -> kv.putString(key, value)
is Boolean -> kv.putBoolean(key, value)
is Int -> kv.putInt(key, value)
is Float -> kv.putFloat(key, value)
is Long -> kv.putLong(key, value)
is ByteArray -> kv.putBytes(key, value)
is Set<*> -> kv.putStringSet(key, value as Set<String>)
else -> {
e(TAG, "got error value type,please check")
}
}
}
fun getByteArray(key: String, vararg args: Any): ByteArray? {
return getKV(*args).getBytes(key, null)
}
fun getBoolean(key: String, vararg args: Any): Boolean {
return getKV(*args).getBoolean(key, false)
}
fun getFloat(key: String, vararg args: Any): Float {
return getKV(*args).getFloat(key, 0f)
}
fun getInt(key: String, vararg args: Any): Int {
return getKV(*args).getInt(key, 0)
}
fun getLong(key: String, vararg args: Any): Long {
return getKV(*args).getLong(key, 0L)
}
fun getString(key: String, vararg args: Any): String? {
return getKV(*args).getString(key, "")
}
fun getStringSet(key: String, vararg args: Any): Set<String>? {
return getKV(*args).getStringSet(key, null)
}
private fun getKV(vararg args: Any): MMKV {
return when (args.size) {
1 -> MMKV.mmkvWithID(args[0] as String)
2 -> MMKV.mmkvWithID(args[0] as String, MMKV.MULTI_PROCESS_MODE)
else -> MMKV.defaultMMKV()
}
}
}

@ -0,0 +1,57 @@
package com.common.commonlib.utils
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkRequest
import android.provider.Settings
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
object NetworkHelper {
/**
* 网络状态监听
*/
inline fun observerNetworkState(activity: FragmentActivity, crossinline callback: (Boolean) -> Unit) {
val manager = activity.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
if (activity.isAlive) {
activity.runOnUiThread { callback(true) }
}
}
override fun onLost(network: Network) {
super.onLost(network)
if (activity.isAlive) {
activity.runOnUiThread { callback(false) }
}
}
}
manager.requestNetwork(NetworkRequest.Builder().build(), networkCallback)
activity.lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
manager.unregisterNetworkCallback(networkCallback)
}
})
}
@SuppressLint("MissingPermission")
fun isNetworkConnect(context: Context): Boolean {
val cm = context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return cm.activeNetworkInfo?.isAvailable ?: false
}
fun toNetworkSetting(context: Context) {
val intent = Intent(Settings.ACTION_WIFI_SETTINGS)
context.startActivity(intent)
}
}

@ -0,0 +1,111 @@
package com.common.commonlib.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import top.zibin.luban.Luban
import top.zibin.luban.OnCompressListener
import java.io.File
import java.io.FileOutputStream
import java.text.SimpleDateFormat
import java.util.*
object PhotoUtils {
private const val TAG = "PhotoUtils";
/**
* 根据时间生成照片文件
*/
fun generatePhotoFileByTime(context: Context, photoPath: String): File {
val rootPath: String = context.applicationContext.getExternalFilesDir(null)?.path ?: ""
val savePhotoFolderPath: String = rootPath + photoPath
val savePhotoFolder = File(savePhotoFolderPath)
if (!savePhotoFolder.exists()) {
//创建路径,需要权限
savePhotoFolder.mkdirs()
}
val photoName = SimpleDateFormat("yyyy_MM_dd_hh_mm_ss_SSS").format(Date()) + ".png"
return File(savePhotoFolder, photoName)
}
fun initPicPath(context: Context?, photoPath: String): String {
val rootPath: String = context?.getExternalFilesDir(null)?.path ?: ""
val savePhotoFilePath: String = rootPath + photoPath
val file = File(savePhotoFilePath)
if (!file.exists()) {
//创建路径,需要权限
file.mkdirs()
}
val photoName = SimpleDateFormat("yyyy_MM_dd_hh_mm_ss_SSS").format(Date()) + ".png"
return "$savePhotoFilePath/$photoName"
}
/**
* 得到保存图片的文件夹地址
*/
fun getImageParentPath(context: Context, photoPath: String): String {
val rootPath: String = context.getExternalFilesDir(null)?.path ?: ""
val savePhotoFilePath: String = rootPath + photoPath
val file = File(savePhotoFilePath)
if (!file.exists()) {
//创建路径,需要权限
file.mkdirs()
}
return savePhotoFilePath
}
/**
* 压缩图片使用Luban框架压缩处理
*/
fun compressPicture(
context: Context,
file: File,
targetDir: String,
listener: OnCompressListener
) {
Luban.with(context.applicationContext)
.load(file)
.ignoreBy(100)
.setTargetDir(targetDir)
.setCompressListener(listener).launch()
}
/**
*把其他格式的图片转换成png格式的图片耗时操作
* originImageFile原图片文件
* imgSavePath图片要保存的地址/sdcard/0/emulator/data/xxx.xxx.xx/file/test.png
*/
fun tans2PNGSave(originImageFile: File?, imgSavePath: String?, callBack: TransformCallBack) {
GlobalScope.launch(Dispatchers.IO) {
val decodeFile = BitmapFactory.decodeFile(originImageFile?.absolutePath)
var out: FileOutputStream? = null
try {
out = FileOutputStream(File(imgSavePath))
decodeFile.compress(Bitmap.CompressFormat.PNG, 90, out)
out.flush()
out.close()
} catch (e: Exception) {
Log.e(TAG, "tans2PNGSave error==" + e.message)
} finally {
out?.close()
}
GlobalScope.launch(Dispatchers.Main) {
//通知主线程图片保存成功
callBack.onSuccess()
}
}
}
interface TransformCallBack {
fun onSuccess()
fun onError();
}
}

@ -0,0 +1,59 @@
package com.common.commonlib.utils
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
/**
* sharedPreference管理类
*
* @author wangym
* @since 2021/7/28
*/
object SpManager {
const val HAS_INIT = "has_init"
private var sp: SharedPreferences? = null
private var editor: SharedPreferences.Editor? = null
@SuppressLint("CommitPrefEdits")
fun init(context: Context, name: String, mod: Int) {
sp = context.getSharedPreferences(name, mod)
editor = sp!!.edit()
}
fun putBoolean(key: String, boolean: Boolean) {
editor!!.putBoolean(key, boolean)
editor!!.apply()
}
fun getBoolean(key: String): Boolean {
return sp!!.getBoolean(key, false)
}
fun putInt(key: String, value: Int) {
editor!!.putInt(key, value)
editor!!.apply()
}
fun getInt(key: String): Int {
return sp!!.getInt(key, -1)
}
fun putString(key: String, value: String) {
editor!!.putString(key, value)
editor!!.apply()
}
fun getString(key: String): String? {
return sp!!.getString(key, "")
}
fun putLong(key: String, value: Long) {
editor!!.putLong(key, value)
editor!!.apply()
}
fun getLong(key: String): Long {
return sp!!.getLong(key, -1L)
}
}

@ -0,0 +1,34 @@
package com.common.commonlib.utils
import android.text.TextUtils
/**
* 字符串工具类
*/
class StringUtils {
/**
* 将字符串List转化为用 | 分割的字符串
*/
fun getStringSeparateByLine(list: List<String>): String {
var result = ""
for (item in list.withIndex()) {
result = if (item.index == (list.size - 1)) {
"$result${item.value}"
} else {
"$result${item.value}|"
}
}
return result
}
/**
* | 分割符转化为list
*/
fun getListByString(source: String): List<String> {
if (TextUtils.isEmpty(source)) {
return ArrayList()
}
return source.split("|")
}
}

@ -0,0 +1,252 @@
package com.common.commonlib.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.common.commonlib.R;
import com.common.commonlib.utils.DisplayUtils;
/**
*
*
* @author miracle
* @since 2021/8/2
*/
public class CommonTitleView extends FrameLayout implements View.OnClickListener {
private Context mContext;
private ImageView ivLeft;
private TextView tv_title;
private ImageView ivRight;
private TextView tv_edit;
private int showStyle;
private static final int STYLE_LEFT = 1;
private static final int STYLE_LEFT_MIDDLE = 2;
private static final int STYLE_LEFT_MIDDLE_RIGHT = 3;
private static final int STYLE_MIDDLE = 4;
private static final int STYLE_MIDDLE_RIGHT = 5;
private static final int STYLE_RIGHT = 6;
private static final int STYLE_EDIT = 7;
private String title;
//是否要固定高度的模式不受match parent\warp content等影响
private boolean isFixedHeight;
//设置固定的高度值,默认是48dp
private int fixedHeight;
private static final int DEFAULT_HEIGHT = 48;
private View rlLeftHotZone;
private View rlRightHotZone;
private View rlEditZone;
private Drawable leftDrawable;
private Drawable rightDrawable;
private int bgColor;
public CommonTitleView(Context context) {
this(context, null);
}
public CommonTitleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CommonTitleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init(attrs);
}
private void init(AttributeSet attrs) {
initAttrs(attrs);
View inflate = LayoutInflater.from(mContext).inflate(R.layout.common_title_view, this, true);
View root_view = findViewById(R.id.root_view);
ivLeft = findViewById(R.id.iv_left);
ivRight = findViewById(R.id.iv_right);
tv_title = findViewById(R.id.tv_title);
rlLeftHotZone = findViewById(R.id.rl_left_hot_zone);
rlRightHotZone = findViewById(R.id.rl_add_hot_zone);
tv_edit = findViewById(R.id.tv_edit);
rlEditZone = findViewById(R.id.rl_edit_hot_zone);
root_view.setBackgroundColor(bgColor);
if (leftDrawable != null) {
ivLeft.setImageDrawable(leftDrawable);
}
if (rightDrawable != null) {
ivRight.setImageDrawable(rightDrawable);
}
initStyle();
setTitle(title);
}
private void initAttrs(AttributeSet attrs) {
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CommonTitleView);
showStyle = typedArray.getInt(R.styleable.CommonTitleView_show_style, STYLE_MIDDLE);
title = typedArray.getString(R.styleable.CommonTitleView_title);
isFixedHeight = typedArray.getBoolean(R.styleable.CommonTitleView_is_fixed_height, true);
fixedHeight = typedArray.getInteger(R.styleable.CommonTitleView_fixed_height, DisplayUtils.INSTANCE.dp2px(mContext, DEFAULT_HEIGHT));
leftDrawable = typedArray.getDrawable(R.styleable.CommonTitleView_drawableLeft);
rightDrawable = typedArray.getDrawable(R.styleable.CommonTitleView_drawableRight);
bgColor = typedArray.getColor(R.styleable.CommonTitleView_bgColor, getResources().getColor(R.color.title_bar_bg_color));
typedArray.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (isFixedHeight) {
int makeMeasureSpec = MeasureSpec.makeMeasureSpec(fixedHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, makeMeasureSpec);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
private void initStyle() {
switch (showStyle) {
case STYLE_LEFT:
ivLeft.setVisibility(VISIBLE);
rlLeftHotZone.setOnClickListener(this);
tv_title.setVisibility(INVISIBLE);
tv_title.setText("");
ivRight.setVisibility(INVISIBLE);
rlRightHotZone.setOnClickListener(null);
break;
case STYLE_LEFT_MIDDLE:
ivLeft.setVisibility(VISIBLE);
rlLeftHotZone.setOnClickListener(this);
tv_title.setVisibility(VISIBLE);
tv_title.setText("");
ivRight.setVisibility(INVISIBLE);
rlRightHotZone.setOnClickListener(null);
break;
case STYLE_MIDDLE_RIGHT:
ivLeft.setVisibility(INVISIBLE);
rlLeftHotZone.setOnClickListener(null);
tv_title.setVisibility(VISIBLE);
tv_title.setText("");
ivRight.setVisibility(VISIBLE);
rlRightHotZone.setOnClickListener(this);
break;
case STYLE_RIGHT:
ivLeft.setVisibility(INVISIBLE);
rlLeftHotZone.setOnClickListener(null);
tv_title.setVisibility(INVISIBLE);
tv_title.setText("");
ivRight.setVisibility(VISIBLE);
rlRightHotZone.setOnClickListener(this);
break;
case STYLE_LEFT_MIDDLE_RIGHT:
ivLeft.setVisibility(VISIBLE);
rlLeftHotZone.setOnClickListener(this);
tv_title.setVisibility(VISIBLE);
tv_title.setText("");
ivRight.setVisibility(VISIBLE);
rlRightHotZone.setOnClickListener(this);
break;
case STYLE_EDIT:
ivLeft.setVisibility(VISIBLE);
rlLeftHotZone.setOnClickListener(this);
tv_title.setVisibility(VISIBLE);
tv_title.setText("");
ivRight.setVisibility(GONE);
rlRightHotZone.setVisibility(GONE);
rlRightHotZone.setOnClickListener(null);
tv_edit.setVisibility(VISIBLE);
rlEditZone.setVisibility(VISIBLE);
rlEditZone.setOnClickListener(this);
break;
case STYLE_MIDDLE:
default:
ivLeft.setVisibility(INVISIBLE);
rlLeftHotZone.setOnClickListener(null);
tv_title.setVisibility(VISIBLE);
tv_title.setText("");
ivRight.setVisibility(INVISIBLE);
rlRightHotZone.setOnClickListener(null);
break;
}
}
public void setTitle(String title) {
this.title = title;
tv_title.setText(title);
}
@Override
public void onClick(View v) {
int viewId = v.getId();
if (viewId == R.id.rl_left_hot_zone) {
if (leftIconListener != null) {
leftIconListener.onClick();
}
} else if (viewId == R.id.rl_add_hot_zone) {
if (rightIconListener != null) {
rightIconListener.onClick();
}
} else if (viewId == R.id.rl_edit_hot_zone) {
if (editListener != null) {
editListener.onClick();
}
}
}
public interface LeftIconListener {
void onClick();
}
private LeftIconListener leftIconListener;
public void setLeftIconListener(LeftIconListener leftIconListener) {
this.leftIconListener = leftIconListener;
}
public interface RightIconListener {
void onClick();
}
private RightIconListener rightIconListener;
public void setRightIconListener(RightIconListener rightIconListener) {
this.rightIconListener = rightIconListener;
}
public interface EditListener {
void onClick();
}
private EditListener editListener;
public void setEditListener(EditListener editListener) {
this.editListener = editListener;
}
public int getEditState() {
if (tv_edit.getText().equals(mContext.getResources().getString(R.string.edit))) {
return 0;
} else if (tv_edit.getText().equals(mContext.getResources().getString(R.string.save))) {
return 1;
} else {
return -1;
}
}
public void setEditText(int state) {
if (state == 0) {
tv_edit.setText(mContext.getResources().getString(R.string.edit));
} else if (state == 1) {
tv_edit.setText(mContext.getResources().getString(R.string.save));
} else {
tv_edit.setText("");
}
}
}

@ -0,0 +1,161 @@
package com.common.commonlib.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import androidx.annotation.Nullable;
import com.common.commonlib.R;
/**
* micView
*
* @author miracle
* @since 2021/8/2
*/
public class MikeView extends View {
//进度条格数
private final int COUNT = 16;
//宽、高、间隔、进度条居上距离
private int mVoiceRectW = 1;
private int mVoiceRectH = 10;
private int mInterval = 3;
private int mVoiceRectMarginTop = 83;
//mic 文字 进度条 背景
private Paint mImagePaint;
private Paint mVoiceRectPaint;
private Paint mBackgroundPaint;
private Bitmap mVoiceRecording;//mic图片
//屏幕宽高
private int SCREEN_HEIGHT = 0;
private int SCREEN_WIDTH = 0;
private int mVoiceRectStart = 0, mDescriptionW = 0;//进度条开始左边位置、文字长度
private int mCurrentPosition = 0;//当前位置
private int mBackgroundRound = 3;//背景圆角
private int mImageW = 42;//新mic图片宽
private int mImageH = 42;//新mic图片高
private int mMarginTop = 25, mTextMarginBottom = 11;//mic图片居上距离 文字居底部距离
public MikeView(Context context) {
this(context, null);
}
public MikeView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MikeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
DisplayMetrics dm = getResources().getDisplayMetrics();
mImageW = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mImageW, dm);
mImageH = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mImageH, dm);
mMarginTop = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mMarginTop, dm);
mTextMarginBottom = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mTextMarginBottom, dm);
mVoiceRectW = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mVoiceRectW, dm);
mVoiceRectH = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mVoiceRectH, dm);
mInterval = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mInterval, dm);
mVoiceRectMarginTop = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mVoiceRectMarginTop, dm);
mBackgroundRound = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mBackgroundRound, dm);
mImagePaint = new Paint();
mVoiceRectPaint = new Paint();
mVoiceRectPaint.setColor(Color.WHITE);
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(Color.TRANSPARENT);//背景颜色
mVoiceRecording = BitmapFactory.decodeResource(getResources(), R.drawable.mike);
mVoiceRecording = scaleBitmap(mVoiceRecording, mImageW, mImageH);
setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (SCREEN_HEIGHT == 0 || SCREEN_WIDTH == 0) {
SCREEN_HEIGHT = getHeight();
SCREEN_WIDTH = getWidth();
}
if (mVoiceRectStart <= 0) {
mVoiceRectStart = SCREEN_WIDTH / 2 - (COUNT * mVoiceRectW + (COUNT - 1) * mInterval) / 2;
}
canvas.drawRoundRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mBackgroundRound, mBackgroundRound, mBackgroundPaint);//画灰色背景和圆角
canvas.drawBitmap(mVoiceRecording, SCREEN_WIDTH / 2 - mVoiceRecording.getWidth() / 2, mMarginTop, mImagePaint);//画mic图片
//画前几个是白色 后几个是灰色
for (int i = 0; i < COUNT; i++) {
if (i < mCurrentPosition) {
mVoiceRectPaint.setColor(Color.WHITE);
} else {
mVoiceRectPaint.setColor(Color.parseColor("#73FFFFFF"));
}
canvas.drawRect(i * mVoiceRectW + (i * mInterval) + mVoiceRectStart, mVoiceRectMarginTop, i * mVoiceRectW + (i * mInterval) + mVoiceRectW + mVoiceRectStart, mVoiceRectMarginTop + mVoiceRectH, mVoiceRectPaint);
}
}
public void setIndex(int p) {
if (p < 0) {
p = 0;
}
if (p >= COUNT) {
p = COUNT - 1;
}
mCurrentPosition = p;
postInvalidate();
}
public void setIndex(double percent) {
int p = (int) (COUNT * percent);
setIndex(p);
}
/**
* bitmap
*
* @param origin
* @param newWidth
* @param newHeight
* @return bitmap
*/
private Bitmap scaleBitmap(Bitmap origin, int newWidth, int newHeight) {
if (origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);// 使用后乘
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
if (!origin.isRecycled()) {
origin.recycle();
}
return newBM;
}
}

@ -0,0 +1,217 @@
package com.common.commonlib.view;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* RecyclerView
*
* @author wangym
* @since 2021/8/2
*/
public class SlideRecyclerView extends RecyclerView {
private static final String TAG = "SlideRecyclerView";
private static final int INVALID_POSITION = -1; // 触摸到的点不在子View范围内
private static final int INVALID_CHILD_WIDTH = -1; // 子ItemView不含两个子View
private static final int SNAP_VELOCITY = 600; // 最小滑动速度
private VelocityTracker mVelocityTracker; // 速度追踪器
private int mTouchSlop; // 认为是滑动的最小距离(一般由系统提供)
private Rect mTouchFrame; // 子View所在的矩形范围
private Scroller mScroller;
private float mLastX; // 滑动过程中记录上次触碰点X
private float mFirstX, mFirstY; // 首次触碰范围
private boolean mIsSlide; // 是否滑动子View
private ViewGroup mFlingView; // 触碰的子View
private int mPosition; // 触碰的view的位置
private int mMenuViewWidth; // 菜单按钮宽度
public SlideRecyclerView(Context context) {
this(context, null);
}
public SlideRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mScroller = new Scroller(context);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int x = (int) e.getX();
int y = (int) e.getY();
obtainVelocity(e);
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) { // 如果动画还没停止,则立即终止动画
mScroller.abortAnimation();
}
mFirstX = mLastX = x;
mFirstY = y;
mPosition = pointToPosition(x, y); // 获取触碰点所在的position
if (mPosition != INVALID_POSITION) {
View view = mFlingView;
// 获取触碰点所在的view
mFlingView = (ViewGroup) getChildAt(mPosition - ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition());
// 这里判断一下如果之前触碰的view已经打开而当前碰到的view不是那个view则立即关闭之前的view此处并不需要担动画没完成冲突因为之前已经abortAnimation
if (view != null && mFlingView != view && view.getScrollX() != 0) {
view.scrollTo(0, 0);
}
// 这里进行了强制的要求RecyclerView的子ViewGroup必须要有2个子view,这样菜单按钮才会有值,
// 需要注意的是:如果不定制RecyclerView的子View则要求子View必须要有固定的width。
// 比如使用LinearLayout作为根布局而content部分width已经是match_parent此时如果菜单view用的是wrap_contentmenu的宽度就会为0。
if (mFlingView.getChildCount() == 2) {
mMenuViewWidth = mFlingView.getChildAt(1).getWidth();
} else {
mMenuViewWidth = INVALID_CHILD_WIDTH;
}
}
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.computeCurrentVelocity(1000);
// 此处有俩判断,满足其一则认为是侧滑:
// 1.如果x方向速度大于y方向速度且大于最小速度限制
// 2.如果x方向的侧滑距离大于y方向滑动距离且x方向达到最小滑动距离
float xVelocity = mVelocityTracker.getXVelocity();
float yVelocity = mVelocityTracker.getYVelocity();
if (Math.abs(xVelocity) > SNAP_VELOCITY && Math.abs(xVelocity) > Math.abs(yVelocity)
|| Math.abs(x - mFirstX) >= mTouchSlop
&& Math.abs(x - mFirstX) > Math.abs(y - mFirstY)) {
mIsSlide = true;
return true;
}
break;
case MotionEvent.ACTION_UP:
releaseVelocity();
break;
}
return super.onInterceptTouchEvent(e);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if (mIsSlide && mPosition != INVALID_POSITION) {
float x = e.getX();
obtainVelocity(e);
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: // 因为没有拦截,所以不会被调用到
break;
case MotionEvent.ACTION_MOVE:
// 随手指滑动
if (mMenuViewWidth != INVALID_CHILD_WIDTH) {
float dx = mLastX - x;
if (mFlingView.getScrollX() + dx <= mMenuViewWidth
&& mFlingView.getScrollX() + dx > 0) {
mFlingView.scrollBy((int) dx, 0);
}
mLastX = x;
}
break;
case MotionEvent.ACTION_UP:
if (mMenuViewWidth != INVALID_CHILD_WIDTH) {
int scrollX = mFlingView.getScrollX();
mVelocityTracker.computeCurrentVelocity(1000);
// 此处有两个原因决定是否打开菜单:
// 1.菜单被拉出宽度大于菜单宽度一半;
// 2.横向滑动速度大于最小滑动速度;
// 注意:之所以要小于负值,是因为向左滑则速度为负值
if (mVelocityTracker.getXVelocity() < -SNAP_VELOCITY) { // 向左侧滑达到侧滑最低速度,则打开
mScroller.startScroll(scrollX, 0, mMenuViewWidth - scrollX, 0, Math.abs(mMenuViewWidth - scrollX));
} else if (mVelocityTracker.getXVelocity() >= SNAP_VELOCITY) { // 向右侧滑达到侧滑最低速度,则关闭
mScroller.startScroll(scrollX, 0, -scrollX, 0, Math.abs(scrollX));
} else if (scrollX >= mMenuViewWidth / 2) { // 如果超过删除按钮一半,则打开
mScroller.startScroll(scrollX, 0, mMenuViewWidth - scrollX, 0, Math.abs(mMenuViewWidth - scrollX));
} else { // 其他情况则关闭
mScroller.startScroll(scrollX, 0, -scrollX, 0, Math.abs(scrollX));
}
invalidate();
}
mMenuViewWidth = INVALID_CHILD_WIDTH;
mIsSlide = false;
mPosition = INVALID_POSITION;
releaseVelocity(); // 这里之所以会调用是因为如果前面拦截了就不会执行ACTION_UP,需要在这里释放追踪
break;
}
return true;
} else {
// 此处防止RecyclerView正常滑动时还有菜单未关闭
closeMenu();
// Velocity这里的释放是防止RecyclerView正常拦截了但是在onTouchEvent中却没有被释放
// 有三种情况1.onInterceptTouchEvent并未拦截在onInterceptTouchEvent方法中DOWN和UP一对获取和释放
// 2.onInterceptTouchEvent拦截DOWN获取但事件不是被侧滑处理需要在这里进行释放
// 3.onInterceptTouchEvent拦截DOWN获取事件被侧滑处理则在onTouchEvent的UP中释放。
releaseVelocity();
}
return super.onTouchEvent(e);
}
private void releaseVelocity() {
if (mVelocityTracker != null) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
private void obtainVelocity(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
public int pointToPosition(int x, int y) {
if (null == getLayoutManager()) return INVALID_POSITION;
int firstPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
Rect frame = mTouchFrame;
if (frame == null) {
mTouchFrame = new Rect();
frame = mTouchFrame;
}
final int count = getChildCount();
for (int i = count - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child.getVisibility() == View.VISIBLE) {
child.getHitRect(frame);
if (frame.contains(x, y)) {
return firstPosition + i;
}
}
}
return INVALID_POSITION;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
mFlingView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
/**
* view
* item
*/
public void closeMenu() {
if (mFlingView != null && mFlingView.getScrollX() != 0) {
mFlingView.scrollTo(0, 0);
}
}
}

@ -0,0 +1,38 @@
package com.common.commonlib.view;
import android.graphics.Rect;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
import org.jetbrains.annotations.NotNull;
/**
* rv
*
* @author wangym
* @since 2021/7/28
*/
public class SpaceItemDecoration extends RecyclerView.ItemDecoration {
/**
*
*/
private static final int DEFAULT_MEDIA_SIZE = 3;
/**
*
*/
private static final int DEFAULT_MEDIA_OFFSET = 30;
@Override
public void getItemOffsets(@NotNull Rect outRect, @NotNull View view, RecyclerView parent, @NotNull RecyclerView.State state) {
int temp = parent.getChildAdapterPosition(view) % DEFAULT_MEDIA_SIZE;
outRect.left = DEFAULT_MEDIA_OFFSET * (temp);
if (parent.getChildAdapterPosition(view) >= DEFAULT_MEDIA_SIZE) {
outRect.top = DEFAULT_MEDIA_OFFSET;
} else {
outRect.top = 0;
}
}
}

@ -0,0 +1,34 @@
package com.common.commonlib.view.activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.common.commonlib.R;
/**
* activity
*
* @author miracle
* @since 2021/8/2
*/
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initStatusBar();
}
private void initStatusBar() {
Window window = getWindow();
//After LOLLIPOP not translucent status bar
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//Then call setStatusBarColor.
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(getResources().getColor(R.color.title_bar_bg_color));
}
}

@ -0,0 +1,133 @@
package com.common.commonlib.view.activity;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.common.commonlib.R;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Activity
* ActivityrequestPermission
*
*
* @author miracle
* @since 2021/8/2
*/
public class PermissionCompatActivity extends AppCompatActivity {
private static final String TAG = "PermissionCompatActivity";
protected String[] permissionArray;
private List<String> unAuthorizedPermissionList;
private static final int REQUEST_PERMISSIONS_CODE = 1608;
private Dialog mPermissionDialog;
@Override
protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onRestart() {
super.onRestart();
checkAndRequestPermission();
}
protected void requestPermission(String[] args) {
permissionArray = args;
checkAndRequestPermission();
}
@SuppressLint("LongLogTag")
private void checkAndRequestPermission() {
if (permissionArray != null && permissionArray.length > 0) {
unAuthorizedPermissionList = new ArrayList<>();
for (int i = 0; i < permissionArray.length; i++) {
if (ContextCompat.checkSelfPermission(this, permissionArray[i]) != PackageManager.PERMISSION_GRANTED) {
//添加还未授予的权限
unAuthorizedPermissionList.add(permissionArray[i]);
}
}
//申请权限
if (unAuthorizedPermissionList.size() > 0) {//有权限没有通过,需要申请
ActivityCompat.requestPermissions(this, permissionArray, REQUEST_PERMISSIONS_CODE);
} else {
//说明权限都已经通过,可以做你想做的事情去
Log.i(TAG, "所有权限都申请到了");
}
}
}
@SuppressLint("LongLogTag")
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull @NotNull String[] permissions, @NonNull @NotNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSIONS_CODE) {
//有权限没有通过
boolean hasPermissionDismiss = false;
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] == -1) {
hasPermissionDismiss = true;
}
}
//如果有权限没有被允许
if (hasPermissionDismiss) {
//跳转到系统设置权限页面,或者直接关闭页面,不让他继续访问
showPermissionDialog();
} else {
//全部权限通过
Log.i(TAG, "所有权限申请完成!");
}
}
}
private void showPermissionDialog() {
if (mPermissionDialog == null) {
mPermissionDialog = new AlertDialog.Builder(this)
.setMessage(getResources().getString(R.string.default_un_authorized_prompt))
.setPositiveButton(getResources().getString(R.string.default_dialog_setting), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
cancelPermissionDialog();
String packageName = getApplication().getPackageName();
Uri packageURI = Uri.parse("package:" + packageName);
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
startActivity(intent);
}
}).setNegativeButton(getResources().getString(R.string.default_dialog_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//关闭页面或者做其他操作
cancelPermissionDialog();
finish();
}
}).create();
}
mPermissionDialog.show();
}
//关闭对话框
private void cancelPermissionDialog() {
if (mPermissionDialog != null && mPermissionDialog.isShowing()) {
mPermissionDialog.cancel();
}
}
}

@ -0,0 +1,164 @@
package com.common.commonlib.view.adapter;
import android.content.Context;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.MultiTransformation;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.common.commonlib.R;
import com.common.commonlib.bean.GridPicBean;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class PicGridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_ONE = 0;
private static final int TYPE_TWO = 1;
private final List<GridPicBean> gridPicBeanList = new ArrayList<>();
private Context mContext;
private onAddClickListener onAddClickListener;
public PicGridAdapter(Context context) {
mContext = context;
}
@Override
public int getItemViewType(int position) {
if (position == gridPicBeanList.size()) {
return TYPE_ONE;
} else {
return TYPE_TWO;
}
}
@NotNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder;
if (viewType == TYPE_ONE) {
View inflate = LayoutInflater.from(mContext).inflate(R.layout.item_grid_pic_add, parent, false);
viewHolder = new AddIconViewHolder(inflate);
} else {
View inflate = LayoutInflater.from(mContext).inflate(R.layout.item_grid_pic_show, parent, false);
viewHolder = new PicViewHolder(inflate);
}
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (position < gridPicBeanList.size()) {
if (holder instanceof PicViewHolder) {
PicViewHolder viewHolder = (PicViewHolder) holder;
GridPicBean gridPicBean = gridPicBeanList.get(position);
Glide.with(mContext).load(gridPicBean.getImagePath())
.transform(new MultiTransformation(new CenterCrop(), new RoundedCorners(5)))
.into(viewHolder.imageView);
viewHolder.imageDeleteView.setOnClickListener(l -> {
gridPicBeanList.remove(holder.getAdapterPosition());
notifyItemRemoved(holder.getAdapterPosition());
});
AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) viewHolder.ivLoading.getDrawable();
if (gridPicBean.isConverting()) {
viewHolder.ivLoading.setVisibility(View.VISIBLE);
viewHolder.imageDeleteView.setVisibility(View.GONE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
drawable.reset();
}
drawable.start();
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
drawable.reset();
}
viewHolder.ivLoading.setVisibility(View.GONE);
viewHolder.imageDeleteView.setVisibility(View.VISIBLE);
}
}
} else {
if (holder instanceof AddIconViewHolder) {
AddIconViewHolder viewHolder = (AddIconViewHolder) holder;
viewHolder.imageView.setOnClickListener(v -> {
if (gridPicBeanList.size() < 4) {
if (onAddClickListener != null) {
onAddClickListener.onClick(viewHolder.getAdapterPosition());
}
} else {
Toast.makeText(mContext, "最多添加四张!", Toast.LENGTH_SHORT).show();
}
});
}
}
}
@Override
public int getItemCount() {
return gridPicBeanList.size() + 1;
}
public void setOnAddClickListener(PicGridAdapter.onAddClickListener onAddClickListener) {
this.onAddClickListener = onAddClickListener;
}
public void addPicData(GridPicBean gridPicBean) {
gridPicBeanList.add(gridPicBean);
notifyItemRangeChanged(gridPicBeanList.size() - 1, 2);
}
public void clearPicData() {
gridPicBeanList.clear();
}
public void replaceData(int position, GridPicBean gridPicBean) {
gridPicBeanList.set(position, gridPicBean);
notifyDataSetChanged();
}
public List<String> getImagePathList() {
List<String> images = new ArrayList<>();
for (GridPicBean gridPicBean : gridPicBeanList) {
images.add(gridPicBean.getImagePath());
}
return images;
}
interface onAddClickListener {
void onClick(int position);
}
private static class AddIconViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
public AddIconViewHolder(@NonNull @NotNull View itemView) {
super(itemView);
imageView = (ImageView) itemView.findViewById(R.id.bg_pic_add);
}
}
private static class PicViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private ImageView imageDeleteView;
private ImageView ivLoading;
public PicViewHolder(@NonNull @NotNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.pic_iv);
imageDeleteView = itemView.findViewById(R.id.pic_delete_iv);
ivLoading = itemView.findViewById(R.id.iv_loading);
}
}
}

@ -0,0 +1,18 @@
package com.common.commonlib.view.fragment;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import org.jetbrains.annotations.NotNull;
public class BaseFragment extends Fragment {
public Context mContext;
@Override
public void onAttach(@NonNull @NotNull Context context) {
super.onAttach(context);
mContext = context;
}
}

@ -0,0 +1,59 @@
package com.common.commonlib.view.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
abstract class BaseNavigationFragment : BaseFragment() {
private var isNavigationViewInit = false//记录是否已经初始化过一次视图
private var lastView: View? = null//记录上次创建的view
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
doPrepare()
//如果fragment的view已经创建则不再重新创建
if (lastView == null) {
lastView = getRootView(
inflater,
container,
savedInstanceState
)
}
return lastView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isNavigationViewInit) {//初始化过视图则不再进行view和data初始化
super.onViewCreated(view, savedInstanceState)
initView()
initData()
initOthers()
isNavigationViewInit = true
}
}
abstract fun getRootView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?
abstract fun initData()
abstract fun initView()
abstract fun initOthers()
abstract fun doPrepare()
fun getIsNavigationViewInit():Boolean{
return isNavigationViewInit
}
fun setIsNavigationViewInit(isNavigationViewInit:Boolean){
this.isNavigationViewInit=isNavigationViewInit
}
}

@ -0,0 +1,63 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.common.commonlib.R;
public class AutoPlayRecyclerView extends RecyclerView {
private AutoPlaySnapHelper autoPlaySnapHelper;
public AutoPlayRecyclerView(Context context) {
this(context, null);
}
public AutoPlayRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoPlayRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AutoPlayRecyclerView);
final int timeInterval = typedArray.getInt(R.styleable.AutoPlayRecyclerView_timeInterval, AutoPlaySnapHelper.TIME_INTERVAL);
final int direction = typedArray.getInt(R.styleable.AutoPlayRecyclerView_direction, AutoPlaySnapHelper.RIGHT);
typedArray.recycle();
autoPlaySnapHelper = new AutoPlaySnapHelper(timeInterval, direction);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean result = super.dispatchTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (autoPlaySnapHelper != null) {
autoPlaySnapHelper.pause();
}
break;
case MotionEvent.ACTION_UP:
if (autoPlaySnapHelper != null) {
autoPlaySnapHelper.start();
}
}
return result;
}
public void start() {
autoPlaySnapHelper.start();
}
public void pause() {
autoPlaySnapHelper.pause();
}
@Override
public void setLayoutManager(LayoutManager layout) {
super.setLayoutManager(layout);
autoPlaySnapHelper.attachToRecyclerView(this);
}
}

@ -0,0 +1,116 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.os.Handler;
import android.os.Looper;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
/**
* Used by {@link AutoPlayRecyclerView} to implement auto play effect
*/
class AutoPlaySnapHelper extends CenterSnapHelper {
final static int TIME_INTERVAL = 2000;
final static int LEFT = 1;
final static int RIGHT = 2;
private Handler handler;
private int timeInterval;
private Runnable autoPlayRunnable;
private boolean runnableAdded;
private int direction;
AutoPlaySnapHelper(int timeInterval, int direction) {
checkTimeInterval(timeInterval);
checkDirection(direction);
handler = new Handler(Looper.getMainLooper());
this.timeInterval = timeInterval;
this.direction = direction;
}
@Override
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException {
if (mRecyclerView == recyclerView) {
return; // nothing to do
}
if (mRecyclerView != null) {
destroyCallbacks();
}
mRecyclerView = recyclerView;
if (mRecyclerView != null) {
final RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
if (!(layoutManager instanceof ViewPagerLayoutManager)) return;
setupCallbacks();
mGravityScroller = new Scroller(mRecyclerView.getContext(),
new DecelerateInterpolator());
snapToCenterView((ViewPagerLayoutManager) layoutManager,
((ViewPagerLayoutManager) layoutManager).onPageChangeListener);
((ViewPagerLayoutManager) layoutManager).setInfinite(true);
autoPlayRunnable = new Runnable() {
@Override
public void run() {
final int currentPosition =
((ViewPagerLayoutManager) layoutManager).getCurrentPositionOffset() *
(((ViewPagerLayoutManager) layoutManager).getReverseLayout() ? -1 : 1);
ScrollHelper.smoothScrollToPosition(mRecyclerView,
(ViewPagerLayoutManager) layoutManager, direction == RIGHT ? currentPosition + 1 : currentPosition - 1);
handler.postDelayed(autoPlayRunnable, timeInterval);
}
};
handler.postDelayed(autoPlayRunnable, timeInterval);
runnableAdded = true;
}
}
@Override
void destroyCallbacks() {
super.destroyCallbacks();
if (runnableAdded) {
handler.removeCallbacks(autoPlayRunnable);
runnableAdded = false;
}
}
void pause() {
if (runnableAdded) {
handler.removeCallbacks(autoPlayRunnable);
runnableAdded = false;
}
}
void start() {
if (!runnableAdded) {
handler.postDelayed(autoPlayRunnable, timeInterval);
runnableAdded = true;
}
}
void setTimeInterval(int timeInterval) {
checkTimeInterval(timeInterval);
this.timeInterval = timeInterval;
}
void setDirection(int direction) {
checkDirection(direction);
this.direction = direction;
}
private void checkDirection(int direction) {
if (direction != LEFT && direction != RIGHT)
throw new IllegalArgumentException("direction should be one of left or right");
}
private void checkTimeInterval(int timeInterval) {
if (timeInterval <= 0)
throw new IllegalArgumentException("time interval should greater than 0");
}
}

@ -0,0 +1,167 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.content.Context;
import android.view.View;
/**
* An implementation of {@link ViewPagerLayoutManager}
* which layouts items like carousel
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class CarouselLayoutManager extends ViewPagerLayoutManager {
private int itemSpace;
private float minScale;
private float moveSpeed;
public CarouselLayoutManager(Context context, int itemSpace) {
this(new Builder(context, itemSpace));
}
public CarouselLayoutManager(Context context, int itemSpace, int orientation) {
this(new Builder(context, itemSpace).setOrientation(orientation));
}
public CarouselLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) {
this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout));
}
public CarouselLayoutManager(Builder builder) {
this(builder.context, builder.itemSpace, builder.minScale, builder.orientation,
builder.maxVisibleItemCount, builder.moveSpeed, builder.distanceToBottom,
builder.reverseLayout);
}
private CarouselLayoutManager(Context context, int itemSpace, float minScale, int orientation,
int maxVisibleItemCount, float moveSpeed, int distanceToBottom,
boolean reverseLayout) {
super(context, orientation, reverseLayout);
setEnableBringCenterToFront(true);
setDistanceToBottom(distanceToBottom);
setMaxVisibleItemCount(maxVisibleItemCount);
this.itemSpace = itemSpace;
this.minScale = minScale;
this.moveSpeed = moveSpeed;
}
public int getItemSpace() {
return itemSpace;
}
public float getMinScale() {
return minScale;
}
public float getMoveSpeed() {
return moveSpeed;
}
public void setItemSpace(int itemSpace) {
assertNotInLayoutOrScroll(null);
if (this.itemSpace == itemSpace) return;
this.itemSpace = itemSpace;
removeAllViews();
}
public void setMinScale(float minScale) {
assertNotInLayoutOrScroll(null);
if (minScale > 1f) minScale = 1f;
if (this.minScale == minScale) return;
this.minScale = minScale;
requestLayout();
}
public void setMoveSpeed(float moveSpeed) {
assertNotInLayoutOrScroll(null);
if (this.moveSpeed == moveSpeed) return;
this.moveSpeed = moveSpeed;
}
@Override
protected float setInterval() {
return (mDecoratedMeasurement - itemSpace);
}
@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
float scale = calculateScale(targetOffset + mSpaceMain);
itemView.setScaleX(scale);
itemView.setScaleY(scale);
}
@Override
protected float getDistanceRatio() {
if (moveSpeed == 0) return Float.MAX_VALUE;
return 1 / moveSpeed;
}
@Override
protected float setViewElevation(View itemView, float targetOffset) {
return itemView.getScaleX() * 5;
}
private float calculateScale(float x) {
float deltaX = Math.abs(x - (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2f);
return (minScale - 1) * deltaX / (mOrientationHelper.getTotalSpace() / 2f) + 1f;
}
public static class Builder {
private static final float DEFAULT_SPEED = 1f;
private static final float MIN_SCALE = 0.5f;
private Context context;
private int itemSpace;
private int orientation;
private float minScale;
private float moveSpeed;
private int maxVisibleItemCount;
private boolean reverseLayout;
private int distanceToBottom;
public Builder(Context context, int itemSpace) {
this.itemSpace = itemSpace;
this.context = context;
orientation = HORIZONTAL;
minScale = MIN_SCALE;
this.moveSpeed = DEFAULT_SPEED;
reverseLayout = false;
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
}
public Builder setOrientation(int orientation) {
this.orientation = orientation;
return this;
}
public Builder setMinScale(float minScale) {
this.minScale = minScale;
return this;
}
public Builder setReverseLayout(boolean reverseLayout) {
this.reverseLayout = reverseLayout;
return this;
}
public Builder setMoveSpeed(float moveSpeed) {
this.moveSpeed = moveSpeed;
return this;
}
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
this.maxVisibleItemCount = maxVisibleItemCount;
return this;
}
public Builder setDistanceToBottom(int distanceToBottom) {
this.distanceToBottom = distanceToBottom;
return this;
}
public CarouselLayoutManager build() {
return new CarouselLayoutManager(this);
}
}
}

@ -0,0 +1,158 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
public class CenterSnapHelper extends RecyclerView.OnFlingListener {
RecyclerView mRecyclerView;
Scroller mGravityScroller;
/**
* when the dataSet is extremely large
* {@link #snapToCenterView(ViewPagerLayoutManager, ViewPagerLayoutManager.OnPageChangeListener)}
* may keep calling itself because the accuracy of float
*/
private boolean snapToCenter = false;
// Handles the snap on scroll case.
private final RecyclerView.OnScrollListener mScrollListener =
new RecyclerView.OnScrollListener() {
boolean mScrolled = false;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
final ViewPagerLayoutManager layoutManager =
(ViewPagerLayoutManager) recyclerView.getLayoutManager();
final ViewPagerLayoutManager.OnPageChangeListener onPageChangeListener =
layoutManager.onPageChangeListener;
if (onPageChangeListener != null) {
onPageChangeListener.onPageScrollStateChanged(newState);
}
if (newState == RecyclerView.SCROLL_STATE_IDLE && mScrolled) {
mScrolled = false;
if (!snapToCenter) {
snapToCenter = true;
snapToCenterView(layoutManager, onPageChangeListener);
} else {
snapToCenter = false;
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dx != 0 || dy != 0) {
mScrolled = true;
}
}
};
@Override
public boolean onFling(int velocityX, int velocityY) {
ViewPagerLayoutManager layoutManager = (ViewPagerLayoutManager) mRecyclerView.getLayoutManager();
if (layoutManager == null) {
return false;
}
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
if (adapter == null) {
return false;
}
if (!layoutManager.getInfinite() &&
(layoutManager.mOffset == layoutManager.getMaxOffset()
|| layoutManager.mOffset == layoutManager.getMinOffset())) {
return false;
}
final int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
mGravityScroller.fling(0, 0, velocityX, velocityY,
Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
if (layoutManager.mOrientation == ViewPagerLayoutManager.VERTICAL
&& Math.abs(velocityY) > minFlingVelocity) {
final int currentPosition = layoutManager.getCurrentPositionOffset();
final int offsetPosition = (int) (mGravityScroller.getFinalY() /
layoutManager.mInterval / layoutManager.getDistanceRatio());
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
-currentPosition - offsetPosition : currentPosition + offsetPosition);
return true;
} else if (layoutManager.mOrientation == ViewPagerLayoutManager.HORIZONTAL
&& Math.abs(velocityX) > minFlingVelocity) {
final int currentPosition = layoutManager.getCurrentPositionOffset();
final int offsetPosition = (int) (mGravityScroller.getFinalX() /
layoutManager.mInterval / layoutManager.getDistanceRatio());
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
-currentPosition - offsetPosition : currentPosition + offsetPosition);
return true;
}
return true;
}
public void attachToRecyclerView(@Nullable RecyclerView recyclerView)
throws IllegalStateException {
if (mRecyclerView == recyclerView) {
return; // nothing to do
}
if (mRecyclerView != null) {
destroyCallbacks();
}
mRecyclerView = recyclerView;
if (mRecyclerView != null) {
final RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
if (!(layoutManager instanceof ViewPagerLayoutManager)) return;
setupCallbacks();
mGravityScroller = new Scroller(mRecyclerView.getContext(),
new DecelerateInterpolator());
snapToCenterView((ViewPagerLayoutManager) layoutManager,
((ViewPagerLayoutManager) layoutManager).onPageChangeListener);
}
}
void snapToCenterView(ViewPagerLayoutManager layoutManager,
ViewPagerLayoutManager.OnPageChangeListener listener) {
final int delta = layoutManager.getOffsetToCenter();
if (delta != 0) {
if (layoutManager.getOrientation()
== RecyclerView.VERTICAL)
mRecyclerView.smoothScrollBy(0, delta);
else
mRecyclerView.smoothScrollBy(delta, 0);
} else {
// set it false to make smoothScrollToPosition keep trigger the listener
snapToCenter = false;
}
if (listener != null)
listener.onPageSelected(layoutManager.getCurrentPosition());
}
/**
* Called when an instance of a {@link RecyclerView} is attached.
*/
void setupCallbacks() throws IllegalStateException {
if (mRecyclerView.getOnFlingListener() != null) {
throw new IllegalStateException("An instance of OnFlingListener already set.");
}
mRecyclerView.addOnScrollListener(mScrollListener);
mRecyclerView.setOnFlingListener(this);
}
/**
* Called when the instance of a {@link RecyclerView} is detached.
*/
void destroyCallbacks() {
mRecyclerView.removeOnScrollListener(mScrollListener);
mRecyclerView.setOnFlingListener(null);
}
}

@ -0,0 +1,357 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.content.Context;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* An implementation of {@link ViewPagerLayoutManager}
* which layouts item in a circle
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class CircleLayoutManager extends ViewPagerLayoutManager {
public static final int LEFT = 10;
public static final int RIGHT = 11;
public static final int TOP = 12;
public static final int BOTTOM = 13;
public static final int LEFT_ON_TOP = 4;
public static final int RIGHT_ON_TOP = 5;
public static final int CENTER_ON_TOP = 6;
private int radius;
private int angleInterval;
private float moveSpeed;
private float maxRemoveAngle;
private float minRemoveAngle;
private int gravity;
private boolean flipRotate;
private int zAlignment;
public CircleLayoutManager(Context context) {
this(new Builder(context));
}
public CircleLayoutManager(Context context, boolean reverseLayout) {
this(new Builder(context).setReverseLayout(reverseLayout));
}
public CircleLayoutManager(Context context, int gravity, boolean reverseLayout) {
this(new Builder(context).setGravity(gravity).setReverseLayout(reverseLayout));
}
public CircleLayoutManager(Builder builder) {
this(builder.context, builder.radius, builder.angleInterval, builder.moveSpeed, builder.maxRemoveAngle,
builder.minRemoveAngle, builder.gravity, builder.zAlignment, builder.flipRotate,
builder.maxVisibleItemCount, builder.distanceToBottom, builder.reverseLayout);
}
private CircleLayoutManager(Context context, int radius, int angleInterval, float moveSpeed,
float max, float min, int gravity, int zAlignment, boolean flipRotate,
int maxVisibleItemCount, int distanceToBottom, boolean reverseLayout) {
super(context, (gravity == LEFT || gravity == RIGHT) ? VERTICAL : HORIZONTAL, reverseLayout);
setEnableBringCenterToFront(true);
setMaxVisibleItemCount(maxVisibleItemCount);
setDistanceToBottom(distanceToBottom);
this.radius = radius;
this.angleInterval = angleInterval;
this.moveSpeed = moveSpeed;
this.maxRemoveAngle = max;
this.minRemoveAngle = min;
this.gravity = gravity;
this.flipRotate = flipRotate;
this.zAlignment = zAlignment;
}
private static void assertGravity(int gravity) {
if (gravity != LEFT && gravity != RIGHT && gravity != TOP && gravity != BOTTOM) {
throw new IllegalArgumentException("gravity must be one of LEFT RIGHT TOP and BOTTOM");
}
}
private static void assertZAlignmentState(int zAlignment) {
if (zAlignment != LEFT_ON_TOP && zAlignment != RIGHT_ON_TOP && zAlignment != CENTER_ON_TOP) {
throw new IllegalArgumentException("zAlignment must be one of LEFT_ON_TOP RIGHT_ON_TOP and CENTER_ON_TOP");
}
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
assertNotInLayoutOrScroll(null);
if (this.radius == radius) return;
this.radius = radius;
removeAllViews();
}
public int getAngleInterval() {
return angleInterval;
}
public void setAngleInterval(int angleInterval) {
assertNotInLayoutOrScroll(null);
if (this.angleInterval == angleInterval) return;
this.angleInterval = angleInterval;
removeAllViews();
}
public float getMoveSpeed() {
return moveSpeed;
}
public void setMoveSpeed(float moveSpeed) {
assertNotInLayoutOrScroll(null);
if (this.moveSpeed == moveSpeed) return;
this.moveSpeed = moveSpeed;
}
public float getMaxRemoveAngle() {
return maxRemoveAngle;
}
public void setMaxRemoveAngle(float maxRemoveAngle) {
assertNotInLayoutOrScroll(null);
if (this.maxRemoveAngle == maxRemoveAngle) return;
this.maxRemoveAngle = maxRemoveAngle;
requestLayout();
}
public float getMinRemoveAngle() {
return minRemoveAngle;
}
public void setMinRemoveAngle(float minRemoveAngle) {
assertNotInLayoutOrScroll(null);
if (this.minRemoveAngle == minRemoveAngle) return;
this.minRemoveAngle = minRemoveAngle;
requestLayout();
}
public int getGravity() {
return gravity;
}
public void setGravity(int gravity) {
assertNotInLayoutOrScroll(null);
assertGravity(gravity);
if (this.gravity == gravity) return;
this.gravity = gravity;
if (gravity == LEFT || gravity == RIGHT) {
setOrientation(RecyclerView.VERTICAL);
} else {
setOrientation(RecyclerView.HORIZONTAL);
}
requestLayout();
}
public boolean getFlipRotate() {
return flipRotate;
}
public void setFlipRotate(boolean flipRotate) {
assertNotInLayoutOrScroll(null);
if (this.flipRotate == flipRotate) return;
this.flipRotate = flipRotate;
requestLayout();
}
public int getZAlignment() {
return zAlignment;
}
public void setZAlignment(int zAlignment) {
assertNotInLayoutOrScroll(null);
assertZAlignmentState(zAlignment);
if (this.zAlignment == zAlignment) return;
this.zAlignment = zAlignment;
requestLayout();
}
@Override
protected float setInterval() {
return angleInterval;
}
@Override
protected void setUp() {
radius = radius == Builder.INVALID_VALUE ? mDecoratedMeasurementInOther : radius;
}
@Override
protected float maxRemoveOffset() {
return maxRemoveAngle;
}
@Override
protected float minRemoveOffset() {
return minRemoveAngle;
}
@Override
protected int calItemLeft(View itemView, float targetOffset) {
switch (gravity) {
case LEFT:
return (int) (radius * Math.sin(Math.toRadians(90 - targetOffset)) - radius);
case RIGHT:
return (int) (radius - radius * Math.sin(Math.toRadians(90 - targetOffset)));
case TOP:
case BOTTOM:
default:
return (int) (radius * Math.cos(Math.toRadians(90 - targetOffset)));
}
}
@Override
protected int calItemTop(View itemView, float targetOffset) {
switch (gravity) {
case LEFT:
case RIGHT:
return (int) (radius * Math.cos(Math.toRadians(90 - targetOffset)));
case TOP:
return (int) (radius * Math.sin(Math.toRadians(90 - targetOffset)) - radius);
case BOTTOM:
default:
return (int) (radius - radius * Math.sin(Math.toRadians(90 - targetOffset)));
}
}
@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
switch (gravity) {
case RIGHT:
case TOP:
if (flipRotate) {
itemView.setRotation(targetOffset);
} else {
itemView.setRotation(360 - targetOffset);
}
break;
case LEFT:
case BOTTOM:
default:
if (flipRotate) {
itemView.setRotation(360 - targetOffset);
} else {
itemView.setRotation(targetOffset);
}
break;
}
}
@Override
protected float setViewElevation(View itemView, float targetOffset) {
if (zAlignment == LEFT_ON_TOP)
return (540 - targetOffset) / 72;
else if (zAlignment == RIGHT_ON_TOP)
return (targetOffset - 540) / 72;
else
return (360 - Math.abs(targetOffset)) / 72;
}
@Override
protected float getDistanceRatio() {
if (moveSpeed == 0) return Float.MAX_VALUE;
return 1 / moveSpeed;
}
public static class Builder {
private static int INTERVAL_ANGLE = 30;// The default mInterval angle between each items
private static float DISTANCE_RATIO = 10f; // Finger swipe distance divide item rotate angle
private static int INVALID_VALUE = Integer.MIN_VALUE;
private static int MAX_REMOVE_ANGLE = 90;
private static int MIN_REMOVE_ANGLE = -90;
private int radius;
private int angleInterval;
private float moveSpeed;
private float maxRemoveAngle;
private float minRemoveAngle;
private boolean reverseLayout;
private Context context;
private int gravity;
private boolean flipRotate;
private int zAlignment;
private int maxVisibleItemCount;
private int distanceToBottom;
public Builder(Context context) {
this.context = context;
radius = INVALID_VALUE;
angleInterval = INTERVAL_ANGLE;
moveSpeed = 1 / DISTANCE_RATIO;
maxRemoveAngle = MAX_REMOVE_ANGLE;
minRemoveAngle = MIN_REMOVE_ANGLE;
reverseLayout = false;
flipRotate = false;
gravity = BOTTOM;
zAlignment = LEFT_ON_TOP;
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
}
public Builder setRadius(int radius) {
this.radius = radius;
return this;
}
public Builder setAngleInterval(int angleInterval) {
this.angleInterval = angleInterval;
return this;
}
public Builder setMoveSpeed(int moveSpeed) {
this.moveSpeed = moveSpeed;
return this;
}
public Builder setMaxRemoveAngle(float maxRemoveAngle) {
this.maxRemoveAngle = maxRemoveAngle;
return this;
}
public Builder setMinRemoveAngle(float minRemoveAngle) {
this.minRemoveAngle = minRemoveAngle;
return this;
}
public Builder setReverseLayout(boolean reverseLayout) {
this.reverseLayout = reverseLayout;
return this;
}
public Builder setGravity(int gravity) {
assertGravity(gravity);
this.gravity = gravity;
return this;
}
public Builder setFlipRotate(boolean flipRotate) {
this.flipRotate = flipRotate;
return this;
}
public Builder setZAlignment(int zAlignment) {
assertZAlignmentState(zAlignment);
this.zAlignment = zAlignment;
return this;
}
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
this.maxVisibleItemCount = maxVisibleItemCount;
return this;
}
public Builder setDistanceToBottom(int distanceToBottom) {
this.distanceToBottom = distanceToBottom;
return this;
}
public CircleLayoutManager build() {
return new CircleLayoutManager(this);
}
}
}

@ -0,0 +1,395 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.content.Context;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* An implementation of {@link ViewPagerLayoutManager}
* which layouts item in a circle and will change the child's centerScale while scrolling
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class CircleScaleLayoutManager extends ViewPagerLayoutManager {
public static final int LEFT = 10;
public static final int RIGHT = 11;
public static final int TOP = 12;
public static final int BOTTOM = 13;
public static final int LEFT_ON_TOP = 4;
public static final int RIGHT_ON_TOP = 5;
public static final int CENTER_ON_TOP = 6;
private int radius;
private int angleInterval;
private float moveSpeed;
private float centerScale;
private float maxRemoveAngle;
private float minRemoveAngle;
private int gravity;
private boolean flipRotate;
private int zAlignment;
public CircleScaleLayoutManager(Context context) {
this(new Builder(context));
}
public CircleScaleLayoutManager(Context context, int gravity, boolean reverseLayout) {
this(new Builder(context).setGravity(gravity).setReverseLayout(reverseLayout));
}
public CircleScaleLayoutManager(Context context, boolean reverseLayout) {
this(new Builder(context).setReverseLayout(reverseLayout));
}
public CircleScaleLayoutManager(Builder builder) {
this(builder.context, builder.radius, builder.angleInterval, builder.centerScale, builder.moveSpeed,
builder.maxRemoveAngle, builder.minRemoveAngle, builder.gravity, builder.zAlignment,
builder.flipRotate, builder.maxVisibleItemCount, builder.distanceToBottom, builder.reverseLayout);
}
private CircleScaleLayoutManager(Context context, int radius, int angleInterval, float centerScale,
float moveSpeed, float max, float min, int gravity, int zAlignment,
boolean flipRotate, int maxVisibleItemCount, int distanceToBottom, boolean reverseLayout) {
super(context, HORIZONTAL, reverseLayout);
setEnableBringCenterToFront(true);
setMaxVisibleItemCount(maxVisibleItemCount);
setDistanceToBottom(distanceToBottom);
this.radius = radius;
this.angleInterval = angleInterval;
this.centerScale = centerScale;
this.moveSpeed = moveSpeed;
this.maxRemoveAngle = max;
this.minRemoveAngle = min;
this.gravity = gravity;
this.flipRotate = flipRotate;
this.zAlignment = zAlignment;
}
private static void assertGravity(int gravity) {
if (gravity != LEFT && gravity != RIGHT && gravity != TOP && gravity != BOTTOM) {
throw new IllegalArgumentException("gravity must be one of LEFT RIGHT TOP and BOTTOM");
}
}
private static void assertZAlignmentState(int zAlignment) {
if (zAlignment != LEFT_ON_TOP && zAlignment != RIGHT_ON_TOP && zAlignment != CENTER_ON_TOP) {
throw new IllegalArgumentException("zAlignment must be one of LEFT_ON_TOP RIGHT_ON_TOP and CENTER_ON_TOP");
}
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
assertNotInLayoutOrScroll(null);
if (this.radius == radius) return;
this.radius = radius;
removeAllViews();
}
public int getAngleInterval() {
return angleInterval;
}
public void setAngleInterval(int angleInterval) {
assertNotInLayoutOrScroll(null);
if (this.angleInterval == angleInterval) return;
this.angleInterval = angleInterval;
removeAllViews();
}
public float getCenterScale() {
return centerScale;
}
public void setCenterScale(float centerScale) {
assertNotInLayoutOrScroll(null);
if (this.centerScale == centerScale) return;
this.centerScale = centerScale;
requestLayout();
}
public float getMoveSpeed() {
return moveSpeed;
}
public void setMoveSpeed(float moveSpeed) {
assertNotInLayoutOrScroll(null);
if (this.moveSpeed == moveSpeed) return;
this.moveSpeed = moveSpeed;
}
public float getMaxRemoveAngle() {
return maxRemoveAngle;
}
public void setMaxRemoveAngle(float maxRemoveAngle) {
assertNotInLayoutOrScroll(null);
if (this.maxRemoveAngle == maxRemoveAngle) return;
this.maxRemoveAngle = maxRemoveAngle;
requestLayout();
}
public float getMinRemoveAngle() {
return minRemoveAngle;
}
public void setMinRemoveAngle(float minRemoveAngle) {
assertNotInLayoutOrScroll(null);
if (this.minRemoveAngle == minRemoveAngle) return;
this.minRemoveAngle = minRemoveAngle;
requestLayout();
}
public int getGravity() {
return gravity;
}
public void setGravity(int gravity) {
assertNotInLayoutOrScroll(null);
assertGravity(gravity);
if (this.gravity == gravity) return;
this.gravity = gravity;
if (gravity == LEFT || gravity == RIGHT) {
setOrientation(RecyclerView.VERTICAL);
} else {
setOrientation(RecyclerView.HORIZONTAL);
}
requestLayout();
}
public boolean getFlipRotate() {
return flipRotate;
}
public void setFlipRotate(boolean flipRotate) {
assertNotInLayoutOrScroll(null);
if (this.flipRotate == flipRotate) return;
this.flipRotate = flipRotate;
requestLayout();
}
public int getZAlignment() {
return zAlignment;
}
public void setZAlignment(int zAlignment) {
assertNotInLayoutOrScroll(null);
assertZAlignmentState(zAlignment);
if (this.zAlignment == zAlignment) return;
this.zAlignment = zAlignment;
requestLayout();
}
@Override
protected float setInterval() {
return angleInterval;
}
@Override
protected void setUp() {
radius = radius == Builder.INVALID_VALUE ? mDecoratedMeasurementInOther : radius;
}
@Override
protected float maxRemoveOffset() {
return maxRemoveAngle;
}
@Override
protected float minRemoveOffset() {
return minRemoveAngle;
}
@Override
protected int calItemLeft(View itemView, float targetOffset) {
switch (gravity) {
case LEFT:
return (int) (radius * Math.sin(Math.toRadians(90 - targetOffset)) - radius);
case RIGHT:
return (int) (radius - radius * Math.sin(Math.toRadians(90 - targetOffset)));
case TOP:
case BOTTOM:
default:
return (int) (radius * Math.cos(Math.toRadians(90 - targetOffset)));
}
}
@Override
protected int calItemTop(View itemView, float targetOffset) {
switch (gravity) {
case LEFT:
case RIGHT:
return (int) (radius * Math.cos(Math.toRadians(90 - targetOffset)));
case TOP:
return (int) (radius * Math.sin(Math.toRadians(90 - targetOffset)) - radius);
case BOTTOM:
default:
return (int) (radius - radius * Math.sin(Math.toRadians(90 - targetOffset)));
}
}
@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
float scale = 1f;
switch (gravity) {
case RIGHT:
case TOP:
if (flipRotate) {
itemView.setRotation(targetOffset);
if (targetOffset < angleInterval && targetOffset > -angleInterval) {
float diff = Math.abs(Math.abs(itemView.getRotation() - angleInterval) - angleInterval);
scale = (centerScale - 1f) / -angleInterval * diff + centerScale;
}
} else {
itemView.setRotation(360 - targetOffset);
if (targetOffset < angleInterval && targetOffset > -angleInterval) {
float diff = Math.abs(Math.abs(360 - itemView.getRotation() - angleInterval) - angleInterval);
scale = (centerScale - 1f) / -angleInterval * diff + centerScale;
}
}
break;
case LEFT:
case BOTTOM:
default:
if (flipRotate) {
itemView.setRotation(360 - targetOffset);
if (targetOffset < angleInterval && targetOffset > -angleInterval) {
float diff = Math.abs(Math.abs(360 - itemView.getRotation() - angleInterval) - angleInterval);
scale = (centerScale - 1f) / -angleInterval * diff + centerScale;
}
} else {
itemView.setRotation(targetOffset);
if (targetOffset < angleInterval && targetOffset > -angleInterval) {
float diff = Math.abs(Math.abs(itemView.getRotation() - angleInterval) - angleInterval);
scale = (centerScale - 1f) / -angleInterval * diff + centerScale;
}
}
break;
}
itemView.setScaleX(scale);
itemView.setScaleY(scale);
}
@Override
protected float setViewElevation(View itemView, float targetOffset) {
if (zAlignment == LEFT_ON_TOP)
return (540 - targetOffset) / 72;
else if (zAlignment == RIGHT_ON_TOP)
return (targetOffset - 540) / 72;
else
return (360 - Math.abs(targetOffset)) / 72;
}
@Override
protected float getDistanceRatio() {
if (moveSpeed == 0) return Float.MAX_VALUE;
return 1 / moveSpeed;
}
public static class Builder {
private static final float SCALE_RATE = 1.2f;
private static int INTERVAL_ANGLE = 30;// The default mInterval angle between each items
private static float DISTANCE_RATIO = 10f; // Finger swipe distance divide item rotate angle
private static int INVALID_VALUE = Integer.MIN_VALUE;
private int radius;
private int angleInterval;
private float centerScale;
private float moveSpeed;
private float maxRemoveAngle;
private float minRemoveAngle;
private boolean reverseLayout;
private Context context;
private int gravity;
private boolean flipRotate;
private int zAlignment;
private int maxVisibleItemCount;
private int distanceToBottom;
public Builder(Context context) {
this.context = context;
radius = INVALID_VALUE;
angleInterval = INTERVAL_ANGLE;
centerScale = SCALE_RATE;
moveSpeed = 1 / DISTANCE_RATIO;
maxRemoveAngle = 90;
minRemoveAngle = -90;
reverseLayout = false;
flipRotate = false;
gravity = BOTTOM;
zAlignment = CENTER_ON_TOP;
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
}
public Builder setRadius(int radius) {
this.radius = radius;
return this;
}
public Builder setAngleInterval(int angleInterval) {
this.angleInterval = angleInterval;
return this;
}
public Builder setCenterScale(float centerScale) {
this.centerScale = centerScale;
return this;
}
public Builder setMoveSpeed(int moveSpeed) {
this.moveSpeed = moveSpeed;
return this;
}
public Builder setMaxRemoveAngle(float maxRemoveAngle) {
this.maxRemoveAngle = maxRemoveAngle;
return this;
}
public Builder setMinRemoveAngle(float minRemoveAngle) {
this.minRemoveAngle = minRemoveAngle;
return this;
}
public Builder setReverseLayout(boolean reverseLayout) {
this.reverseLayout = reverseLayout;
return this;
}
public Builder setGravity(int gravity) {
assertGravity(gravity);
this.gravity = gravity;
return this;
}
public Builder setFlipRotate(boolean flipRotate) {
this.flipRotate = flipRotate;
return this;
}
public Builder setZAlignment(int zAlignment) {
assertZAlignmentState(zAlignment);
this.zAlignment = zAlignment;
return this;
}
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
this.maxVisibleItemCount = maxVisibleItemCount;
return this;
}
public Builder setDistanceToBottom(int distanceToBottom) {
this.distanceToBottom = distanceToBottom;
return this;
}
public CircleScaleLayoutManager build() {
return new CircleScaleLayoutManager(this);
}
}
}

@ -0,0 +1,288 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.content.Context;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* An implementation of {@link ViewPagerLayoutManager}
* which will change rotate x or rotate y
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class GalleryLayoutManager extends ViewPagerLayoutManager {
private final float MAX_ELEVATION = 5F;
private int itemSpace;
private float moveSpeed;
private float maxAlpha;
private float minAlpha;
private float angle;
private boolean flipRotate;
private boolean rotateFromEdge;
public GalleryLayoutManager(Context context, int itemSpace) {
this(new Builder(context, itemSpace));
}
public GalleryLayoutManager(Context context, int itemSpace, int orientation) {
this(new Builder(context, itemSpace).setOrientation(orientation));
}
public GalleryLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) {
this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout));
}
public GalleryLayoutManager(Builder builder) {
this(builder.context, builder.itemSpace, builder.angle, builder.maxAlpha, builder.minAlpha,
builder.orientation, builder.moveSpeed, builder.flipRotate, builder.rotateFromEdge,
builder.maxVisibleItemCount, builder.distanceToBottom, builder.reverseLayout);
}
private GalleryLayoutManager(Context context, int itemSpace, float angle, float maxAlpha, float minAlpha,
int orientation, float moveSpeed, boolean flipRotate, boolean rotateFromEdge,
int maxVisibleItemCount, int distanceToBottom, boolean reverseLayout) {
super(context, orientation, reverseLayout);
setDistanceToBottom(distanceToBottom);
setMaxVisibleItemCount(maxVisibleItemCount);
this.itemSpace = itemSpace;
this.moveSpeed = moveSpeed;
this.angle = angle;
this.maxAlpha = maxAlpha;
this.minAlpha = minAlpha;
this.flipRotate = flipRotate;
this.rotateFromEdge = rotateFromEdge;
}
public int getItemSpace() {
return itemSpace;
}
public void setItemSpace(int itemSpace) {
assertNotInLayoutOrScroll(null);
if (this.itemSpace == itemSpace) return;
this.itemSpace = itemSpace;
removeAllViews();
}
public float getMaxAlpha() {
return maxAlpha;
}
public void setMaxAlpha(float maxAlpha) {
assertNotInLayoutOrScroll(null);
if (maxAlpha > 1) maxAlpha = 1;
if (this.maxAlpha == maxAlpha) return;
this.maxAlpha = maxAlpha;
requestLayout();
}
public float getMinAlpha() {
return minAlpha;
}
public void setMinAlpha(float minAlpha) {
assertNotInLayoutOrScroll(null);
if (minAlpha < 0) minAlpha = 0;
if (this.minAlpha == minAlpha) return;
this.minAlpha = minAlpha;
requestLayout();
}
public float getAngle() {
return angle;
}
public void setAngle(float angle) {
assertNotInLayoutOrScroll(null);
if (this.angle == angle) return;
this.angle = angle;
requestLayout();
}
public float getMoveSpeed() {
return moveSpeed;
}
public void setMoveSpeed(float moveSpeed) {
assertNotInLayoutOrScroll(null);
if (this.moveSpeed == moveSpeed) return;
this.moveSpeed = moveSpeed;
}
public boolean getFlipRotate() {
return flipRotate;
}
public void setFlipRotate(boolean flipRotate) {
assertNotInLayoutOrScroll(null);
if (this.flipRotate == flipRotate) return;
this.flipRotate = flipRotate;
requestLayout();
}
public boolean getRotateFromEdge() {
return rotateFromEdge;
}
public void setRotateFromEdge(boolean rotateFromEdge) {
assertNotInLayoutOrScroll(null);
if (this.rotateFromEdge == rotateFromEdge) return;
this.rotateFromEdge = rotateFromEdge;
removeAllViews();
}
@Override
protected float setInterval() {
return mDecoratedMeasurement + itemSpace;
}
@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
final float rotation = calRotation(targetOffset);
if (getOrientation() == RecyclerView.HORIZONTAL) {
if (rotateFromEdge) {
itemView.setPivotX(rotation > 0 ? 0 : mDecoratedMeasurement);
itemView.setPivotY(mDecoratedMeasurementInOther * 0.5f);
}
if (flipRotate) {
itemView.setRotationX(rotation);
} else {
itemView.setRotationY(rotation);
}
} else {
if (rotateFromEdge) {
itemView.setPivotY(rotation > 0 ? 0 : mDecoratedMeasurement);
itemView.setPivotX(mDecoratedMeasurementInOther * 0.5f);
}
if (flipRotate) {
itemView.setRotationY(-rotation);
} else {
itemView.setRotationX(-rotation);
}
}
final float alpha = calAlpha(targetOffset);
itemView.setAlpha(alpha);
}
@Override
protected float setViewElevation(View itemView, float targetOffset) {
final float ele = Math.max(Math.abs(itemView.getRotationX()), Math.abs(itemView.getRotationY())) * MAX_ELEVATION / 360;
return MAX_ELEVATION - ele;
}
@Override
protected float getDistanceRatio() {
if (moveSpeed == 0) return Float.MAX_VALUE;
return 1 / moveSpeed;
}
private float calRotation(float targetOffset) {
return -angle / mInterval * targetOffset;
}
private float calAlpha(float targetOffset) {
final float offset = Math.abs(targetOffset);
float alpha = (minAlpha - maxAlpha) / mInterval * offset + maxAlpha;
if (offset >= mInterval) alpha = minAlpha;
return alpha;
}
public static class Builder {
private static final float DEFAULT_SPEED = 1f;
private static float INTERVAL_ANGLE = 30f;
private static float MIN_ALPHA = 0.5f;
private static float MAX_ALPHA = 1f;
private int itemSpace;
private float moveSpeed;
private int orientation;
private float maxAlpha;
private float minAlpha;
private float angle;
private boolean flipRotate;
private boolean reverseLayout;
private Context context;
private int maxVisibleItemCount;
private int distanceToBottom;
private boolean rotateFromEdge;
public Builder(Context context, int itemSpace) {
this.itemSpace = itemSpace;
this.context = context;
orientation = HORIZONTAL;
angle = INTERVAL_ANGLE;
maxAlpha = MAX_ALPHA;
minAlpha = MIN_ALPHA;
this.moveSpeed = DEFAULT_SPEED;
reverseLayout = false;
flipRotate = false;
rotateFromEdge = false;
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
}
public Builder setItemSpace(int itemSpace) {
this.itemSpace = itemSpace;
return this;
}
public Builder setMoveSpeed(float moveSpeed) {
this.moveSpeed = moveSpeed;
return this;
}
public Builder setOrientation(int orientation) {
this.orientation = orientation;
return this;
}
public Builder setMaxAlpha(float maxAlpha) {
if (maxAlpha > 1) maxAlpha = 1;
this.maxAlpha = maxAlpha;
return this;
}
public Builder setMinAlpha(float minAlpha) {
if (minAlpha < 0) minAlpha = 0;
this.minAlpha = minAlpha;
return this;
}
public Builder setAngle(float angle) {
this.angle = angle;
return this;
}
public Builder setFlipRotate(boolean flipRotate) {
this.flipRotate = flipRotate;
return this;
}
public Builder setReverseLayout(boolean reverseLayout) {
this.reverseLayout = reverseLayout;
return this;
}
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
this.maxVisibleItemCount = maxVisibleItemCount;
return this;
}
public Builder setDistanceToBottom(int distanceToBottom) {
this.distanceToBottom = distanceToBottom;
return this;
}
public Builder setRotateFromEdge(boolean rotateFromEdge) {
this.rotateFromEdge = rotateFromEdge;
return this;
}
public GalleryLayoutManager build() {
return new GalleryLayoutManager(this);
}
}
}

@ -0,0 +1,413 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.graphics.Rect;
import android.view.View;
import android.widget.LinearLayout;
import androidx.recyclerview.widget.RecyclerView;
@SuppressWarnings({"WeakerAccess","unused"})
public abstract class OrientationHelper {
private static final int INVALID_SIZE = Integer.MIN_VALUE;
protected final RecyclerView.LayoutManager mLayoutManager;
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
private int mLastTotalSpace = INVALID_SIZE;
final Rect mTmpRect = new Rect();
private OrientationHelper(RecyclerView.LayoutManager layoutManager) {
mLayoutManager = layoutManager;
}
/**
* Call this method after onLayout method is complete if state is NOT pre-layout.
* This method records information like layout bounds that might be useful in the next layout
* calculations.
*/
public void onLayoutComplete() {
mLastTotalSpace = getTotalSpace();
}
/**
* Returns the layout space change between the previous layout pass and current layout pass.
* <p>
* Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's
* {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler,
* RecyclerView.State)} method.
*
* @return The difference between the current total space and previous layout's total space.
* @see #onLayoutComplete()
*/
public int getTotalSpaceChange() {
return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace;
}
/**
* Returns the start of the view including its decoration and margin.
* <p>
* For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left
* decoration and 3px left margin, returned value will be 15px.
*
* @param view The view element to check
* @return The first pixel of the element
* @see #getDecoratedEnd(View)
*/
public abstract int getDecoratedStart(View view);
/**
* Returns the end of the view including its decoration and margin.
* <p>
* For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right
* decoration and 3px right margin, returned value will be 205.
*
* @param view The view element to check
* @return The last pixel of the element
* @see #getDecoratedStart(View)
*/
public abstract int getDecoratedEnd(View view);
/**
* Returns the end of the View after its matrix transformations are applied to its layout
* position.
* <p>
* This method is useful when trying to detect the visible edge of a View.
* <p>
* It includes the decorations but does not include the margins.
*
* @param view The view whose transformed end will be returned
* @return The end of the View after its decor insets and transformation matrix is applied to
* its position
* @see RecyclerView.LayoutManager#getTransformedBoundingBox(View, boolean, Rect)
*/
public abstract int getTransformedEndWithDecoration(View view);
/**
* Returns the start of the View after its matrix transformations are applied to its layout
* position.
* <p>
* This method is useful when trying to detect the visible edge of a View.
* <p>
* It includes the decorations but does not include the margins.
*
* @param view The view whose transformed start will be returned
* @return The start of the View after its decor insets and transformation matrix is applied to
* its position
* @see RecyclerView.LayoutManager#getTransformedBoundingBox(View, boolean, Rect)
*/
public abstract int getTransformedStartWithDecoration(View view);
/**
* Returns the space occupied by this View in the current orientation including decorations and
* margins.
*
* @param view The view element to check
* @return Total space occupied by this view
* @see #getDecoratedMeasurementInOther(View)
*/
public abstract int getDecoratedMeasurement(View view);
/**
* Returns the space occupied by this View in the perpendicular orientation including
* decorations and margins.
*
* @param view The view element to check
* @return Total space occupied by this view in the perpendicular orientation to current one
* @see #getDecoratedMeasurement(View)
*/
public abstract int getDecoratedMeasurementInOther(View view);
/**
* Returns the start position of the layout after the start padding is added.
*
* @return The very first pixel we can draw.
*/
public abstract int getStartAfterPadding();
/**
* Returns the end position of the layout after the end padding is removed.
*
* @return The end boundary for this layout.
*/
public abstract int getEndAfterPadding();
/**
* Returns the end position of the layout without taking padding into account.
*
* @return The end boundary for this layout without considering padding.
*/
public abstract int getEnd();
/**
* Returns the total space to layout. This number is the difference between
* {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.
*
* @return Total space to layout children
*/
public abstract int getTotalSpace();
/**
* Returns the total space in other direction to layout. This number is the difference between
* {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.
*
* @return Total space to layout children
*/
public abstract int getTotalSpaceInOther();
/**
* Returns the padding at the end of the layout. For horizontal helper, this is the right
* padding and for vertical helper, this is the bottom padding. This method does not check
* whether the layout is RTL or not.
*
* @return The padding at the end of the layout.
*/
public abstract int getEndPadding();
/**
* Returns the MeasureSpec mode for the current orientation from the LayoutManager.
*
* @return The current measure spec mode.
* @see View.MeasureSpec
* @see RecyclerView.LayoutManager#getWidthMode()
* @see RecyclerView.LayoutManager#getHeightMode()
*/
public abstract int getMode();
/**
* Returns the MeasureSpec mode for the perpendicular orientation from the LayoutManager.
*
* @return The current measure spec mode.
* @see View.MeasureSpec
* @see RecyclerView.LayoutManager#getWidthMode()
* @see RecyclerView.LayoutManager#getHeightMode()
*/
public abstract int getModeInOther();
/**
* Creates an OrientationHelper for the given LayoutManager and orientation.
*
* @param layoutManager LayoutManager to attach to
* @param orientation Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
* @return A new OrientationHelper
*/
public static OrientationHelper createOrientationHelper(
RecyclerView.LayoutManager layoutManager, int orientation) {
switch (orientation) {
case HORIZONTAL:
return createHorizontalHelper(layoutManager);
case VERTICAL:
return createVerticalHelper(layoutManager);
}
throw new IllegalArgumentException("invalid orientation");
}
/**
* Creates a horizontal OrientationHelper for the given LayoutManager.
*
* @param layoutManager The LayoutManager to attach to.
* @return A new OrientationHelper
*/
public static OrientationHelper createHorizontalHelper(
RecyclerView.LayoutManager layoutManager) {
return new OrientationHelper(layoutManager) {
@Override
public int getEndAfterPadding() {
return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight();
}
@Override
public int getEnd() {
return mLayoutManager.getWidth();
}
@Override
public int getStartAfterPadding() {
return mLayoutManager.getPaddingLeft();
}
@Override
public int getDecoratedMeasurement(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
+ params.rightMargin;
}
@Override
public int getDecoratedMeasurementInOther(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
+ params.bottomMargin;
}
@Override
public int getDecoratedEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedRight(view) + params.rightMargin;
}
@Override
public int getDecoratedStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedLeft(view) - params.leftMargin;
}
@Override
public int getTransformedEndWithDecoration(View view) {
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
return mTmpRect.right;
}
@Override
public int getTransformedStartWithDecoration(View view) {
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
return mTmpRect.left;
}
@Override
public int getTotalSpace() {
return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
- mLayoutManager.getPaddingRight();
}
@Override
public int getTotalSpaceInOther() {
return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()
- mLayoutManager.getPaddingBottom();
}
@Override
public int getEndPadding() {
return mLayoutManager.getPaddingRight();
}
@Override
public int getMode() {
return mLayoutManager.getWidthMode();
}
@Override
public int getModeInOther() {
return mLayoutManager.getHeightMode();
}
};
}
/**
* Creates a vertical OrientationHelper for the given LayoutManager.
*
* @param layoutManager The LayoutManager to attach to.
* @return A new OrientationHelper
*/
public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
return new OrientationHelper(layoutManager) {
@Override
public int getEndAfterPadding() {
return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom();
}
@Override
public int getEnd() {
return mLayoutManager.getHeight();
}
@Override
public int getStartAfterPadding() {
return mLayoutManager.getPaddingTop();
}
@Override
public int getDecoratedMeasurement(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
+ params.bottomMargin;
}
@Override
public int getDecoratedMeasurementInOther(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
+ params.rightMargin;
}
@Override
public int getDecoratedEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin;
}
@Override
public int getDecoratedStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedTop(view) - params.topMargin;
}
@Override
public int getTransformedEndWithDecoration(View view) {
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
return mTmpRect.bottom;
}
@Override
public int getTransformedStartWithDecoration(View view) {
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
return mTmpRect.top;
}
@Override
public int getTotalSpace() {
return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()
- mLayoutManager.getPaddingBottom();
}
@Override
public int getTotalSpaceInOther() {
return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
- mLayoutManager.getPaddingRight();
}
@Override
public int getEndPadding() {
return mLayoutManager.getPaddingBottom();
}
@Override
public int getMode() {
return mLayoutManager.getHeightMode();
}
@Override
public int getModeInOther() {
return mLayoutManager.getWidthMode();
}
};
}
}

@ -0,0 +1,46 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import androidx.recyclerview.widget.RecyclerView;
public class PageSnapHelper extends CenterSnapHelper {
@Override
public boolean onFling(int velocityX, int velocityY) {
ViewPagerLayoutManager layoutManager = (ViewPagerLayoutManager) mRecyclerView.getLayoutManager();
if (layoutManager == null) {
return false;
}
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
if (adapter == null) {
return false;
}
if (!layoutManager.getInfinite() &&
(layoutManager.mOffset == layoutManager.getMaxOffset()
|| layoutManager.mOffset == layoutManager.getMinOffset())) {
return false;
}
final int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
mGravityScroller.fling(0, 0, velocityX, velocityY,
Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
if (layoutManager.mOrientation == ViewPagerLayoutManager.VERTICAL
&& Math.abs(velocityY) > minFlingVelocity) {
final int currentPosition = layoutManager.getCurrentPositionOffset();
final int offsetPosition = mGravityScroller.getFinalY() * layoutManager.getDistanceRatio() > layoutManager.mInterval ? 1 : 0;
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
-currentPosition - offsetPosition : currentPosition + offsetPosition);
return true;
} else if (layoutManager.mOrientation == ViewPagerLayoutManager.HORIZONTAL
&& Math.abs(velocityX) > minFlingVelocity) {
final int currentPosition = layoutManager.getCurrentPositionOffset();
final int offsetPosition = mGravityScroller.getFinalX() * layoutManager.getDistanceRatio() > layoutManager.mInterval ? 1 : 0;
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
-currentPosition - offsetPosition : currentPosition + offsetPosition);
return true;
}
return true;
}
}

@ -0,0 +1,178 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.content.Context;
import android.view.View;
/**
* An implementation of {@link ViewPagerLayoutManager}
* which rotates items
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class RotateLayoutManager extends ViewPagerLayoutManager {
private int itemSpace;
private float angle;
private float moveSpeed;
private boolean reverseRotate;
public RotateLayoutManager(Context context, int itemSpace) {
this(new Builder(context, itemSpace));
}
public RotateLayoutManager(Context context, int itemSpace, int orientation) {
this(new Builder(context, itemSpace).setOrientation(orientation));
}
public RotateLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) {
this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout));
}
public RotateLayoutManager(Builder builder) {
this(builder.context, builder.itemSpace, builder.angle, builder.orientation, builder.moveSpeed,
builder.reverseRotate, builder.maxVisibleItemCount, builder.distanceToBottom,
builder.reverseLayout);
}
private RotateLayoutManager(Context context, int itemSpace, float angle, int orientation, float moveSpeed,
boolean reverseRotate, int maxVisibleItemCount, int distanceToBottom,
boolean reverseLayout) {
super(context, orientation, reverseLayout);
setDistanceToBottom(distanceToBottom);
setMaxVisibleItemCount(maxVisibleItemCount);
this.itemSpace = itemSpace;
this.angle = angle;
this.moveSpeed = moveSpeed;
this.reverseRotate = reverseRotate;
}
public int getItemSpace() {
return itemSpace;
}
public float getAngle() {
return angle;
}
public float getMoveSpeed() {
return moveSpeed;
}
public boolean getReverseRotate() {
return reverseRotate;
}
public void setItemSpace(int itemSpace) {
assertNotInLayoutOrScroll(null);
if (this.itemSpace == itemSpace) return;
this.itemSpace = itemSpace;
removeAllViews();
}
public void setAngle(float centerScale) {
assertNotInLayoutOrScroll(null);
if (this.angle == centerScale) return;
this.angle = centerScale;
requestLayout();
}
public void setMoveSpeed(float moveSpeed) {
assertNotInLayoutOrScroll(null);
if (this.moveSpeed == moveSpeed) return;
this.moveSpeed = moveSpeed;
}
public void setReverseRotate(boolean reverseRotate) {
assertNotInLayoutOrScroll(null);
if (this.reverseRotate == reverseRotate) return;
this.reverseRotate = reverseRotate;
requestLayout();
}
@Override
protected float setInterval() {
return mDecoratedMeasurement + itemSpace;
}
@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
itemView.setRotation(calRotation(targetOffset));
}
@Override
protected float getDistanceRatio() {
if (moveSpeed == 0) return Float.MAX_VALUE;
return 1 / moveSpeed;
}
private float calRotation(float targetOffset) {
final float realAngle = reverseRotate ? angle : -angle;
return realAngle / mInterval * targetOffset;
}
public static class Builder {
private static float INTERVAL_ANGLE = 360f;
private static final float DEFAULT_SPEED = 1f;
private int itemSpace;
private int orientation;
private float angle;
private float moveSpeed;
private boolean reverseRotate;
private boolean reverseLayout;
private Context context;
private int maxVisibleItemCount;
private int distanceToBottom;
public Builder(Context context, int itemSpace) {
this.context = context;
this.itemSpace = itemSpace;
orientation = HORIZONTAL;
angle = INTERVAL_ANGLE;
this.moveSpeed = DEFAULT_SPEED;
reverseRotate = false;
reverseLayout = false;
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
}
public Builder setOrientation(int orientation) {
this.orientation = orientation;
return this;
}
public Builder setAngle(float angle) {
this.angle = angle;
return this;
}
public Builder setReverseLayout(boolean reverseLayout) {
this.reverseLayout = reverseLayout;
return this;
}
public Builder setMoveSpeed(float moveSpeed) {
this.moveSpeed = moveSpeed;
return this;
}
public Builder setReverseRotate(boolean reverseRotate) {
this.reverseRotate = reverseRotate;
return this;
}
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
this.maxVisibleItemCount = maxVisibleItemCount;
return this;
}
public Builder setDistanceToBottom(int distanceToBottom) {
this.distanceToBottom = distanceToBottom;
return this;
}
public RotateLayoutManager build() {
return new RotateLayoutManager(this);
}
}
}

@ -0,0 +1,221 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.content.Context;
import android.view.View;
/**
* An implementation of {@link ViewPagerLayoutManager}
* which zooms the center item
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class ScaleLayoutManager extends ViewPagerLayoutManager {
private int itemSpace;
private float minScale;
private float moveSpeed;
private float maxAlpha;
private float minAlpha;
public ScaleLayoutManager(Context context, int itemSpace) {
this(new Builder(context, itemSpace));
}
public ScaleLayoutManager(Context context, int itemSpace, int orientation) {
this(new Builder(context, itemSpace).setOrientation(orientation));
}
public ScaleLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) {
this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout));
}
public ScaleLayoutManager(Builder builder) {
this(builder.context, builder.itemSpace, builder.minScale, builder.maxAlpha, builder.minAlpha,
builder.orientation, builder.moveSpeed, builder.maxVisibleItemCount, builder.distanceToBottom,
builder.reverseLayout);
}
private ScaleLayoutManager(Context context, int itemSpace, float minScale, float maxAlpha, float minAlpha,
int orientation, float moveSpeed, int maxVisibleItemCount, int distanceToBottom,
boolean reverseLayout) {
super(context, orientation, reverseLayout);
setDistanceToBottom(distanceToBottom);
setMaxVisibleItemCount(maxVisibleItemCount);
this.itemSpace = itemSpace;
this.minScale = minScale;
this.moveSpeed = moveSpeed;
this.maxAlpha = maxAlpha;
this.minAlpha = minAlpha;
}
public int getItemSpace() {
return itemSpace;
}
public float getMinScale() {
return minScale;
}
public float getMoveSpeed() {
return moveSpeed;
}
public float getMaxAlpha() {
return maxAlpha;
}
public float getMinAlpha() {
return minAlpha;
}
public void setItemSpace(int itemSpace) {
assertNotInLayoutOrScroll(null);
if (this.itemSpace == itemSpace) return;
this.itemSpace = itemSpace;
removeAllViews();
}
public void setMinScale(float minScale) {
assertNotInLayoutOrScroll(null);
if (this.minScale == minScale) return;
this.minScale = minScale;
removeAllViews();
}
public void setMaxAlpha(float maxAlpha) {
assertNotInLayoutOrScroll(null);
if (maxAlpha > 1) maxAlpha = 1;
if (this.maxAlpha == maxAlpha) return;
this.maxAlpha = maxAlpha;
requestLayout();
}
public void setMinAlpha(float minAlpha) {
assertNotInLayoutOrScroll(null);
if (minAlpha < 0) minAlpha = 0;
if (this.minAlpha == minAlpha) return;
this.minAlpha = minAlpha;
requestLayout();
}
public void setMoveSpeed(float moveSpeed) {
assertNotInLayoutOrScroll(null);
if (this.moveSpeed == moveSpeed) return;
this.moveSpeed = moveSpeed;
}
@Override
protected float setInterval() {
return itemSpace + mDecoratedMeasurement;
}
@Override
protected void setItemViewProperty(View itemView, float targetOffset) {
float scale = calculateScale(targetOffset + mSpaceMain);
itemView.setScaleX(scale);
itemView.setScaleY(scale);
final float alpha = calAlpha(targetOffset);
itemView.setAlpha(alpha);
}
private float calAlpha(float targetOffset) {
final float offset = Math.abs(targetOffset);
float alpha = (minAlpha - maxAlpha) / mInterval * offset + maxAlpha;
if (offset >= mInterval) alpha = minAlpha;
return alpha;
}
@Override
protected float getDistanceRatio() {
if (moveSpeed == 0) return Float.MAX_VALUE;
return 1 / moveSpeed;
}
/**
* @param x start positon of the view you want scale
* @return the scale rate of current scroll mOffset
*/
private float calculateScale(float x) {
float deltaX = Math.abs(x - mSpaceMain);
if (deltaX - mDecoratedMeasurement > 0) deltaX = mDecoratedMeasurement;
return 1f - deltaX / mDecoratedMeasurement * (1f - minScale);
}
public static class Builder {
private static final float SCALE_RATE = 0.8f;
private static final float DEFAULT_SPEED = 1f;
private static float MIN_ALPHA = 1f;
private static float MAX_ALPHA = 1f;
private int itemSpace;
private int orientation;
private float minScale;
private float moveSpeed;
private float maxAlpha;
private float minAlpha;
private boolean reverseLayout;
private Context context;
private int maxVisibleItemCount;
private int distanceToBottom;
public Builder(Context context, int itemSpace) {
this.itemSpace = itemSpace;
this.context = context;
orientation = HORIZONTAL;
minScale = SCALE_RATE;
this.moveSpeed = DEFAULT_SPEED;
maxAlpha = MAX_ALPHA;
minAlpha = MIN_ALPHA;
reverseLayout = false;
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
}
public Builder setOrientation(int orientation) {
this.orientation = orientation;
return this;
}
public Builder setMinScale(float minScale) {
this.minScale = minScale;
return this;
}
public Builder setReverseLayout(boolean reverseLayout) {
this.reverseLayout = reverseLayout;
return this;
}
public Builder setMaxAlpha(float maxAlpha) {
if (maxAlpha > 1) maxAlpha = 1;
this.maxAlpha = maxAlpha;
return this;
}
public Builder setMinAlpha(float minAlpha) {
if (minAlpha < 0) minAlpha = 0;
this.minAlpha = minAlpha;
return this;
}
public Builder setMoveSpeed(float moveSpeed) {
this.moveSpeed = moveSpeed;
return this;
}
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
this.maxVisibleItemCount = maxVisibleItemCount;
return this;
}
public Builder setDistanceToBottom(int distanceToBottom) {
this.distanceToBottom = distanceToBottom;
return this;
}
public ScaleLayoutManager build() {
return new ScaleLayoutManager(this);
}
}
}

@ -0,0 +1,24 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
public class ScrollHelper {
/* package */
static void smoothScrollToPosition(RecyclerView recyclerView, ViewPagerLayoutManager viewPagerLayoutManager, int targetPosition) {
final int delta = viewPagerLayoutManager.getOffsetToPosition(targetPosition);
if (viewPagerLayoutManager.getOrientation() == RecyclerView.VERTICAL) {
recyclerView.smoothScrollBy(0, delta);
} else {
recyclerView.smoothScrollBy(delta, 0);
}
}
public static void smoothScrollToTargetView(RecyclerView recyclerView, View targetView) {
final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (!(layoutManager instanceof ViewPagerLayoutManager)) return;
final int targetPosition = ((ViewPagerLayoutManager) layoutManager).getLayoutPositionOfView(targetView);
smoothScrollToPosition(recyclerView, (ViewPagerLayoutManager) layoutManager, targetPosition);
}
}

@ -0,0 +1,936 @@
package com.common.commonlib.view.viewpagerlayoutmanager;
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue"})
public abstract class ViewPagerLayoutManager extends LinearLayoutManager {
public static final int DETERMINE_BY_MAX_AND_MIN = -1;
public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;
public static final int VERTICAL = OrientationHelper.VERTICAL;
protected static final int INVALID_SIZE = Integer.MAX_VALUE;
private static final int DIRECTION_NO_WHERE = -1;
private static final int DIRECTION_FORWARD = 0;
private static final int DIRECTION_BACKWARD = 1;
protected int mDecoratedMeasurement;
protected int mDecoratedMeasurementInOther;
protected int mSpaceMain;
protected int mSpaceInOther;
/**
* The offset of property which will change while scrolling
*/
protected float mOffset;
/**
* Many calculations are made depending on orientation. To keep it clean, this interface
* helps {@link LinearLayoutManager} make those decisions.
* Based on {@link #mOrientation}, an implementation is lazily created in
* method.
*/
protected OrientationHelper mOrientationHelper;
protected float mInterval; //the mInterval of each item's mOffset
/**
* Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}
*/
int mOrientation;
/* package */ OnPageChangeListener onPageChangeListener;
private SparseArray<View> positionCache = new SparseArray<>();
/**
* Defines if layout should be calculated from end to start.
*/
private boolean mReverseLayout = false;
/**
* This keeps the final value for how LayoutManager should start laying out views.
* It is calculated by checking {@link #getReverseLayout()} and View's layout direction.
* {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run.
*/
private boolean mShouldReverseLayout = false;
/**
* Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}.
* see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}
*/
private boolean mSmoothScrollbarEnabled = true;
/**
* When LayoutManager needs to scroll to a position, it sets this variable and requests a
* layout which will check this variable and re-layout accordingly.
*/
private int mPendingScrollPosition = NO_POSITION;
private SavedState mPendingSavedState = null;
private boolean mRecycleChildrenOnDetach;
private boolean mInfinite = false;
private boolean mEnableBringCenterToFront;
private int mLeftItems;
private int mRightItems;
/**
* max visible item count
*/
private int mMaxVisibleItemCount = DETERMINE_BY_MAX_AND_MIN;
private Interpolator mSmoothScrollInterpolator;
private int mDistanceToBottom = INVALID_SIZE;
/**
* use for handle focus
*/
private View currentFocusView;
/**
* Creates a horizontal ViewPagerLayoutManager
*/
public ViewPagerLayoutManager(Context context) {
this(context, HORIZONTAL, false);
}
/**
* @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
* @param reverseLayout When set to true, layouts from end to start
*/
public ViewPagerLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context);
setOrientation(orientation);
setReverseLayout(reverseLayout);
setAutoMeasureEnabled(true);
setItemPrefetchEnabled(false);
}
/**
* @return the mInterval of each item's mOffset
*/
protected abstract float setInterval();
protected abstract void setItemViewProperty(View itemView, float targetOffset);
/**
* cause elevation is not support below api 21,
* so you can set your elevation here for supporting it below api 21
* or you can just setElevation in {@link #setItemViewProperty(View, float)}
*/
protected float setViewElevation(View itemView, float targetOffset) {
return 0;
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
/**
* Returns whether LayoutManager will recycle its children when it is detached from
* RecyclerView.
*
* @return true if LayoutManager will recycle its children when it is detached from
* RecyclerView.
*/
public boolean getRecycleChildrenOnDetach() {
return mRecycleChildrenOnDetach;
}
/**
* Set whether LayoutManager will recycle its children when it is detached from
* RecyclerView.
* <p>
* If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set
* this flag to <code>true</code> so that views will be available to other RecyclerViews
* immediately.
* <p>
* Note that, setting this flag will result in a performance drop if RecyclerView
* is restored.
*
* @param recycleChildrenOnDetach Whether children should be recycled in detach or not.
*/
public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
mRecycleChildrenOnDetach = recycleChildrenOnDetach;
}
@Override
public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
super.onDetachedFromWindow(view, recycler);
if (mRecycleChildrenOnDetach) {
removeAndRecycleAllViews(recycler);
recycler.clear();
}
}
@Override
public Parcelable onSaveInstanceState() {
if (mPendingSavedState != null) {
return new SavedState(mPendingSavedState);
}
SavedState savedState = new SavedState();
savedState.position = mPendingScrollPosition;
savedState.offset = mOffset;
savedState.isReverseLayout = mShouldReverseLayout;
return savedState;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) {
mPendingSavedState = new SavedState((SavedState) state);
requestLayout();
}
}
/**
* @return true if {@link #getOrientation()} is {@link #HORIZONTAL}
*/
@Override
public boolean canScrollHorizontally() {
return mOrientation == HORIZONTAL;
}
/**
* @return true if {@link #getOrientation()} is {@link #VERTICAL}
*/
@Override
public boolean canScrollVertically() {
return mOrientation == VERTICAL;
}
/**
* Returns the current orientation of the layout.
*
* @return Current orientation, either {@link #HORIZONTAL} or {@link #VERTICAL}
* @see #setOrientation(int)
*/
public int getOrientation() {
return mOrientation;
}
/**
* Sets the orientation of the layout. {@link ViewPagerLayoutManager}
* will do its best to keep scroll position.
*
* @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
*/
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL && orientation != VERTICAL) {
throw new IllegalArgumentException("invalid orientation:" + orientation);
}
assertNotInLayoutOrScroll(null);
if (orientation == mOrientation) {
return;
}
mOrientation = orientation;
mOrientationHelper = null;
mDistanceToBottom = INVALID_SIZE;
removeAllViews();
}
/**
* Returns the max visible item count, {@link #DETERMINE_BY_MAX_AND_MIN} means it haven't been set now
* And it will use {@link #maxRemoveOffset()} and {@link #minRemoveOffset()} to handle the range
*
* @return Max visible item count
*/
public int getMaxVisibleItemCount() {
return mMaxVisibleItemCount;
}
/**
* Set the max visible item count, {@link #DETERMINE_BY_MAX_AND_MIN} means it haven't been set now
* And it will use {@link #maxRemoveOffset()} and {@link #minRemoveOffset()} to handle the range
*
* @param mMaxVisibleItemCount Max visible item count
*/
public void setMaxVisibleItemCount(int mMaxVisibleItemCount) {
assertNotInLayoutOrScroll(null);
if (this.mMaxVisibleItemCount == mMaxVisibleItemCount) return;
this.mMaxVisibleItemCount = mMaxVisibleItemCount;
removeAllViews();
}
/**
* Calculates the view layout order. (e.g. from end to start or start to end)
* RTL layout support is applied automatically. So if layout is RTL and
* {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left.
*/
private void resolveShouldLayoutReverse() {
// A == B is the same result, but we rather keep it readable
if (mOrientation == VERTICAL || !isLayoutRTL()) {
mShouldReverseLayout = mReverseLayout;
} else {
mShouldReverseLayout = !mReverseLayout;
}
}
/**
* Returns if views are laid out from the opposite direction of the layout.
*
* @return If layout is reversed or not.
* @see #setReverseLayout(boolean)
*/
public boolean getReverseLayout() {
return mReverseLayout;
}
public void setReverseLayout(boolean reverseLayout) {
assertNotInLayoutOrScroll(null);
if (reverseLayout == mReverseLayout) {
return;
}
mReverseLayout = reverseLayout;
removeAllViews();
}
public void setSmoothScrollInterpolator(Interpolator smoothScrollInterpolator) {
this.mSmoothScrollInterpolator = smoothScrollInterpolator;
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
final int offsetPosition;
// fix wrong scroll direction when infinite enable
if (mInfinite) {
final int currentPosition = getCurrentPosition();
final int total = getItemCount();
final int targetPosition;
if (position < currentPosition) {
int d1 = currentPosition - position;
int d2 = total - currentPosition + position;
targetPosition = d1 < d2 ? (currentPosition - d1) : (currentPosition + d2);
} else {
int d1 = position - currentPosition;
int d2 = currentPosition + total - position;
targetPosition = d1 < d2 ? (currentPosition + d1) : (currentPosition - d2);
}
offsetPosition = getOffsetToPosition(targetPosition);
} else {
offsetPosition = getOffsetToPosition(position);
}
if (mOrientation == VERTICAL) {
recyclerView.smoothScrollBy(0, offsetPosition, mSmoothScrollInterpolator);
} else {
recyclerView.smoothScrollBy(offsetPosition, 0, mSmoothScrollInterpolator);
}
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.getItemCount() == 0) {
removeAndRecycleAllViews(recycler);
mOffset = 0;
return;
}
ensureLayoutState();
resolveShouldLayoutReverse();
//make sure properties are correct while measure more than once
View scrap = getMeasureView(recycler, state, 0);
if (scrap == null) {
removeAndRecycleAllViews(recycler);
mOffset = 0;
return;
}
measureChildWithMargins(scrap, 0, 0);
mDecoratedMeasurement = mOrientationHelper.getDecoratedMeasurement(scrap);
mDecoratedMeasurementInOther = mOrientationHelper.getDecoratedMeasurementInOther(scrap);
mSpaceMain = (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2;
if (mDistanceToBottom == INVALID_SIZE) {
mSpaceInOther = (mOrientationHelper.getTotalSpaceInOther() - mDecoratedMeasurementInOther) / 2;
} else {
mSpaceInOther = mOrientationHelper.getTotalSpaceInOther() - mDecoratedMeasurementInOther - mDistanceToBottom;
}
mInterval = setInterval();
setUp();
if (mInterval == 0) {
mLeftItems = 1;
mRightItems = 1;
} else {
mLeftItems = (int) Math.abs(minRemoveOffset() / mInterval) + 1;
mRightItems = (int) Math.abs(maxRemoveOffset() / mInterval) + 1;
}
if (mPendingSavedState != null) {
mShouldReverseLayout = mPendingSavedState.isReverseLayout;
mPendingScrollPosition = mPendingSavedState.position;
mOffset = mPendingSavedState.offset;
}
if (mPendingScrollPosition != NO_POSITION) {
mOffset = mShouldReverseLayout ?
mPendingScrollPosition * -mInterval : mPendingScrollPosition * mInterval;
}
layoutItems(recycler);
}
private View getMeasureView(RecyclerView.Recycler recycler, RecyclerView.State state, int index) {
if (index >= state.getItemCount() || index < 0) return null;
try {
return recycler.getViewForPosition(index);
} catch (Exception e) {
return getMeasureView(recycler, state, index + 1);
}
}
@Override
public void onLayoutCompleted(RecyclerView.State state) {
super.onLayoutCompleted(state);
mPendingSavedState = null;
mPendingScrollPosition = NO_POSITION;
}
@Override
public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views, int direction, int focusableMode) {
final int currentPosition = getCurrentPosition();
final View currentView = findViewByPosition(currentPosition);
if (currentView == null) return true;
if (recyclerView.hasFocus()) {
final int movement = getMovement(direction);
if (movement != DIRECTION_NO_WHERE) {
final int targetPosition = movement == DIRECTION_BACKWARD ?
currentPosition - 1 : currentPosition + 1;
ScrollHelper.smoothScrollToPosition(recyclerView, this, targetPosition);
}
} else {
currentView.addFocusables(views, direction, focusableMode);
}
return true;
}
@Override
public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) {
return null;
}
private int getMovement(int direction) {
if (mOrientation == VERTICAL) {
if (direction == View.FOCUS_UP) {
return mShouldReverseLayout ? DIRECTION_FORWARD : DIRECTION_BACKWARD;
} else if (direction == View.FOCUS_DOWN) {
return mShouldReverseLayout ? DIRECTION_BACKWARD : DIRECTION_FORWARD;
} else {
return DIRECTION_NO_WHERE;
}
} else {
if (direction == View.FOCUS_LEFT) {
return mShouldReverseLayout ? DIRECTION_FORWARD : DIRECTION_BACKWARD;
} else if (direction == View.FOCUS_RIGHT) {
return mShouldReverseLayout ? DIRECTION_BACKWARD : DIRECTION_FORWARD;
} else {
return DIRECTION_NO_WHERE;
}
}
}
void ensureLayoutState() {
if (mOrientationHelper == null) {
mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
}
}
/**
* You can set up your own properties here or change the exist properties like mSpaceMain and mSpaceInOther
*/
protected void setUp() {
}
private float getProperty(int position) {
return mShouldReverseLayout ? position * -mInterval : position * mInterval;
}
@Override
public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
removeAllViews();
mOffset = 0;
}
@Override
public void scrollToPosition(int position) {
if (!mInfinite && (position < 0 || position >= getItemCount())) return;
mPendingScrollPosition = position;
mOffset = mShouldReverseLayout ? position * -mInterval : position * mInterval;
requestLayout();
}
@Override
public int computeHorizontalScrollOffset(RecyclerView.State state) {
return computeScrollOffset();
}
@Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
return computeScrollOffset();
}
@Override
public int computeHorizontalScrollExtent(RecyclerView.State state) {
return computeScrollExtent();
}
@Override
public int computeVerticalScrollExtent(RecyclerView.State state) {
return computeScrollExtent();
}
@Override
public int computeHorizontalScrollRange(RecyclerView.State state) {
return computeScrollRange();
}
@Override
public int computeVerticalScrollRange(RecyclerView.State state) {
return computeScrollRange();
}
private int computeScrollOffset() {
if (getChildCount() == 0) {
return 0;
}
if (!mSmoothScrollbarEnabled) {
return !mShouldReverseLayout ?
getCurrentPosition() : getItemCount() - getCurrentPosition() - 1;
}
final float realOffset = getOffsetOfRightAdapterPosition();
return !mShouldReverseLayout ? (int) realOffset : (int) ((getItemCount() - 1) * mInterval + realOffset);
}
private int computeScrollExtent() {
if (getChildCount() == 0) {
return 0;
}
if (!mSmoothScrollbarEnabled) {
return 1;
}
return (int) mInterval;
}
private int computeScrollRange() {
if (getChildCount() == 0) {
return 0;
}
if (!mSmoothScrollbarEnabled) {
return getItemCount();
}
return (int) (getItemCount() * mInterval);
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (mOrientation == VERTICAL) {
return 0;
}
return scrollBy(dx, recycler, state);
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (mOrientation == HORIZONTAL) {
return 0;
}
return scrollBy(dy, recycler, state);
}
private int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getChildCount() == 0 || dy == 0) {
return 0;
}
ensureLayoutState();
int willScroll = dy;
float realDx = dy / getDistanceRatio();
if (Math.abs(realDx) < 0.00000001f) {
return 0;
}
float targetOffset = mOffset + realDx;
//handle the boundary
if (!mInfinite && targetOffset < getMinOffset()) {
willScroll -= (targetOffset - getMinOffset()) * getDistanceRatio();
} else if (!mInfinite && targetOffset > getMaxOffset()) {
willScroll = (int) ((getMaxOffset() - mOffset) * getDistanceRatio());
}
realDx = willScroll / getDistanceRatio();
mOffset += realDx;
//handle recycle
layoutItems(recycler);
return willScroll;
}
private void layoutItems(RecyclerView.Recycler recycler) {
detachAndScrapAttachedViews(recycler);
positionCache.clear();
final int itemCount = getItemCount();
if (itemCount == 0) return;
// make sure that current position start from 0 to 1
final int currentPos = mShouldReverseLayout ?
-getCurrentPositionOffset() : getCurrentPositionOffset();
int start = currentPos - mLeftItems;
int end = currentPos + mRightItems;
// handle max visible count
if (useMaxVisibleCount()) {
boolean isEven = mMaxVisibleItemCount % 2 == 0;
if (isEven) {
int offset = mMaxVisibleItemCount / 2;
start = currentPos - offset + 1;
end = currentPos + offset + 1;
} else {
int offset = (mMaxVisibleItemCount - 1) / 2;
start = currentPos - offset;
end = currentPos + offset + 1;
}
}
if (!mInfinite) {
if (start < 0) {
start = 0;
if (useMaxVisibleCount()) end = mMaxVisibleItemCount;
}
if (end > itemCount) end = itemCount;
}
float lastOrderWeight = Float.MIN_VALUE;
for (int i = start; i < end; i++) {
if (useMaxVisibleCount() || !removeCondition(getProperty(i) - mOffset)) {
// start and end base on current position,
// so we need to calculate the adapter position
int adapterPosition = i;
if (i >= itemCount) {
adapterPosition %= itemCount;
} else if (i < 0) {
int delta = (-adapterPosition) % itemCount;
if (delta == 0) delta = itemCount;
adapterPosition = itemCount - delta;
}
final View scrap = recycler.getViewForPosition(adapterPosition);
measureChildWithMargins(scrap, 0, 0);
resetViewProperty(scrap);
// we need i to calculate the real offset of current view
final float targetOffset = getProperty(i) - mOffset;
layoutScrap(scrap, targetOffset);
final float orderWeight = mEnableBringCenterToFront ?
setViewElevation(scrap, targetOffset) : adapterPosition;
if (orderWeight > lastOrderWeight) {
addView(scrap);
} else {
addView(scrap, 0);
}
if (i == currentPos) currentFocusView = scrap;
lastOrderWeight = orderWeight;
positionCache.put(i, scrap);
}
}
currentFocusView.requestFocus();
}
private boolean useMaxVisibleCount() {
return mMaxVisibleItemCount != DETERMINE_BY_MAX_AND_MIN;
}
private boolean removeCondition(float targetOffset) {
return targetOffset > maxRemoveOffset() || targetOffset < minRemoveOffset();
}
private void resetViewProperty(View v) {
v.setRotation(0);
v.setRotationY(0);
v.setRotationX(0);
v.setScaleX(1f);
v.setScaleY(1f);
v.setAlpha(1f);
}
/* package */ float getMaxOffset() {
return !mShouldReverseLayout ? (getItemCount() - 1) * mInterval : 0;
}
/* package */ float getMinOffset() {
return !mShouldReverseLayout ? 0 : -(getItemCount() - 1) * mInterval;
}
private void layoutScrap(View scrap, float targetOffset) {
final int left = calItemLeft(scrap, targetOffset);
final int top = calItemTop(scrap, targetOffset);
if (mOrientation == VERTICAL) {
layoutDecorated(scrap, mSpaceInOther + left, mSpaceMain + top,
mSpaceInOther + left + mDecoratedMeasurementInOther, mSpaceMain + top + mDecoratedMeasurement);
} else {
layoutDecorated(scrap, mSpaceMain + left, mSpaceInOther + top,
mSpaceMain + left + mDecoratedMeasurement, mSpaceInOther + top + mDecoratedMeasurementInOther);
}
setItemViewProperty(scrap, targetOffset);
}
protected int calItemLeft(View itemView, float targetOffset) {
return mOrientation == VERTICAL ? 0 : (int) targetOffset;
}
protected int calItemTop(View itemView, float targetOffset) {
return mOrientation == VERTICAL ? (int) targetOffset : 0;
}
/**
* when the target offset reach this,
* the view will be removed and recycled in {@link #layoutItems(RecyclerView.Recycler)}
*/
protected float maxRemoveOffset() {
return mOrientationHelper.getTotalSpace() - mSpaceMain;
}
/**
* when the target offset reach this,
* the view will be removed and recycled in {@link #layoutItems(RecyclerView.Recycler)}
*/
protected float minRemoveOffset() {
return -mDecoratedMeasurement - mOrientationHelper.getStartAfterPadding() - mSpaceMain;
}
protected float getDistanceRatio() {
return 1f;
}
public int getCurrentPosition() {
if (getItemCount() == 0) return 0;
int position = getCurrentPositionOffset();
if (!mInfinite) return Math.abs(position);
position = !mShouldReverseLayout ?
//take care of position = getItemCount()
(position >= 0 ?
position % getItemCount() :
getItemCount() + position % getItemCount()) :
(position > 0 ?
getItemCount() - position % getItemCount() :
-position % getItemCount());
return position == getItemCount() ? 0 : position;
}
@Override
public View findViewByPosition(int position) {
final int itemCount = getItemCount();
if (itemCount == 0) return null;
for (int i = 0; i < positionCache.size(); i++) {
final int key = positionCache.keyAt(i);
if (key >= 0) {
if (position == key % itemCount) return positionCache.valueAt(i);
} else {
int delta = key % itemCount;
if (delta == 0) delta = -itemCount;
if (itemCount + delta == position) return positionCache.valueAt(i);
}
}
return null;
}
public int getLayoutPositionOfView(View v) {
for (int i = 0; i < positionCache.size(); i++) {
int key = positionCache.keyAt(i);
View value = positionCache.get(key);
if (value == v) return key;
}
return -1;
}
/* package */ int getCurrentPositionOffset() {
if (mInterval == 0) return 0;
return Math.round(mOffset / mInterval);
}
/**
* Sometimes we need to get the right offset of matching adapter position
* cause when {@link #mInfinite} is set true, there will be no limitation of {@link #mOffset}
*/
private float getOffsetOfRightAdapterPosition() {
if (mShouldReverseLayout)
return mInfinite ?
(mOffset <= 0 ?
(mOffset % (mInterval * getItemCount())) :
(getItemCount() * -mInterval + mOffset % (mInterval * getItemCount()))) :
mOffset;
else
return mInfinite ?
(mOffset >= 0 ?
(mOffset % (mInterval * getItemCount())) :
(getItemCount() * mInterval + mOffset % (mInterval * getItemCount()))) :
mOffset;
}
/**
* used by {@link CenterSnapHelper} to center the current view
*
* @return the dy between center and current position
*/
public int getOffsetToCenter() {
if (mInfinite)
return (int) ((getCurrentPositionOffset() * mInterval - mOffset) * getDistanceRatio());
return (int) ((getCurrentPosition() *
(!mShouldReverseLayout ? mInterval : -mInterval) - mOffset) * getDistanceRatio());
}
public int getOffsetToPosition(int position) {
if (mInfinite)
return (int) (((getCurrentPositionOffset() +
(!mShouldReverseLayout ? position - getCurrentPositionOffset() : -getCurrentPositionOffset() - position)) *
mInterval - mOffset) * getDistanceRatio());
return (int) ((position *
(!mShouldReverseLayout ? mInterval : -mInterval) - mOffset) * getDistanceRatio());
}
public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
this.onPageChangeListener = onPageChangeListener;
}
public boolean getInfinite() {
return mInfinite;
}
public void setInfinite(boolean enable) {
assertNotInLayoutOrScroll(null);
if (enable == mInfinite) {
return;
}
mInfinite = enable;
requestLayout();
}
public int getDistanceToBottom() {
return mDistanceToBottom == INVALID_SIZE ?
(mOrientationHelper.getTotalSpaceInOther() - mDecoratedMeasurementInOther) / 2 : mDistanceToBottom;
}
public void setDistanceToBottom(int mDistanceToBottom) {
assertNotInLayoutOrScroll(null);
if (this.mDistanceToBottom == mDistanceToBottom) return;
this.mDistanceToBottom = mDistanceToBottom;
removeAllViews();
}
public boolean getEnableBringCenterToFront() {
return mEnableBringCenterToFront;
}
public void setEnableBringCenterToFront(boolean bringCenterToTop) {
assertNotInLayoutOrScroll(null);
if (mEnableBringCenterToFront == bringCenterToTop) {
return;
}
this.mEnableBringCenterToFront = bringCenterToTop;
requestLayout();
}
/**
* Returns the current state of the smooth scrollbar feature. It is enabled by default.
*
* @return True if smooth scrollbar is enabled, false otherwise.
* @see #setSmoothScrollbarEnabled(boolean)
*/
public boolean getSmoothScrollbarEnabled() {
return mSmoothScrollbarEnabled;
}
/**
* When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed
* based on the number of visible pixels in the visible items. This however assumes that all
* list items have similar or equal widths or heights (depending on list orientation).
* If you use a list in which items have different dimensions, the scrollbar will change
* appearance as the user scrolls through the list. To avoid this issue, you need to disable
* this property.
* <p>
* When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based
* solely on the number of items in the adapter and the position of the visible items inside
* the adapter. This provides a stable scrollbar as the user navigates through a list of items
* with varying widths / heights.
*
* @param enabled Whether or not to enable smooth scrollbar.
* @see #setSmoothScrollbarEnabled(boolean)
*/
public void setSmoothScrollbarEnabled(boolean enabled) {
mSmoothScrollbarEnabled = enabled;
}
public interface OnPageChangeListener {
void onPageSelected(int position);
void onPageScrollStateChanged(int state);
}
private static class SavedState implements Parcelable {
public static final Creator<SavedState> CREATOR
= new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
int position;
float offset;
boolean isReverseLayout;
SavedState() {
}
SavedState(Parcel in) {
position = in.readInt();
offset = in.readFloat();
isReverseLayout = in.readInt() == 1;
}
public SavedState(SavedState other) {
position = other.position;
offset = other.offset;
isReverseLayout = other.isReverseLayout;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(position);
dest.writeFloat(offset);
dest.writeInt(isReverseLayout ? 1 : 0);
}
}
}

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#E9E9E9"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#E9E9E9"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/title_bar_bg_color"
android:gravity="center_vertical"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/rl_left_hot_zone"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingStart="@dimen/font_20dp"
android:paddingEnd="@dimen/font_20dp">
<ImageView
android:id="@+id/iv_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@drawable/ic_back" />
</RelativeLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:textColor="@color/white"
android:textSize="@dimen/font_16dp" />
<RelativeLayout
android:id="@+id/rl_edit_hot_zone"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingStart="@dimen/font_20dp"
android:paddingEnd="@dimen/font_20dp"
android:visibility="gone">
<TextView
android:id="@+id/tv_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/edit"
android:textColor="@color/white"
android:textSize="@dimen/font_14dp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_add_hot_zone"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingStart="@dimen/font_20dp"
android:paddingEnd="@dimen/font_20dp">
<ImageView
android:id="@+id/iv_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@drawable/ic_add" />
</RelativeLayout>
</LinearLayout>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/font_80dp"
android:layout_height="@dimen/font_80dp">
<ImageView
android:id="@+id/bg_pic_add"
android:layout_width="@dimen/font_74dp"
android:layout_height="@dimen/font_74dp"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:src="@drawable/ic_add" />
</RelativeLayout>

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/font_80dp"
android:layout_height="@dimen/font_80dp">
<ImageView
android:id="@+id/pic_iv"
android:layout_width="@dimen/font_74dp"
android:layout_height="@dimen/font_74dp"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:scaleType="center" />
<ImageView
android:id="@+id/pic_delete_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/pic_iv"
android:layout_alignEnd="@+id/pic_iv"
android:layout_marginTop="-5dp"
android:layout_marginEnd="-5dp"
android:src="@drawable/ic_pic_delete" />
<ImageView
android:id="@+id/iv_loading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#33000000"
android:padding="@dimen/font_20dp"
android:visibility="gone" />
</RelativeLayout>

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CommonTitleView">
<attr name="title" format="string"></attr>
<attr name="show_style" format="enum">
<enum name="left" value="1" />
<enum name="left_middle" value="2" />
<enum name="left_middle_right" value="3" />
<enum name="middle" value="4" />
<enum name="middle_right" value="5" />
<enum name="right" value="6" />
<enum name="edit" value="7" />
</attr>
<attr name="is_fixed_height" format="boolean" />
<attr name="fixed_height" format="integer" />
<attr name="drawableLeft" format="reference" />
<attr name="drawableRight" format="reference" />
<attr name="bgColor" format="reference|color" />
</declare-styleable>
<declare-styleable name="AutoPlayRecyclerView">
<attr name="timeInterval" format="integer" />
<attr name="direction" format="enum">
<enum name="left" value="1" />
<enum name="right" value="2" />
</attr>
</declare-styleable>
</resources>

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="red">#FF0000</color>
<color name="bg_delete_btn">#FF5151</color>
<color name="bg_export_btn">#FFAA60</color>
<color name="font_666">#666666</color>
<color name="font_333">#333333</color>
<color name="font_999">#999999</color>
<color name="bg_F5">#F5F5F5</color>
<color name="bg_E9">#E9E9E9</color>
<color name="title_bar_bg_color">#0786F0</color>
<color name="btn_bg_start">#3BA6FF</color>
<color name="btn_bg_end">#0786F0</color>
<color name="yellow_button_color">#FFAA61</color>
<color name="home_template_text_color">#2E69FF</color>
<color name="home_record_text_color">#FF973B</color>
<color name="color_33">#333333</color>
<color name="color_f5">#F5F5F5</color>
<color name="color_66">#666666</color>
<color name="color_99">#999999</color>
</resources>

@ -0,0 +1,52 @@
<resources>
<dimen name="font_0.5dp">0.5dp</dimen>
<dimen name="font_1dp">1dp</dimen>
<dimen name="font_2dp">2dp</dimen>
<dimen name="font_3dp">3dp</dimen>
<dimen name="font_4dp">4dp</dimen>
<dimen name="font_8dp">8dp</dimen>
<dimen name="font_5dp">5dp</dimen>
<dimen name="font_6dp">6dp</dimen>
<dimen name="font_7dp">7dp</dimen>
<dimen name="font_9dp">9dp</dimen>
<dimen name="font_10dp">10dp</dimen>
<dimen name="font_11dp">11dp</dimen>
<dimen name="font_12dp">12dp</dimen>
<dimen name="font_13dp">13dp</dimen>
<dimen name="font_14dp">14dp</dimen>
<dimen name="font_15dp">15dp</dimen>
<dimen name="font_16dp">16dp</dimen>
<dimen name="font_17dp">17dp</dimen>
<dimen name="font_18dp">18dp</dimen>
<dimen name="font_19dp">19dp</dimen>
<dimen name="font_20dp">20dp</dimen>
<dimen name="font_21dp">21dp</dimen>
<dimen name="font_22dp">22dp</dimen>
<dimen name="font_23dp">23dp</dimen>
<dimen name="font_24dp">24dp</dimen>
<dimen name="font_26dp">26dp</dimen>
<dimen name="font_27dp">27dp</dimen>
<dimen name="font_28dp">28dp</dimen>
<dimen name="font_30dp">30dp</dimen>
<dimen name="font_29dp">29dp</dimen>
<dimen name="font_40dp">40dp</dimen>
<dimen name="font_42dp">42dp</dimen>
<dimen name="font_46dp">46dp</dimen>
<dimen name="font_60dp">60dp</dimen>
<dimen name="font_65dp">65dp</dimen>
<dimen name="font_70dp">70dp</dimen>
<dimen name="font_72dp">72dp</dimen>
<dimen name="font_74dp">74dp</dimen>
<dimen name="font_79dp">79dp</dimen>
<dimen name="font_80dp">80dp</dimen>
<dimen name="font_90dp">90dp</dimen>
<dimen name="font_84dp">84dp</dimen>
<dimen name="font_100dp">100dp</dimen>
<dimen name="font_115dp">115dp</dimen>
<dimen name="font_152dp">152dp</dimen>
<dimen name="font_160dp">160dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="action_height">48dp</dimen>
<dimen name="font_size">15dp</dimen>
<dimen name="font_185dp">185dp</dimen>
</resources>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="edit">编辑</string>
<string name="save">保存</string>
<string name="default_un_authorized_prompt">部分权限未授权,请手动授予,若不授予权限则无法使用app</string>
<string name="default_dialog_setting">设置</string>
<string name="default_dialog_cancel">取消</string>
</resources>

@ -3,3 +3,4 @@ include ':app' //, ':libuvccamera-release'
include ':library-push'
include ':library-ijkplayer'
include ':library-serialPort'
include ':library-common'

Loading…
Cancel
Save