# Conflicts: # app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt # app/src/main/java/io/legado/app/utils/MenuExtensions.kt # app/src/main/res/layout/activity_source_edit.xml # app/src/main/res/layout/view_titlebar.xmlpull/32/head
commit
3f3593c616
@ -0,0 +1,15 @@ |
|||||||
|
package io.legado.app.data.dao |
||||||
|
|
||||||
|
import androidx.paging.DataSource |
||||||
|
import androidx.room.Dao |
||||||
|
import androidx.room.Query |
||||||
|
import io.legado.app.data.entities.BookGroup |
||||||
|
|
||||||
|
@Dao |
||||||
|
interface BookGroupDao { |
||||||
|
|
||||||
|
@Query("SELECT * FROM book_groups ORDER BY `order`") |
||||||
|
fun observeAll(): DataSource.Factory<Int, BookGroup> |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
package io.legado.app.data.dao |
||||||
|
|
||||||
|
import androidx.paging.DataSource |
||||||
|
import androidx.room.* |
||||||
|
import io.legado.app.data.entities.BookSource |
||||||
|
|
||||||
|
@Dao |
||||||
|
interface BookSourceDao { |
||||||
|
|
||||||
|
@Query("select * from book_sources order by customOrder asc") |
||||||
|
fun observeAll(): DataSource.Factory<Int, BookSource> |
||||||
|
|
||||||
|
@Query("select * from book_sources where name like :searchKey or `group` like :searchKey or origin like :searchKey order by customOrder asc") |
||||||
|
fun observeSearch(searchKey: String = ""): DataSource.Factory<Int, BookSource> |
||||||
|
|
||||||
|
@Query("select * from book_sources where origin = :key") |
||||||
|
fun findByKey(key: String): BookSource? |
||||||
|
|
||||||
|
@Query("select count(*) from book_sources") |
||||||
|
fun allCount(): Int |
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE) |
||||||
|
fun insert(bookSource: BookSource): Long |
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE) |
||||||
|
fun insert(vararg bookSource: BookSource) |
||||||
|
|
||||||
|
@Update |
||||||
|
fun update(bookSource: BookSource) |
||||||
|
|
||||||
|
@Update |
||||||
|
fun update(vararg bookSource: BookSource) |
||||||
|
|
||||||
|
@Delete |
||||||
|
fun delete(bookSource: BookSource) |
||||||
|
|
||||||
|
@Delete |
||||||
|
fun delete(vararg bookSource: BookSource) |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package io.legado.app.data.entities |
||||||
|
|
||||||
|
import android.os.Parcelable |
||||||
|
import androidx.room.Entity |
||||||
|
import androidx.room.PrimaryKey |
||||||
|
import kotlinx.android.parcel.Parcelize |
||||||
|
|
||||||
|
@Parcelize |
||||||
|
@Entity(tableName = "book_groups") |
||||||
|
data class BookGroup( |
||||||
|
@PrimaryKey |
||||||
|
var groupId: Int = 0, |
||||||
|
var groupName: String, |
||||||
|
var order: Int = 0 |
||||||
|
) : Parcelable |
@ -0,0 +1,127 @@ |
|||||||
|
package io.legado.app.help |
||||||
|
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager |
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper |
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout |
||||||
|
import androidx.viewpager.widget.ViewPager |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by GKF on 2018/3/16. |
||||||
|
*/ |
||||||
|
|
||||||
|
class ItemTouchCallback : ItemTouchHelper.Callback() { |
||||||
|
|
||||||
|
private var swipeRefreshLayout: SwipeRefreshLayout? = null |
||||||
|
private var viewPager: ViewPager? = null |
||||||
|
|
||||||
|
/** |
||||||
|
* Item操作的回调 |
||||||
|
*/ |
||||||
|
var onItemTouchCallbackListener: OnItemTouchCallbackListener? = null |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否可以拖拽 |
||||||
|
*/ |
||||||
|
var isCanDrag = false |
||||||
|
/** |
||||||
|
* 是否可以被滑动 |
||||||
|
*/ |
||||||
|
var isCanSwipe = false |
||||||
|
|
||||||
|
/** |
||||||
|
* 当Item被长按的时候是否可以被拖拽 |
||||||
|
*/ |
||||||
|
override fun isLongPressDragEnabled(): Boolean { |
||||||
|
return isCanDrag |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Item是否可以被滑动(H:左右滑动,V:上下滑动) |
||||||
|
*/ |
||||||
|
override fun isItemViewSwipeEnabled(): Boolean { |
||||||
|
return isCanSwipe |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向 |
||||||
|
*/ |
||||||
|
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { |
||||||
|
val layoutManager = recyclerView.layoutManager |
||||||
|
if (layoutManager is GridLayoutManager) {// GridLayoutManager |
||||||
|
// flag如果值是0,相当于这个功能被关闭 |
||||||
|
val dragFlag = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT or ItemTouchHelper.UP or ItemTouchHelper.DOWN |
||||||
|
val swipeFlag = 0 |
||||||
|
// create make |
||||||
|
return makeMovementFlags(dragFlag, swipeFlag) |
||||||
|
} else if (layoutManager is LinearLayoutManager) {// linearLayoutManager |
||||||
|
val linearLayoutManager = layoutManager as LinearLayoutManager? |
||||||
|
val orientation = linearLayoutManager!!.orientation |
||||||
|
|
||||||
|
var dragFlag = 0 |
||||||
|
var swipeFlag = 0 |
||||||
|
|
||||||
|
// 为了方便理解,相当于分为横着的ListView和竖着的ListView |
||||||
|
if (orientation == LinearLayoutManager.HORIZONTAL) {// 如果是横向的布局 |
||||||
|
swipeFlag = ItemTouchHelper.UP or ItemTouchHelper.DOWN |
||||||
|
dragFlag = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT |
||||||
|
} else if (orientation == LinearLayoutManager.VERTICAL) {// 如果是竖向的布局,相当于ListView |
||||||
|
dragFlag = ItemTouchHelper.UP or ItemTouchHelper.DOWN |
||||||
|
swipeFlag = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT |
||||||
|
} |
||||||
|
return makeMovementFlags(dragFlag, swipeFlag) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 当Item被拖拽的时候被回调 |
||||||
|
* |
||||||
|
* @param recyclerView recyclerView |
||||||
|
* @param srcViewHolder 拖拽的ViewHolder |
||||||
|
* @param targetViewHolder 目的地的viewHolder |
||||||
|
*/ |
||||||
|
override fun onMove( |
||||||
|
recyclerView: RecyclerView, |
||||||
|
srcViewHolder: RecyclerView.ViewHolder, |
||||||
|
targetViewHolder: RecyclerView.ViewHolder |
||||||
|
): Boolean { |
||||||
|
onItemTouchCallbackListener?.let { |
||||||
|
return it.onMove(srcViewHolder.adapterPosition, targetViewHolder.adapterPosition) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { |
||||||
|
onItemTouchCallbackListener?.let { |
||||||
|
return it.onSwiped(viewHolder.adapterPosition) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { |
||||||
|
super.onSelectedChanged(viewHolder, actionState) |
||||||
|
val swiping = actionState == ItemTouchHelper.ACTION_STATE_DRAG |
||||||
|
swipeRefreshLayout?.isEnabled = !swiping |
||||||
|
viewPager?.requestDisallowInterceptTouchEvent(swiping) |
||||||
|
} |
||||||
|
|
||||||
|
interface OnItemTouchCallbackListener { |
||||||
|
/** |
||||||
|
* 当某个Item被滑动删除的时候 |
||||||
|
* |
||||||
|
* @param adapterPosition item的position |
||||||
|
*/ |
||||||
|
fun onSwiped(adapterPosition: Int) |
||||||
|
|
||||||
|
/** |
||||||
|
* 当两个Item位置互换的时候被回调 |
||||||
|
* |
||||||
|
* @param srcPosition 拖拽的item的position |
||||||
|
* @param targetPosition 目的地的Item的position |
||||||
|
* @return 开发者处理了操作应该返回true,开发者没有处理就返回false |
||||||
|
*/ |
||||||
|
fun onMove(srcPosition: Int, targetPosition: Int): Boolean |
||||||
|
} |
||||||
|
} |
@ -1,35 +0,0 @@ |
|||||||
package io.legado.app.lib.theme.view |
|
||||||
|
|
||||||
import android.content.Context |
|
||||||
import android.util.AttributeSet |
|
||||||
import android.view.View |
|
||||||
import android.widget.Switch |
|
||||||
import androidx.appcompat.widget.SwitchCompat |
|
||||||
import io.legado.app.lib.theme.ATH |
|
||||||
import io.legado.app.lib.theme.ThemeStore |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Aidan Follestad (afollestad) |
|
||||||
*/ |
|
||||||
class ATEStockSwitch : SwitchCompat { |
|
||||||
|
|
||||||
constructor(context: Context) : super(context) { |
|
||||||
init(context, null) |
|
||||||
} |
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { |
|
||||||
init(context, attrs) |
|
||||||
} |
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { |
|
||||||
init(context, attrs) |
|
||||||
} |
|
||||||
|
|
||||||
private fun init(context: Context, attrs: AttributeSet?) { |
|
||||||
ATH.setTint(this, ThemeStore.accentColor(context)) |
|
||||||
} |
|
||||||
|
|
||||||
override fun isShown(): Boolean { |
|
||||||
return parent != null && visibility == View.VISIBLE |
|
||||||
} |
|
||||||
} |
|
@ -1,15 +1,59 @@ |
|||||||
package io.legado.app.ui.config |
package io.legado.app.ui.config |
||||||
|
|
||||||
|
import android.content.SharedPreferences |
||||||
import android.os.Bundle |
import android.os.Bundle |
||||||
|
import androidx.preference.ListPreference |
||||||
|
import androidx.preference.Preference |
||||||
import androidx.preference.PreferenceFragmentCompat |
import androidx.preference.PreferenceFragmentCompat |
||||||
import io.legado.app.R |
import io.legado.app.R |
||||||
|
import io.legado.app.utils.getPrefString |
||||||
|
|
||||||
|
|
||||||
class ConfigFragment : PreferenceFragmentCompat() { |
class ConfigFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener, |
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener { |
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { |
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { |
||||||
addPreferencesFromResource(R.xml.pref_config) |
addPreferencesFromResource(R.xml.pref_config) |
||||||
} |
} |
||||||
|
|
||||||
|
override fun onResume() { |
||||||
|
super.onResume() |
||||||
|
preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onPause() { |
||||||
|
preferenceManager.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) |
||||||
|
super.onPause() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean { |
||||||
|
val stringValue = newValue.toString() |
||||||
|
|
||||||
|
if (preference is ListPreference) { |
||||||
|
val index = preference.findIndexOfValue(stringValue) |
||||||
|
// Set the summary to reflect the new value. |
||||||
|
preference.setSummary(if (index >= 0) preference.entries[index] else null) |
||||||
|
} else { |
||||||
|
// For all other preferences, set the summary to the value's |
||||||
|
preference?.summary = stringValue |
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
private fun bindPreferenceSummaryToValue(preference: Preference?) { |
||||||
|
preference?.let { |
||||||
|
preference.onPreferenceChangeListener = this |
||||||
|
onPreferenceChange( |
||||||
|
preference, |
||||||
|
preference.context.getPrefString(preference.key, "") |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
} |
} |
@ -1,13 +1,43 @@ |
|||||||
package io.legado.app.ui.config |
package io.legado.app.ui.config |
||||||
|
|
||||||
import android.os.Bundle |
import android.os.Bundle |
||||||
|
import androidx.preference.ListPreference |
||||||
|
import androidx.preference.Preference |
||||||
import androidx.preference.PreferenceFragmentCompat |
import androidx.preference.PreferenceFragmentCompat |
||||||
import io.legado.app.R |
import io.legado.app.R |
||||||
|
import io.legado.app.utils.getPrefString |
||||||
|
|
||||||
class WebDavConfigFragment : PreferenceFragmentCompat() { |
class WebDavConfigFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener { |
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { |
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { |
||||||
addPreferencesFromResource(R.xml.pref_config_web_dav) |
addPreferencesFromResource(R.xml.pref_config_web_dav) |
||||||
|
bindPreferenceSummaryToValue(findPreference("web_dav_url")) |
||||||
|
bindPreferenceSummaryToValue(findPreference("web_dav_account")) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean { |
||||||
|
val stringValue = newValue.toString() |
||||||
|
|
||||||
|
if (preference is ListPreference) { |
||||||
|
val index = preference.findIndexOfValue(stringValue) |
||||||
|
// Set the summary to reflect the new value. |
||||||
|
preference.setSummary(if (index >= 0) preference.entries[index] else null) |
||||||
|
} else { |
||||||
|
// For all other preferences, set the summary to the value's |
||||||
|
preference?.summary = stringValue |
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
private fun bindPreferenceSummaryToValue(preference: Preference?) { |
||||||
|
preference?.let { |
||||||
|
preference.onPreferenceChangeListener = this |
||||||
|
onPreferenceChange( |
||||||
|
preference, |
||||||
|
preference.context.getPrefString(preference.key, "") |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
} |
} |
||||||
|
|
||||||
} |
} |
@ -0,0 +1,38 @@ |
|||||||
|
package io.legado.app.ui.main.bookshelf |
||||||
|
|
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import androidx.paging.PagedListAdapter |
||||||
|
import androidx.recyclerview.widget.DiffUtil |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.data.entities.BookGroup |
||||||
|
|
||||||
|
class BookGroupAdapter : PagedListAdapter<BookGroup, BookGroupAdapter.MyViewHolder>(DIFF_CALLBACK) { |
||||||
|
|
||||||
|
companion object { |
||||||
|
@JvmField |
||||||
|
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<BookGroup>() { |
||||||
|
override fun areItemsTheSame(oldItem: BookGroup, newItem: BookGroup): Boolean = |
||||||
|
oldItem.groupId == newItem.groupId |
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: BookGroup, newItem: BookGroup): Boolean = |
||||||
|
oldItem.groupId == newItem.groupId |
||||||
|
&& oldItem.groupName == newItem.groupName |
||||||
|
&& oldItem.order == newItem.order |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { |
||||||
|
return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_book_group, parent, false)) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
package io.legado.app.ui.main.bookshelf |
||||||
|
|
||||||
|
import android.text.TextUtils.isEmpty |
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import androidx.paging.PagedListAdapter |
||||||
|
import androidx.recyclerview.widget.DiffUtil |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import com.bumptech.glide.Glide |
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.data.entities.Book |
||||||
|
import io.legado.app.lib.theme.ThemeStore |
||||||
|
import kotlinx.android.synthetic.main.item_bookshelf_list.view.* |
||||||
|
import kotlinx.android.synthetic.main.item_relace_rule.view.tv_name |
||||||
|
import java.io.File |
||||||
|
|
||||||
|
class BookshelfAdapter : PagedListAdapter<Book, BookshelfAdapter.MyViewHolder>(DIFF_CALLBACK) { |
||||||
|
|
||||||
|
companion object { |
||||||
|
@JvmField |
||||||
|
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Book>() { |
||||||
|
override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean = |
||||||
|
oldItem.descUrl == newItem.descUrl |
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean = |
||||||
|
oldItem.descUrl == newItem.descUrl |
||||||
|
&& oldItem.durChapterTitle == newItem.durChapterTitle |
||||||
|
&& oldItem.latestChapterTitle == newItem.latestChapterTitle |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var callBack: CallBack? = null |
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { |
||||||
|
return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_bookshelf_list, parent, false)) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) { |
||||||
|
currentList?.get(position)?.let { |
||||||
|
holder.bind(it, callBack) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { |
||||||
|
|
||||||
|
init { |
||||||
|
itemView.setBackgroundColor(ThemeStore.backgroundColor(itemView.context)) |
||||||
|
} |
||||||
|
|
||||||
|
fun bind(book: Book, callBack: CallBack?) = with(itemView) { |
||||||
|
tv_name.text = book.name |
||||||
|
tv_author.text = book.author |
||||||
|
tv_read.text = book.durChapterTitle |
||||||
|
tv_last.text = book.latestChapterTitle |
||||||
|
val cover = if (isEmpty(book.customCoverUrl)) book.coverUrl else book.customCoverUrl |
||||||
|
cover?.let { |
||||||
|
if (it.startsWith("http")) { |
||||||
|
Glide.with(itemView).load(it) |
||||||
|
.placeholder(R.drawable.img_cover_default) |
||||||
|
.centerCrop() |
||||||
|
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) |
||||||
|
.into(iv_cover) |
||||||
|
} else { |
||||||
|
Glide.with(itemView).load(File(it)) |
||||||
|
.placeholder(R.drawable.img_cover_default) |
||||||
|
.centerCrop() |
||||||
|
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) |
||||||
|
.into(iv_cover) |
||||||
|
} |
||||||
|
} |
||||||
|
itemView.setOnClickListener { callBack?.open(book) } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
interface CallBack { |
||||||
|
fun open(book: Book) |
||||||
|
} |
||||||
|
} |
@ -1,2 +1,123 @@ |
|||||||
package io.legado.app.ui.main.booksource |
package io.legado.app.ui.main.booksource |
||||||
|
|
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.Menu |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import android.widget.PopupMenu |
||||||
|
import androidx.paging.PagedListAdapter |
||||||
|
import androidx.recyclerview.widget.DiffUtil |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.data.entities.BookSource |
||||||
|
import io.legado.app.help.ItemTouchCallback.OnItemTouchCallbackListener |
||||||
|
import io.legado.app.lib.theme.ThemeStore |
||||||
|
import kotlinx.android.synthetic.main.item_book_source.view.* |
||||||
|
import org.jetbrains.anko.sdk27.listeners.onClick |
||||||
|
|
||||||
|
class BookSourceAdapter : PagedListAdapter<BookSource, BookSourceAdapter.MyViewHolder>(DIFF_CALLBACK) { |
||||||
|
|
||||||
|
companion object { |
||||||
|
|
||||||
|
@JvmField |
||||||
|
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<BookSource>() { |
||||||
|
override fun areItemsTheSame(oldItem: BookSource, newItem: BookSource): Boolean = |
||||||
|
oldItem.origin == newItem.origin |
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: BookSource, newItem: BookSource): Boolean = |
||||||
|
oldItem.origin == newItem.origin |
||||||
|
&& oldItem.name == newItem.name |
||||||
|
&& oldItem.group == newItem.group |
||||||
|
&& oldItem.isEnabled == newItem.isEnabled |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var callBack: CallBack? = null |
||||||
|
val checkedList = HashSet<String>() |
||||||
|
|
||||||
|
val itemTouchCallbackListener = object : OnItemTouchCallbackListener { |
||||||
|
override fun onSwiped(adapterPosition: Int) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun onMove(srcPosition: Int, targetPosition: Int): Boolean { |
||||||
|
currentList?.let { |
||||||
|
val srcSource = it[srcPosition] |
||||||
|
val targetSource = it[targetPosition] |
||||||
|
srcSource?.let { a -> |
||||||
|
targetSource?.let { b -> |
||||||
|
a.customOrder = targetPosition |
||||||
|
b.customOrder = srcPosition |
||||||
|
callBack?.update(a, b) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { |
||||||
|
return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_book_source, parent, false)) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) { |
||||||
|
getItem(position)?.let { holder.bind(it, checkedList, callBack) } |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { |
||||||
|
|
||||||
|
init { |
||||||
|
itemView.setBackgroundColor(ThemeStore.backgroundColor(itemView.context)) |
||||||
|
} |
||||||
|
|
||||||
|
fun bind(bookSource: BookSource, checkedList: HashSet<String>, callBack: CallBack?) = with(itemView) { |
||||||
|
cb_book_source.text = String.format("%s (%s)", bookSource.name, bookSource.group) |
||||||
|
cb_book_source.onClick { |
||||||
|
if (cb_book_source.isChecked) { |
||||||
|
checkedList.add(bookSource.origin) |
||||||
|
} else { |
||||||
|
checkedList.remove(bookSource.origin) |
||||||
|
} |
||||||
|
} |
||||||
|
sw_enabled.isChecked = bookSource.isEnabled |
||||||
|
sw_enabled.setOnClickListener { |
||||||
|
bookSource.isEnabled = sw_enabled.isChecked |
||||||
|
callBack?.update(bookSource) |
||||||
|
} |
||||||
|
iv_more.setOnClickListener { |
||||||
|
val popupMenu = PopupMenu(context, iv_more) |
||||||
|
popupMenu.menu.add(Menu.NONE, R.id.menu_edit, Menu.NONE, R.string.edit) |
||||||
|
popupMenu.menu.add(Menu.NONE, R.id.menu_del, Menu.NONE, R.string.delete) |
||||||
|
popupMenu.menu.add(Menu.NONE, R.id.menu_top, Menu.NONE, R.string.to_top) |
||||||
|
popupMenu.setOnMenuItemClickListener { |
||||||
|
when (it.itemId) { |
||||||
|
R.id.menu_edit -> { |
||||||
|
callBack?.edit(bookSource) |
||||||
|
true |
||||||
|
} |
||||||
|
R.id.menu_del -> { |
||||||
|
callBack?.del(bookSource) |
||||||
|
true |
||||||
|
} |
||||||
|
R.id.menu_top -> { |
||||||
|
true |
||||||
|
} |
||||||
|
else -> { |
||||||
|
false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
popupMenu.show() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
interface CallBack { |
||||||
|
fun del(bookSource: BookSource) |
||||||
|
fun edit(bookSource: BookSource) |
||||||
|
fun update(bookSource: BookSource) |
||||||
|
fun update(vararg bookSource: BookSource) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
package io.legado.app.ui.sourcedebug |
||||||
|
|
@ -0,0 +1,72 @@ |
|||||||
|
package io.legado.app.ui.sourceedit |
||||||
|
|
||||||
|
import android.text.Editable |
||||||
|
import android.text.TextWatcher |
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import io.legado.app.R |
||||||
|
import kotlinx.android.synthetic.main.item_source_edit.view.* |
||||||
|
|
||||||
|
class SourceEditAdapter : RecyclerView.Adapter<SourceEditAdapter.MyViewHolder>() { |
||||||
|
|
||||||
|
var sourceEditEntities: ArrayList<SourceEditActivity.SourceEditEntity> = ArrayList() |
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { |
||||||
|
return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_source_edit, parent, false)) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) { |
||||||
|
holder.bind(sourceEditEntities[position]) |
||||||
|
} |
||||||
|
|
||||||
|
override fun getItemCount(): Int { |
||||||
|
return sourceEditEntities.size |
||||||
|
} |
||||||
|
|
||||||
|
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { |
||||||
|
fun bind(sourceEditEntity: SourceEditActivity.SourceEditEntity) = with(itemView) { |
||||||
|
if (editText.getTag(R.id.tag1) == null) { |
||||||
|
val listener = object : View.OnAttachStateChangeListener { |
||||||
|
override fun onViewAttachedToWindow(v: View) { |
||||||
|
editText.isCursorVisible = false |
||||||
|
editText.isCursorVisible = true |
||||||
|
editText.isFocusable = true |
||||||
|
editText.isFocusableInTouchMode = true |
||||||
|
} |
||||||
|
|
||||||
|
override fun onViewDetachedFromWindow(v: View) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
editText.addOnAttachStateChangeListener(listener) |
||||||
|
editText.setTag(R.id.tag1, listener) |
||||||
|
} |
||||||
|
editText.getTag(R.id.tag2)?.let { |
||||||
|
if (it is TextWatcher) { |
||||||
|
editText.removeTextChangedListener(it) |
||||||
|
} |
||||||
|
} |
||||||
|
editText.setText(sourceEditEntity.value) |
||||||
|
textInputLayout.hint = context.getString(sourceEditEntity.hint) |
||||||
|
val textWatcher = object : TextWatcher { |
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable?) { |
||||||
|
sourceEditEntity.value = (s?.toString()) |
||||||
|
} |
||||||
|
} |
||||||
|
editText.addTextChangedListener(textWatcher) |
||||||
|
editText.setTag(R.id.tag2, textWatcher) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,233 @@ |
|||||||
|
package io.legado.app.ui.widget |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.graphics.Color |
||||||
|
import android.graphics.drawable.ShapeDrawable |
||||||
|
import android.graphics.drawable.shapes.RoundRectShape |
||||||
|
import android.text.TextUtils |
||||||
|
import android.util.AttributeSet |
||||||
|
import android.util.TypedValue |
||||||
|
import android.view.Gravity |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import android.widget.FrameLayout |
||||||
|
import android.widget.FrameLayout.LayoutParams |
||||||
|
import android.widget.TabWidget |
||||||
|
import androidx.appcompat.widget.AppCompatTextView |
||||||
|
import io.legado.app.R |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Created by milad heydari on 5/6/2016. |
||||||
|
*/ |
||||||
|
class BadgeView @JvmOverloads constructor( |
||||||
|
context: Context, |
||||||
|
attrs: AttributeSet? = null, |
||||||
|
defStyle: Int = android.R.attr.textViewStyle |
||||||
|
) : AppCompatTextView(context, attrs, defStyle) { |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Returns true if view is hidden on badge value 0 or null; |
||||||
|
*/ |
||||||
|
/** |
||||||
|
* @param hideOnNull the hideOnNull to set |
||||||
|
*/ |
||||||
|
var isHideOnNull = true |
||||||
|
set(hideOnNull) { |
||||||
|
field = hideOnNull |
||||||
|
text = text |
||||||
|
} |
||||||
|
private var radius: Float = 0.toFloat() |
||||||
|
|
||||||
|
val badgeCount: Int? |
||||||
|
get() { |
||||||
|
if (text == null) { |
||||||
|
return null |
||||||
|
} |
||||||
|
val text = text.toString() |
||||||
|
try { |
||||||
|
return Integer.parseInt(text) |
||||||
|
} catch (e: NumberFormatException) { |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
var badgeGravity: Int |
||||||
|
get() { |
||||||
|
val params = layoutParams as LayoutParams |
||||||
|
return params.gravity |
||||||
|
} |
||||||
|
set(gravity) { |
||||||
|
val params = layoutParams as LayoutParams |
||||||
|
params.gravity = gravity |
||||||
|
layoutParams = params |
||||||
|
} |
||||||
|
|
||||||
|
val badgeMargin: IntArray |
||||||
|
get() { |
||||||
|
val params = layoutParams as LayoutParams |
||||||
|
return intArrayOf(params.leftMargin, params.topMargin, params.rightMargin, params.bottomMargin) |
||||||
|
} |
||||||
|
|
||||||
|
init { |
||||||
|
|
||||||
|
init() |
||||||
|
} |
||||||
|
|
||||||
|
private fun init() { |
||||||
|
if (layoutParams !is LayoutParams) { |
||||||
|
val layoutParams = LayoutParams( |
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT, |
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT, |
||||||
|
Gravity.CENTER |
||||||
|
) |
||||||
|
setLayoutParams(layoutParams) |
||||||
|
} |
||||||
|
|
||||||
|
// set default font |
||||||
|
setTextColor(Color.WHITE) |
||||||
|
//setTypeface(Typeface.DEFAULT_BOLD); |
||||||
|
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f) |
||||||
|
setPadding(dip2Px(5f), dip2Px(1f), dip2Px(5f), dip2Px(1f)) |
||||||
|
radius = 8f |
||||||
|
|
||||||
|
// set default background |
||||||
|
setBackground(radius, Color.parseColor("#d3321b")) |
||||||
|
|
||||||
|
gravity = Gravity.CENTER |
||||||
|
|
||||||
|
// default values |
||||||
|
isHideOnNull = true |
||||||
|
setBadgeCount(0) |
||||||
|
minWidth = dip2Px(16f) |
||||||
|
minHeight = dip2Px(16f) |
||||||
|
} |
||||||
|
|
||||||
|
fun setBackground(dipRadius: Float, badgeColor: Int) { |
||||||
|
val radius = dip2Px(dipRadius) |
||||||
|
val radiusArray = floatArrayOf( |
||||||
|
radius.toFloat(), |
||||||
|
radius.toFloat(), |
||||||
|
radius.toFloat(), |
||||||
|
radius.toFloat(), |
||||||
|
radius.toFloat(), |
||||||
|
radius.toFloat(), |
||||||
|
radius.toFloat(), |
||||||
|
radius.toFloat() |
||||||
|
) |
||||||
|
|
||||||
|
val roundRect = RoundRectShape(radiusArray, null, null) |
||||||
|
val bgDrawable = ShapeDrawable(roundRect) |
||||||
|
bgDrawable.paint.color = badgeColor |
||||||
|
background = bgDrawable |
||||||
|
} |
||||||
|
|
||||||
|
fun setBackground(badgeColor: Int) { |
||||||
|
setBackground(radius, badgeColor) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @see android.widget.TextView.setText |
||||||
|
*/ |
||||||
|
override fun setText(text: CharSequence, type: BufferType) { |
||||||
|
if (isHideOnNull && TextUtils.isEmpty(text)) { |
||||||
|
visibility = View.GONE |
||||||
|
} else { |
||||||
|
visibility = View.VISIBLE |
||||||
|
} |
||||||
|
super.setText(text, type) |
||||||
|
} |
||||||
|
|
||||||
|
fun setBadgeCount(count: Int) { |
||||||
|
text = count.toString() |
||||||
|
if (count == 0) { |
||||||
|
visibility = View.GONE |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun setHighlight(highlight: Boolean) { |
||||||
|
setBackground(resources.getColor(if (highlight) R.color.highlight else R.color.darker_gray)) |
||||||
|
} |
||||||
|
|
||||||
|
fun setBadgeMargin(dipMargin: Int) { |
||||||
|
setBadgeMargin(dipMargin, dipMargin, dipMargin, dipMargin) |
||||||
|
} |
||||||
|
|
||||||
|
fun setBadgeMargin(leftDipMargin: Int, topDipMargin: Int, rightDipMargin: Int, bottomDipMargin: Int) { |
||||||
|
val params = layoutParams as LayoutParams |
||||||
|
params.leftMargin = dip2Px(leftDipMargin.toFloat()) |
||||||
|
params.topMargin = dip2Px(topDipMargin.toFloat()) |
||||||
|
params.rightMargin = dip2Px(rightDipMargin.toFloat()) |
||||||
|
params.bottomMargin = dip2Px(bottomDipMargin.toFloat()) |
||||||
|
layoutParams = params |
||||||
|
} |
||||||
|
|
||||||
|
fun incrementBadgeCount(increment: Int) { |
||||||
|
val count = badgeCount |
||||||
|
if (count == null) { |
||||||
|
setBadgeCount(increment) |
||||||
|
} else { |
||||||
|
setBadgeCount(increment + count) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun decrementBadgeCount(decrement: Int) { |
||||||
|
incrementBadgeCount(-decrement) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Attach the BadgeView to the TabWidget |
||||||
|
* @param target the TabWidget to attach the BadgeView |
||||||
|
* @param tabIndex index of the tab |
||||||
|
*/ |
||||||
|
fun setTargetView(target: TabWidget, tabIndex: Int) { |
||||||
|
val tabView = target.getChildTabViewAt(tabIndex) |
||||||
|
setTargetView(tabView) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Attach the BadgeView to the target view |
||||||
|
* @param target the view to attach the BadgeView |
||||||
|
*/ |
||||||
|
fun setTargetView(target: View?) { |
||||||
|
if (parent != null) { |
||||||
|
(parent as ViewGroup).removeView(this) |
||||||
|
} |
||||||
|
|
||||||
|
if (target == null) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (target.parent is FrameLayout) { |
||||||
|
(target.parent as FrameLayout).addView(this) |
||||||
|
|
||||||
|
} else if (target.parent is ViewGroup) { |
||||||
|
// use a new FrameLayout container for adding badge |
||||||
|
val parentContainer = target.parent as ViewGroup |
||||||
|
val groupIndex = parentContainer.indexOfChild(target) |
||||||
|
parentContainer.removeView(target) |
||||||
|
|
||||||
|
val badgeContainer = FrameLayout(context) |
||||||
|
val parentLayoutParams = target.layoutParams |
||||||
|
|
||||||
|
badgeContainer.layoutParams = parentLayoutParams |
||||||
|
target.layoutParams = ViewGroup.LayoutParams( |
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT |
||||||
|
) |
||||||
|
|
||||||
|
parentContainer.addView(badgeContainer, groupIndex, parentLayoutParams) |
||||||
|
badgeContainer.addView(target) |
||||||
|
|
||||||
|
badgeContainer.addView(this) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* converts dip to px |
||||||
|
*/ |
||||||
|
private fun dip2Px(dip: Float): Int { |
||||||
|
return (dip * context.resources.displayMetrics.density + 0.5f).toInt() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,212 @@ |
|||||||
|
package io.legado.app.ui.widget |
||||||
|
|
||||||
|
import android.animation.Animator |
||||||
|
import android.animation.AnimatorSet |
||||||
|
import android.animation.ObjectAnimator |
||||||
|
import android.content.Context |
||||||
|
import android.graphics.Canvas |
||||||
|
import android.graphics.Color |
||||||
|
import android.graphics.Paint |
||||||
|
import android.graphics.RectF |
||||||
|
import android.util.AttributeSet |
||||||
|
import android.util.TypedValue |
||||||
|
import android.view.View |
||||||
|
import android.view.animation.LinearInterpolator |
||||||
|
import io.legado.app.R |
||||||
|
|
||||||
|
/** |
||||||
|
* RotateLoading |
||||||
|
* Created by Victor on 2015/4/28. |
||||||
|
*/ |
||||||
|
class RotateLoading : View { |
||||||
|
|
||||||
|
private var mPaint: Paint? = null |
||||||
|
|
||||||
|
private var loadingRectF: RectF? = null |
||||||
|
private var shadowRectF: RectF? = null |
||||||
|
|
||||||
|
private var topDegree = 10 |
||||||
|
private var bottomDegree = 190 |
||||||
|
|
||||||
|
private var arc: Float = 0.toFloat() |
||||||
|
|
||||||
|
private var thisWidth: Int = 0 |
||||||
|
|
||||||
|
private var changeBigger = true |
||||||
|
|
||||||
|
private var shadowPosition: Int = 0 |
||||||
|
|
||||||
|
var isStart = false |
||||||
|
private set |
||||||
|
|
||||||
|
var loadingColor: Int = 0 |
||||||
|
|
||||||
|
private var speedOfDegree: Int = 0 |
||||||
|
|
||||||
|
private var speedOfArc: Float = 0.toFloat() |
||||||
|
|
||||||
|
constructor(context: Context) : super(context) { |
||||||
|
initView(context, null) |
||||||
|
} |
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { |
||||||
|
initView(context, attrs) |
||||||
|
} |
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { |
||||||
|
initView(context, attrs) |
||||||
|
} |
||||||
|
|
||||||
|
private fun initView(context: Context, attrs: AttributeSet?) { |
||||||
|
loadingColor = Color.WHITE |
||||||
|
thisWidth = dpToPx(context, DEFAULT_WIDTH.toFloat()) |
||||||
|
shadowPosition = dpToPx(getContext(), DEFAULT_SHADOW_POSITION.toFloat()) |
||||||
|
speedOfDegree = DEFAULT_SPEED_OF_DEGREE |
||||||
|
|
||||||
|
if (null != attrs) { |
||||||
|
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.RotateLoading) |
||||||
|
loadingColor = typedArray.getColor(R.styleable.RotateLoading_loading_color, Color.WHITE) |
||||||
|
thisWidth = typedArray.getDimensionPixelSize( |
||||||
|
R.styleable.RotateLoading_loading_width, |
||||||
|
dpToPx(context, DEFAULT_WIDTH.toFloat()) |
||||||
|
) |
||||||
|
shadowPosition = typedArray.getInt(R.styleable.RotateLoading_shadow_position, DEFAULT_SHADOW_POSITION) |
||||||
|
speedOfDegree = typedArray.getInt(R.styleable.RotateLoading_loading_speed, DEFAULT_SPEED_OF_DEGREE) |
||||||
|
typedArray.recycle() |
||||||
|
} |
||||||
|
speedOfArc = (speedOfDegree / 4).toFloat() |
||||||
|
mPaint = Paint() |
||||||
|
mPaint!!.color = loadingColor |
||||||
|
mPaint!!.isAntiAlias = true |
||||||
|
mPaint!!.style = Paint.Style.STROKE |
||||||
|
mPaint!!.strokeWidth = thisWidth.toFloat() |
||||||
|
mPaint!!.strokeCap = Paint.Cap.ROUND |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { |
||||||
|
super.onSizeChanged(w, h, oldw, oldh) |
||||||
|
|
||||||
|
arc = 10f |
||||||
|
|
||||||
|
loadingRectF = |
||||||
|
RectF( |
||||||
|
(2 * thisWidth).toFloat(), |
||||||
|
(2 * thisWidth).toFloat(), |
||||||
|
(w - 2 * thisWidth).toFloat(), |
||||||
|
(h - 2 * thisWidth).toFloat() |
||||||
|
) |
||||||
|
shadowRectF = RectF( |
||||||
|
(2 * thisWidth + shadowPosition).toFloat(), |
||||||
|
(2 * thisWidth + shadowPosition).toFloat(), |
||||||
|
(w - 2 * thisWidth + shadowPosition).toFloat(), |
||||||
|
(h - 2 * thisWidth + shadowPosition).toFloat() |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) { |
||||||
|
super.onDraw(canvas) |
||||||
|
|
||||||
|
if (!isStart) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
mPaint!!.color = Color.parseColor("#1a000000") |
||||||
|
canvas.drawArc(shadowRectF!!, topDegree.toFloat(), arc, false, mPaint!!) |
||||||
|
canvas.drawArc(shadowRectF!!, bottomDegree.toFloat(), arc, false, mPaint!!) |
||||||
|
|
||||||
|
mPaint!!.color = loadingColor |
||||||
|
canvas.drawArc(loadingRectF!!, topDegree.toFloat(), arc, false, mPaint!!) |
||||||
|
canvas.drawArc(loadingRectF!!, bottomDegree.toFloat(), arc, false, mPaint!!) |
||||||
|
|
||||||
|
topDegree += speedOfDegree |
||||||
|
bottomDegree += speedOfDegree |
||||||
|
if (topDegree > 360) { |
||||||
|
topDegree = topDegree - 360 |
||||||
|
} |
||||||
|
if (bottomDegree > 360) { |
||||||
|
bottomDegree = bottomDegree - 360 |
||||||
|
} |
||||||
|
|
||||||
|
if (changeBigger) { |
||||||
|
if (arc < 160) { |
||||||
|
arc += speedOfArc |
||||||
|
invalidate() |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (arc > speedOfDegree) { |
||||||
|
arc -= 2 * speedOfArc |
||||||
|
invalidate() |
||||||
|
} |
||||||
|
} |
||||||
|
if (arc >= 160 || arc <= 10) { |
||||||
|
changeBigger = !changeBigger |
||||||
|
invalidate() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun start() { |
||||||
|
startAnimator() |
||||||
|
isStart = true |
||||||
|
invalidate() |
||||||
|
} |
||||||
|
|
||||||
|
fun stop() { |
||||||
|
stopAnimator() |
||||||
|
invalidate() |
||||||
|
} |
||||||
|
|
||||||
|
private fun startAnimator() { |
||||||
|
val scaleXAnimator = ObjectAnimator.ofFloat(this, "scaleX", 0.0f, 1f) |
||||||
|
val scaleYAnimator = ObjectAnimator.ofFloat(this, "scaleY", 0.0f, 1f) |
||||||
|
scaleXAnimator.setDuration(300) |
||||||
|
scaleXAnimator.setInterpolator(LinearInterpolator()) |
||||||
|
scaleYAnimator.setDuration(300) |
||||||
|
scaleYAnimator.setInterpolator(LinearInterpolator()) |
||||||
|
val animatorSet = AnimatorSet() |
||||||
|
animatorSet.playTogether(scaleXAnimator, scaleYAnimator) |
||||||
|
animatorSet.start() |
||||||
|
} |
||||||
|
|
||||||
|
private fun stopAnimator() { |
||||||
|
val scaleXAnimator = ObjectAnimator.ofFloat(this, "scaleX", 1f, 0f) |
||||||
|
val scaleYAnimator = ObjectAnimator.ofFloat(this, "scaleY", 1f, 0f) |
||||||
|
scaleXAnimator.setDuration(300) |
||||||
|
scaleXAnimator.setInterpolator(LinearInterpolator()) |
||||||
|
scaleYAnimator.setDuration(300) |
||||||
|
scaleYAnimator.setInterpolator(LinearInterpolator()) |
||||||
|
val animatorSet = AnimatorSet() |
||||||
|
animatorSet.playTogether(scaleXAnimator, scaleYAnimator) |
||||||
|
animatorSet.addListener(object : Animator.AnimatorListener { |
||||||
|
override fun onAnimationStart(animation: Animator) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun onAnimationEnd(animation: Animator) { |
||||||
|
isStart = false |
||||||
|
} |
||||||
|
|
||||||
|
override fun onAnimationCancel(animation: Animator) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun onAnimationRepeat(animation: Animator) { |
||||||
|
|
||||||
|
} |
||||||
|
}) |
||||||
|
animatorSet.start() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
fun dpToPx(context: Context, dpVal: Float): Int { |
||||||
|
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.resources.displayMetrics).toInt() |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
|
||||||
|
private val DEFAULT_WIDTH = 6 |
||||||
|
private val DEFAULT_SHADOW_POSITION = 2 |
||||||
|
private val DEFAULT_SPEED_OF_DEGREE = 10 |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
package io.legado.app.ui.widget.image |
||||||
|
|
||||||
|
import android.annotation.SuppressLint |
||||||
|
import android.content.Context |
||||||
|
import android.graphics.Canvas |
||||||
|
import android.graphics.Path |
||||||
|
import android.util.AttributeSet |
||||||
|
|
||||||
|
|
||||||
|
class CoverImageView : androidx.appcompat.widget.AppCompatImageView { |
||||||
|
internal var width: Float = 0.toFloat() |
||||||
|
internal var height: Float = 0.toFloat() |
||||||
|
|
||||||
|
constructor(context: Context) : super(context) |
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) |
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) |
||||||
|
|
||||||
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { |
||||||
|
super.onLayout(changed, left, top, right, bottom) |
||||||
|
width = getWidth().toFloat() |
||||||
|
height = getHeight().toFloat() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { |
||||||
|
val measuredWidth = MeasureSpec.getSize(widthMeasureSpec) |
||||||
|
val measuredHeight = measuredWidth * 7 / 5 |
||||||
|
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) { |
||||||
|
if (width >= 10 && height > 10) { |
||||||
|
@SuppressLint("DrawAllocation") |
||||||
|
val path = Path() |
||||||
|
//四个圆角 |
||||||
|
path.moveTo(10f, 0f) |
||||||
|
path.lineTo(width - 10, 0f) |
||||||
|
path.quadTo(width, 0f, width, 10f) |
||||||
|
path.lineTo(width, height - 10) |
||||||
|
path.quadTo(width, height, width - 10, height) |
||||||
|
path.lineTo(10f, height) |
||||||
|
path.quadTo(0f, height, 0f, height - 10) |
||||||
|
path.lineTo(0f, 10f) |
||||||
|
path.quadTo(0f, 0f, 10f, 0f) |
||||||
|
|
||||||
|
canvas.clipPath(path) |
||||||
|
} |
||||||
|
super.onDraw(canvas) |
||||||
|
} |
||||||
|
|
||||||
|
fun setHeight(height: Int) { |
||||||
|
val width = height * 5 / 7 |
||||||
|
minimumWidth = width |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
package io.legado.app.ui.widget.image |
||||||
|
|
||||||
|
import android.annotation.SuppressLint |
||||||
|
import android.content.Context |
||||||
|
import android.graphics.Canvas |
||||||
|
import android.graphics.Path |
||||||
|
import android.util.AttributeSet |
||||||
|
import androidx.appcompat.widget.AppCompatImageView |
||||||
|
import io.legado.app.R |
||||||
|
|
||||||
|
class FilletImageView : AppCompatImageView { |
||||||
|
internal var width: Float = 0.toFloat() |
||||||
|
internal var height: Float = 0.toFloat() |
||||||
|
private var leftTopRadius: Int = 0 |
||||||
|
private var rightTopRadius: Int = 0 |
||||||
|
private var rightBottomRadius: Int = 0 |
||||||
|
private var leftBottomRadius: Int = 0 |
||||||
|
|
||||||
|
constructor(context: Context) : super(context) |
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { |
||||||
|
init(context, attrs) |
||||||
|
} |
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { |
||||||
|
init(context, attrs) |
||||||
|
} |
||||||
|
|
||||||
|
private fun init(context: Context, attrs: AttributeSet) { |
||||||
|
// 读取配置 |
||||||
|
val array = context.obtainStyledAttributes(attrs, R.styleable.FilletImageView) |
||||||
|
val defaultRadius = 5 |
||||||
|
val radius = array.getDimensionPixelOffset(R.styleable.FilletImageView_radius, defaultRadius) |
||||||
|
leftTopRadius = array.getDimensionPixelOffset(R.styleable.FilletImageView_left_top_radius, defaultRadius) |
||||||
|
rightTopRadius = array.getDimensionPixelOffset(R.styleable.FilletImageView_right_top_radius, defaultRadius) |
||||||
|
rightBottomRadius = |
||||||
|
array.getDimensionPixelOffset(R.styleable.FilletImageView_right_bottom_radius, defaultRadius) |
||||||
|
leftBottomRadius = array.getDimensionPixelOffset(R.styleable.FilletImageView_left_bottom_radius, defaultRadius) |
||||||
|
|
||||||
|
//如果四个角的值没有设置,那么就使用通用的radius的值。 |
||||||
|
if (defaultRadius == leftTopRadius) { |
||||||
|
leftTopRadius = radius |
||||||
|
} |
||||||
|
if (defaultRadius == rightTopRadius) { |
||||||
|
rightTopRadius = radius |
||||||
|
} |
||||||
|
if (defaultRadius == rightBottomRadius) { |
||||||
|
rightBottomRadius = radius |
||||||
|
} |
||||||
|
if (defaultRadius == leftBottomRadius) { |
||||||
|
leftBottomRadius = radius |
||||||
|
} |
||||||
|
array.recycle() |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { |
||||||
|
super.onLayout(changed, left, top, right, bottom) |
||||||
|
width = getWidth().toFloat() |
||||||
|
height = getHeight().toFloat() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) { |
||||||
|
//这里做下判断,只有图片的宽高大于设置的圆角距离的时候才进行裁剪 |
||||||
|
val maxLeft = Math.max(leftTopRadius, leftBottomRadius) |
||||||
|
val maxRight = Math.max(rightTopRadius, rightBottomRadius) |
||||||
|
val minWidth = maxLeft + maxRight |
||||||
|
val maxTop = Math.max(leftTopRadius, rightTopRadius) |
||||||
|
val maxBottom = Math.max(leftBottomRadius, rightBottomRadius) |
||||||
|
val minHeight = maxTop + maxBottom |
||||||
|
if (width >= minWidth && height > minHeight) { |
||||||
|
@SuppressLint("DrawAllocation") val path = Path() |
||||||
|
//四个角:右上,右下,左下,左上 |
||||||
|
path.moveTo(leftTopRadius.toFloat(), 0f) |
||||||
|
path.lineTo(width - rightTopRadius, 0f) |
||||||
|
path.quadTo(width, 0f, width, rightTopRadius.toFloat()) |
||||||
|
|
||||||
|
path.lineTo(width, height - rightBottomRadius) |
||||||
|
path.quadTo(width, height, width - rightBottomRadius, height) |
||||||
|
|
||||||
|
path.lineTo(leftBottomRadius.toFloat(), height) |
||||||
|
path.quadTo(0f, height, 0f, height - leftBottomRadius) |
||||||
|
|
||||||
|
path.lineTo(0f, leftTopRadius.toFloat()) |
||||||
|
path.quadTo(0f, 0f, leftTopRadius.toFloat(), 0f) |
||||||
|
|
||||||
|
canvas.clipPath(path) |
||||||
|
} |
||||||
|
super.onDraw(canvas) |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,5 +1,7 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" |
<ripple xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
android:color="@color/btn_bg_press_2"> |
android:color="@color/btn_bg_press_2"> |
||||||
<item android:id="@android:id/mask" android:drawable="@color/btn_bg_press_2"/> |
<item |
||||||
|
android:id="@android:id/mask" |
||||||
|
android:drawable="@color/btn_bg_press_2" /> |
||||||
</ripple> |
</ripple> |
@ -1,6 +1,7 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
android:shape="rectangle"> |
android:shape="rectangle"> |
||||||
<stroke android:width="1dp" |
<stroke |
||||||
|
android:width="1dp" |
||||||
android:color="@color/btn_bg_press" /> |
android:color="@color/btn_bg_press" /> |
||||||
</shape> |
</shape> |
@ -1,10 +1,7 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" |
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:ignore="PrivateResource"> |
||||||
tools:ignore="PrivateResource"> |
<item android:drawable="@drawable/abc_textfield_search_activated_mtrl_alpha" android:state_enabled="true" android:state_focused="true" /> |
||||||
<item android:drawable="@drawable/abc_textfield_search_activated_mtrl_alpha" android:state_enabled="true" |
<item android:drawable="@drawable/abc_textfield_search_activated_mtrl_alpha" android:state_activated="true" android:state_enabled="true" /> |
||||||
android:state_focused="true"/> |
|
||||||
<item android:drawable="@drawable/abc_textfield_search_activated_mtrl_alpha" android:state_activated="true" |
|
||||||
android:state_enabled="true"/> |
|
||||||
<item android:drawable="@drawable/abc_textfield_search_default_mtrl_alpha" android:state_enabled="true" /> |
<item android:drawable="@drawable/abc_textfield_search_default_mtrl_alpha" android:state_enabled="true" /> |
||||||
<item android:drawable="@drawable/abc_textfield_search_default_mtrl_alpha" /> |
<item android:drawable="@drawable/abc_textfield_search_default_mtrl_alpha" /> |
||||||
</selector> |
</selector> |
@ -1,19 +1,32 @@ |
|||||||
<vector android:autoMirrored="true" android:height="24dp" |
<vector android:autoMirrored="true" |
||||||
android:viewportHeight="48" android:viewportWidth="48" |
android:height="24dp" |
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
android:viewportHeight="48" |
||||||
<path android:fillColor="#373838" |
android:viewportWidth="48" |
||||||
|
android:width="24dp" |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<path |
||||||
|
android:fillColor="#373838" |
||||||
android:pathData="M20.4,12.2c0,-0.3 -0.1,-0.5 -0.3,-0.7c-0.4,-0.4 -1.1,-0.4 -1.5,0L6.9,23.3c-0.2,0.2 -0.3,0.5 -0.3,0.7c0,0.1 0,0.3 0.1,0.4c0,0.1 0.1,0.2 0.2,0.3c0,0 0,0 0,0.1l0,0c0,0 0,0 0,0c0,0 0,0 0,0l11.8,11.8c0.2,0.2 0.5,0.3 0.7,0.3c0.3,0 0.5,-0.1 0.7,-0.3c0.2,-0.2 0.3,-0.5 0.3,-0.7s-0.1,-0.5 -0.3,-0.7l-11,-11l11,-11C20.3,12.8 20.4,12.5 20.4,12.2z" |
android:pathData="M20.4,12.2c0,-0.3 -0.1,-0.5 -0.3,-0.7c-0.4,-0.4 -1.1,-0.4 -1.5,0L6.9,23.3c-0.2,0.2 -0.3,0.5 -0.3,0.7c0,0.1 0,0.3 0.1,0.4c0,0.1 0.1,0.2 0.2,0.3c0,0 0,0 0,0.1l0,0c0,0 0,0 0,0c0,0 0,0 0,0l11.8,11.8c0.2,0.2 0.5,0.3 0.7,0.3c0.3,0 0.5,-0.1 0.7,-0.3c0.2,-0.2 0.3,-0.5 0.3,-0.7s-0.1,-0.5 -0.3,-0.7l-11,-11l11,-11C20.3,12.8 20.4,12.5 20.4,12.2z" |
||||||
android:strokeColor="#39393A" android:strokeWidth="1"/> |
android:strokeColor="#39393A" |
||||||
<path android:fillColor="#373838" |
android:strokeWidth="1" /> |
||||||
|
<path |
||||||
|
android:fillColor="#373838" |
||||||
android:pathData="M41.1,23.3L41.1,23.3L41.1,23.3L41.1,23.3L29.3,11.5c-0.2,-0.2 -0.5,-0.3 -0.7,-0.3c-0.3,0 -0.5,0.1 -0.7,0.3c-0.2,0.2 -0.3,0.5 -0.3,0.7c0,0.3 0.1,0.5 0.3,0.7l11,11l-11,11c-0.2,0.2 -0.3,0.5 -0.3,0.7c0,0.3 0.1,0.5 0.3,0.7c0.4,0.4 1.1,0.4 1.5,0l11.8,-11.8c0.2,-0.2 0.3,-0.5 0.3,-0.7C41.4,23.7 41.3,23.5 41.1,23.3z" |
android:pathData="M41.1,23.3L41.1,23.3L41.1,23.3L41.1,23.3L29.3,11.5c-0.2,-0.2 -0.5,-0.3 -0.7,-0.3c-0.3,0 -0.5,0.1 -0.7,0.3c-0.2,0.2 -0.3,0.5 -0.3,0.7c0,0.3 0.1,0.5 0.3,0.7l11,11l-11,11c-0.2,0.2 -0.3,0.5 -0.3,0.7c0,0.3 0.1,0.5 0.3,0.7c0.4,0.4 1.1,0.4 1.5,0l11.8,-11.8c0.2,-0.2 0.3,-0.5 0.3,-0.7C41.4,23.7 41.3,23.5 41.1,23.3z" |
||||||
android:strokeColor="#39393A" android:strokeWidth="1"/> |
android:strokeColor="#39393A" |
||||||
<path android:fillColor="#373838" |
android:strokeWidth="1" /> |
||||||
|
<path |
||||||
|
android:fillColor="#373838" |
||||||
android:pathData="M16.2,24c0,0.7 0.6,1.3 1.3,1.3c0.7,0 1.3,-0.6 1.3,-1.3c0,-0.7 -0.6,-1.3 -1.3,-1.3C16.7,22.7 16.2,23.3 16.2,24z" |
android:pathData="M16.2,24c0,0.7 0.6,1.3 1.3,1.3c0.7,0 1.3,-0.6 1.3,-1.3c0,-0.7 -0.6,-1.3 -1.3,-1.3C16.7,22.7 16.2,23.3 16.2,24z" |
||||||
android:strokeColor="#39393A" android:strokeWidth="1"/> |
android:strokeColor="#39393A" |
||||||
<path android:fillColor="#373838" |
android:strokeWidth="1" /> |
||||||
|
<path |
||||||
|
android:fillColor="#373838" |
||||||
android:pathData="M24,22.7c-0.7,0 -1.3,0.6 -1.3,1.3c0,0.7 0.6,1.3 1.3,1.3c0.7,0 1.3,-0.6 1.3,-1.3C25.3,23.3 24.7,22.7 24,22.7z" |
android:pathData="M24,22.7c-0.7,0 -1.3,0.6 -1.3,1.3c0,0.7 0.6,1.3 1.3,1.3c0.7,0 1.3,-0.6 1.3,-1.3C25.3,23.3 24.7,22.7 24,22.7z" |
||||||
android:strokeColor="#39393A" android:strokeWidth="1"/> |
android:strokeColor="#39393A" |
||||||
<path android:fillColor="#373838" |
android:strokeWidth="1" /> |
||||||
|
<path |
||||||
|
android:fillColor="#373838" |
||||||
android:pathData="M32.3,24c0,-0.7 -0.6,-1.3 -1.3,-1.3c-0.7,0 -1.3,0.6 -1.3,1.3c0,0.7 0.6,1.3 1.3,1.3C31.7,25.3 32.3,24.7 32.3,24z" |
android:pathData="M32.3,24c0,-0.7 -0.6,-1.3 -1.3,-1.3c-0.7,0 -1.3,0.6 -1.3,1.3c0,0.7 0.6,1.3 1.3,1.3C31.7,25.3 32.3,24.7 32.3,24z" |
||||||
android:strokeColor="#39393A" android:strokeWidth="1"/> |
android:strokeColor="#39393A" |
||||||
|
android:strokeWidth="1" /> |
||||||
</vector> |
</vector> |
||||||
|
@ -1,7 +1,13 @@ |
|||||||
<vector android:autoMirrored="true" android:height="24dp" |
<vector android:autoMirrored="true" |
||||||
android:viewportHeight="48" android:viewportWidth="48" |
android:height="24dp" |
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
android:viewportHeight="48" |
||||||
<path android:fillColor="#39393A" android:fillType="evenOdd" |
android:viewportWidth="48" |
||||||
|
android:width="24dp" |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<path |
||||||
|
android:fillColor="#39393A" |
||||||
|
android:fillType="evenOdd" |
||||||
android:pathData="M39.4,11H8.6c-0.7,0 -1.2,-0.5 -1.2,-1.2c0,-0.7 0.5,-1.2 1.2,-1.2h30.8c0.7,0 1.2,0.5 1.2,1.2C40.6,10.4 40.1,11 39.4,11zM8.6,18.1h21.3c0.7,0 1.2,0.5 1.2,1.2c0,0.7 -0.5,1.2 -1.2,1.2H8.6c-0.7,0 -1.2,-0.5 -1.2,-1.2C7.4,18.6 7.9,18.1 8.6,18.1zM8.6,27.6h30.8c0.7,0 1.2,0.5 1.2,1.2c0,0.7 -0.5,1.2 -1.2,1.2H8.6c-0.7,0 -1.2,-0.5 -1.2,-1.2C7.4,28.1 7.9,27.6 8.6,27.6zM8.6,37h17.8c0.7,0 1.2,0.5 1.2,1.2c0,0.7 -0.5,1.2 -1.2,1.2H8.6c-0.7,0 -1.2,-0.5 -1.2,-1.2C7.4,37.6 7.9,37 8.6,37z" |
android:pathData="M39.4,11H8.6c-0.7,0 -1.2,-0.5 -1.2,-1.2c0,-0.7 0.5,-1.2 1.2,-1.2h30.8c0.7,0 1.2,0.5 1.2,1.2C40.6,10.4 40.1,11 39.4,11zM8.6,18.1h21.3c0.7,0 1.2,0.5 1.2,1.2c0,0.7 -0.5,1.2 -1.2,1.2H8.6c-0.7,0 -1.2,-0.5 -1.2,-1.2C7.4,18.6 7.9,18.1 8.6,18.1zM8.6,27.6h30.8c0.7,0 1.2,0.5 1.2,1.2c0,0.7 -0.5,1.2 -1.2,1.2H8.6c-0.7,0 -1.2,-0.5 -1.2,-1.2C7.4,28.1 7.9,27.6 8.6,27.6zM8.6,37h17.8c0.7,0 1.2,0.5 1.2,1.2c0,0.7 -0.5,1.2 -1.2,1.2H8.6c-0.7,0 -1.2,-0.5 -1.2,-1.2C7.4,37.6 7.9,37 8.6,37z" |
||||||
android:strokeColor="#39393A" android:strokeWidth="1"/> |
android:strokeColor="#39393A" |
||||||
|
android:strokeWidth="1" /> |
||||||
</vector> |
</vector> |
||||||
|
@ -1,74 +1,170 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<vector |
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
android:height="108dp" |
android:height="108dp" |
||||||
android:width="108dp" |
android:width="108dp" |
||||||
android:viewportHeight="108" |
android:viewportHeight="108" |
||||||
android:viewportWidth="108"> |
android:viewportWidth="108"> |
||||||
<path android:fillColor="#008577" |
<path |
||||||
|
android:fillColor="#008577" |
||||||
android:pathData="M0,0h108v108h-108z" /> |
android:pathData="M0,0h108v108h-108z" /> |
||||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108" |
<path |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:fillColor="#00000000" |
||||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108" |
android:pathData="M9,0L9,108" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeColor="#33FFFFFF" |
||||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108" |
android:strokeWidth="0.8" /> |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
<path |
||||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108" |
android:fillColor="#00000000" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:pathData="M19,0L19,108" |
||||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108" |
android:strokeColor="#33FFFFFF" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeWidth="0.8" /> |
||||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108" |
<path |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:fillColor="#00000000" |
||||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108" |
android:pathData="M29,0L29,108" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeColor="#33FFFFFF" |
||||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108" |
android:strokeWidth="0.8" /> |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
<path |
||||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108" |
android:fillColor="#00000000" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:pathData="M39,0L39,108" |
||||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108" |
android:strokeColor="#33FFFFFF" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeWidth="0.8" /> |
||||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9" |
<path |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:fillColor="#00000000" |
||||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19" |
android:pathData="M49,0L49,108" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeColor="#33FFFFFF" |
||||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29" |
android:strokeWidth="0.8" /> |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
<path |
||||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39" |
android:fillColor="#00000000" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:pathData="M59,0L59,108" |
||||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49" |
android:strokeColor="#33FFFFFF" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeWidth="0.8" /> |
||||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59" |
<path |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:fillColor="#00000000" |
||||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69" |
android:pathData="M69,0L69,108" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeColor="#33FFFFFF" |
||||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79" |
android:strokeWidth="0.8" /> |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
<path |
||||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89" |
android:fillColor="#00000000" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:pathData="M79,0L79,108" |
||||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99" |
android:strokeColor="#33FFFFFF" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeWidth="0.8" /> |
||||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29" |
<path |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:fillColor="#00000000" |
||||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39" |
android:pathData="M89,0L89,108" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeColor="#33FFFFFF" |
||||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49" |
android:strokeWidth="0.8" /> |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
<path |
||||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59" |
android:fillColor="#00000000" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:pathData="M99,0L99,108" |
||||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69" |
android:strokeColor="#33FFFFFF" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeWidth="0.8" /> |
||||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79" |
<path |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:fillColor="#00000000" |
||||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89" |
android:pathData="M0,9L108,9" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeColor="#33FFFFFF" |
||||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89" |
android:strokeWidth="0.8" /> |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
<path |
||||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89" |
android:fillColor="#00000000" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:pathData="M0,19L108,19" |
||||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89" |
android:strokeColor="#33FFFFFF" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeWidth="0.8" /> |
||||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89" |
<path |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:fillColor="#00000000" |
||||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89" |
android:pathData="M0,29L108,29" |
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/> |
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M0,39L108,39" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M0,49L108,49" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M0,59L108,59" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M0,69L108,69" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M0,79L108,79" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M0,89L108,89" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M0,99L108,99" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M19,29L89,29" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M19,39L89,39" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M19,49L89,49" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M19,59L89,59" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M19,69L89,69" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M19,79L89,79" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M29,19L29,89" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M39,19L39,89" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M49,19L49,89" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M59,19L59,89" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M69,19L69,89" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
|
<path |
||||||
|
android:fillColor="#00000000" |
||||||
|
android:pathData="M79,19L79,89" |
||||||
|
android:strokeColor="#33FFFFFF" |
||||||
|
android:strokeWidth="0.8" /> |
||||||
</vector> |
</vector> |
||||||
|
@ -1,10 +1,7 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
<item android:state_pressed="true" |
<item android:state_pressed="true" android:drawable="@color/btn_bg_press" /> |
||||||
android:drawable="@color/btn_bg_press"/> |
<item android:state_selected="true" android:drawable="@color/btn_bg_press" /> |
||||||
<item android:state_selected="true" |
|
||||||
android:drawable="@color/btn_bg_press"/> |
|
||||||
|
|
||||||
<item |
<item android:drawable="@color/background" /> |
||||||
android:drawable="@color/background"/> |
|
||||||
</selector> |
</selector> |
@ -1,17 +1,24 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<androidx.constraintlayout.widget.ConstraintLayout |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
android:orientation="vertical" |
|
||||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||||
android:layout_height="match_parent"> |
android:layout_height="match_parent" |
||||||
|
android:orientation="vertical"> |
||||||
|
|
||||||
<io.legado.app.ui.widget.TitleBar |
<io.legado.app.ui.widget.TitleBar |
||||||
android:id="@+id/title_bar" |
android:id="@+id/title_bar" |
||||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||||
android:layout_height="wrap_content" |
android:layout_height="wrap_content" |
||||||
app:attachToActivity="false" |
app:attachToActivity="false" |
||||||
app:layout_constraintTop_toTopOf="parent" |
|
||||||
app:title="@string/bookshelf" /> |
app:title="@string/bookshelf" /> |
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout> |
<androidx.recyclerview.widget.RecyclerView |
||||||
|
android:id="@+id/rv_book_group" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" /> |
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView |
||||||
|
android:id="@+id/rv_bookshelf" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" /> |
||||||
|
</LinearLayout> |
@ -0,0 +1,5 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_margin="5dp" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" /> |
@ -0,0 +1,36 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
|
android:background="@color/background" |
||||||
|
android:padding="16dp" |
||||||
|
android:orientation="horizontal" |
||||||
|
android:gravity="center_vertical"> |
||||||
|
|
||||||
|
<io.legado.app.lib.theme.view.ATECheckBox |
||||||
|
android:id="@+id/cb_book_source" |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_gravity="center" |
||||||
|
android:layout_weight="1" |
||||||
|
android:singleLine="true" |
||||||
|
android:text="" |
||||||
|
android:textColor="@color/tv_text_default" /> |
||||||
|
|
||||||
|
<io.legado.app.lib.theme.view.ATESwitch |
||||||
|
android:id="@+id/sw_enabled" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" /> |
||||||
|
|
||||||
|
<ImageView |
||||||
|
android:id="@+id/iv_more" |
||||||
|
android:layout_width="24dp" |
||||||
|
android:layout_height="24dp" |
||||||
|
android:layout_gravity="center" |
||||||
|
android:background="@drawable/bg_ib_pre_round" |
||||||
|
android:layout_margin="6dp" |
||||||
|
android:contentDescription="@string/to_top" |
||||||
|
android:src="@drawable/ic_more_vert" |
||||||
|
app:tint="@color/tv_text_default" /> |
||||||
|
</LinearLayout> |
@ -0,0 +1,175 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||||
|
android:id="@+id/cv_content" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:clickable="true" |
||||||
|
android:focusable="true" |
||||||
|
android:foreground="?android:attr/selectableItemBackground" |
||||||
|
tools:ignore="UnusedAttribute"> |
||||||
|
|
||||||
|
<io.legado.app.ui.widget.image.CoverImageView |
||||||
|
android:id="@+id/iv_cover" |
||||||
|
android:layout_width="60dp" |
||||||
|
android:layout_height="80dp" |
||||||
|
android:layout_margin="8dp" |
||||||
|
android:contentDescription="@string/img_cover" |
||||||
|
android:scaleType="centerCrop" |
||||||
|
android:src="@drawable/img_cover_default" |
||||||
|
android:transitionName="img_cover" |
||||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||||
|
app:layout_constraintLeft_toLeftOf="parent" |
||||||
|
app:layout_constraintTop_toTopOf="parent" |
||||||
|
tools:ignore="UnusedAttribute" /> |
||||||
|
|
||||||
|
<FrameLayout |
||||||
|
android:id="@+id/fl_has_new" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
app:layout_constraintRight_toRightOf="parent" |
||||||
|
app:layout_constraintTop_toTopOf="@id/tv_name"> |
||||||
|
|
||||||
|
<io.legado.app.ui.widget.BadgeView |
||||||
|
android:id="@+id/bv_unread" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_gravity="right" |
||||||
|
android:layout_margin="5dp" |
||||||
|
android:includeFontPadding="false" |
||||||
|
tools:ignore="RtlHardcoded" /> |
||||||
|
|
||||||
|
<com.victor.loading.rotate.RotateLoading |
||||||
|
android:id="@+id/rl_loading" |
||||||
|
android:layout_width="26dp" |
||||||
|
android:layout_height="26dp" |
||||||
|
android:layout_gravity="right" |
||||||
|
android:visibility="invisible" |
||||||
|
app:loading_color="@color/colorAccent" |
||||||
|
app:loading_width="2dp" |
||||||
|
tools:ignore="RtlHardcoded" /> |
||||||
|
</FrameLayout> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/tv_name" |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_marginLeft="8dp" |
||||||
|
android:layout_marginTop="8dp" |
||||||
|
android:includeFontPadding="false" |
||||||
|
android:paddingLeft="4dp" |
||||||
|
android:singleLine="true" |
||||||
|
android:text="@string/book_name" |
||||||
|
android:textColor="@color/tv_text_default" |
||||||
|
android:textSize="16sp" |
||||||
|
app:layout_constraintBottom_toTopOf="@+id/tv_author" |
||||||
|
app:layout_constraintLeft_toRightOf="@+id/iv_cover" |
||||||
|
app:layout_constraintRight_toLeftOf="@id/fl_has_new" |
||||||
|
app:layout_constraintTop_toTopOf="parent" |
||||||
|
tools:ignore="RtlHardcoded,RtlSymmetry" /> |
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView |
||||||
|
android:id="@+id/iv_author" |
||||||
|
android:layout_width="@dimen/desc_icon_size" |
||||||
|
android:layout_height="@dimen/desc_icon_size" |
||||||
|
android:layout_marginLeft="8dp" |
||||||
|
android:contentDescription="@string/author" |
||||||
|
android:paddingStart="2dp" |
||||||
|
android:paddingEnd="2dp" |
||||||
|
android:src="@drawable/ic_author" |
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/tv_author" |
||||||
|
app:layout_constraintLeft_toRightOf="@+id/iv_cover" |
||||||
|
app:layout_constraintTop_toTopOf="@+id/tv_author" |
||||||
|
app:tint="@color/tv_text_secondary" |
||||||
|
tools:ignore="RtlHardcoded,RtlSymmetry" /> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/tv_author" |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:includeFontPadding="false" |
||||||
|
android:maxLines="1" |
||||||
|
android:paddingEnd="6dp" |
||||||
|
android:text="@string/author" |
||||||
|
android:textColor="@color/tv_text_secondary" |
||||||
|
android:textSize="13sp" |
||||||
|
app:layout_constraintBottom_toTopOf="@+id/tv_read" |
||||||
|
app:layout_constraintLeft_toRightOf="@+id/iv_author" |
||||||
|
app:layout_constraintRight_toLeftOf="@id/fl_has_new" |
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_name" |
||||||
|
tools:ignore="RtlSymmetry" /> |
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView |
||||||
|
android:id="@+id/iv_read" |
||||||
|
android:layout_width="@dimen/desc_icon_size" |
||||||
|
android:layout_height="@dimen/desc_icon_size" |
||||||
|
android:layout_marginLeft="8dp" |
||||||
|
android:contentDescription="@string/read_dur_progress" |
||||||
|
android:paddingStart="2dp" |
||||||
|
android:paddingEnd="2dp" |
||||||
|
android:src="@drawable/ic_history" |
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/tv_read" |
||||||
|
app:layout_constraintLeft_toRightOf="@+id/iv_cover" |
||||||
|
app:layout_constraintTop_toTopOf="@+id/tv_read" |
||||||
|
app:tint="@color/tv_text_secondary" |
||||||
|
tools:ignore="RtlHardcoded,RtlSymmetry" /> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/tv_read" |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:includeFontPadding="false" |
||||||
|
android:singleLine="true" |
||||||
|
android:text="@string/read_dur_progress" |
||||||
|
android:textColor="@color/tv_text_secondary" |
||||||
|
android:textSize="13sp" |
||||||
|
app:layout_constraintBottom_toTopOf="@id/tv_last" |
||||||
|
app:layout_constraintLeft_toRightOf="@+id/iv_read" |
||||||
|
app:layout_constraintRight_toLeftOf="@id/fl_has_new" |
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_author" /> |
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView |
||||||
|
android:id="@+id/iv_last" |
||||||
|
android:layout_width="@dimen/desc_icon_size" |
||||||
|
android:layout_height="@dimen/desc_icon_size" |
||||||
|
android:layout_marginLeft="8dp" |
||||||
|
android:contentDescription="@string/book_search_last" |
||||||
|
android:paddingStart="2dp" |
||||||
|
android:paddingEnd="2dp" |
||||||
|
android:src="@drawable/ic_book_last" |
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/tv_last" |
||||||
|
app:layout_constraintLeft_toRightOf="@+id/iv_cover" |
||||||
|
app:layout_constraintTop_toTopOf="@+id/tv_last" |
||||||
|
app:tint="@color/tv_text_secondary" |
||||||
|
tools:ignore="RtlHardcoded,RtlSymmetry" /> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/tv_last" |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_marginBottom="8dp" |
||||||
|
android:singleLine="true" |
||||||
|
android:text="@string/book_search_last" |
||||||
|
android:textColor="@color/tv_text_secondary" |
||||||
|
android:textSize="13sp" |
||||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||||
|
app:layout_constraintLeft_toRightOf="@+id/iv_last" |
||||||
|
app:layout_constraintRight_toLeftOf="@id/fl_has_new" |
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_read" /> |
||||||
|
|
||||||
|
<View |
||||||
|
android:id="@+id/vw_select" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="0dp" |
||||||
|
android:visibility="gone" |
||||||
|
app:layout_constraintTop_toTopOf="parent" |
||||||
|
app:layout_constraintBottom_toBottomOf="parent" /> |
||||||
|
|
||||||
|
<View |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="0.5dp" |
||||||
|
android:background="@color/btn_bg_press" |
||||||
|
app:layout_constraintBottom_toBottomOf="parent" /> |
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
@ -0,0 +1,11 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<io.legado.app.lib.theme.view.ATETextInputLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:id="@+id/textInputLayout" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content"> |
||||||
|
|
||||||
|
<io.legado.app.lib.theme.view.ATEEditText |
||||||
|
android:id="@+id/editText" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" /> |
||||||
|
</io.legado.app.lib.theme.view.ATETextInputLayout> |
@ -0,0 +1,17 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
|
android:id="@+id/toolbar" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:theme="?attr/actionBarStyle" |
||||||
|
app:popupTheme="@style/AppTheme.PopupOverlay"> |
||||||
|
|
||||||
|
<androidx.appcompat.widget.SearchView |
||||||
|
android:id="@+id/search_view" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:visibility="gone" |
||||||
|
app:defaultQueryHint="搜索" /> |
||||||
|
|
||||||
|
</androidx.appcompat.widget.Toolbar> |
@ -0,0 +1,51 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_save" |
||||||
|
android:icon="@drawable/ic_save" |
||||||
|
android:title="@string/action_save" |
||||||
|
app:showAsAction="ifRoom" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_login" |
||||||
|
android:title="@string/login" |
||||||
|
app:showAsAction="never" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_copy_source" |
||||||
|
android:title="@string/copy_source" |
||||||
|
app:showAsAction="never" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_paste_source" |
||||||
|
android:title="@string/paste_source" |
||||||
|
app:showAsAction="never" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_qr_code_camera" |
||||||
|
android:title="@string/import_by_qr_code" |
||||||
|
app:showAsAction="never" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_share_it" |
||||||
|
android:title="@string/qr_share" |
||||||
|
app:showAsAction="never" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_share_wifi" |
||||||
|
android:title="@string/wifi_share" |
||||||
|
app:showAsAction="never" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_debug_source" |
||||||
|
android:title="@string/debug_source" |
||||||
|
app:showAsAction="never" /> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_rule_summary" |
||||||
|
android:title="@string/rule_summary" |
||||||
|
app:showAsAction="never" /> |
||||||
|
|
||||||
|
</menu> |
@ -1,4 +1,10 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<resources> |
<resources> |
||||||
|
<item name="menu_edit" type="id" /> |
||||||
|
<item name="menu_del" type="id" /> |
||||||
|
<item name="menu_top" type="id" /> |
||||||
|
|
||||||
|
<item name="tag1" type="id" /> |
||||||
|
<item name="tag2" type="id" /> |
||||||
|
|
||||||
</resources> |
</resources> |
@ -1,6 +1,5 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<androidx.preference.PreferenceScreen |
<androidx.preference.PreferenceScreen> |
||||||
> |
|
||||||
|
|
||||||
|
|
||||||
</androidx.preference.PreferenceScreen> |
</androidx.preference.PreferenceScreen> |
Loading…
Reference in new issue