# 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 |
@ -1,6 +1,6 @@ |
||||
package io.legado.app.data.entities.rule |
||||
|
||||
data class ContentRule ( |
||||
data class ContentRule( |
||||
var fulltext: Rule, |
||||
var resourceUrl: Rule, |
||||
var nextUrl: Rule |
||||
|
@ -1,6 +1,6 @@ |
||||
package io.legado.app.data.entities.rule |
||||
|
||||
data class PutRule ( |
||||
data class PutRule( |
||||
var selector: Rule, |
||||
var key: String |
||||
) |
@ -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 |
||||
|
||||
import android.content.SharedPreferences |
||||
import android.os.Bundle |
||||
import androidx.preference.ListPreference |
||||
import androidx.preference.Preference |
||||
import androidx.preference.PreferenceFragmentCompat |
||||
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?) { |
||||
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 |
||||
|
||||
import android.os.Bundle |
||||
import androidx.preference.ListPreference |
||||
import androidx.preference.Preference |
||||
import androidx.preference.PreferenceFragmentCompat |
||||
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?) { |
||||
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 |
||||
|
||||
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"?> |
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" |
||||
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> |
@ -1,3 +1,3 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:color="@color/btn_bg_press"/> |
||||
android:color="@color/btn_bg_press" /> |
@ -1,6 +1,7 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:shape="rectangle"> |
||||
<stroke android:width="1dp" |
||||
android:color="@color/btn_bg_press"/> |
||||
<stroke |
||||
android:width="1dp" |
||||
android:color="@color/btn_bg_press" /> |
||||
</shape> |
@ -1,5 +1,5 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:drawable="@android:color/transparent" android:state_pressed="false"/> |
||||
<item android:drawable="@color/btn_bg_press" android:state_pressed="true"/> |
||||
<item android:drawable="@android:color/transparent" android:state_pressed="false" /> |
||||
<item android:drawable="@color/btn_bg_press" android:state_pressed="true" /> |
||||
</selector> |
@ -1,4 +1,4 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:drawable="@android:color/transparent"/> |
||||
<item android:drawable="@android:color/transparent" /> |
||||
</selector> |
@ -1,10 +1,7 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" |
||||
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_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"/> |
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" 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_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" /> |
||||
</selector> |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue