From 891f6ed404315c9983a0417d385f75bd8374802a Mon Sep 17 00:00:00 2001 From: atbest Date: Thu, 23 May 2019 01:49:40 -0400 Subject: [PATCH 01/12] Updated entities --- app/src/main/java/io/legado/app/data/entities/Book.kt | 2 +- app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt | 4 ++-- app/src/main/java/io/legado/app/data/entities/Source.kt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index e207f8c03..ace8e1b1d 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -5,7 +5,7 @@ import androidx.room.* import kotlinx.android.parcel.Parcelize @Parcelize -@Entity(tableName = "books", indices = [(Index(value = ["descUrl"]))]) +@Entity(tableName = "books", indices = [(Index(value = ["descUrl"], unique = true))]) data class Book(@PrimaryKey var descUrl: String = "", // 详情页Url(本地书源存储完整文件路径) var sourceId: Int = -1, // 书源规则id(默认-1,表示本地书籍) diff --git a/app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt b/app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt index fdfa41d3f..2436fb641 100644 --- a/app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt +++ b/app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt @@ -16,8 +16,8 @@ data class ReplaceRule( val pattern: String? = null, val replacement: String? = null, val scope: String? = null, - val isEnabled: Boolean? = null, - val isRegex: Boolean? = null, + val isEnabled: Boolean = true, + val isRegex: Boolean = true, val order: Int = 0 ) : Parcelable diff --git a/app/src/main/java/io/legado/app/data/entities/Source.kt b/app/src/main/java/io/legado/app/data/entities/Source.kt index 1c6cfcf18..aa38236b5 100644 --- a/app/src/main/java/io/legado/app/data/entities/Source.kt +++ b/app/src/main/java/io/legado/app/data/entities/Source.kt @@ -14,14 +14,14 @@ data class Source(@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "sourceId") var id: Int = 0, // 编号 var name: String = "", // 名称 - var origin: String = "", // 地址,包括 http/https + var origin: String = "", // 地址,包括 http/https var type: Int = 0, // 类型,0 文本,1 音频 var group: String? = null, // 分组 var header: String? = null, // header var loginUrl: String? = null, // 登录地址 var isEnabled: Boolean = true, // 是否启用 var lastUpdateTime: Long = 0, // 最后更新时间,用于排序 - var serialNumber: Int = 0, // 手动排序编号 + var customOrder: Int = 0, // 手动排序编号 var weight: Int = 0, // 智能排序的权重 var exploreRule: String? = null, // 发现规则 var searchRule: String? = null, // 搜索规则 From 11f7c21adce8688fae0b7eeb6fbd10c01d60001a Mon Sep 17 00:00:00 2001 From: Antecer Date: Thu, 23 May 2019 14:04:20 +0800 Subject: [PATCH 02/12] =?UTF-8?q?=E4=BF=AE=E6=94=B9Book=E5=B1=9E=E6=80=A7,?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E5=AD=97=E6=AE=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/data/entities/Book.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index e207f8c03..d0ba835d7 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -9,14 +9,18 @@ import kotlinx.android.parcel.Parcelize data class Book(@PrimaryKey var descUrl: String = "", // 详情页Url(本地书源存储完整文件路径) var sourceId: Int = -1, // 书源规则id(默认-1,表示本地书籍) - var name: String = "", // 书籍名称(允许用户修改,适用于本地书籍) - var author: String? = null, // 作者名称(允许用户修改,适用于本地书籍) - var tag: String? = null, // 分类信息(允许用户修改,适用于本地书籍) - var coverUrl: String? = null, // 封面Url - var customCoverUrl: String? = null, // 自定义封面Url(允许用户修改,适用于网络和本地书籍) - var description: String? = null, // 简介内容(允许用户修改,适用于网络和本地书籍) + var name: String = "", // 书籍名称(书源获取) + var customName: String = "", // 书籍名称(用户修改) + var author: String? = null, // 作者名称(书源获取) + var customAuthor: String? = null, // 作者名称(用户修改) + var tag: String? = null, // 分类信息(书源获取) + var customTag: String? = null, // 分类信息(用户修改) + var coverUrl: String? = null, // 封面Url(书源获取) + var customCoverUrl: String? = null, // 封面Url(用户修改) + var description: String? = null, // 简介内容(书源获取) + var customDescription: String? = null, // 简介内容(用户修改) var charset: String? = null, // 自定义字符集名称(仅适用于本地书籍) - var type: Int = 0, // 0: 文本读物, 1: 有声读物 + var type: Int = 0, // 0: 文本读物, 1: 有声读物, 3: 本地书籍 var group: Int = 0, // 自定义分组索引号 var tocUrl: String = "", // 目录页Url (toc=table of Contents) var latestChapterTitle: String? = null, // 最新章节标题 From 7656572c771a70795ab9cdb28699aa942c272214 Mon Sep 17 00:00:00 2001 From: Antecer Date: Thu, 23 May 2019 14:06:42 +0800 Subject: [PATCH 03/12] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E6=B3=A8=E9=87=8A.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/data/entities/Book.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index d0ba835d7..b929e183c 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -20,7 +20,7 @@ data class Book(@PrimaryKey var description: String? = null, // 简介内容(书源获取) var customDescription: String? = null, // 简介内容(用户修改) var charset: String? = null, // 自定义字符集名称(仅适用于本地书籍) - var type: Int = 0, // 0: 文本读物, 1: 有声读物, 3: 本地书籍 + var type: Int = 0, // 0: 文本读物, 1: 有声读物 var group: Int = 0, // 自定义分组索引号 var tocUrl: String = "", // 目录页Url (toc=table of Contents) var latestChapterTitle: String? = null, // 最新章节标题 From 742e3f36938f8f60501f92cc83028447d1c286e8 Mon Sep 17 00:00:00 2001 From: atbest Date: Thu, 23 May 2019 04:46:15 -0400 Subject: [PATCH 04/12] Added String extensions --- app/src/main/java/io/legado/app/utils/StringExtensions.kt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 app/src/main/java/io/legado/app/utils/StringExtensions.kt diff --git a/app/src/main/java/io/legado/app/utils/StringExtensions.kt b/app/src/main/java/io/legado/app/utils/StringExtensions.kt new file mode 100644 index 000000000..957d5b091 --- /dev/null +++ b/app/src/main/java/io/legado/app/utils/StringExtensions.kt @@ -0,0 +1,3 @@ +package io.legado.app.utils + +fun String?.strim() = if (this.isNullOrBlank()) null else this.trim() \ No newline at end of file From 72803353376be913704b4fb81dad45334f7934bb Mon Sep 17 00:00:00 2001 From: atbest Date: Thu, 23 May 2019 04:50:17 -0400 Subject: [PATCH 05/12] Updated Book --- .../main/java/io/legado/app/data/entities/Book.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index 2be672dd2..fc601f9fe 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -2,15 +2,17 @@ package io.legado.app.data.entities import android.os.Parcelable import androidx.room.* +import io.legado.app.utils.strim import kotlinx.android.parcel.Parcelize @Parcelize @Entity(tableName = "books", indices = [(Index(value = ["descUrl"], unique = true))]) data class Book(@PrimaryKey var descUrl: String = "", // 详情页Url(本地书源存储完整文件路径) + var tocUrl: String = "", // 目录页Url (toc=table of Contents) var sourceId: Int = -1, // 书源规则id(默认-1,表示本地书籍) var name: String = "", // 书籍名称(书源获取) - var customName: String = "", // 书籍名称(用户修改) + var customName: String? = null, // 书籍名称(用户修改) var author: String? = null, // 作者名称(书源获取) var customAuthor: String? = null, // 作者名称(用户修改) var tag: String? = null, // 分类信息(书源获取) @@ -22,13 +24,12 @@ data class Book(@PrimaryKey var charset: String? = null, // 自定义字符集名称(仅适用于本地书籍) var type: Int = 0, // 0: 文本读物, 1: 有声读物 var group: Int = 0, // 自定义分组索引号 - var tocUrl: String = "", // 目录页Url (toc=table of Contents) var latestChapterTitle: String? = null, // 最新章节标题 var latestChapterTime: Long = 0, // 最新章节标题更新时间 var lastCheckTime: Long = 0, // 最近一次更新书籍信息的时间 var lastCheckCount: Int = 0, // 最近一次发现新章节的数量 var totalChapterNum: Int = 0, // 书籍目录总数 - var durChapterTitle: String = "", // 当前章节名称 + var durChapterTitle: String? = null, // 当前章节名称 var durChapterIndex: Int = 0, // 当前章节索引 var durChapterPos: Int = 0, // 当前阅读的进度(首行字符的索引位置) var durChapterTime: Long = 0, // 最近一次阅读书籍的时间(打开正文的时间) @@ -38,4 +39,12 @@ data class Book(@PrimaryKey fun getUnreadChapterNum() = Math.max(totalChapterNum - durChapterIndex - 1, 0) + fun getDisplayName() = customName.strim() ?: name + + fun getDisplayAuthor() = customAuthor.strim() ?: author + + fun getDisplayCover() = customCoverUrl.strim() ?: coverUrl + + fun getDisplayDescription() = customDescription.strim() ?: description + } \ No newline at end of file From 49c0ee63b6cae2324dc1f8b675b776aff3022e6f Mon Sep 17 00:00:00 2001 From: atbest Date: Thu, 23 May 2019 05:10:14 -0400 Subject: [PATCH 06/12] Update String extension --- app/src/main/java/io/legado/app/utils/StringExtensions.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/utils/StringExtensions.kt b/app/src/main/java/io/legado/app/utils/StringExtensions.kt index 957d5b091..23f35a357 100644 --- a/app/src/main/java/io/legado/app/utils/StringExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/StringExtensions.kt @@ -1,3 +1,6 @@ package io.legado.app.utils -fun String?.strim() = if (this.isNullOrBlank()) null else this.trim() \ No newline at end of file +fun String?.strim() = if (this.isNullOrBlank()) null else this.trim() + +fun String.isAbsUrl() = this.startsWith("http://", true) + || this.startsWith("https://", true) From 1edd0fc20557c3f4df97fb8f750caebeef551a56 Mon Sep 17 00:00:00 2001 From: GKF Date: Thu, 23 May 2019 17:32:06 +0800 Subject: [PATCH 07/12] dataBinding --- .../io/legado/app/ui/main/MainActivity.kt | 28 +++------ .../io/legado/app/ui/main/MainDataBinding.kt | 63 +++++++++++++++++++ .../java/io/legado/app/ui/main/MainModel.kt | 5 -- .../io/legado/app/ui/search/SearchActivity.kt | 2 - app/src/main/res/layout/activity_search.xml | 2 +- 5 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/main/MainDataBinding.kt delete mode 100644 app/src/main/java/io/legado/app/ui/main/MainModel.kt diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 47a023576..d04315dd6 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -5,27 +5,24 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.ActionBarDrawerToggle -import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.ViewModelProviders import com.google.android.material.navigation.NavigationView import io.legado.app.R +import io.legado.app.base.BaseActivity import io.legado.app.ui.search.SearchActivity -import io.legado.app.utils.longSnackbar import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.app_bar_main.* -import org.jetbrains.anko.startActivity -class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { +class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener { + override val viewModel: MainViewModel + get() = ViewModelProvider.AndroidViewModelFactory.getInstance(application).create(MainViewModel::class.java) + override val layoutID: Int + get() = R.layout.activity_main - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + override fun onViewModelCreated(viewModel: MainViewModel, savedInstanceState: Bundle?) { setSupportActionBar(toolbar) - fab.setOnClickListener { startActivity(Intent(this, SearchActivity::class.java)) } val toggle = ActionBarDrawerToggle( @@ -37,7 +34,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte toggle.syncState() nav_view.setNavigationItemSelectedListener(this) - val mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) } override fun onBackPressed() { @@ -55,14 +51,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte return true } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - return when (item.itemId) { - R.id.action_settings -> true - else -> super.onOptionsItemSelected(item) - } + override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { + return super.onCompatOptionsItemSelected(item) } override fun onNavigationItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/main/MainDataBinding.kt b/app/src/main/java/io/legado/app/ui/main/MainDataBinding.kt new file mode 100644 index 000000000..7c950c419 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/main/MainDataBinding.kt @@ -0,0 +1,63 @@ +package io.legado.app.ui.main + +import android.view.View +import androidx.databinding.ViewDataBinding + +class MainDataBinding(bindingComponent: Any?, root: View?, localFieldCount: Int) : + ViewDataBinding(bindingComponent, root, localFieldCount) { + /** + * Set a value value in the Binding class. + * + * + * Typically, the developer will be able to call the subclass's set method directly. For + * example, if there is a variable `x` in the Binding, a `setX` method + * will be generated. However, there are times when the specific subclass of ViewDataBinding + * is unknown, so the generated method cannot be discovered without reflection. The + * setVariable call allows the values of variables to be set without reflection. + * + * @param variableId the BR id of the variable to be set. For example, if the variable is + * `x`, then variableId will be `BR.x`. + * @param value The new value of the variable to be set. + * @return `true` if the variable is declared or used in the binding or + * `false` otherwise. + */ + override fun setVariable(variableId: Int, value: Any?): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + /** + * @hide + */ + override fun executeBindings() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + /** + * Called when an observed object changes. Sets the appropriate dirty flag if applicable. + * @param localFieldId The index into mLocalFieldObservers that this Object resides in. + * @param object The object that has changed. + * @param fieldId The BR ID of the field being changed or _all if + * no specific field is being notified. + * @return true if this change should cause a change to the UI. + * @hide + */ + override fun onFieldChange(localFieldId: Int, `object`: Any?, fieldId: Int): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + /** + * Invalidates all binding expressions and requests a new rebind to refresh UI. + */ + override fun invalidateAll() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + /** + * Returns whether the UI needs to be refresh to represent the current data. + * + * @return true if any field has changed and the binding should be evaluated. + */ + override fun hasPendingBindings(): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/MainModel.kt b/app/src/main/java/io/legado/app/ui/main/MainModel.kt deleted file mode 100644 index 86da567f6..000000000 --- a/app/src/main/java/io/legado/app/ui/main/MainModel.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.legado.app.ui.main - -class MainModel { - -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt index f583abc6f..2d4f64ba2 100644 --- a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt @@ -4,7 +4,6 @@ import android.os.Bundle import androidx.lifecycle.ViewModelProvider import io.legado.app.R import io.legado.app.base.BaseActivity -import io.legado.app.search.SearchDataBinding class SearchActivity : BaseActivity() { @@ -15,7 +14,6 @@ class SearchActivity : BaseActivity() { get() = R.layout.activity_search override fun onViewModelCreated(viewModel: SearchViewModel, savedInstanceState: Bundle?) { - dataBinding.searchViewModel = viewModel } diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index 486b05137..a7dfaad9b 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -1,7 +1,7 @@ - + From 2579530af8df4328ffb721de54f23bc729fa5fe5 Mon Sep 17 00:00:00 2001 From: gedoor Date: Thu, 23 May 2019 19:49:06 +0800 Subject: [PATCH 08/12] dataBinding --- .../io/legado/app/ui/main/MainDataBinding.kt | 63 ------------------- app/src/main/res/layout/activity_main.xml | 45 ++++++------- 2 files changed, 24 insertions(+), 84 deletions(-) delete mode 100644 app/src/main/java/io/legado/app/ui/main/MainDataBinding.kt diff --git a/app/src/main/java/io/legado/app/ui/main/MainDataBinding.kt b/app/src/main/java/io/legado/app/ui/main/MainDataBinding.kt deleted file mode 100644 index 7c950c419..000000000 --- a/app/src/main/java/io/legado/app/ui/main/MainDataBinding.kt +++ /dev/null @@ -1,63 +0,0 @@ -package io.legado.app.ui.main - -import android.view.View -import androidx.databinding.ViewDataBinding - -class MainDataBinding(bindingComponent: Any?, root: View?, localFieldCount: Int) : - ViewDataBinding(bindingComponent, root, localFieldCount) { - /** - * Set a value value in the Binding class. - * - * - * Typically, the developer will be able to call the subclass's set method directly. For - * example, if there is a variable `x` in the Binding, a `setX` method - * will be generated. However, there are times when the specific subclass of ViewDataBinding - * is unknown, so the generated method cannot be discovered without reflection. The - * setVariable call allows the values of variables to be set without reflection. - * - * @param variableId the BR id of the variable to be set. For example, if the variable is - * `x`, then variableId will be `BR.x`. - * @param value The new value of the variable to be set. - * @return `true` if the variable is declared or used in the binding or - * `false` otherwise. - */ - override fun setVariable(variableId: Int, value: Any?): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - /** - * @hide - */ - override fun executeBindings() { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - /** - * Called when an observed object changes. Sets the appropriate dirty flag if applicable. - * @param localFieldId The index into mLocalFieldObservers that this Object resides in. - * @param object The object that has changed. - * @param fieldId The BR ID of the field being changed or _all if - * no specific field is being notified. - * @return true if this change should cause a change to the UI. - * @hide - */ - override fun onFieldChange(localFieldId: Int, `object`: Any?, fieldId: Int): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - /** - * Invalidates all binding expressions and requests a new rebind to refresh UI. - */ - override fun invalidateAll() { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - /** - * Returns whether the UI needs to be refresh to represent the current data. - * - * @return true if any field has changed and the binding should be evaluated. - */ - override fun hasPendingBindings(): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ac9a123b3..92789a7fb 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,26 +1,29 @@ - - - + + + + - - + tools:openDrawer="start"> + + + + - + + From 7b0d3d0be2726dd2635aed7a61b82457d14ed9e5 Mon Sep 17 00:00:00 2001 From: Awesome <1760316362@qq.com> Date: Thu, 23 May 2019 21:24:21 +0800 Subject: [PATCH 09/12] =?UTF-8?q?=E6=B7=BB=E5=8A=A0BaseAdapter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/base/adapter/CommonRecyclerAdapter.kt | 398 ++++++++++++++++++ .../legado/app/base/adapter/ItemAnimation.kt | 91 ++++ .../app/base/adapter/ItemViewDelegate.kt | 16 + .../legado/app/base/adapter/ItemViewHolder.kt | 9 + .../app/base/adapter/SimpleRecyclerAdapter.kt | 26 ++ .../adapter/animations/AlphaInAnimation.kt | 17 + .../base/adapter/animations/BaseAnimation.kt | 13 + .../adapter/animations/ScaleInAnimation.kt | 21 + .../animations/SlideInBottomAnimation.kt | 13 + .../animations/SlideInLeftAnimation.kt | 14 + .../animations/SlideInRightAnimation.kt | 14 + .../java/io/legado/app/help/LayoutManager.kt | 54 ++- .../io/legado/app/ui/search/SearchActivity.kt | 2 +- 13 files changed, 658 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/ItemAnimation.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/ItemViewDelegate.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/ItemViewHolder.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/SimpleRecyclerAdapter.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/animations/AlphaInAnimation.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/animations/BaseAnimation.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/animations/ScaleInAnimation.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/animations/SlideInBottomAnimation.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/animations/SlideInLeftAnimation.kt create mode 100644 app/src/main/java/io/legado/app/base/adapter/animations/SlideInRightAnimation.kt diff --git a/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt b/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt new file mode 100644 index 000000000..c9b9f0cf2 --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt @@ -0,0 +1,398 @@ +package io.legado.app.base.adapter + +import android.content.Context +import android.util.SparseArray +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import java.util.* + + +/** + * Created by Invincible on 2017/11/24. + * + * 通用的adapter 可添加header,footer,以及不同类型item + */ +abstract class CommonRecyclerAdapter(protected val context: Context) : RecyclerView.Adapter() { + + constructor(context: Context, vararg delegates: Pair>) : this(context) { + addItemViewDelegates(*delegates) + } + + constructor(context: Context, vararg delegates: ItemViewDelegate) : this(context) { + addItemViewDelegates(*delegates) + } + + private val inflater: LayoutInflater = LayoutInflater.from(context) + + private var headerItems: SparseArray? = null + private var footerItems: SparseArray? = null + + private val itemDelegates: HashMap> = hashMapOf() + private val items: MutableList = mutableListOf() + + private val lock = Object() + + private var itemClickListener: ((holder: ItemViewHolder, item: ITEM) -> Unit)? = null + private var itemLongClickListener: ((holder: ItemViewHolder, item: ITEM) -> Boolean)? = null + + private var itemAnimation: ItemAnimation? = null + + fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) { + itemClickListener = listener + } + + fun setOnItemLongClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Boolean) { + itemLongClickListener = listener + } + + fun bindToRecyclerView(recyclerView: RecyclerView) { + recyclerView.adapter = this + } + + fun > addItemViewDelegate(viewType: Int, delegate: DELEGATE) { + itemDelegates.put(viewType, delegate) + } + + fun > addItemViewDelegate(delegate: DELEGATE) { + itemDelegates.put(itemDelegates.size, delegate) + } + + fun > addItemViewDelegates(vararg delegates: DELEGATE) { + delegates.forEach { + addItemViewDelegate(it) + } + } + + fun addItemViewDelegates(vararg delegates: Pair>) { + delegates.forEach { + addItemViewDelegate(it.first, it.second) + } + } + + fun addHeaderView(header: View) { + synchronized(lock) { + if (headerItems == null) { + headerItems = SparseArray() + } + headerItems?.let { + val index = it.size() + it.put(TYPE_HEADER_VIEW + it.size(), header) + notifyItemInserted(index) + } + } + } + + fun addFooterView(footer: View) { + synchronized(lock) { + if (footerItems == null) { + footerItems = SparseArray() + } + footerItems?.let { + val index = getActualItemCount() + it.size() + it.put(TYPE_FOOTER_VIEW + it.size(), footer) + notifyItemInserted(index) + } + } + } + + fun removeHeaderView(header: View) { + synchronized(lock) { + headerItems?.let { + val index = it.indexOfValue(header) + if (index >= 0) { + it.remove(index) + notifyItemRemoved(index) + } + } + } + } + + fun removeFooterView(footer: View) { + synchronized(lock) { + footerItems?.let { + val index = it.indexOfValue(footer) + if (index >= 0) { + it.remove(index) + notifyItemRemoved(getActualItemCount() + index - 2) + } + } + } + } + + fun setItems(items: List?) { + synchronized(lock) { + if (this.items.isNotEmpty()) { + this.items.clear() + } + if (items != null) { + this.items.addAll(items) + } + notifyDataSetChanged() + } + } + + fun setItem(position: Int, item: ITEM) { + synchronized(lock) { + val oldSize = getActualItemCount() + if (position in 0 until oldSize) { + this.items[position] = item + notifyItemChanged(position + getHeaderCount()) + } + } + } + + fun addItem(item: ITEM) { + synchronized(lock) { + val oldSize = getActualItemCount() + if (this.items.add(item)) { + if (oldSize == 0) { + notifyDataSetChanged() + } else { + notifyItemInserted(oldSize + getHeaderCount()) + } + } + } + } + + fun addItems(position: Int, newItems: List) { + synchronized(lock) { + val oldSize = getActualItemCount() + if (position in 0 until oldSize) { + if (if (oldSize == 0) this.items.addAll(newItems) else this.items.addAll(position, newItems)) { + if (oldSize == 0) { + notifyDataSetChanged() + } else { + notifyItemRangeChanged(position + getHeaderCount(), newItems.size) + } + } + } + } + } + + fun addItems(newItems: List) { + synchronized(lock) { + val oldSize = getActualItemCount() + if (this.items.addAll(newItems)) { + if (oldSize == 0) { + notifyDataSetChanged() + } else { + notifyItemRangeChanged(oldSize + getHeaderCount(), newItems.size) + } + } + } + } + + fun removeItem(position: Int) { + synchronized(lock) { + if (this.items.removeAt(position) != null) + notifyItemRemoved(position + getHeaderCount()) + } + } + + fun removeItem(item: ITEM) { + synchronized(lock) { + if (this.items.remove(item)) + notifyItemRemoved(this.items.indexOf(item) + getHeaderCount()) + } + } + + fun removeItems(items: List) { + synchronized(lock) { + if (this.items.removeAll(items)) + notifyDataSetChanged() + } + } + + fun swapItem(oldPosition: Int, newPosition: Int) { + synchronized(lock) { + val size = getActualItemCount() + if (oldPosition in 0 until size && newPosition in 0 until size) { + Collections.swap(this.items, oldPosition + getHeaderCount(), newPosition + getHeaderCount()) + notifyDataSetChanged() + } + } + } + + fun updateItem(position: Int, payload: Any) { + synchronized(lock) { + val size = getActualItemCount() + if (position in 0 until size) { + notifyItemChanged(position + getHeaderCount(), payload) + } + } + } + + fun updateItems(fromPosition: Int, toPosition: Int, payloads: Any) { + synchronized(lock) { + val size = getActualItemCount() + if (fromPosition in 0 until size && toPosition in 0 until size) { + notifyItemRangeChanged(fromPosition + getHeaderCount(), toPosition - fromPosition + 1, payloads) + } + } + } + + fun isEmpty(): Boolean { + return items.isEmpty() + } + + fun isNotEmpty(): Boolean { + return items.isNotEmpty() + } + + /** + * 除去header和footer + */ + fun getActualItemCount(): Int { + return items.size + } + + fun getHeaderCount(): Int { + return headerItems?.size() ?: 0 + } + + fun getFooterCount(): Int { + return footerItems?.size() ?: 0 + } + + fun getItem(position: Int): ITEM = items[position % items.size] + + fun getItems(): List = items + + protected open fun getItemViewType(item: ITEM, position: Int): Int { + return 0 + } + + /** + * grid 模式下使用 + */ + protected open fun getSpanSize(item: ITEM, viewType: Int, position: Int): Int { + return 1 + } + + final override fun getItemCount(): Int { + return getActualItemCount() + getHeaderCount() + getFooterCount() + } + + final override fun getItemViewType(position: Int): Int { + return when { + isHeader(position) -> TYPE_HEADER_VIEW + position + isFooter(position) -> TYPE_FOOTER_VIEW + position - getActualItemCount() - getHeaderCount() + else -> getItemViewType(getItem(getRealPosition(position)), getRealPosition(position)) + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { + return when { + viewType < TYPE_HEADER_VIEW + getHeaderCount() -> { + ItemViewHolder(headerItems!!.get(viewType)) + } + + viewType >= TYPE_FOOTER_VIEW -> { + ItemViewHolder(footerItems!!.get(viewType)) + } + + else -> { + val holder = ItemViewHolder(inflater.inflate(itemDelegates.getValue(viewType).layoutID, parent, false)) + + if (itemClickListener != null) { + holder.itemView.setOnClickListener { + itemClickListener!!.invoke(holder, getItem(holder.layoutPosition)) + } + } + + if (itemLongClickListener != null) { + holder.itemView.setOnLongClickListener { + itemLongClickListener!!.invoke(holder, getItem(holder.layoutPosition)) + } + } + + holder + } + } + } + + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + onBindViewHolder(holder, position, mutableListOf()) + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int, payloads: MutableList) { + if (!isHeader(holder.layoutPosition) && !isFooter(holder.layoutPosition)) { + itemDelegates.getValue(getItemViewType(holder.layoutPosition)) + .convert(holder, getItem(holder.layoutPosition), payloads) + } + } + + override fun onViewAttachedToWindow(holder: ItemViewHolder) { + super.onViewAttachedToWindow(holder) + if (!isHeader(holder.layoutPosition) && !isFooter(holder.layoutPosition)) { + addAnimation(holder) + } + } + + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + super.onAttachedToRecyclerView(recyclerView) + val manager = recyclerView.layoutManager + if (manager is GridLayoutManager) { + manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return if (isHeader(position) || isFooter(position)) manager.spanCount else getSpanSize( + getItem(position), getItemViewType(position), position + ) + } + } + } + } + + fun setAnimationConfig(item: ItemAnimation) { + itemAnimation = item + } + + private fun isHeader(position: Int): Boolean { + return position < getHeaderCount() + } + + private fun isFooter(position: Int): Boolean { + return position >= getActualItemCount() + getHeaderCount() + } + + private fun getRealPosition(position: Int): Int { + return position - getHeaderCount() + } + + private fun addAnimation(holder: ItemViewHolder) { + if (itemAnimation == null) { + itemAnimation = ItemAnimation.create().enabled(true) + } + + itemAnimation?.let { + if (it.itemAnimEnabled) { + if (!it.itemAnimFirstOnly || holder.layoutPosition > it.itemAnimStartPosition) { + startAnimation(holder, it) + it.itemAnimStartPosition = holder.layoutPosition + } + } + } + } + + + protected open fun startAnimation(holder: ItemViewHolder, item: ItemAnimation) { + for (anim in item.itemAnimation.getAnimators(holder.itemView)) { + anim.setDuration(item.itemAnimDuration).start() + anim.interpolator = item.itemAnimInterpolator + } + } + + companion object { + private const val TYPE_HEADER_VIEW = Int.MIN_VALUE + private const val TYPE_FOOTER_VIEW = Int.MAX_VALUE - 999 + } + +} + + + + diff --git a/app/src/main/java/io/legado/app/base/adapter/ItemAnimation.kt b/app/src/main/java/io/legado/app/base/adapter/ItemAnimation.kt new file mode 100644 index 000000000..9e6f20c61 --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/ItemAnimation.kt @@ -0,0 +1,91 @@ +package io.legado.app.base.adapter + +import android.view.animation.Interpolator +import android.view.animation.LinearInterpolator +import io.legado.app.base.adapter.animations.AlphaInAnimation +import io.legado.app.base.adapter.animations.BaseAnimation +import io.legado.app.base.animations.ScaleInAnimation +import io.legado.app.base.adapter.animations.SlideInBottomAnimation +import io.legado.app.base.adapter.animations.SlideInLeftAnimation +import io.legado.app.base.adapter.animations.SlideInRightAnimation + +/** + * Created by Invincible on 2017/12/15. + */ +class ItemAnimation private constructor() { + + var itemAnimEnabled = false + var itemAnimFirstOnly = true + var itemAnimation: BaseAnimation = SlideInBottomAnimation() + var itemAnimInterpolator: Interpolator = LinearInterpolator() + var itemAnimDuration: Long = 300L + var itemAnimStartPosition: Int = -1 + + fun interpolator(interpolator: Interpolator): ItemAnimation { + itemAnimInterpolator = interpolator + return this + } + + fun duration(duration: Long): ItemAnimation { + itemAnimDuration = duration + return this + } + + fun startPostion(startPos: Int): ItemAnimation { + itemAnimStartPosition = startPos + return this + } + + fun animation(animationType: Int = FADE_IN, animation: BaseAnimation? = null): ItemAnimation { + if (animation != null) { + itemAnimation = animation + } else { + when (animationType) { + FADE_IN -> itemAnimation = AlphaInAnimation() + SCALE_IN -> itemAnimation = ScaleInAnimation() + BOTTOM_SLIDE_IN -> itemAnimation = SlideInBottomAnimation() + LEFT_SLIDE_IN -> itemAnimation = SlideInLeftAnimation() + RIGHT_SLIDE_IN -> itemAnimation = SlideInRightAnimation() + } + } + return this + } + + fun enabled(enabled: Boolean): ItemAnimation { + itemAnimEnabled = enabled + return this + } + + fun firstOnly(firstOnly: Boolean): ItemAnimation { + itemAnimFirstOnly = firstOnly + return this + } + + companion object { + + /** + * Use with [.openLoadAnimation] + */ + const val FADE_IN: Int = 0x00000001 + /** + * Use with [.openLoadAnimation] + */ + const val SCALE_IN: Int = 0x00000002 + /** + * Use with [.openLoadAnimation] + */ + const val BOTTOM_SLIDE_IN: Int = 0x00000003 + /** + * Use with [.openLoadAnimation] + */ + const val LEFT_SLIDE_IN: Int = 0x00000004 + /** + * Use with [.openLoadAnimation] + */ + const val RIGHT_SLIDE_IN: Int = 0x00000005 + + fun create(): ItemAnimation { + return ItemAnimation() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/base/adapter/ItemViewDelegate.kt b/app/src/main/java/io/legado/app/base/adapter/ItemViewDelegate.kt new file mode 100644 index 000000000..789ca250e --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/ItemViewDelegate.kt @@ -0,0 +1,16 @@ +package io.legado.app.base.adapter + +import android.content.Context + +/** + * Created by Invincible on 2017/11/24. + * + * item代理, + */ +abstract class ItemViewDelegate(protected val context: Context) { + + abstract val layoutID: Int + + abstract fun convert(holder: ItemViewHolder, item: ITEM, payloads: MutableList) + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/base/adapter/ItemViewHolder.kt b/app/src/main/java/io/legado/app/base/adapter/ItemViewHolder.kt new file mode 100644 index 000000000..c415fa4b6 --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/ItemViewHolder.kt @@ -0,0 +1,9 @@ +package io.legado.app.base.adapter + +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +/** + * Created by Invincible on 2017/11/28. + */ +class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/base/adapter/SimpleRecyclerAdapter.kt b/app/src/main/java/io/legado/app/base/adapter/SimpleRecyclerAdapter.kt new file mode 100644 index 000000000..176098f50 --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/SimpleRecyclerAdapter.kt @@ -0,0 +1,26 @@ +package io.legado.app.base.adapter + +import android.content.Context + +/** + * Created by Invincible on 2017/12/15. + */ +abstract class SimpleRecyclerAdapter(context: Context) : CommonRecyclerAdapter(context) { + + init { + addItemViewDelegate(object : ItemViewDelegate(context) { + override val layoutID: Int + get() = this@SimpleRecyclerAdapter.layoutID + + override fun convert(holder: ItemViewHolder, item: ITEM, payloads: MutableList) { + this@SimpleRecyclerAdapter.convert(holder, item, payloads) + } + + }) + + } + + abstract val layoutID: Int + + abstract fun convert(holder: ItemViewHolder, item: ITEM, payloads: MutableList) +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/base/adapter/animations/AlphaInAnimation.kt b/app/src/main/java/io/legado/app/base/adapter/animations/AlphaInAnimation.kt new file mode 100644 index 000000000..b307300a3 --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/animations/AlphaInAnimation.kt @@ -0,0 +1,17 @@ +package io.legado.app.base.adapter.animations + +import android.animation.Animator +import android.animation.ObjectAnimator +import android.view.View + + +class AlphaInAnimation @JvmOverloads constructor(private val mFrom: Float = DEFAULT_ALPHA_FROM) : BaseAnimation { + + override fun getAnimators(view: View): Array = + arrayOf(ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f)) + + companion object { + + private const val DEFAULT_ALPHA_FROM = 0f + } +} diff --git a/app/src/main/java/io/legado/app/base/adapter/animations/BaseAnimation.kt b/app/src/main/java/io/legado/app/base/adapter/animations/BaseAnimation.kt new file mode 100644 index 000000000..735ceca57 --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/animations/BaseAnimation.kt @@ -0,0 +1,13 @@ +package io.legado.app.base.adapter.animations + +import android.animation.Animator +import android.view.View + +/** + * adapter item 动画 + */ +interface BaseAnimation { + + fun getAnimators(view: View): Array + +} diff --git a/app/src/main/java/io/legado/app/base/adapter/animations/ScaleInAnimation.kt b/app/src/main/java/io/legado/app/base/adapter/animations/ScaleInAnimation.kt new file mode 100644 index 000000000..3f73e7a3c --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/animations/ScaleInAnimation.kt @@ -0,0 +1,21 @@ +package io.legado.app.base.animations + +import android.animation.Animator +import android.animation.ObjectAnimator +import android.view.View +import io.legado.app.base.adapter.animations.BaseAnimation + + +class ScaleInAnimation @JvmOverloads constructor(private val mFrom: Float = DEFAULT_SCALE_FROM) : BaseAnimation { + + override fun getAnimators(view: View): Array { + val scaleX = ObjectAnimator.ofFloat(view, "scaleX", mFrom, 1f) + val scaleY = ObjectAnimator.ofFloat(view, "scaleY", mFrom, 1f) + return arrayOf(scaleX, scaleY) + } + + companion object { + + private const val DEFAULT_SCALE_FROM = .5f + } +} diff --git a/app/src/main/java/io/legado/app/base/adapter/animations/SlideInBottomAnimation.kt b/app/src/main/java/io/legado/app/base/adapter/animations/SlideInBottomAnimation.kt new file mode 100644 index 000000000..0fd2bfde6 --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/animations/SlideInBottomAnimation.kt @@ -0,0 +1,13 @@ +package io.legado.app.base.adapter.animations + +import android.animation.Animator +import android.animation.ObjectAnimator +import android.view.View +import io.legado.app.base.adapter.animations.BaseAnimation + +class SlideInBottomAnimation : BaseAnimation { + + + override fun getAnimators(view: View): Array = + arrayOf(ObjectAnimator.ofFloat(view, "translationY", view.measuredHeight.toFloat(), 0f)) +} diff --git a/app/src/main/java/io/legado/app/base/adapter/animations/SlideInLeftAnimation.kt b/app/src/main/java/io/legado/app/base/adapter/animations/SlideInLeftAnimation.kt new file mode 100644 index 000000000..daa6f17a1 --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/animations/SlideInLeftAnimation.kt @@ -0,0 +1,14 @@ +package io.legado.app.base.adapter.animations + +import android.animation.Animator +import android.animation.ObjectAnimator +import android.view.View +import io.legado.app.base.adapter.animations.BaseAnimation + + +class SlideInLeftAnimation : BaseAnimation { + + + override fun getAnimators(view: View): Array = + arrayOf(ObjectAnimator.ofFloat(view, "translationX", -view.rootView.width.toFloat(), 0f)) +} diff --git a/app/src/main/java/io/legado/app/base/adapter/animations/SlideInRightAnimation.kt b/app/src/main/java/io/legado/app/base/adapter/animations/SlideInRightAnimation.kt new file mode 100644 index 000000000..71c8feac7 --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/animations/SlideInRightAnimation.kt @@ -0,0 +1,14 @@ +package io.legado.app.base.adapter.animations + +import android.animation.Animator +import android.animation.ObjectAnimator +import android.view.View +import io.legado.app.base.adapter.animations.BaseAnimation + + +class SlideInRightAnimation : BaseAnimation { + + + override fun getAnimators(view: View): Array = + arrayOf(ObjectAnimator.ofFloat(view, "translationX", view.rootView.width.toFloat(), 0f)) +} diff --git a/app/src/main/java/io/legado/app/help/LayoutManager.kt b/app/src/main/java/io/legado/app/help/LayoutManager.kt index 9fc551222..e633993c7 100644 --- a/app/src/main/java/io/legado/app/help/LayoutManager.kt +++ b/app/src/main/java/io/legado/app/help/LayoutManager.kt @@ -6,7 +6,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.StaggeredGridLayoutManager -class LayoutManager private constructor() { +object LayoutManager { interface LayoutManagerFactory { fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager @@ -16,50 +16,46 @@ class LayoutManager private constructor() { @Retention(AnnotationRetention.SOURCE) annotation class Orientation - companion object { - - - fun linear(): LayoutManagerFactory { - return object : LayoutManagerFactory { - override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { - return LinearLayoutManager(recyclerView.context) - } + fun linear(): LayoutManagerFactory { + return object : LayoutManagerFactory { + override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { + return LinearLayoutManager(recyclerView.context) } } + } - fun linear(@Orientation orientation: Int, reverseLayout: Boolean): LayoutManagerFactory { - return object : LayoutManagerFactory { - override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { - return LinearLayoutManager(recyclerView.context, orientation, reverseLayout) - } + fun linear(@Orientation orientation: Int, reverseLayout: Boolean): LayoutManagerFactory { + return object : LayoutManagerFactory { + override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { + return LinearLayoutManager(recyclerView.context, orientation, reverseLayout) } } + } - fun grid(spanCount: Int): LayoutManagerFactory { - return object : LayoutManagerFactory { - override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { - return GridLayoutManager(recyclerView.context, spanCount) - } + fun grid(spanCount: Int): LayoutManagerFactory { + return object : LayoutManagerFactory { + override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { + return GridLayoutManager(recyclerView.context, spanCount) } } + } - fun grid(spanCount: Int, @Orientation orientation: Int, reverseLayout: Boolean): LayoutManagerFactory { - return object : LayoutManagerFactory { - override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { - return GridLayoutManager(recyclerView.context, spanCount, orientation, reverseLayout) - } + fun grid(spanCount: Int, @Orientation orientation: Int, reverseLayout: Boolean): LayoutManagerFactory { + return object : LayoutManagerFactory { + override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { + return GridLayoutManager(recyclerView.context, spanCount, orientation, reverseLayout) } } + } - fun staggeredGrid(spanCount: Int, @Orientation orientation: Int): LayoutManagerFactory { - return object : LayoutManagerFactory { - override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { - return StaggeredGridLayoutManager(spanCount, orientation) - } + fun staggeredGrid(spanCount: Int, @Orientation orientation: Int): LayoutManagerFactory { + return object : LayoutManagerFactory { + override fun create(recyclerView: RecyclerView): RecyclerView.LayoutManager { + return StaggeredGridLayoutManager(spanCount, orientation) } } } diff --git a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt index 2d4f64ba2..33be6236f 100644 --- a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import androidx.lifecycle.ViewModelProvider import io.legado.app.R import io.legado.app.base.BaseActivity +import io.legado.app.help.LayoutManager class SearchActivity : BaseActivity() { @@ -15,7 +16,6 @@ class SearchActivity : BaseActivity() { override fun onViewModelCreated(viewModel: SearchViewModel, savedInstanceState: Bundle?) { - } } From 57acb76df651ff832bc03598f25ef9bc125c7821 Mon Sep 17 00:00:00 2001 From: Awesome <1760316362@qq.com> Date: Thu, 23 May 2019 22:21:47 +0800 Subject: [PATCH 10/12] TitleBar --- .../base/adapter/InfiniteScrollListener.kt | 32 +++++++++++++++++ .../legado/app/base/adapter/ItemAnimation.kt | 2 +- .../adapter/animations/ScaleInAnimation.kt | 3 +- .../io/legado/app/data/entities/SearchBook.kt | 1 + .../io/legado/app/ui/main/MainActivity.kt | 4 +-- .../io/legado/app/ui/search/SearchActivity.kt | 1 - .../io/legado/app/ui/search/SearchAdapter.kt | 34 +++++++++++++++++++ .../java/io/legado/app/ui/widget/TitleBar.kt | 18 +++++++--- app/src/main/res/layout/app_bar_main.xml | 15 ++------ app/src/main/res/layout/item_search.xml | 19 +++++++++++ app/src/main/res/layout/view_titlebar.xml | 1 + app/src/main/res/values/attrs.xml | 2 ++ 12 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/io/legado/app/base/adapter/InfiniteScrollListener.kt create mode 100644 app/src/main/java/io/legado/app/ui/search/SearchAdapter.kt create mode 100644 app/src/main/res/layout/item_search.xml diff --git a/app/src/main/java/io/legado/app/base/adapter/InfiniteScrollListener.kt b/app/src/main/java/io/legado/app/base/adapter/InfiniteScrollListener.kt new file mode 100644 index 000000000..fc7c48bab --- /dev/null +++ b/app/src/main/java/io/legado/app/base/adapter/InfiniteScrollListener.kt @@ -0,0 +1,32 @@ +package io.legado.app.base.adapter + +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView + +/** + * Created by Invincible on 2017/12/15. + * + * 上拉加载更多 + */ +abstract class InfiniteScrollListener() : RecyclerView.OnScrollListener() { + private val loadMoreRunnable = Runnable { onLoadMore() } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { +// if (dy < 0 || dataLoading.isDataLoading()) return + + val layoutManager:LinearLayoutManager = recyclerView.layoutManager as LinearLayoutManager + val visibleItemCount = recyclerView.childCount + val totalItemCount = layoutManager.itemCount + val firstVisibleItem = layoutManager.findFirstVisibleItemPosition() + + if (totalItemCount - visibleItemCount <= firstVisibleItem + VISIBLE_THRESHOLD) { + recyclerView.post(loadMoreRunnable) + } + } + + abstract fun onLoadMore() + + companion object { + private const val VISIBLE_THRESHOLD = 5 + } +} diff --git a/app/src/main/java/io/legado/app/base/adapter/ItemAnimation.kt b/app/src/main/java/io/legado/app/base/adapter/ItemAnimation.kt index 9e6f20c61..6db4a7415 100644 --- a/app/src/main/java/io/legado/app/base/adapter/ItemAnimation.kt +++ b/app/src/main/java/io/legado/app/base/adapter/ItemAnimation.kt @@ -4,7 +4,7 @@ import android.view.animation.Interpolator import android.view.animation.LinearInterpolator import io.legado.app.base.adapter.animations.AlphaInAnimation import io.legado.app.base.adapter.animations.BaseAnimation -import io.legado.app.base.animations.ScaleInAnimation +import io.legado.app.base.adapter.animations.ScaleInAnimation import io.legado.app.base.adapter.animations.SlideInBottomAnimation import io.legado.app.base.adapter.animations.SlideInLeftAnimation import io.legado.app.base.adapter.animations.SlideInRightAnimation diff --git a/app/src/main/java/io/legado/app/base/adapter/animations/ScaleInAnimation.kt b/app/src/main/java/io/legado/app/base/adapter/animations/ScaleInAnimation.kt index 3f73e7a3c..3464f4079 100644 --- a/app/src/main/java/io/legado/app/base/adapter/animations/ScaleInAnimation.kt +++ b/app/src/main/java/io/legado/app/base/adapter/animations/ScaleInAnimation.kt @@ -1,9 +1,8 @@ -package io.legado.app.base.animations +package io.legado.app.base.adapter.animations import android.animation.Animator import android.animation.ObjectAnimator import android.view.View -import io.legado.app.base.adapter.animations.BaseAnimation class ScaleInAnimation @JvmOverloads constructor(private val mFrom: Float = DEFAULT_SCALE_FROM) : BaseAnimation { diff --git a/app/src/main/java/io/legado/app/data/entities/SearchBook.kt b/app/src/main/java/io/legado/app/data/entities/SearchBook.kt index feecd7295..8994f14bd 100644 --- a/app/src/main/java/io/legado/app/data/entities/SearchBook.kt +++ b/app/src/main/java/io/legado/app/data/entities/SearchBook.kt @@ -1,2 +1,3 @@ package io.legado.app.data.entities +class SearchBook \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index d04315dd6..cf4a2329d 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -14,6 +14,7 @@ import io.legado.app.base.BaseActivity import io.legado.app.ui.search.SearchActivity import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.app_bar_main.* +import kotlinx.android.synthetic.main.view_titlebar.* class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener { override val viewModel: MainViewModel @@ -22,11 +23,10 @@ class MainActivity : BaseActivity(), NavigationV get() = R.layout.activity_main override fun onViewModelCreated(viewModel: MainViewModel, savedInstanceState: Bundle?) { - setSupportActionBar(toolbar) fab.setOnClickListener { startActivity(Intent(this, SearchActivity::class.java)) } val toggle = ActionBarDrawerToggle( - this, drawer_layout, toolbar, + this, drawer_layout, titleBar.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) diff --git a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt index 33be6236f..14994c535 100644 --- a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt @@ -15,7 +15,6 @@ class SearchActivity : BaseActivity() { get() = R.layout.activity_search override fun onViewModelCreated(viewModel: SearchViewModel, savedInstanceState: Bundle?) { - } } diff --git a/app/src/main/java/io/legado/app/ui/search/SearchAdapter.kt b/app/src/main/java/io/legado/app/ui/search/SearchAdapter.kt new file mode 100644 index 000000000..a23ee7605 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/search/SearchAdapter.kt @@ -0,0 +1,34 @@ +package io.legado.app.ui.search + +import android.content.Context +import io.legado.app.R +import io.legado.app.base.adapter.ItemViewDelegate +import io.legado.app.base.adapter.ItemViewHolder +import io.legado.app.base.adapter.SimpleRecyclerAdapter +import io.legado.app.data.entities.SearchBook +import kotlinx.android.synthetic.main.item_search.view.* + +class SearchAdapter(context: Context) : SimpleRecyclerAdapter(context) { + + init { + addItemViewDelegate(TestItemDelegate(context)) + } + + override val layoutID: Int + get() = R.layout.item_search + + override fun convert(holder: ItemViewHolder, item: SearchBook, payloads: MutableList) { + holder.itemView.bookName.text = "我欲封天" + } + + internal class TestItemDelegate(context: Context) : ItemViewDelegate(context){ + override val layoutID: Int + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + + override fun convert(holder: ItemViewHolder, item: SearchBook, payloads: MutableList) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt index e7df5b914..b354266eb 100644 --- a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt +++ b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt @@ -5,7 +5,10 @@ import android.content.res.ColorStateList import android.graphics.PorterDuff import android.graphics.drawable.Drawable import android.util.AttributeSet +import android.view.Menu +import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar import androidx.core.graphics.drawable.DrawableCompat import com.google.android.material.appbar.AppBarLayout import io.legado.app.R @@ -13,11 +16,16 @@ import kotlinx.android.synthetic.main.view_titlebar.view.* class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, attrs) { + val toolbar: Toolbar + val menu: Menu + get() = toolbar.menu + init { inflate(context, R.layout.view_titlebar, this) + toolbar = findViewById(R.id.toolbar) val a = context.obtainStyledAttributes( attrs, R.styleable.TitleBar, - 0, 0 + R.attr.titleBarStyle, 0 ) val navigationIcon = a.getDrawable(R.styleable.TitleBar_navigationIcon) val navigationContentDescription = a.getText(R.styleable.TitleBar_navigationContentDescription) @@ -40,13 +48,15 @@ class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, a } } + fun setNavigationOnClickListener(clickListener: ((View) -> Unit)){ + toolbar.setNavigationOnClickListener(clickListener) + } + private fun attachToActivity(context: Context) { val activity = getCompatActivity(context) activity?.let { activity.setSupportActionBar(toolbar) - activity.supportActionBar?.let { - it.setDisplayHomeAsUpEnabled(true) - } + activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) } } diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml index ef693f0b6..ae86cf117 100644 --- a/app/src/main/res/layout/app_bar_main.xml +++ b/app/src/main/res/layout/app_bar_main.xml @@ -7,19 +7,10 @@ android:layout_height="match_parent" tools:context=".ui.main.MainActivity"> - - - - - + android:layout_height="wrap_content"/> diff --git a/app/src/main/res/layout/item_search.xml b/app/src/main/res/layout/item_search.xml new file mode 100644 index 000000000..4fe3215d2 --- /dev/null +++ b/app/src/main/res/layout/item_search.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_titlebar.xml b/app/src/main/res/layout/view_titlebar.xml index f1befb3b3..82f286582 100644 --- a/app/src/main/res/layout/view_titlebar.xml +++ b/app/src/main/res/layout/view_titlebar.xml @@ -5,4 +5,5 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" + android:theme="@style/AppTheme.AppBarOverlay" app:popupTheme="@style/AppTheme.PopupOverlay"/> diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 7456589c4..942cea81c 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -29,6 +29,8 @@ + + From b8175f9fbb326cc5bdbff8946b52e1552e43ebd9 Mon Sep 17 00:00:00 2001 From: Awesome <1760316362@qq.com> Date: Thu, 23 May 2019 23:10:07 +0800 Subject: [PATCH 11/12] TitleBar --- .../java/io/legado/app/ui/widget/TitleBar.kt | 31 +++++++++++++++++-- app/src/main/res/layout/activity_search.xml | 2 +- app/src/main/res/values/attrs.xml | 6 ++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt index b354266eb..be1fafbf7 100644 --- a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt +++ b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt @@ -33,16 +33,43 @@ class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, a val navigationIconTintMode = a.getInt(R.styleable.TitleBar_navigationIconTintMode, 9) val showNavigationIcon = a.getBoolean(R.styleable.TitleBar_showNavigationIcon, true) val attachToActivity = a.getBoolean(R.styleable.TitleBar_attachToActivity, true) + val titleText = a.getString(R.styleable.TitleBar_title) + val subtitleText = a.getString(R.styleable.TitleBar_subtitle) a.recycle() - if (showNavigationIcon) { - toolbar.apply { + toolbar.apply { + if(showNavigationIcon){ this.navigationIcon = navigationIcon this.navigationContentDescription = navigationContentDescription wrapDrawableTint(this.navigationIcon, navigationIconTint, navigationIconTintMode) } + + if (a.hasValue(R.styleable.TitleBar_titleTextAppearance)) { + this.setTitleTextAppearance(context, a.getResourceId(R.styleable.TitleBar_titleTextAppearance, 0)) + } + + if (a.hasValue(R.styleable.TitleBar_titleTextColor)) { + this.setTitleTextColor(a.getColor(R.styleable.TitleBar_titleTextColor, -0x1)) + } + + if (a.hasValue(R.styleable.TitleBar_subtitleTextAppearance)) { + this.setSubtitleTextAppearance(context, a.getResourceId(R.styleable.TitleBar_subtitleTextAppearance, 0)) + } + + if (a.hasValue(R.styleable.TitleBar_subtitleTextColor)) { + this.setSubtitleTextColor(a.getColor(R.styleable.TitleBar_subtitleTextColor, -0x1)) + } + + if(!titleText.isNullOrBlank()){ + this.title = titleText + } + + if(!subtitleText.isNullOrBlank()){ + this.subtitle = subtitleText + } } + if (attachToActivity) { attachToActivity(context) } diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index a7dfaad9b..da4d4a9c6 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -17,7 +17,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent" - /> + app:title="搜索"/> + + + + + + From 7441fcd156ee26f2e04db21688e75580c8efa03e Mon Sep 17 00:00:00 2001 From: Awesome <1760316362@qq.com> Date: Thu, 23 May 2019 23:14:36 +0800 Subject: [PATCH 12/12] TitleBar --- .../java/io/legado/app/ui/widget/TitleBar.kt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt index be1fafbf7..548c22ec5 100644 --- a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt +++ b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt @@ -7,6 +7,8 @@ import android.graphics.drawable.Drawable import android.util.AttributeSet import android.view.Menu import android.view.View +import androidx.annotation.ColorInt +import androidx.annotation.StyleRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.core.graphics.drawable.DrawableCompat @@ -79,6 +81,38 @@ class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, a toolbar.setNavigationOnClickListener(clickListener) } + fun setTitle(title: CharSequence?) { + toolbar.title = title + } + + fun setTitle(titleId: Int) { + toolbar.setTitle(titleId) + } + + fun setSubTitle(subtitle: CharSequence?) { + toolbar.subtitle = subtitle + } + + fun setSubTitle(subtitleId: Int) { + toolbar.setSubtitle(subtitleId) + } + + fun setTitleTextColor(@ColorInt color: Int){ + toolbar.setTitleTextColor(color) + } + + fun setTitleTextAppearance(@StyleRes resId: Int){ + toolbar.setTitleTextAppearance(context, resId) + } + + fun setSubTitleTextColor(@ColorInt color: Int){ + toolbar.setSubtitleTextColor(color) + } + + fun setSubTitleTextAppearance(@StyleRes resId: Int){ + toolbar.setSubtitleTextAppearance(context, resId) + } + private fun attachToActivity(context: Context) { val activity = getCompatActivity(context) activity?.let {