feat: 吧内搜索筛选、排序、历史记录
This commit is contained in:
parent
b94d638928
commit
867d47fa59
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<litepal>
|
<litepal>
|
||||||
<dbname value="tblite" />
|
<dbname value="tblite" />
|
||||||
<version value="23" />
|
<version value="24" />
|
||||||
<list>
|
<list>
|
||||||
<mapping class="com.huanchengfly.tieba.post.models.database.Account" />
|
<mapping class="com.huanchengfly.tieba.post.models.database.Account" />
|
||||||
<mapping class="com.huanchengfly.tieba.post.models.database.Draft" />
|
<mapping class="com.huanchengfly.tieba.post.models.database.Draft" />
|
||||||
|
|
@ -9,5 +9,6 @@
|
||||||
<mapping class="com.huanchengfly.tieba.post.models.database.Block" />
|
<mapping class="com.huanchengfly.tieba.post.models.database.Block" />
|
||||||
<mapping class="com.huanchengfly.tieba.post.models.database.TopForum" />
|
<mapping class="com.huanchengfly.tieba.post.models.database.TopForum" />
|
||||||
<mapping class="com.huanchengfly.tieba.post.models.database.SearchHistory" />
|
<mapping class="com.huanchengfly.tieba.post.models.database.SearchHistory" />
|
||||||
|
<mapping class="com.huanchengfly.tieba.post.models.database.SearchPostHistory" />
|
||||||
</list>
|
</list>
|
||||||
</litepal>
|
</litepal>
|
||||||
|
|
@ -17,7 +17,6 @@ import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
|
@ -280,7 +279,6 @@ class ForumActivity : BaseActivity(), View.OnClickListener, OnRefreshedListener,
|
||||||
} else {
|
} else {
|
||||||
1f
|
1f
|
||||||
}
|
}
|
||||||
Log.i("OffsetChange", "changed")
|
|
||||||
if (titleVisible != isTitleVisible) {
|
if (titleVisible != isTitleVisible) {
|
||||||
title = if (isTitleVisible) getString(R.string.title_forum, forumName) else null
|
title = if (isTitleVisible) getString(R.string.title_forum, forumName) else null
|
||||||
titleVisible = isTitleVisible
|
titleVisible = isTitleVisible
|
||||||
|
|
|
||||||
|
|
@ -2,28 +2,52 @@ package com.huanchengfly.tieba.post.activities
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.LinearInterpolator
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.widget.ListPopupWindow
|
||||||
|
import androidx.core.widget.TextViewCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import cn.dreamtobe.kpswitch.util.KeyboardUtil
|
import cn.dreamtobe.kpswitch.util.KeyboardUtil
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.huanchengfly.tieba.post.R
|
import com.huanchengfly.tieba.post.R
|
||||||
import com.huanchengfly.tieba.post.adapters.SearchPostAdapter
|
import com.huanchengfly.tieba.post.adapters.SearchPostAdapter
|
||||||
|
import com.huanchengfly.tieba.post.adapters.SearchPostHistoryAdapter
|
||||||
|
import com.huanchengfly.tieba.post.adapters.SingleChooseAdapter
|
||||||
|
import com.huanchengfly.tieba.post.adapters.SingleLayoutAdapter
|
||||||
import com.huanchengfly.tieba.post.api.TiebaApi.getInstance
|
import com.huanchengfly.tieba.post.api.TiebaApi.getInstance
|
||||||
import com.huanchengfly.tieba.post.api.models.SearchPostBean
|
import com.huanchengfly.tieba.post.api.models.SearchPostBean
|
||||||
|
import com.huanchengfly.tieba.post.api.retrofit.doIfFailure
|
||||||
|
import com.huanchengfly.tieba.post.api.retrofit.doIfSuccess
|
||||||
|
import com.huanchengfly.tieba.post.components.AutoLineFeedLayoutManager
|
||||||
import com.huanchengfly.tieba.post.components.MyLinearLayoutManager
|
import com.huanchengfly.tieba.post.components.MyLinearLayoutManager
|
||||||
|
import com.huanchengfly.tieba.post.components.MyViewHolder
|
||||||
import com.huanchengfly.tieba.post.components.dividers.SpacesItemDecoration
|
import com.huanchengfly.tieba.post.components.dividers.SpacesItemDecoration
|
||||||
import com.huanchengfly.tieba.post.dpToPx
|
import com.huanchengfly.tieba.post.dpToPx
|
||||||
|
import com.huanchengfly.tieba.post.models.database.SearchPostHistory
|
||||||
|
import com.huanchengfly.tieba.post.toastShort
|
||||||
|
import com.huanchengfly.tieba.post.utils.PopupUtil
|
||||||
import com.huanchengfly.tieba.post.utils.ThemeUtil
|
import com.huanchengfly.tieba.post.utils.ThemeUtil
|
||||||
import com.huanchengfly.tieba.post.utils.Util
|
import com.huanchengfly.tieba.post.utils.Util
|
||||||
|
import com.huanchengfly.tieba.post.utils.anim.animSet
|
||||||
import com.scwang.smart.refresh.layout.SmartRefreshLayout
|
import com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||||
|
import org.litepal.LitePal
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
|
||||||
class SearchPostActivity : BaseActivity() {
|
class SearchPostActivity : BaseActivity() {
|
||||||
|
private var state: State = State.INPUT
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidateState()
|
||||||
|
}
|
||||||
|
|
||||||
@BindView(R.id.search_post_refresh_layout)
|
@BindView(R.id.search_post_refresh_layout)
|
||||||
lateinit var refreshLayout: SmartRefreshLayout
|
lateinit var refreshLayout: SmartRefreshLayout
|
||||||
|
|
||||||
|
|
@ -36,14 +60,47 @@ class SearchPostActivity : BaseActivity() {
|
||||||
@BindView(R.id.search_edit_text)
|
@BindView(R.id.search_edit_text)
|
||||||
lateinit var editText: EditText
|
lateinit var editText: EditText
|
||||||
|
|
||||||
lateinit var searchPostAdapter: SearchPostAdapter
|
private var headerView: View? = null
|
||||||
|
|
||||||
|
private val searchPostAdapter: SearchPostAdapter by lazy { SearchPostAdapter(this) }
|
||||||
private var forumName: String? = null
|
private var forumName: String? = null
|
||||||
private var keyword: String? = null
|
private var keyword: String? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
if (value != null) refreshLayout.autoRefresh()
|
state = if (value == null) {
|
||||||
|
State.INPUT
|
||||||
|
} else {
|
||||||
|
State.SEARCH
|
||||||
|
}
|
||||||
|
if (value != null) {
|
||||||
|
refreshLayout.autoRefresh()
|
||||||
|
SearchPostHistory(value, forumName)
|
||||||
|
.saveOrUpdate("content = ?", value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private var page = 1
|
private var page = 1
|
||||||
|
private var sortMode = 1
|
||||||
|
set(value) {
|
||||||
|
val changed = field != value
|
||||||
|
field = value
|
||||||
|
if (changed) {
|
||||||
|
refreshHeader()
|
||||||
|
if (keyword != null) {
|
||||||
|
refreshLayout.autoRefresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var onlyThread = false
|
||||||
|
set(value) {
|
||||||
|
val changed = field != value
|
||||||
|
field = value
|
||||||
|
if (changed) {
|
||||||
|
refreshHeader()
|
||||||
|
if (keyword != null) {
|
||||||
|
refreshLayout.autoRefresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getLayoutId(): Int = R.layout.activity_search_post
|
override fun getLayoutId(): Int = R.layout.activity_search_post
|
||||||
|
|
||||||
|
|
@ -69,13 +126,120 @@ class SearchPostActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun invalidateState() {
|
||||||
|
when (state) {
|
||||||
|
State.INPUT -> {
|
||||||
|
refreshLayout.setEnableRefresh(false)
|
||||||
|
refreshLayout.setEnableLoadMore(false)
|
||||||
|
recyclerView.adapter = null
|
||||||
|
loadHistory()
|
||||||
|
}
|
||||||
|
State.SEARCH -> {
|
||||||
|
refreshLayout.setEnableRefresh(true)
|
||||||
|
refreshLayout.setEnableLoadMore(true)
|
||||||
|
recyclerView.adapter = searchPostAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshHeader() {
|
||||||
|
headerView?.apply {
|
||||||
|
val allBtn: TextView = findViewById(R.id.search_post_header_btn_all)
|
||||||
|
val onlyThreadBtn: TextView = findViewById(R.id.search_post_header_btn_only_thread)
|
||||||
|
val sortModeText: TextView = findViewById(R.id.search_post_header_sort_mode_title)
|
||||||
|
if (onlyThread) {
|
||||||
|
TextViewCompat.setTextAppearance(allBtn, R.style.TextAppearance_Normal)
|
||||||
|
TextViewCompat.setTextAppearance(onlyThreadBtn, R.style.TextAppearance_Bold)
|
||||||
|
} else {
|
||||||
|
TextViewCompat.setTextAppearance(allBtn, R.style.TextAppearance_Bold)
|
||||||
|
TextViewCompat.setTextAppearance(onlyThreadBtn, R.style.TextAppearance_Normal)
|
||||||
|
}
|
||||||
|
sortModeText.text = when (sortMode) {
|
||||||
|
1 -> getString(R.string.title_search_post_sort_by_time)
|
||||||
|
2 -> getString(R.string.title_search_post_sort_by_relevant)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (state == State.SEARCH) {
|
||||||
|
state = State.INPUT
|
||||||
|
KeyboardUtil.showKeyboard(editText)
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSortModeMenu(view: View) {
|
||||||
|
val arrow: ImageView = view.findViewById(R.id.search_post_header_sort_mode_arrow)
|
||||||
|
val animSet = animSet {
|
||||||
|
anim {
|
||||||
|
values = floatArrayOf(180f, 0f)
|
||||||
|
action = { value -> arrow.rotation = value as Float }
|
||||||
|
duration = 150
|
||||||
|
interpolator = LinearInterpolator()
|
||||||
|
}
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
val adapter = SingleChooseAdapter(
|
||||||
|
this,
|
||||||
|
listOf(
|
||||||
|
getString(R.string.title_search_post_sort_by_time),
|
||||||
|
getString(R.string.title_search_post_sort_by_relevant)
|
||||||
|
),
|
||||||
|
sortMode - 1
|
||||||
|
)
|
||||||
|
ListPopupWindow(this).apply {
|
||||||
|
PopupUtil.replaceBackground(this)
|
||||||
|
anchorView = view
|
||||||
|
width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
isModal = true
|
||||||
|
setAdapter(adapter)
|
||||||
|
setOnItemClickListener { _, _, position: Int, _ ->
|
||||||
|
dismiss()
|
||||||
|
sortMode = position + 1
|
||||||
|
}
|
||||||
|
setOnDismissListener {
|
||||||
|
animSet.reverse()
|
||||||
|
}
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadHistory() {
|
||||||
|
LitePal.order("timestamp DESC").findAsync(SearchPostHistory::class.java)
|
||||||
|
.listen { histories ->
|
||||||
|
if (state == State.INPUT) {
|
||||||
|
recyclerView.post {
|
||||||
|
recyclerView.adapter = SearchHistoryDelegateAdapter(histories)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
refreshLayout.apply {
|
refreshLayout.apply {
|
||||||
ThemeUtil.setThemeForSmartRefreshLayout(this)
|
ThemeUtil.setThemeForSmartRefreshLayout(this)
|
||||||
setOnLoadMoreListener { loadMore() }
|
setOnLoadMoreListener { loadMore() }
|
||||||
setOnRefreshListener { refresh() }
|
setOnRefreshListener { refresh() }
|
||||||
}
|
}
|
||||||
searchPostAdapter = SearchPostAdapter(this)
|
searchPostAdapter.addHeaderView(R.layout.layout_search_post_header) {
|
||||||
|
headerView = this
|
||||||
|
findViewById<View>(R.id.search_post_header_sort_mode).setOnClickListener {
|
||||||
|
showSortModeMenu(
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
findViewById<View>(R.id.search_post_header_btn_all).setOnClickListener {
|
||||||
|
onlyThread = false
|
||||||
|
}
|
||||||
|
findViewById<View>(R.id.search_post_header_btn_only_thread).setOnClickListener {
|
||||||
|
onlyThread = true
|
||||||
|
}
|
||||||
|
refreshHeader()
|
||||||
|
}
|
||||||
searchPostAdapter.setOnItemClickListener { _, item, _ ->
|
searchPostAdapter.setOnItemClickListener { _, item, _ ->
|
||||||
ThreadActivity.launch(this, item.tid!!, item.pid)
|
ThreadActivity.launch(this, item.tid!!, item.pid)
|
||||||
}
|
}
|
||||||
|
|
@ -101,22 +265,22 @@ class SearchPostActivity : BaseActivity() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
page = 1
|
page = 1
|
||||||
getInstance().searchPost(keyword!!, forumName!!, false, page, 30)
|
launchIO {
|
||||||
.enqueue(object : Callback<SearchPostBean> {
|
getInstance().searchPostAsync(
|
||||||
override fun onResponse(
|
keyword!!,
|
||||||
call: Call<SearchPostBean>,
|
forumName!!,
|
||||||
response: Response<SearchPostBean>
|
onlyThread = onlyThread,
|
||||||
) {
|
sortMode = sortMode,
|
||||||
val data = response.body()
|
page = page,
|
||||||
searchPostAdapter.setData(data!!.postList)
|
pageSize = 30
|
||||||
|
)
|
||||||
|
.doIfSuccess {
|
||||||
|
searchPostAdapter.setData(it.postList)
|
||||||
refreshLayout.finishRefresh()
|
refreshLayout.finishRefresh()
|
||||||
refreshLayout.setNoMoreData("1" != data.page!!.hasMore)
|
refreshLayout.setNoMoreData("1" != it.page!!.hasMore)
|
||||||
}
|
}
|
||||||
|
.doIfFailure { refreshLayout.finishRefresh(false) }
|
||||||
override fun onFailure(call: Call<SearchPostBean?>, t: Throwable) {
|
|
||||||
refreshLayout.finishRefresh(false)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadMore() {
|
private fun loadMore() {
|
||||||
|
|
@ -124,7 +288,14 @@ class SearchPostActivity : BaseActivity() {
|
||||||
refreshLayout.finishLoadMore(false)
|
refreshLayout.finishLoadMore(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
getInstance().searchPost(keyword!!, forumName!!, false, page + 1, 30)
|
getInstance().searchPost(
|
||||||
|
keyword!!,
|
||||||
|
forumName!!,
|
||||||
|
onlyThread = onlyThread,
|
||||||
|
sortMode = sortMode,
|
||||||
|
page = page + 1,
|
||||||
|
pageSize = 30
|
||||||
|
)
|
||||||
.enqueue(object : Callback<SearchPostBean> {
|
.enqueue(object : Callback<SearchPostBean> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SearchPostBean>,
|
call: Call<SearchPostBean>,
|
||||||
|
|
@ -143,6 +314,64 @@ class SearchPostActivity : BaseActivity() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
INPUT,
|
||||||
|
SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class SearchHistoryDelegateAdapter(
|
||||||
|
val data: List<SearchPostHistory>? = null
|
||||||
|
) : SingleLayoutAdapter(
|
||||||
|
this,
|
||||||
|
R.layout.layout_search_post_history
|
||||||
|
) {
|
||||||
|
val adapter: SearchPostHistoryAdapter = SearchPostHistoryAdapter(context).apply {
|
||||||
|
setData(data)
|
||||||
|
setOnItemClickListener { _, item, _ ->
|
||||||
|
editText.apply {
|
||||||
|
setText(item.content)
|
||||||
|
clearFocus()
|
||||||
|
KeyboardUtil.hideKeyboard(this)
|
||||||
|
}
|
||||||
|
keyword = item.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val layoutManager: AutoLineFeedLayoutManager = AutoLineFeedLayoutManager()
|
||||||
|
|
||||||
|
override fun initView(view: View) {
|
||||||
|
view.findViewById<RecyclerView>(R.id.recyclerview)
|
||||||
|
.addItemDecoration(SpacesItemDecoration(0, 0, 8.dpToPx(), 8.dpToPx()))
|
||||||
|
view.findViewById<View>(R.id.end_icon).setOnClickListener {
|
||||||
|
LitePal.deleteAllAsync(SearchPostHistory::class.java).listen {
|
||||||
|
toastShort(R.string.toast_delete_success)
|
||||||
|
recyclerView.post {
|
||||||
|
loadHistory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convert(viewHolder: MyViewHolder, itemView: View) {
|
||||||
|
if (data.isNullOrEmpty()) {
|
||||||
|
viewHolder.getView<RecyclerView>(R.id.recyclerview).apply {
|
||||||
|
visibility = View.GONE
|
||||||
|
}
|
||||||
|
viewHolder.getView<View>(R.id.no_data).apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
viewHolder.getView<RecyclerView>(R.id.recyclerview).apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
layoutManager = this@SearchHistoryDelegateAdapter.layoutManager
|
||||||
|
adapter = this@SearchHistoryDelegateAdapter.adapter
|
||||||
|
}
|
||||||
|
viewHolder.getView<View>(R.id.no_data).apply {
|
||||||
|
visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = SearchPostActivity::class.java.simpleName
|
val TAG = SearchPostActivity::class.java.simpleName
|
||||||
const val PARAM_FORUM = "forum_name"
|
const val PARAM_FORUM = "forum_name"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.huanchengfly.tieba.post.adapters
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.huanchengfly.tieba.post.R
|
||||||
|
import com.huanchengfly.tieba.post.adapters.base.BaseSingleTypeAdapter
|
||||||
|
import com.huanchengfly.tieba.post.components.MyViewHolder
|
||||||
|
import com.huanchengfly.tieba.post.models.database.SearchPostHistory
|
||||||
|
|
||||||
|
class SearchPostHistoryAdapter(context: Context) :
|
||||||
|
BaseSingleTypeAdapter<SearchPostHistory>(context) {
|
||||||
|
override fun getItemLayoutId(): Int = R.layout.item_search_history_chip
|
||||||
|
|
||||||
|
override fun convert(viewHolder: MyViewHolder, item: SearchPostHistory, position: Int) {
|
||||||
|
viewHolder.setText(R.id.text, item.content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.huanchengfly.tieba.post.adapters
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.huanchengfly.tieba.post.components.MyViewHolder
|
||||||
|
|
||||||
|
open class SingleLayoutAdapter(
|
||||||
|
val context: Context,
|
||||||
|
val itemView: View
|
||||||
|
) : RecyclerView.Adapter<MyViewHolder>() {
|
||||||
|
constructor(
|
||||||
|
context: Context,
|
||||||
|
@LayoutRes
|
||||||
|
layoutResId: Int
|
||||||
|
) : this(
|
||||||
|
context,
|
||||||
|
View.inflate(context, layoutResId, null)
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
context: Context,
|
||||||
|
createViewFunction: () -> View
|
||||||
|
) : this(
|
||||||
|
context,
|
||||||
|
createViewFunction()
|
||||||
|
)
|
||||||
|
|
||||||
|
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder =
|
||||||
|
MyViewHolder(itemView.apply { initView(itemView) })
|
||||||
|
|
||||||
|
final override fun onBindViewHolder(viewHolder: MyViewHolder, position: Int) {
|
||||||
|
convert(viewHolder, itemView)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun getItemCount(): Int = 1
|
||||||
|
|
||||||
|
open fun initView(view: View) {}
|
||||||
|
|
||||||
|
open fun convert(viewHolder: MyViewHolder, itemView: View) {}
|
||||||
|
}
|
||||||
|
|
@ -654,13 +654,6 @@ class ThreadReplyAdapter(context: Context) :
|
||||||
context.getString(R.string.btn_agree_post_default)
|
context.getString(R.string.btn_agree_post_default)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
viewHolder.setText(
|
|
||||||
R.id.thread_list_item_reply_btn, if (item.subPostNumber != "0") {
|
|
||||||
context.getString(R.string.btn_reply_post, item.subPostNumber)
|
|
||||||
} else {
|
|
||||||
context.getString(R.string.btn_reply_post_default)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
viewHolder.itemView.background = getItemBackgroundDrawable(
|
viewHolder.itemView.background = getItemBackgroundDrawable(
|
||||||
context,
|
context,
|
||||||
position,
|
position,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ abstract class BaseAdapter<Item>(
|
||||||
|
|
||||||
val onItemChildClickListeners: MutableMap<Int, OnItemChildClickListener<Item>?> = mutableMapOf()
|
val onItemChildClickListeners: MutableMap<Int, OnItemChildClickListener<Item>?> = mutableMapOf()
|
||||||
|
|
||||||
val headers: MutableList<View> = mutableListOf()
|
val headers: MutableList<Layout> = mutableListOf()
|
||||||
|
|
||||||
val footers: MutableList<View> = mutableListOf()
|
val footers: MutableList<View> = mutableListOf()
|
||||||
|
|
||||||
|
|
@ -71,15 +71,19 @@ abstract class BaseAdapter<Item>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addHeaderView(view: View) {
|
fun addHeaderView(view: View) {
|
||||||
headers.add(view)
|
headers.add(Layout(view = view))
|
||||||
notifyItemInserted(headers.size - 1)
|
notifyItemInserted(headers.size - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeHeaderView(view: View) {
|
fun addHeaderView(layoutRes: Int, viewInitializer: (View.() -> Unit)?) {
|
||||||
val index = headers.indexOf(view)
|
headers.add(Layout(layoutRes = layoutRes, viewInitializer = viewInitializer))
|
||||||
if (index >= 0) {
|
notifyItemInserted(headers.size - 1)
|
||||||
headers.removeAt(index)
|
}
|
||||||
notifyItemRemoved(index)
|
|
||||||
|
fun removeHeaderViewAt(position: Int) {
|
||||||
|
if (position >= 0) {
|
||||||
|
headers.removeAt(position)
|
||||||
|
notifyItemRemoved(position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,7 +144,16 @@ abstract class BaseAdapter<Item>(
|
||||||
|
|
||||||
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
|
||||||
return if (viewType <= ITEM_TYPE_HEADER) {
|
return if (viewType <= ITEM_TYPE_HEADER) {
|
||||||
MyViewHolder(headers[ITEM_TYPE_HEADER - viewType])
|
val headerLayout = headers[ITEM_TYPE_HEADER - viewType]
|
||||||
|
if (headerLayout.layoutRes != -1) {
|
||||||
|
MyViewHolder(context, headerLayout.layoutRes, parent).apply {
|
||||||
|
headerLayout.viewInitializer?.invoke(itemView)
|
||||||
|
}
|
||||||
|
} else if (headerLayout.view != null) {
|
||||||
|
MyViewHolder(headerLayout.view)
|
||||||
|
} else {
|
||||||
|
throw IllegalArgumentException("Layout is empty!")
|
||||||
|
}
|
||||||
} else if (viewType >= ITEM_TYPE_FOOTER) {
|
} else if (viewType >= ITEM_TYPE_FOOTER) {
|
||||||
MyViewHolder(footers[viewType - ITEM_TYPE_FOOTER])
|
MyViewHolder(footers[viewType - ITEM_TYPE_FOOTER])
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -205,4 +218,10 @@ abstract class BaseAdapter<Item>(
|
||||||
itemList.clear()
|
itemList.clear()
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Layout(
|
||||||
|
val layoutRes: Int = -1,
|
||||||
|
val view: View? = null,
|
||||||
|
val viewInitializer: (View.() -> Unit)? = null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -282,6 +282,7 @@ interface ITiebaApi {
|
||||||
* @param keyword 搜索关键词
|
* @param keyword 搜索关键词
|
||||||
* @param forumName 搜索吧名
|
* @param forumName 搜索吧名
|
||||||
* @param onlyThread 是否仅搜索主题贴
|
* @param onlyThread 是否仅搜索主题贴
|
||||||
|
* @param sortMode 排序模式(1 = 时间倒序,2 = 相关性排序)
|
||||||
* @param page 分页页码(从 1 开始)
|
* @param page 分页页码(从 1 开始)
|
||||||
* @param pageSize 每页贴数(默认 30)
|
* @param pageSize 每页贴数(默认 30)
|
||||||
*/
|
*/
|
||||||
|
|
@ -289,10 +290,30 @@ interface ITiebaApi {
|
||||||
keyword: String,
|
keyword: String,
|
||||||
forumName: String,
|
forumName: String,
|
||||||
onlyThread: Boolean = false,
|
onlyThread: Boolean = false,
|
||||||
|
sortMode: Int = 1,
|
||||||
page: Int = 1,
|
page: Int = 1,
|
||||||
pageSize: Int = 30
|
pageSize: Int = 30
|
||||||
): Call<SearchPostBean>
|
): Call<SearchPostBean>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 吧内搜索(异步)
|
||||||
|
*
|
||||||
|
* @param keyword 搜索关键词
|
||||||
|
* @param forumName 搜索吧名
|
||||||
|
* @param onlyThread 是否仅搜索主题贴
|
||||||
|
* @param sortMode 排序模式(1 = 时间倒序,2 = 相关性排序)
|
||||||
|
* @param page 分页页码(从 1 开始)
|
||||||
|
* @param pageSize 每页贴数(默认 30)
|
||||||
|
*/
|
||||||
|
fun searchPostAsync(
|
||||||
|
keyword: String,
|
||||||
|
forumName: String,
|
||||||
|
onlyThread: Boolean = false,
|
||||||
|
sortMode: Int = 1,
|
||||||
|
page: Int = 1,
|
||||||
|
pageSize: Int = 30
|
||||||
|
): Deferred<ApiResult<SearchPostBean>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 搜索用户
|
* 搜索用户
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -172,13 +172,35 @@ object MixedTiebaApiImpl : ITiebaApi {
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun searchPost(
|
override fun searchPost(
|
||||||
keyword: String, forumName: String, onlyThread: Boolean, page: Int, pageSize: Int
|
keyword: String,
|
||||||
|
forumName: String,
|
||||||
|
onlyThread: Boolean,
|
||||||
|
sortMode: Int,
|
||||||
|
page: Int,
|
||||||
|
pageSize: Int
|
||||||
): Call<SearchPostBean> = RetrofitTiebaApi.MINI_TIEBA_API.searchPost(
|
): Call<SearchPostBean> = RetrofitTiebaApi.MINI_TIEBA_API.searchPost(
|
||||||
keyword,
|
keyword,
|
||||||
forumName,
|
forumName,
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
only_thread = if (onlyThread) 1 else 0
|
only_thread = if (onlyThread) 1 else 0,
|
||||||
|
sortMode = sortMode
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun searchPostAsync(
|
||||||
|
keyword: String,
|
||||||
|
forumName: String,
|
||||||
|
onlyThread: Boolean,
|
||||||
|
sortMode: Int,
|
||||||
|
page: Int,
|
||||||
|
pageSize: Int
|
||||||
|
): Deferred<ApiResult<SearchPostBean>> = RetrofitTiebaApi.MINI_TIEBA_API.searchPostAsync(
|
||||||
|
keyword,
|
||||||
|
forumName,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
only_thread = if (onlyThread) 1 else 0,
|
||||||
|
sortMode = sortMode
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun searchUser(keyword: String): Call<SearchUserBean> =
|
override fun searchUser(keyword: String): Call<SearchUserBean> =
|
||||||
|
|
|
||||||
|
|
@ -277,9 +277,21 @@ interface MiniTiebaApi {
|
||||||
@Field("kw") forumName: String,
|
@Field("kw") forumName: String,
|
||||||
@Field("pn") page: Int = 1,
|
@Field("pn") page: Int = 1,
|
||||||
@Field("rn") pageSize: Int = 30,
|
@Field("rn") pageSize: Int = 30,
|
||||||
@Field("only_thread") only_thread: Int = 0
|
@Field("only_thread") only_thread: Int = 0,
|
||||||
|
@Field("sm") sortMode: Int = 1
|
||||||
): Call<SearchPostBean>
|
): Call<SearchPostBean>
|
||||||
|
|
||||||
|
@POST("/c/s/searchpost")
|
||||||
|
@FormUrlEncoded
|
||||||
|
fun searchPostAsync(
|
||||||
|
@Field("word") keyword: String,
|
||||||
|
@Field("kw") forumName: String,
|
||||||
|
@Field("pn") page: Int = 1,
|
||||||
|
@Field("rn") pageSize: Int = 30,
|
||||||
|
@Field("only_thread") only_thread: Int = 0,
|
||||||
|
@Field("sm") sortMode: Int = 1
|
||||||
|
): Deferred<ApiResult<SearchPostBean>>
|
||||||
|
|
||||||
@GET("/mo/q/search/user")
|
@GET("/mo/q/search/user")
|
||||||
fun searchUser(
|
fun searchUser(
|
||||||
@Query("word") keyword: String,
|
@Query("word") keyword: String,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.huanchengfly.tieba.post.models.database;
|
||||||
|
|
||||||
|
import org.litepal.crud.LitePalSupport;
|
||||||
|
|
||||||
|
public class SearchPostHistory extends LitePalSupport {
|
||||||
|
private int id;
|
||||||
|
private long timestamp;
|
||||||
|
private String content;
|
||||||
|
private String forumName;
|
||||||
|
|
||||||
|
public SearchPostHistory(String content, String forumName) {
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
this.content = content;
|
||||||
|
this.forumName = forumName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(long timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getForumName() {
|
||||||
|
return forumName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setForumName(String forumName) {
|
||||||
|
this.forumName = forumName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout 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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.huanchengfly.tieba.post.widgets.theme.TintLinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_radius_16dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:backgroundTint="@color/default_color_card">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/search_post_header_sort_mode"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.huanchengfly.tieba.post.widgets.theme.TintTextView
|
||||||
|
android:id="@+id/search_post_header_sort_mode_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:tint="@color/default_color_text"
|
||||||
|
tools:text="时间倒序" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/search_post_header_sort_mode_arrow"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:rotation="180"
|
||||||
|
app:srcCompat="@drawable/ic_round_arrow_drop_up" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.huanchengfly.tieba.post.widgets.theme.TintTextView
|
||||||
|
android:id="@+id/search_post_header_btn_all"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:text="@string/title_all_reply"
|
||||||
|
app:tint="@color/default_color_text"
|
||||||
|
tools:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.huanchengfly.tieba.post.widgets.theme.TintView
|
||||||
|
android:layout_width="0.75dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
app:backgroundTint="@color/default_color_text_disabled" />
|
||||||
|
|
||||||
|
<com.huanchengfly.tieba.post.widgets.theme.TintTextView
|
||||||
|
android:id="@+id/search_post_header_btn_only_thread"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:text="@string/title_search_filter_only_thread"
|
||||||
|
app:tint="@color/default_color_text" />
|
||||||
|
</com.huanchengfly.tieba.post.widgets.theme.TintLinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.huanchengfly.tieba.post.widgets.theme.TintLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_radius_8dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
|
app:backgroundTint="@color/default_color_card">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/header_root_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp">
|
||||||
|
|
||||||
|
<com.huanchengfly.tieba.post.widgets.theme.TintImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
app:srcCompat="@drawable/ic_round_watch_later_blue"
|
||||||
|
app:tint="@color/default_color_primary" />
|
||||||
|
|
||||||
|
<com.huanchengfly.tieba.post.widgets.theme.TintTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_toStartOf="@id/end_icon"
|
||||||
|
android:layout_toEndOf="@id/icon"
|
||||||
|
android:text="@string/title_search_history"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:tint="@color/default_color_primary" />
|
||||||
|
|
||||||
|
<com.huanchengfly.tieba.post.widgets.theme.TintImageView
|
||||||
|
android:id="@+id/end_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
app:srcCompat="@drawable/ic_round_delete"
|
||||||
|
app:tint="@color/default_color_primary" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@id/recyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@id/no_data"
|
||||||
|
layout="@layout/layout_no_data" />
|
||||||
|
</com.huanchengfly.tieba.post.widgets.theme.TintLinearLayout>
|
||||||
|
|
@ -378,7 +378,7 @@
|
||||||
<string name="text_no_data">这里什么都没有</string>
|
<string name="text_no_data">这里什么都没有</string>
|
||||||
<string name="title_exact_match">推荐结果</string>
|
<string name="title_exact_match">推荐结果</string>
|
||||||
<string name="title_fuzzy_match">查询结果</string>
|
<string name="title_fuzzy_match">查询结果</string>
|
||||||
<string name="title_search_filter_only_thread">仅看主题贴</string>
|
<string name="title_search_filter_only_thread">只看主题贴</string>
|
||||||
<string name="title_search_order_new">新贴在前</string>
|
<string name="title_search_order_new">新贴在前</string>
|
||||||
<string name="title_search_order_old">旧贴在前</string>
|
<string name="title_search_order_old">旧贴在前</string>
|
||||||
<string name="title_search_order_relevant">相关度</string>
|
<string name="title_search_order_relevant">相关度</string>
|
||||||
|
|
@ -523,4 +523,6 @@
|
||||||
<string name="title_hide_forum_intro_and_stat">精简吧页面</string>
|
<string name="title_hide_forum_intro_and_stat">精简吧页面</string>
|
||||||
<string name="summary_hide_forum_intro_and_stat">开启后将隐藏吧页面的吧简介和数据(仍然可以通过“关于本吧”查看这些内容)</string>
|
<string name="summary_hide_forum_intro_and_stat">开启后将隐藏吧页面的吧简介和数据(仍然可以通过“关于本吧”查看这些内容)</string>
|
||||||
<string name="relative_date_today">今天 %1$s</string>
|
<string name="relative_date_today">今天 %1$s</string>
|
||||||
|
<string name="title_search_post_sort_by_time">时间倒序</string>
|
||||||
|
<string name="title_search_post_sort_by_relevant">相关性排序</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,23 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<data-extraction-rules>
|
<data-extraction-rules>
|
||||||
<cloud-backup></cloud-backup>
|
<cloud-backup>
|
||||||
|
<exclude
|
||||||
|
domain="sharedpref"
|
||||||
|
path="crash.xml" />
|
||||||
|
<exclude
|
||||||
|
domain="sharedpref"
|
||||||
|
path="AppCenter.xml" />
|
||||||
|
<exclude
|
||||||
|
domain="database"
|
||||||
|
path="com.microsoft.appcenter.persistence" />
|
||||||
|
<exclude
|
||||||
|
domain="database"
|
||||||
|
path="com.microsoft.appcenter.persistence-journal" />
|
||||||
|
<exclude
|
||||||
|
domain="file"
|
||||||
|
path="error" />
|
||||||
|
<exclude
|
||||||
|
domain="file"
|
||||||
|
path="appcenter" />
|
||||||
|
</cloud-backup>
|
||||||
</data-extraction-rules>
|
</data-extraction-rules>
|
||||||
Loading…
Reference in New Issue