parent
c34a46e529
commit
aeaa853ece
@ -0,0 +1,12 @@ |
||||
package xyz.fycz.myreader.entity |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2021/12/5 20:18 |
||||
*/ |
||||
data class SearchWord1( |
||||
var bookId: String, |
||||
var chapterNum: Int, |
||||
var chapterTitle: String, |
||||
var searchWord2List: MutableList<SearchWord2> |
||||
) |
@ -0,0 +1,14 @@ |
||||
package xyz.fycz.myreader.entity |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2021/12/5 20:03 |
||||
*/ |
||||
data class SearchWord2( |
||||
var keyword: String, |
||||
var chapterNum: Int, |
||||
var dataStr: String, |
||||
var dataIndex: Int, |
||||
var index: Int, |
||||
var count: Int |
||||
) |
@ -0,0 +1,195 @@ |
||||
package xyz.fycz.myreader.model |
||||
|
||||
import io.reactivex.* |
||||
import io.reactivex.android.schedulers.AndroidSchedulers |
||||
import io.reactivex.disposables.CompositeDisposable |
||||
import io.reactivex.disposables.Disposable |
||||
import io.reactivex.schedulers.Schedulers |
||||
import xyz.fycz.myreader.R |
||||
import xyz.fycz.myreader.application.App |
||||
import xyz.fycz.myreader.entity.SearchWord1 |
||||
import xyz.fycz.myreader.entity.SearchWord2 |
||||
import xyz.fycz.myreader.greendao.entity.Book |
||||
import xyz.fycz.myreader.greendao.entity.Chapter |
||||
import xyz.fycz.myreader.greendao.service.ChapterService |
||||
import xyz.fycz.myreader.util.SharedPreUtils |
||||
import xyz.fycz.myreader.util.ToastUtils |
||||
import xyz.fycz.myreader.util.help.ChapterContentHelp |
||||
import xyz.fycz.myreader.widget.page.PageLoader |
||||
import java.io.File |
||||
import java.util.concurrent.ExecutorService |
||||
import java.util.concurrent.Executors |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2021/12/5 21:17 |
||||
*/ |
||||
class SearchWordEngine( |
||||
private val book: Book, |
||||
private val chapters: List<Chapter>, |
||||
private val pageLoader: PageLoader |
||||
) { |
||||
private val TAG = "SearchWordEngine" |
||||
|
||||
//线程池 |
||||
private var executorService: ExecutorService |
||||
|
||||
private var scheduler: Scheduler |
||||
private var compositeDisposable: CompositeDisposable |
||||
private lateinit var searchListener: OnSearchListener |
||||
private val threadsNum = |
||||
SharedPreUtils.getInstance().getInt(App.getmContext().getString(R.string.threadNum), 8); |
||||
private var searchSiteIndex = 0 |
||||
private var searchSuccessNum = 0 |
||||
private var searchFinishNum = 0 |
||||
private var isLocalBook = false |
||||
|
||||
fun setOnSearchListener(searchListener: OnSearchListener) { |
||||
this.searchListener = searchListener |
||||
} |
||||
|
||||
init { |
||||
executorService = Executors.newFixedThreadPool(threadsNum) |
||||
scheduler = Schedulers.from(executorService) |
||||
compositeDisposable = CompositeDisposable() |
||||
} |
||||
|
||||
fun stopSearch() { |
||||
compositeDisposable.dispose() |
||||
compositeDisposable = CompositeDisposable() |
||||
searchListener.loadFinish(searchSuccessNum == 0) |
||||
} |
||||
|
||||
/** |
||||
* 关闭引擎 |
||||
*/ |
||||
fun closeSearchEngine() { |
||||
executorService.shutdown() |
||||
if (!compositeDisposable.isDisposed) compositeDisposable.dispose() |
||||
} |
||||
|
||||
/** |
||||
* 搜索关键字(模糊搜索) |
||||
* |
||||
* @param keyword |
||||
*/ |
||||
fun search(keyword: String) { |
||||
if ("本地书籍" == book.type) { |
||||
isLocalBook = true |
||||
if (!File(book.chapterUrl).exists()) { |
||||
ToastUtils.showWarring("当前书籍源文件不存在,无法搜索!") |
||||
searchListener.loadFinish(true) |
||||
return |
||||
} |
||||
} |
||||
if (chapters.isEmpty()) { |
||||
ToastUtils.showWarring("当前书籍章节目录为空,无法搜索!") |
||||
searchListener.loadFinish(true) |
||||
return |
||||
} |
||||
searchSuccessNum = 0 |
||||
searchSiteIndex = -1 |
||||
searchFinishNum = 0 |
||||
for (i in 0 until Math.min(threadsNum, chapters.size)) { |
||||
searchOnEngine(keyword) |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
private fun searchOnEngine(keyword: String) { |
||||
searchSiteIndex++ |
||||
if (searchSiteIndex < chapters.size) { |
||||
val chapterNum = searchSiteIndex |
||||
val chapter = chapters[chapterNum] |
||||
Observable.create(ObservableOnSubscribe<SearchWord1> { emitter -> |
||||
val searchWord1 = |
||||
SearchWord1(book.id, chapterNum, chapter.title, mutableListOf()) |
||||
if (!isLocalBook && !ChapterService.isChapterCached(book.id, chapter.title)) { |
||||
emitter.onNext(searchWord1) |
||||
return@ObservableOnSubscribe |
||||
} |
||||
var content = pageLoader.getChapterReader(chapter) |
||||
content = pageLoader.contentHelper.replaceContent( |
||||
book.name + "-" + book.author, |
||||
book.source, |
||||
content, |
||||
true |
||||
) |
||||
if (book.reSeg) { |
||||
content = ChapterContentHelp.LightNovelParagraph2(content, chapter.title) |
||||
} |
||||
val allLine: List<String> = content.split("\n") |
||||
var count = 0 |
||||
allLine.forEach { |
||||
var index: Int = -1 |
||||
while (it.indexOf(keyword, index + 1).also { index = it } != -1) { |
||||
var leftI = 0 |
||||
var rightI = it.length |
||||
var leftS = "" |
||||
var rightS = "" |
||||
if (leftI < index - 20) { |
||||
leftI = index - 20 |
||||
leftS = "..." |
||||
} |
||||
if (rightI > index + 20) { |
||||
rightI = index + 20 |
||||
rightS = "..." |
||||
} |
||||
val str = leftS + it.substring(leftI, rightI) + rightS |
||||
val searchWord2 = |
||||
SearchWord2( |
||||
keyword, |
||||
chapterNum, |
||||
str, |
||||
index - leftI + leftS.length, |
||||
index, |
||||
count |
||||
) |
||||
searchWord1.searchWord2List.add(searchWord2) |
||||
count++ |
||||
} |
||||
} |
||||
emitter.onNext(searchWord1) |
||||
emitter.onComplete() |
||||
}).subscribeOn(scheduler).observeOn(AndroidSchedulers.mainThread()) |
||||
.subscribe(object : Observer<SearchWord1?> { |
||||
override fun onSubscribe(d: Disposable) { |
||||
compositeDisposable.add(d) |
||||
} |
||||
|
||||
override fun onNext(searchWord1: SearchWord1) { |
||||
searchFinishNum++ |
||||
if (searchWord1.searchWord2List.isNotEmpty()) { |
||||
searchSuccessNum++ |
||||
searchListener.loadMore(searchWord1) |
||||
} |
||||
searchOnEngine(keyword) |
||||
} |
||||
|
||||
override fun onError(e: Throwable) { |
||||
searchFinishNum++ |
||||
searchOnEngine(keyword) |
||||
if (App.isDebug()) e.printStackTrace() |
||||
} |
||||
|
||||
override fun onComplete() { |
||||
} |
||||
|
||||
}) |
||||
} else { |
||||
if (searchFinishNum == chapters.size) { |
||||
if (searchSuccessNum == 0) { |
||||
ToastUtils.showWarring("搜索结果为空") |
||||
searchListener.loadFinish(true) |
||||
} else { |
||||
searchListener.loadFinish(false) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
interface OnSearchListener { |
||||
fun loadFinish(isEmpty: Boolean) |
||||
fun loadMore(item: SearchWord1) |
||||
} |
||||
} |
@ -0,0 +1,155 @@ |
||||
package xyz.fycz.myreader.ui.activity |
||||
|
||||
import android.os.Bundle |
||||
import android.view.KeyEvent |
||||
import android.view.View |
||||
import android.view.inputmethod.EditorInfo |
||||
import android.view.inputmethod.InputMethodManager |
||||
import androidx.appcompat.widget.Toolbar |
||||
import androidx.recyclerview.widget.LinearLayoutManager |
||||
import org.jetbrains.anko.contentView |
||||
import xyz.fycz.myreader.R |
||||
import xyz.fycz.myreader.application.App |
||||
import xyz.fycz.myreader.base.BaseActivity |
||||
import xyz.fycz.myreader.base.BitIntentDataManager |
||||
import xyz.fycz.myreader.base.adapter.BaseListAdapter |
||||
import xyz.fycz.myreader.base.adapter.IViewHolder |
||||
import xyz.fycz.myreader.base.adapter2.onClick |
||||
import xyz.fycz.myreader.common.APPCONST |
||||
import xyz.fycz.myreader.databinding.ActivitySearchWordBinding |
||||
import xyz.fycz.myreader.entity.SearchWord1 |
||||
import xyz.fycz.myreader.greendao.entity.Book |
||||
import xyz.fycz.myreader.greendao.entity.Chapter |
||||
import xyz.fycz.myreader.model.SearchWordEngine |
||||
import xyz.fycz.myreader.ui.adapter.holder.SearchWord1Holder |
||||
import xyz.fycz.myreader.ui.adapter.holder.SearchWord2Holder |
||||
import xyz.fycz.myreader.util.ToastUtils |
||||
import xyz.fycz.myreader.widget.page.PageLoader |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2021/12/5 19:57 |
||||
*/ |
||||
class SearchWordActivity : BaseActivity() { |
||||
|
||||
private lateinit var binding: ActivitySearchWordBinding |
||||
private lateinit var book: Book |
||||
private lateinit var searchWordEngine: SearchWordEngine |
||||
private lateinit var adapter: BaseListAdapter<SearchWord1> |
||||
|
||||
override fun bindView() { |
||||
binding = ActivitySearchWordBinding.inflate(layoutInflater) |
||||
setContentView(binding.root) |
||||
} |
||||
|
||||
override fun setUpToolbar(toolbar: Toolbar?) { |
||||
super.setUpToolbar(toolbar) |
||||
setStatusBarColor(R.color.colorPrimary, true) |
||||
supportActionBar?.title = getString(R.string.search_word) |
||||
} |
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) { |
||||
super.onWindowFocusChanged(hasFocus) |
||||
if (hasFocus) { |
||||
App.getHandler().postDelayed({ |
||||
binding.etSearchKey.requestFocus() |
||||
val imm = |
||||
getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager |
||||
imm.showSoftInput( |
||||
binding.etSearchKey, |
||||
InputMethodManager.SHOW_IMPLICIT |
||||
) |
||||
}, 400) |
||||
} |
||||
} |
||||
|
||||
override fun initData(savedInstanceState: Bundle?) { |
||||
super.initData(savedInstanceState) |
||||
book = BitIntentDataManager.getInstance().getData(APPCONST.BOOK_KEY) as Book |
||||
val chapters = |
||||
BitIntentDataManager.getInstance().getData(APPCONST.CHAPTERS_KEY) as List<Chapter> |
||||
val pageLoader = |
||||
BitIntentDataManager.getInstance().getData(APPCONST.PAGE_LOADER_KEY) as PageLoader |
||||
searchWordEngine = SearchWordEngine(book, chapters, pageLoader) |
||||
} |
||||
|
||||
override fun initWidget() { |
||||
//enter事件 |
||||
binding.etSearchKey.setOnEditorActionListener { _, i, keyEvent -> |
||||
if (i == EditorInfo.IME_ACTION_UNSPECIFIED) { |
||||
search() |
||||
return@setOnEditorActionListener keyEvent.keyCode == KeyEvent.KEYCODE_ENTER |
||||
} |
||||
false |
||||
} |
||||
searchWordEngine.setOnSearchListener(object : SearchWordEngine.OnSearchListener { |
||||
override fun loadFinish(isEmpty: Boolean) { |
||||
binding.fabSearchStop.visibility = View.GONE |
||||
binding.rpb.isAutoLoading = false |
||||
} |
||||
|
||||
@Synchronized |
||||
override fun loadMore(item: SearchWord1) { |
||||
if (adapter.itemSize == 0) { |
||||
adapter.addItem(item) |
||||
} else { |
||||
for ((index, searchWord1) in adapter.items.withIndex()) { |
||||
if (index == 0 && item.chapterNum < searchWord1.chapterNum) { |
||||
adapter.addItem(0, item) |
||||
break |
||||
} else if (item.chapterNum >= searchWord1.chapterNum && |
||||
item.chapterNum < adapter.items[index + 1].chapterNum |
||||
) { |
||||
adapter.addItem(index + 1, item) |
||||
break |
||||
} else if (index == adapter.itemSize - 1) { |
||||
adapter.addItem(item) |
||||
break |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
}) |
||||
adapter = object : BaseListAdapter<SearchWord1>() { |
||||
override fun createViewHolder(viewType: Int): IViewHolder<SearchWord1> { |
||||
return SearchWord1Holder(this@SearchWordActivity) |
||||
} |
||||
} |
||||
binding.rvSearchWord1.layoutManager = LinearLayoutManager(this) |
||||
binding.rvSearchWord1.adapter = adapter |
||||
} |
||||
|
||||
override fun initClick() { |
||||
binding.tvSearchConform.onClick { search() } |
||||
binding.fabSearchStop.onClick { stopSearch() } |
||||
} |
||||
|
||||
private fun search() { |
||||
val keyword = binding.etSearchKey.text.toString() |
||||
if (keyword.isNotEmpty()) { |
||||
adapter.clear() |
||||
binding.fabSearchStop.visibility = View.VISIBLE |
||||
binding.rpb.isAutoLoading = true |
||||
searchWordEngine.search(keyword) |
||||
//收起软键盘 |
||||
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager |
||||
imm.hideSoftInputFromWindow( |
||||
binding.etSearchKey.windowToken, |
||||
InputMethodManager.HIDE_NOT_ALWAYS |
||||
) |
||||
} |
||||
} |
||||
|
||||
private fun stopSearch() { |
||||
binding.fabSearchStop.visibility = View.GONE |
||||
binding.rpb.isAutoLoading = false |
||||
searchWordEngine.stopSearch() |
||||
} |
||||
|
||||
override fun onDestroy() { |
||||
super.onDestroy() |
||||
searchWordEngine.stopSearch() |
||||
searchWordEngine.closeSearchEngine() |
||||
} |
||||
} |
@ -0,0 +1,56 @@ |
||||
package xyz.fycz.myreader.ui.adapter.holder |
||||
|
||||
import android.app.Activity |
||||
import android.content.Intent |
||||
import android.view.View |
||||
import android.widget.TextView |
||||
import androidx.appcompat.app.AppCompatActivity |
||||
import androidx.recyclerview.widget.LinearLayoutManager |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import xyz.fycz.myreader.R |
||||
import xyz.fycz.myreader.base.adapter.BaseListAdapter |
||||
import xyz.fycz.myreader.base.adapter.IViewHolder |
||||
import xyz.fycz.myreader.base.adapter.ViewHolderImpl |
||||
import xyz.fycz.myreader.entity.SearchWord1 |
||||
import xyz.fycz.myreader.entity.SearchWord2 |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2021/12/5 20:28 |
||||
*/ |
||||
class SearchWord1Holder(var activity: AppCompatActivity) : ViewHolderImpl<SearchWord1>() { |
||||
|
||||
private lateinit var tvChapterTitle: TextView |
||||
private lateinit var rvWordList: RecyclerView |
||||
private lateinit var adapter: BaseListAdapter<SearchWord2> |
||||
|
||||
override fun getItemLayoutId(): Int { |
||||
return R.layout.item_search_word1 |
||||
} |
||||
|
||||
override fun initView() { |
||||
tvChapterTitle = findById(R.id.tv_chapter_title) |
||||
rvWordList = findById(R.id.rv_search_word2) |
||||
} |
||||
|
||||
override fun onBind(holder: RecyclerView.ViewHolder?, data: SearchWord1?, pos: Int) { |
||||
tvChapterTitle.text = data?.chapterTitle |
||||
adapter = object : BaseListAdapter<SearchWord2>() { |
||||
override fun createViewHolder(viewType: Int): IViewHolder<SearchWord2> { |
||||
return SearchWord2Holder() |
||||
} |
||||
} |
||||
rvWordList.layoutManager = LinearLayoutManager(context) |
||||
rvWordList.adapter = adapter |
||||
adapter.refreshItems(data?.searchWord2List) |
||||
adapter.setOnItemClickListener { _, pos1 -> |
||||
val searchWord2 = adapter.getItem(pos1) |
||||
val intent = Intent() |
||||
intent.putExtra("chapterNum", searchWord2.chapterNum) |
||||
intent.putExtra("countInChapter", searchWord2.count) |
||||
intent.putExtra("keyword", searchWord2.keyword) |
||||
activity.setResult(Activity.RESULT_OK, intent) |
||||
activity.finish() |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,37 @@ |
||||
package xyz.fycz.myreader.ui.adapter.holder |
||||
|
||||
import android.graphics.Color |
||||
import android.text.SpannableString |
||||
import android.text.Spanned |
||||
import android.text.style.ForegroundColorSpan |
||||
import android.widget.TextView |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import xyz.fycz.myreader.R |
||||
import xyz.fycz.myreader.base.adapter.ViewHolderImpl |
||||
import xyz.fycz.myreader.entity.SearchWord2 |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2021/12/5 20:46 |
||||
*/ |
||||
class SearchWord2Holder : ViewHolderImpl<SearchWord2>() { |
||||
|
||||
private lateinit var tvSearchWord: TextView |
||||
|
||||
override fun getItemLayoutId(): Int { |
||||
return R.layout.item_search_word2 |
||||
} |
||||
|
||||
override fun initView() { |
||||
tvSearchWord = findById(R.id.tv_search_word) |
||||
} |
||||
|
||||
override fun onBind(holder: RecyclerView.ViewHolder, data: SearchWord2, pos: Int) { |
||||
val spannableString = SpannableString(data.dataStr) |
||||
spannableString.setSpan( |
||||
ForegroundColorSpan(Color.RED), |
||||
data.dataIndex, data.dataIndex + data.keyword.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE |
||||
) |
||||
tvSearchWord.text = spannableString |
||||
} |
||||
} |
@ -0,0 +1,12 @@ |
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:width="24dp" |
||||
android:height="24dp" |
||||
android:viewportWidth="1024" |
||||
android:viewportHeight="1024"> |
||||
<path |
||||
android:pathData="M602.3,603.46c9.09,-7.68 33.73,4.93 55.04,28.1l270.14,293.12c21.31,23.17 23.04,54.91 3.97,70.98 -19.2,16 -50.11,8.77 -69.25,-16.19L620.48,662.4c-19.07,-24.96 -27.2,-51.46 -18.18,-59.01z" |
||||
android:fillColor="#FFFFFF"/> |
||||
<path |
||||
android:pathData="M560,633.47a288,288 0,1 0,-288 -498.82,288 288,0 0,0 288,498.82zM592,688.9a352,352 0,1 1,-352 -609.66,352 352,0 0,1 352,609.66z" |
||||
android:fillColor="#FFFFFF"/> |
||||
</vector> |
@ -0,0 +1,77 @@ |
||||
<?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="match_parent" |
||||
android:orientation="vertical"> |
||||
|
||||
<include layout="@layout/toolbar" /> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="60dp" |
||||
android:gravity="center" |
||||
android:orientation="horizontal" |
||||
android:paddingLeft="15dp" |
||||
android:paddingTop="10dp" |
||||
android:paddingRight="15dp" |
||||
android:paddingBottom="10dp"> |
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText |
||||
android:id="@+id/et_search_key" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="35dp" |
||||
android:layout_weight="2" |
||||
android:background="@drawable/search_et_backcolor" |
||||
android:hint="@string/search_word_tip" |
||||
android:imeOptions="actionSearch" |
||||
android:paddingStart="10dp" |
||||
android:textColor="@color/textPrimary" /> |
||||
|
||||
|
||||
<TextView |
||||
android:id="@+id/tv_search_conform" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="35dp" |
||||
android:layout_weight="8" |
||||
android:background="@drawable/search_btn_backcolor" |
||||
android:gravity="center" |
||||
android:text="@string/common_search" |
||||
android:textColor="@color/textPrimaryInverted" |
||||
android:textSize="18sp" /> |
||||
</LinearLayout> |
||||
|
||||
<xyz.fycz.myreader.widget.RefreshProgressBar |
||||
android:id="@+id/rpb" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="3dp" |
||||
android:visibility="visible" /> |
||||
|
||||
<RelativeLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
|
||||
<xyz.fycz.myreader.widget.scroller.FastScrollRecyclerView |
||||
android:id="@+id/rv_search_word1" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:paddingHorizontal="10dp" |
||||
tools:ignore="SpeakableTextPresentCheck" /> |
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton |
||||
android:id="@+id/fabSearchStop" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_alignParentEnd="true" |
||||
android:layout_alignParentBottom="true" |
||||
android:layout_marginEnd="30dp" |
||||
android:layout_marginBottom="30dp" |
||||
android:src="@drawable/ic_stop_black_24dp" |
||||
android:visibility="gone" |
||||
app:elevation="5dp" |
||||
app:fabSize="mini" |
||||
app:layout_anchorGravity="right|bottom" |
||||
android:contentDescription="@string/stop" /> |
||||
</RelativeLayout> |
||||
</LinearLayout> |
@ -0,0 +1,21 @@ |
||||
<?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" |
||||
android:paddingBottom="10dp" |
||||
android:orientation="vertical"> |
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView |
||||
android:id="@+id/tv_chapter_title" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:paddingVertical="5dp" |
||||
android:text="@string/app_name" |
||||
android:textColor="@color/textSecondary" |
||||
android:textSize="@dimen/text_normal_size" /> |
||||
|
||||
<androidx.recyclerview.widget.RecyclerView |
||||
android:id="@+id/rv_search_word2" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" /> |
||||
</LinearLayout> |
@ -0,0 +1,20 @@ |
||||
<?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" |
||||
android:orientation="vertical"> |
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView |
||||
android:id="@+id/tv_search_word" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:textColor="@color/textPrimary" |
||||
android:paddingVertical="10dp" |
||||
android:textSize="@dimen/text_normal_size" |
||||
android:text="@string/app_name"/> |
||||
|
||||
<View |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="0.5dp" |
||||
android:background="@color/sys_window_back" /> |
||||
</LinearLayout> |
@ -1,3 +1,3 @@ |
||||
#Fri Jun 18 21:45:31 CST 2021 |
||||
VERSION_CODE=223 |
||||
NEED_CREATE_RELEASE=true |
||||
VERSION_CODE=224 |
||||
NEED_CREATE_RELEASE=false |
||||
|
Loading…
Reference in new issue